| 1 |
// Written in the D programming language 1.0 |
|---|
| 2 |
/** |
|---|
| 3 |
* BLADE -- Basic Linear Algebra D Expressions |
|---|
| 4 |
* |
|---|
| 5 |
* Given an expression string of the form "A+(B*C)*D", and a ranklist string |
|---|
| 6 |
* the entries of which correspond to A, B, C, ..., |
|---|
| 7 |
* values being'0'=scalar, '1'=vector, '2'=matrix. |
|---|
| 8 |
* This string is converted to postfix, applying simple X86-specific optimisations. |
|---|
| 9 |
* |
|---|
| 10 |
* The elements of the postfix string may be: |
|---|
| 11 |
* ABC a variable or constant, to be pushed onto the stack |
|---|
| 12 |
* 0 the literal zero (used to initialize a dot product, for example) |
|---|
| 13 |
* *+-/ ST(0)*ST(1), ST(0)+ST(1), ST(0)-ST(1), ST(0)/ST(1) and pop stack |
|---|
| 14 |
* _ ST(1)-ST(0) and pop stack |
|---|
| 15 |
* = store ST(0) and pop stack |
|---|
| 16 |
* , duplicate stack top (so ,* means ST=ST*ST, ,+ means ST*=2) |
|---|
| 17 |
* a abs |
|---|
| 18 |
* n unary negation |
|---|
| 19 |
* m ST(0) = max(ST(0), ST(1)) |
|---|
| 20 |
* u ST(1) = min(ST(0), ST(1)) |
|---|
| 21 |
* q sqrt |
|---|
| 22 |
* 1 the literal one (used to initialize a product, for example) |
|---|
| 23 |
* |
|---|
| 24 |
* NOT YET IMPLEMENTED: |
|---|
| 25 |
* scel sin, cos, exp, log |
|---|
| 26 |
* |
|---|
| 27 |
* Unassigned lower case letters: bdfghijkoprtvwxyz |
|---|
| 28 |
*/ |
|---|
| 29 |
|
|---|
| 30 |
module blade.PostfixX86; |
|---|
| 31 |
private import blade.BladeVisitor; |
|---|
| 32 |
|
|---|
| 33 |
public: |
|---|
| 34 |
/// Converts an infix string into postfix. |
|---|
| 35 |
/// Apply x87-specific optimisations during the conversion. |
|---|
| 36 |
char [] makePostfixForX87(char [] operations, char [][] typelist, char[] ranklist) |
|---|
| 37 |
{ |
|---|
| 38 |
return beginVisit(X87PostfixVisitor(typelist, ranklist), operations); |
|---|
| 39 |
} |
|---|
| 40 |
|
|---|
| 41 |
/// Converts an infix string into postfix. |
|---|
| 42 |
/// Apply SSE/SSE2-specific optimisations during the conversion. |
|---|
| 43 |
char [] makePostfixForSSE(char [] operations, char [] ranklist) |
|---|
| 44 |
{ |
|---|
| 45 |
return beginVisit(SSEPostfixVisitor(ranklist), operations); |
|---|
| 46 |
} |
|---|
| 47 |
|
|---|
| 48 |
private: |
|---|
| 49 |
// ------------------------------------------------ |
|---|
| 50 |
// Convert infix string to postfix |
|---|
| 51 |
// ------------------------------------------------ |
|---|
| 52 |
|
|---|
| 53 |
struct X87PostfixVisitor { |
|---|
| 54 |
alias typeof(*this) This; |
|---|
| 55 |
alias char [] ReturnType; |
|---|
| 56 |
char [][] typelist; |
|---|
| 57 |
char [] ranklist; |
|---|
| 58 |
static: |
|---|
| 59 |
ReturnType onVisitSymbol(This this_, char [] sym) { |
|---|
| 60 |
return sym; |
|---|
| 61 |
} |
|---|
| 62 |
ReturnType onVisitFunction(This this_, char [] func, char [][] args) { |
|---|
| 63 |
char [] left = doVisit(this_,args[0]); |
|---|
| 64 |
switch(func) { |
|---|
| 65 |
case "dot": |
|---|
| 66 |
char [] right = doVisit(this_, args[1]); |
|---|
| 67 |
if (left==right) return "0" ~ left ~ ",*+"; |
|---|
| 68 |
return "0" ~ left ~ right ~ "*+"; |
|---|
| 69 |
case "prod": |
|---|
| 70 |
return "1" ~ left ~ "*"; |
|---|
| 71 |
case "sum": return "0" ~ left ~ "+"; |
|---|
| 72 |
// case "max": return left ~ "m"; |
|---|
| 73 |
// case "min": return left ~ "u"; |
|---|
| 74 |
case "abs": return left ~ "a"; |
|---|
| 75 |
case "sqrt": return left ~ "q"; |
|---|
| 76 |
default: assert(0, "BLADE ICE: Unsupported"); |
|---|
| 77 |
} |
|---|
| 78 |
} |
|---|
| 79 |
ReturnType onVisitPrefix(This this_, char [] op, char [] expr) { |
|---|
| 80 |
if (op=="-") return doVisit(this_, expr) ~ "n"; // unary minus |
|---|
| 81 |
assert(0, "BLADE ICE: Unsupported"); |
|---|
| 82 |
} |
|---|
| 83 |
ReturnType onVisitPostfix(This this_, char [] op, char [] expr) { |
|---|
| 84 |
assert(0, "BLADE ICE: Unsupported"); |
|---|
| 85 |
} |
|---|
| 86 |
ReturnType onVisitIndex(This this_, char [] base, char [][2][] slices) { |
|---|
| 87 |
assert(0, "BLADE ICE: Unsupported"); |
|---|
| 88 |
} |
|---|
| 89 |
ReturnType onVisitBinaryOp(This this_, char [] op, char [] left, char [] right) { |
|---|
| 90 |
char [] first = doVisit(this_, left); |
|---|
| 91 |
char [] second; |
|---|
| 92 |
if (op.length==2 && op[1]=='=') { // +=, -=, *=, /= |
|---|
| 93 |
// Convert "A+=B" into "A=A+B" |
|---|
| 94 |
second = beginVisit(this_, left ~ op[0] ~ right); |
|---|
| 95 |
return second ~ first ~ "="; |
|---|
| 96 |
} |
|---|
| 97 |
second = doVisit(this_, right); |
|---|
| 98 |
if (op=="=") { |
|---|
| 99 |
return second ~ first ~ "="; |
|---|
| 100 |
} |
|---|
| 101 |
if (second == first) return first ~ "," ~ op; |
|---|
| 102 |
|
|---|
| 103 |
// x87 OPTIMISATION #1 |
|---|
| 104 |
// On x87, fmul has a long latency, so we want to delay using the |
|---|
| 105 |
// result of a multiply. Since + is commutative, we can achieve this |
|---|
| 106 |
// by calculating the value with the multiply, before the other one. |
|---|
| 107 |
// We can also do the same thing with -, but we'll need to use fsubr |
|---|
| 108 |
// instead of fsub. We use _ to mean reversed subtraction. |
|---|
| 109 |
if (op=="+" || op=="-") { |
|---|
| 110 |
char [] oprvs = (op=="-")? "_" : op; |
|---|
| 111 |
if (second[second.length-1]=='*'&& first[first.length-1]!='*') { |
|---|
| 112 |
return second ~ first ~ oprvs; |
|---|
| 113 |
} |
|---|
| 114 |
// x87 OPTIMISATION #2 |
|---|
| 115 |
// When an operation is performed between a real[] and a non-real[], |
|---|
| 116 |
// we want to have the real[] being the one which is loaded first. |
|---|
| 117 |
if (second.length==1 && this_.typelist[second[0]-'A']=="real" && this_.ranklist[second[0]-'A']=='1') { |
|---|
| 118 |
return second ~ first ~ oprvs; |
|---|
| 119 |
} |
|---|
| 120 |
} |
|---|
| 121 |
return first ~ second ~ op; |
|---|
| 122 |
} |
|---|
| 123 |
} |
|---|
| 124 |
|
|---|
| 125 |
|
|---|
| 126 |
unittest { |
|---|
| 127 |
assert(makePostfixForX87("A=B", ["double", "double"],"11")=="BA="); |
|---|
| 128 |
assert(makePostfixForX87("(B*C)+A", ["double", "float", "float"],"111")=="BC*A+"); |
|---|
| 129 |
assert(makePostfixForX87("(B*C)+A", ["real", "float", "float"],"111")=="ABC*+"); |
|---|
| 130 |
assert(makePostfixForX87("A-(B*C)", ["double", "float", "float"],"100")=="BC*A_"); |
|---|
| 131 |
assert(makePostfixForX87("(B*C)-A", ["float", "float", "float"],"100")=="BC*A-"); |
|---|
| 132 |
assert(makePostfixForX87("(B*C)-A", ["real", "float", "float"],"100")=="ABC*_"); |
|---|
| 133 |
assert(makePostfixForX87("C+=((B*C)-A)", ["real", "float", "float"],"101")=="CABC*_+C="); |
|---|
| 134 |
assert(makePostfixForX87("C-=((B*C)-A)", ["real", "float", "float"],"101")=="CABC*_-C="); |
|---|
| 135 |
assert(makePostfixForX87("C-=(B*A)", ["real", "float", "float"],"101") =="BA*C_C="); |
|---|
| 136 |
assert(makePostfixForX87("C-=(B*A)", ["real", "float", "real"],"101") =="BA*C_C="); |
|---|
| 137 |
assert(makePostfixForX87("((A*B)+(C*D))+(E*F)", ["int", "int", "int"],"000")=="EF*AB*CD*++"); |
|---|
| 138 |
} |
|---|
| 139 |
|
|---|
| 140 |
/// Converts an infix string into postfix. |
|---|
| 141 |
/// Apply SSE/SSE2-specific optimisations during the conversion. |
|---|
| 142 |
struct SSEPostfixVisitor { |
|---|
| 143 |
alias typeof(*this) This; |
|---|
| 144 |
alias char [] ReturnType; |
|---|
| 145 |
char [] ranklist; |
|---|
| 146 |
static: |
|---|
| 147 |
ReturnType onVisitSymbol(This this_, char [] sym) { |
|---|
| 148 |
return sym; |
|---|
| 149 |
} |
|---|
| 150 |
ReturnType onVisitFunction(This this_, char [] func, char [][] args) { |
|---|
| 151 |
switch(func) { |
|---|
| 152 |
case "dot": |
|---|
| 153 |
char [] left = doVisit(this_,args[0]); |
|---|
| 154 |
char [] right = doVisit(this_, args[1]); |
|---|
| 155 |
if (left==right) return "0" ~ left ~ ",*+"; |
|---|
| 156 |
return "0" ~ left ~ right ~ "*+"; |
|---|
| 157 |
case "sum": |
|---|
| 158 |
return "0" ~ doVisit(this_, args[0]) ~ "+"; |
|---|
| 159 |
case "abs": return doVisit(this_,args[0]) ~ "a"; |
|---|
| 160 |
case "sqrt": return doVisit(this_,args[0]) ~ "q"; |
|---|
| 161 |
case "prod": |
|---|
| 162 |
return "1" ~ doVisit(this_, args[0]) ~ "*"; |
|---|
| 163 |
default: assert(0, "BLADE ICE: Unsupported"); |
|---|
| 164 |
} |
|---|
| 165 |
} |
|---|
| 166 |
ReturnType onVisitPrefix(This this_, char [] op, char [] expr) { |
|---|
| 167 |
if (op=="-") return doVisit(this_, expr) ~ "n"; // unary minus |
|---|
| 168 |
assert(0, "BLADE ICE: Unsupported"); |
|---|
| 169 |
} |
|---|
| 170 |
ReturnType onVisitPostfix(This this_, char [] op, char [] expr) { |
|---|
| 171 |
assert(0, "BLADE ICE: Unsupported"); |
|---|
| 172 |
} |
|---|
| 173 |
ReturnType onVisitIndex(This this_, char [] base, char [][2][] slices) { |
|---|
| 174 |
assert(0, "BLADE ICE: Unsupported"); |
|---|
| 175 |
} |
|---|
| 176 |
ReturnType onVisitBinaryOp(This this_, char [] op, char [] left, char [] right) { |
|---|
| 177 |
char [] first = doVisit(this_, left); |
|---|
| 178 |
char [] second; |
|---|
| 179 |
if (op.length==2 && op[1]=='=') { // +=, -=, *=, /= |
|---|
| 180 |
// Convert "A+=B" into "A=A+B" |
|---|
| 181 |
second = beginVisit(this_, left ~ op[0] ~ right); |
|---|
| 182 |
return second ~ first ~ "="; |
|---|
| 183 |
} |
|---|
| 184 |
second = doVisit(this_, right); |
|---|
| 185 |
if (op=="=") { |
|---|
| 186 |
return second ~ first ~ "="; |
|---|
| 187 |
} |
|---|
| 188 |
if (second == first) return first ~ "," ~ op; |
|---|
| 189 |
|
|---|
| 190 |
// SSE OPTIMISATION #1 |
|---|
| 191 |
// Multiplies have a long latency, so we want to delay using the |
|---|
| 192 |
// result of a multiply. Since + is commutative, we can achieve this |
|---|
| 193 |
// by calculating the value with the multiply, before the other one. |
|---|
| 194 |
if (op=="+") { |
|---|
| 195 |
if (second[second.length-1]=='*'&& first[first.length-1]!='*') { |
|---|
| 196 |
return second ~ first ~ op; |
|---|
| 197 |
} |
|---|
| 198 |
} |
|---|
| 199 |
if (op=="*") { |
|---|
| 200 |
// SSE OPTIMISATION #2 |
|---|
| 201 |
// When an operation is performed between a vector and a scalar |
|---|
| 202 |
// we want to have the vector being the one which is loaded first. |
|---|
| 203 |
if (first.length==1 && this_.ranklist[first[0]-'A']=='0') { |
|---|
| 204 |
return second ~ first ~ op; |
|---|
| 205 |
} |
|---|
| 206 |
} |
|---|
| 207 |
return first ~ second ~ op; |
|---|
| 208 |
} |
|---|
| 209 |
} |
|---|
| 210 |
|
|---|
| 211 |
unittest { |
|---|
| 212 |
assert(makePostfixForSSE("A=B", "11")=="BA="); |
|---|
| 213 |
assert(makePostfixForSSE("(A*B)+C", "101")=="AB*C+"); |
|---|
| 214 |
assert(makePostfixForSSE("A=(B*C)", "110")=="BC*A="); |
|---|
| 215 |
assert(makePostfixForSSE("prod((A*B))", "10")=="1AB**"); |
|---|
| 216 |
} |
|---|