Changeset 167

Show
Ignore:
Timestamp:
01/03/08 09:23:43 (8 months ago)
Author:
Don Clugston
Message:

Added the front end code generation for dot product (this is also the way forward for BLAS2 and BLAS3 operations). No backend codegen yet, although that should be relatively easy.

Files:

Legend:

Unmodified
Added
Removed
Modified
Copied
Moved
  • trunk/blade/Blade.d

    r165 r167  
    1313* 
    1414* FEATURES: 
    15 *  - Supports any mix of vector addition, subtraction, dot product, and multiplication 
    16 *    by a scalar. 
     15*  - Supports any mix of vector addition, subtraction, dot product, slicing, and 
     16*    multiplication by a scalar. 
    1717*  - Generates either x87 asm code, SSE or SSE2 asm code or pure D, depending on 
    1818*    the complexity of the expression, and the availability of inline asm. 
     
    2525* FUTURE DIRECTIONS (in order of expected implementation): 
    2626* - Dot product (which was present in BLADE 0.2). 
    27 * - The x87 code generation targets early Pentiums, which are now irrelevant. 
    28 * - Support for strided vectors. 
    29 * - Matrix support. 
     27* - Dense matrix support. 
     28* - Triangular, banded, symmetric, and sparse matrix support 
    3029* 
    3130* THEORY: 
     
    4039* 0.2 - Support for a wider variety of expressions. Dot product, imaginary numbers, etc. 
    4140* 0.3 - Based on string mixins. Most of the new features of 0.2 are gone, but SSE2 is added. 
    42 * 0.4 - Added D code generator. Nice error messages. Optimal parameter passing. 
     41* 0.4 - Added D code generator. Nice error messages. Optimal parameter passing  
     42*       (passes pointers, not arrays). 
     43* 0.5 - Expression simplification step. 
    4344*/ 
    4445 
     
    4748public import blade.SyntaxTree : AbstractSyntaxTree, syntaxtreeof, AST, Symbol; 
    4849private import blade.BladeUtil : enquote, itoa; 
    49 private import blade.BladeRank : isStrided
    50 private import blade.BladeSimplify : simplifySyntaxTree, RevisedExpression
     50private import blade.BladeRank : isStrided, exprRank
     51private import blade.BladeSimplify : simplifySyntaxTree, RevisedExpression, remapCompounds
    5152private import blade.CodegenX86 : generateCodeForAsmX87, MAX_X87_VECTORS, 
    5253                                 MAX_87_REALSCALARSPLUSTEMPORARIES, 
    5354                                 generateCodeForSSE,  MAX_SSE_VECTORS; 
     55private import blade.BladeVisitor: expressionContainsAssignment; 
    5456 
    5557private import blade.PostfixX86 : makePostfixForX87; 
    5658 
    57 struct BladeStrided(T) 
    58 { 
    59     T * data;     // Pointer to the first element 
    60 } 
    6159public: 
    6260 
     
    7977    if (revised.errorMessage.length>0)  return `static assert(0, "BLADE: ` ~ enquote(revised.errorMessage) ~ `");`; 
    8078    VecExpressionType exprType = categorizeExpression(revised); 
     79    char [] result = generateAsserts(revised, (exprType == VecExpressionType.SSE2Expression || exprType == VecExpressionType.SSE1Expression)); 
     80    debug(BladeFrontEnd) { 
     81        result ~= "// Simplified: " ~ revised.expression ~ \n; 
     82    }     
    8183    if (exprType == VecExpressionType.SSE2Expression || exprType == VecExpressionType.SSE1Expression) { 
    82         return invokeSSE((exprType == VecExpressionType.SSE2Expression), revised)
     84        return result ~ invokeSSE((exprType == VecExpressionType.SSE2Expression), revised)~ ";"
    8385    } else if (exprType == VecExpressionType.X87Expression) { 
    84         return invokeX87(revised)
     86        return result ~ invokeX87(revised) ~ ";"
    8587    } else { 
    86         return DCodeGenerator(revised); 
     88        return result ~ DCodeGenerator(revised); 
    8789    }     
    8890} 
    8991 
     92// For a compound of a different dimensionality (eg a dot product), we may need 
     93// to calculate the result seperately. 
     94char [] makeVectorCodeForDimensionalCompound(char [] expression, AbstractSyntaxTree tree) 
     95{ 
     96    // TODO: 
     97    return expression;     
     98} 
    9099 
    91100// These functions have the complete expression encoded in the template type. 
     
    139148    SSE1 = false; 
    140149    X87 = false; 
     150} 
     151int wholerank = exprRank(tree.expression, tree.rank); 
     152 
     153if (wholerank==0 && expressionContainsAssignment(tree.expression)) { 
     154    return VecExpressionType.DExpression; // Scalar assignments always use inline D. 
    141155} 
    142156    int numvectors = 0; 
     
    192206} 
    193207 
     208 
     209// This is mainly a workaround for compiler bug #1125. Ideally both 
     210// pointer and stride would be stored together. 
     211struct BladeStrided(T) 
     212{ 
     213    T * data;     // Pointer to the first element 
     214} 
     215 
    194216//------------------------------------------------------- 
    195217//                Invoker functions 
     
    203225char [] invokeX87(RevisedExpression tree) 
    204226{ 
    205     char [] result = assertAllVectorLengthsEqual(tree)
     227    char [] result = ""
    206228    char [] stridelist=""; 
    207229    char [] alltypes=""; 
     
    272294    int firstVector = findVectorForLength(tree); 
    273295    return result ~ getValueForSymbol(tree.mapping[firstVector], tree) ~ ".length"  
    274         ~ vals ~ stridelist  ~ ");"; 
     296        ~ vals ~ stridelist  ~ ")"; 
     297
     298 
     299char [] generateAsserts(RevisedExpression tree, bool checkAlignment) 
     300
     301    char [] result = assertAllVectorLengthsEqual(tree); 
     302    if (checkAlignment) result ~= assertAllVectorsAlign128(tree); 
     303    return result; 
    275304} 
    276305 
    277306/// Generate code which will call the SSE/SSE2 code generation function 
    278307char [] invokeSSE(bool SSE2, RevisedExpression tree) 
    279 
    280     char [] result = assertAllVectorLengthsEqual(tree); 
    281     result ~= assertAllVectorsAlign128(tree); 
    282  
    283         
    284     result ~= "SSEVECGEN!(" ~ (SSE2?"2":"1") ~ `,"` ~ enquote(tree.expression) ~ `"`; 
     308{       
     309    char [] result = "SSEVECGEN!(" ~ (SSE2?"2":"1") ~ `,"` ~ enquote(tree.expression) ~ `"`; 
    285310    // For SSE2, everything must be implicitly convertible to double. 
    286311    char [] vals; 
     
    303328    result ~= vals; 
    304329 
    305     return result ~ ");"; 
     330    return result ~ ")"; 
    306331} 
    307332 
     
    399424        if (comp[$-1]!=']') { // simple compound expression 
    400425            foreach(d; comp) { 
     426                if (d=='{') assert(0, "BLADE ICE"); 
    401427                if (d>='A' && d<='Z') v ~= tree.symbolTable[d-'A'].value; 
    402428                else v ~= d; 
     
    468494} 
    469495 
     496 
     497char [] invokeNestedExpression(char [] expr, Symbol[] symbolTable) 
     498{ 
     499    char [] ranks; 
     500    for (int i=0; i<symbolTable.length; ++i) { 
     501        ranks ~= symbolTable[i].rank; 
     502    } 
     503    RevisedExpression revised = remapCompounds(expr, ranks, symbolTable); 
     504     
     505    VecExpressionType exprType = categorizeExpression(revised); 
     506    if (exprType == VecExpressionType.SSE2Expression || exprType == VecExpressionType.SSE1Expression) { 
     507        return invokeSSE((exprType == VecExpressionType.SSE2Expression), revised); 
     508    } else if (exprType == VecExpressionType.X87Expression) { 
     509        return invokeX87(revised); 
     510    } else { 
     511        assert(0, "BLADE ICE: Nested D expressions are not yet supported"); 
     512        return DCodeGenerator(revised); 
     513    } 
     514} 
     515 
    470516char [] getValueForSymbol(char c, RevisedExpression tree, char [] firstIndexExpr="") 
    471517{ 
     
    476522    if (c-'A'<tree.symbolTable.length) { 
    477523        v = tree.symbolTable[c-'A'].value; 
    478     } else {  // else it's a compound or an indexed array 
     524    } else {  // else it's a compound or an indexed array       
    479525        char [] comp = tree.compounds[c-'A'-tree.symbolTable.length]; 
    480526         
    481         if (comp[$-1]!=']') { // simple compound expression 
    482             foreach(d; comp) { 
    483                 if (d>='A' && d<='Z') v ~= tree.symbolTable[d-'A'].value; 
    484                 else v ~= d; 
     527        if (comp[$-1]!=']') { // compound expression (not an indexed array) 
     528            if (comp[0]=='d') { 
     529                // dot product is a nested expression 
     530                return invokeNestedExpression(comp, tree.symbolTable); 
     531            } 
     532            for (int k=0; k<comp.length; ++k) { 
     533                char d = comp[k]; 
     534                 if (d=='{') { 
     535                     int braceStart = k+1; 
     536                     for (; comp[k]!='}'; ++k) {}                     
     537                     v ~= invokeNestedExpression(comp[braceStart..k], tree.symbolTable); 
     538                 } else if (d>='A' && d<='Z') { 
     539                     v ~= tree.symbolTable[d-'A'].value; 
     540                 } else v ~= d; 
    485541            } 
    486542        } else { 
     
    542598} 
    543599 
    544 // Convert the compound expression str back into its values. 
    545 char [] reconstituteCompoundExpression(char [] str, Symbol[] table) 
    546 { 
    547     char [] v = ""; 
    548     foreach(d; str) { 
    549         if (d>='A' && d<='Z') v ~= table[d-'A'].value; 
    550         else v ~= d; 
    551     } 
    552     return v; 
    553 } 
    554600 
    555601// Generate inline D code for the expression 
    556602char [] DCodeGenerator(RevisedExpression tree) 
    557603{ 
    558     int lenvec = findVectorForLength(tree); 
    559     char [] result = assertAllVectorLengthsEqual(tree); 
    560     result ~= "// " ~ tree.expression ~ \n; 
    561     result ~= "for (int blade_index=0; blade_index<"  
    562     ~ getDimensionLengthForSymbol(tree.mapping[lenvec], tree, 1) ~ 
    563         "; ++blade_index) {"\n; 
     604    char [] result = "// D generate:" ~ tree.braceExpression ~ \n; 
     605    int wholerank = exprRank(tree.expression, tree.rank); 
     606    if (wholerank ==1) {   
     607        int lenvec = findVectorForLength(tree); 
     608        result = "for (int blade_index=0; blade_index<"  
     609               ~ getDimensionLengthForSymbol(tree.mapping[lenvec], tree, 1) 
     610               ~ "; ++blade_index) {"\n; 
     611    } 
    564612    foreach (c; tree.expression) { 
    565         if (c>='A' && c<'Z') { 
     613        if (c>='A' && c<'Z') {             
    566614            // restore all symbols into the expression 
    567615            // If it's a vector, index it 
     
    571619        } else result ~= c; 
    572620    } 
     621    if (wholerank==0) return result ~ ";";    
    573622    return result ~ "; }"; 
    574623} 
  • trunk/blade/BladeDemo.d

    r166 r167  
    1515// cdouble[] always remains aligned, even when sliced. 
    1616 
     17float dot_product(float[] a, float[] b) 
     18{ 
     19    return 0; 
     20} 
    1721   
    1822void main() 
    19 {    
     23{  
    2024    static z = [3.4, 565, 31.3, 0]; 
    2125    double [] a = new double[4]; 
     
    5357    mixin(vectorize("another[0..$,1]=6*a[0..2]")); 
    5458 
    55 // Parses, and simplifies to A*A, where A = dot(q,q). No codegen yet. 
    56 //   mixin(vectorize("dot(q,q*dot(q,q))")); 
     59// Parses, and simplifies to A*A, where A = dot(q,q). No asm codegen yet. 
     60   double u; 
     61//   mixin(vectorize("u = dot(q,q*dot(q,q))")); 
     62//   mixin(vectorize("q *=dot(q,q*dot(q+q,q))")); 
    5763 
    5864    writefln("a=", a); 
    5965} 
    60  
  • trunk/blade/BladeRank.d

    r161 r167  
    6464/** Returns the (tensor) rank of the expression expr. 
    6565 * A negative number will be returned if an error is detected. 
     66 * TODO: Should warn of expressions with no effect (ie, with no =). 
    6667 * 
    6768 * Params: 
     
    226227    // bug fixes: 
    227228    assert(exprRank("(A[B..$,C])+=D", "2001")==1); 
    228  
    229     //NO LONGER SUPPORTED 
    230     //    assert(exprRank("A[B,([B,C]),B]", "600")==4); 
    231 //    assert(exprRank("A[B,(([B,C])[B]),B]", "600")==3); 
    232  
    233 
    234  
    235  
    236 // Return true if the entire expression contains a multiplication by a scalar 
    237 bool hasScalarMultiply(char [] expr, char [] rank) 
    238 
    239     if (expr.length>2 && (expr[0..2]=="++" || expr[0..2]=="--" || expr[$-2..$]=="++" || expr[$-2..$]=="--")) { 
    240         return false; 
    241     } 
    242     if (expr[0]=='+' || expr[0]=='-') return hasScalarMultiply(expr[1..$], rank); 
    243      
    244     int x = exprLength(expr); 
    245     int y = x+1; 
    246     assert(y < expr.length, "BLADE BUG:" ~ expr); 
    247     // Deal with shifts, op=, and NCEG operators 
    248     while (expr[y+1]=='<' || expr[y+1]=='>' || expr[y+1]=='=') ++y;     
    249  
    250     char [] op = expr[x+1..y+1];     
    251     char [] left = expr[0..x+1]; 
    252     char [] right = expr[y+1..$]; 
    253     if (op=="[") { 
    254         // (A)[C] can still have a multiply by scalar, if A contains a 
    255         // multiply. 
    256         if (left.length==1) return false; 
    257         return hasScalarMultiply(left[1..$], rank); 
    258     } 
    259     if (op=="/") return true; 
    260     if (op!="*" && op!="/") { 
    261         if (left.length==1 || right.length==1) return false;  
    262         // (A+B) could contain a multiply by scalar, if both A and B 
    263         // contain multiplies. 
    264         return hasScalarMultiply(left[1..$-1], rank) && hasScalarMultiply(right[1..$-1], rank); 
    265     } 
    266     // it's not true for matrix*matrix multiplies. 
    267     if (subexprRank(left, rank)==0) return true; 
    268     return subexprRank(right, rank) == 0; 
    269 
    270  
    271 unittest { 
    272     assert(hasScalarMultiply("(A*B)+(B*C)","101")); 
    273     assert(!hasScalarMultiply("(A*B)-(C*C)","101")); 
    274     assert(!hasScalarMultiply("A+(B*C)","101")); 
    275     assert(hasScalarMultiply("(A/B)-((A*B)+(C*B))","101")); 
    276     assert(!hasScalarMultiply("A[B]","20")); 
    277     assert(!hasScalarMultiply("(C[B])[B..A]","002") ); 
    278 
     229
  • trunk/blade/BladeSimplify.d

    r166 r167  
    1313*      where B and C are both rank 0, and A is rank 1 or more, the slice can 
    1414*      be moved to every vector inside A. 
    15 *    - Use associativity of *: A*(B*C[]) == (A*B)*C[] 
     15*    - Use associativity of *: A*(B*C[]) == (A*B)*C[] (Not strictly true for 
     16*      floating point; results may differ by 1ulp,  
     17       eg (1.3L*3.1L)*4.7L < 1.3L*(3.1L*4.7L) 
     18*      Note that floating point addition is not associative at all). 
    1619* (D) Expression standardisation  
    1720*    - Move multiplies to left: Convert A[]*B into B*A[] (assumes * is commutative, 
    1821*      not valid for quaternions). 
    1922*    - Convert C-A*B into C+(-A)*B whenever possible. 
     23*    - Remove unary minus where possible, eg convert A-(-B) into A+B. 
    2024* 
    2125* Author: 
     
    2933public import blade.SyntaxTree : AbstractSyntaxTree, Symbol; 
    3034private import blade.BladeVisitor; 
    31 private import blade.BladeRank : exprLength, exprRank, subexprRank, 
    32         hasScalarMultiply, getRankErrorText, isStrided;  
     35private import blade.BladeRank : exprRank, subexprRank, getRankErrorText; 
    3336 
    3437 
    3538/// A simplified vector expression 
    3639struct RevisedExpression { 
     40    char [] braceExpression; // the expression with compounds in braces 
    3741    char [] expression; // the revised expression using new variable names 
    3842                        // (so, for example, B+=(D-F) becomes A+=(B-C) ). 
     
    6064    // Check for undefined symbols 
    6165    if (err.length > 0)  
    62         return RevisedExpression(tree.expression, tree.symbolTable, [""], "","", "Undefined symbols:" ~ err); 
     66        return RevisedExpression(tree.expression, "", tree.symbolTable, [""], "","", "Undefined symbols:" ~ err); 
    6367    else { 
     68        // Remove duplicate symbols, convert intrinsics 
    6469        char [] expr2 = removeDuplicates(tree); 
    6570        // Check for rank errors 
    6671        int wholerank = exprRank(expr2, ranks); 
    6772        if (wholerank<0) 
    68             return RevisedExpression(expr2, tree.symbolTable, [""], "","", getRankErrorText(wholerank)); 
    69       return simplifyVectorExpression(expr2, ranks, tree.symbolTable); 
     73            return RevisedExpression(expr2, "", tree.symbolTable, [""], "","", getRankErrorText(wholerank)); 
     74        // Perform scalar foldings and dimension reduction 
     75        char [] expr3 = foldScalars(foldIndices(expr2, ranks), ranks); 
     76        return remapCompounds(expr3, ranks, tree.symbolTable); 
    7077    } 
    7178} 
     
    136143} 
    137144 
    138 RevisedExpression simplifyVectorExpression(char [] expr, char [] rank, Symbol[] symTable=[]) 
    139 
    140     char [] s = foldScalars(foldIndices(expr, rank), rank); 
     145public: 
     146// Remove everything inside {} from expr, and create new variables for it. 
     147RevisedExpression remapCompounds(char [] expr, char [] rank, Symbol[] symTable) 
     148
    141149    char [][] comp; 
    142150    char [] used = ""; // which of the old variables are used; gives the new mapping 
     
    146154    char next = cast(char)('A' + rank.length); 
    147155    char [] e = ""; 
    148     for (int i=0; i<s.length; ++i) { 
    149         if (s[i]=='{') { 
     156    for (int i=0; i<expr.length; ++i) { 
     157        if (expr[i]=='{') { 
    150158            int k; 
    151159            int bracecount=1; 
    152160            for (k=i+1; bracecount>0; ++k) { 
    153                 if (s[k]=='{') ++bracecount; 
    154                 if (s[k]=='}') --bracecount; 
     161                if (expr[k]=='{') ++bracecount; 
     162                if (expr[k]=='}') --bracecount; 
    155163            } 
    156164            --k; 
    157             char [] newexpr = s[i+1..k]; // strip off the {}             
     165            char [] newexpr = expr[i+1..k]; // strip off the {}             
    158166            int newi = k; 
    159             if (i>0 && k<s.length-1 && s[i-1]=='(' && s[k+1]==')') { 
     167            if (i>0 && k<expr.length-1 && expr[i-1]=='(' && expr[k+1]==')') { 
    160168                e = e[0..$-1]; // remove the last '(' 
    161169                newi=k+1; // don't add ')' 
     
    167175                e ~= next; 
    168176                ++next; 
    169                 comp ~= s[i+1..k]; // strip off the {} 
    170                 if (s[k-1]==']') {                 
     177                comp ~= expr[i+1..k]; // strip off the {} 
     178                if (expr[k-1]==']') {                 
    171179                    // it's a vector/matrix of some kind, with rank reduced 
    172180                    // by indices. Can't just use exprRank, because the [] 
    173181                    // aren't wrapped by (). 
    174                     r ~= (rank[s[i+1]-'A'] - indexRank(s[i+1..k])); 
     182                    r ~= (rank[expr[i+1]-'A'] - indexRank(expr[i+1..k])); 
    175183                } else { 
    176184                    // it's a scalar expression. Note that it could involve 
     
    181189            i = newi; 
    182190        } else { 
    183             e ~= s[i]; 
    184             if (s[i]>='A' && s[i]<='Z') used[s[i]-'A']=s[i]; 
     191            e ~= expr[i]; 
     192            if (expr[i]>='A' && expr[i]<='Z') used[expr[i]-'A']=expr[i]; 
    185193        } 
    186194    } 
     
    210218        } else f ~= c; 
    211219    } 
    212     return RevisedExpression(f, symTable, comp, old_ranks~r, mapping); 
     220    return RevisedExpression(expr, f, symTable, comp, old_ranks~r, mapping); 
     221
     222 
     223private: 
     224RevisedExpression simplifyVectorExpression(char [] expr, char [] rank, Symbol[] symTable) 
     225
     226    return remapCompounds(foldScalars(foldIndices(expr, rank), rank), rank, symTable); 
    213227} 
    214228 
    215229unittest {     
    216     RevisedExpression e = simplifyVectorExpression("A+=(((D[B])*C)[B])", "2004"); 
     230    RevisedExpression e = simplifyVectorExpression("A+=(((D[B])*C)[B])", "2004",[]); 
    217231    assert(e.rank=="202"); 
    218232    assert(e.compounds[0]=="D[B,B]"); 
     
    441455            ScalarFold left = doVisit(this_,args[0]); 
    442456            ScalarFold right = doVisit(this_, args[1]); 
    443             return ScalarFold("", combineMul(combineMul(left.multiplier, right.multiplier), func ~ "(" ~ left.expr ~ "," ~ right.expr ~ ")")); 
     457            return ScalarFold("", combineMul(combineMul(left.multiplier, right.multiplier), "{" ~ func ~ "(" ~ left.expr ~ "," ~ right.expr ~ ")}")); 
    444458        } else { 
    445459            assert(0, "BLADE: Unsupported function"); 
     
    554568    ScalarFold f = beginVisit(ScalarFoldingVisitor(ranks), expr); 
    555569    if (f.multiplier=="") return f.expr; 
    556     else if (f.expr=="") return "{" ~ f.multiplier ~ "}"
     570    else if (f.expr=="") return f.multiplier
    557571    else return combineMulWithCompound(f.expr, f.multiplier); 
    558572} 
     
    560574unittest { 
    561575    assert(foldScalars("A*=(B*C)", "100")=="A*={(B*C)}"); 
    562     assert(foldScalars("d(A,(A*d(A,A)))", "1")=="{(d(A,A))*(d(A,A))}"); 
     576    assert(foldScalars("d(A,(A*d(A,A)))", "1")=="({d(A,A)})*({d(A,A)})"); 
    563577    assert(foldScalars("A-(B*C)", "101")=="A+(C*{(-B)})"); 
    564578    assert(foldScalars("A-(B*(-C))", "101")=="A+(C*{(-(-B))})"); 
  • trunk/blade/BladeVisitor.d

    r161 r167  
    132132    return (a.length==1)? a : "(" ~ a ~ ")"; 
    133133} 
     134 
     135// Return true if this expression contains an assignment. 
     136bool expressionContainsAssignment(char [] expr) 
     137{ 
     138    // BUG: also need to deal with comma, ?:, &&, ||, is, !is, in,  
     139    // unary &, unary ! 
     140    if (expr.length>1 && expr[0]=='(') expr = expr[1..$-1]; 
     141    else if (expr.length==1 && ((expr[0]>='A' && expr[0]<='Z') || expr[0]=='$')){ 
     142        return false; 
     143    } 
     144    if (expr.length>3 && (expr[0]>='a' && expr[1]<='z') && expr[1]=='(') { // function 
     145        return false; 
     146    } 
     147    if (expr.length>2 && (expr[0..2]=="++" || expr[0..2]=="--")) { 
     148        return false; 
     149    } 
     150    if (expr.length>1 && (expr[0]=='+' || expr[0]=='-')) { 
     151        return false; 
     152    } 
     153    if (expr.length>2 && (expr[$-2..$]=="++" || expr[$-2..$]=="--")) { 
     154        return false; // BUG: actually this is an assignment 
     155    } 
     156    int x = exprLength(expr); 
     157    int y = x+1; 
     158    assert(y < expr.length, "BLADE ICE:" ~ expr); 
     159    // Deal with shifts, op=, and NCEG operators 
     160    if (expr[y+1]=='<' || expr[y+1]=='>') return false; 
     161    if (expr[y]=='=' && expr[y+1]=='=') return false; 
     162    if (expr[y]=='=') return true; 
     163    if (expr[y+1]!='=') return false; 
     164    if (expr[y]=='<' || expr[y]=='<' || expr[y]=='!') return false; 
     165    return true; 
     166} 
  • trunk/blade/CodegenX86.d

    r166 r167  
    289289char [] generateCodeForAsmX87(int numStrides, Values...)(char [] postfixOperations) 
    290290{ 
     291// Because of compiler bug #1125, no structs can be stored in the 'values' tuple. 
     292// Thus, lengths and strides must be stored seperately from vector pointers. 
    291293    char [] ranklist; 
    292294    char [][] typelist;