diff options
| author | Tim Foley <tfoleyNV@users.noreply.github.com> | 2017-09-06 14:15:11 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2017-09-06 14:15:11 -0700 |
| commit | ca16ede67d3fc34ec1cc81b8f835199c5ef1ab9a (patch) | |
| tree | fd83b7e861dbc6a0d442d5a913a1bcd9df547408 /source/slang/emit.cpp | |
| parent | e59a1b39317c10815baaed3b70c4a6580dbe1e63 (diff) | |
| parent | 5900f32fff9970b4221ce7fb7e94133e387ff9de (diff) | |
Merge pull request #176 from tfoleyNV/ir-work
Continue work on IR-based codegen
Diffstat (limited to 'source/slang/emit.cpp')
| -rw-r--r-- | source/slang/emit.cpp | 614 |
1 files changed, 468 insertions, 146 deletions
diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 2deb0cea7..450d9eb86 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -1845,106 +1845,28 @@ struct EmitVisitor { auto funcDeclRef = funcDeclRefExpr->declRef; auto funcDecl = funcDeclRef.getDecl(); - if(!funcDecl) + if (!funcDecl) { emitUncheckedCallExpr(callExpr, funcDeclRefExpr->name, arg); return; } - else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>()) - { - switch (intrinsicOpModifier->op) - { - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return - CASE(Mul, *); - CASE(Div, / ); - CASE(Mod, %); - CASE(Add, +); - CASE(Sub, -); - CASE(Lsh, << ); - CASE(Rsh, >> ); - CASE(Eql, == ); - CASE(Neq, != ); - CASE(Greater, >); - CASE(Less, <); - CASE(Geq, >= ); - CASE(Leq, <= ); - CASE(BitAnd, &); - CASE(BitXor, ^); - CASE(BitOr, | ); - CASE(And, &&); - CASE(Or, || ); - #undef CASE - - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return - CASE(Assign, =); - CASE(AddAssign, +=); - CASE(SubAssign, -=); - CASE(MulAssign, *=); - CASE(DivAssign, /=); - CASE(ModAssign, %=); - CASE(LshAssign, <<=); - CASE(RshAssign, >>=); - CASE(OrAssign, |=); - CASE(AndAssign, &=); - CASE(XorAssign, ^=); - #undef CASE - - case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return; - - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return - CASE(Pos, +); - CASE(Neg, -); - CASE(Not, !); - CASE(BitNot, ~); - #undef CASE - - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return - CASE(PreInc, ++); - CASE(PreDec, --); - #undef CASE - - #define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return - CASE(PostInc, ++); - CASE(PostDec, --); - #undef CASE - - case IntrinsicOp::InnerProduct_Vector_Vector: - // HLSL allows `mul()` to be used as a synonym for `dot()`, - // so we need to translate to `dot` for GLSL - if (context->shared->target == CodeGenTarget::GLSL) - { - Emit("dot("); - EmitExpr(callExpr->Arguments[0]); - Emit(", "); - EmitExpr(callExpr->Arguments[1]); - Emit(")"); - return; - } - break; - - case IntrinsicOp::InnerProduct_Matrix_Matrix: - case IntrinsicOp::InnerProduct_Matrix_Vector: - case IntrinsicOp::InnerProduct_Vector_Matrix: - // HLSL exposes these with the `mul()` function, while GLSL uses ordinary - // `operator*`. - // - // The other critical detail here is that the way we handle matrix - // conventions requires that the operands to the product be swapped. - if (context->shared->target == CodeGenTarget::GLSL) - { - Emit("(("); - EmitExpr(callExpr->Arguments[1]); - Emit(") * ("); - EmitExpr(callExpr->Arguments[0]); - Emit("))"); - return; - } - break; - - default: - break; - } - } + // Note: We check for a "target intrinsic" modifier that flags the + // operation as having a custom elaboration for a specific target + // *before* we check for an "intrinsic op." The basic problem is + // that a single operation could have both finds of modifiers on it. + // The "target" intrinsic modifier tags something expansion during + // our current source-to-source translation approach, while an + // intrinsic op is needed for helping things lower to our IR. + // + // We need to check for this case first to make sure that when a + // function gets an intrinsic op added it doesn't break existing + // cross-compilation logic. + // + // The long term fix will be to not use the AST-based cross-compilation + // logic (which has all kinds of problems) and instead use the IR + // exclusively, at which point the notion of a "target intrinsic" modifier + // goes away (although we may have something similar to express how + // a particular op should lower/expand for a given target). else if(auto targetIntrinsicModifier = findTargetIntrinsicModifier(funcDecl)) { if (context->shared->target == CodeGenTarget::GLSL) @@ -2220,6 +2142,101 @@ struct EmitVisitor } } } + else if (auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>()) + { + switch (intrinsicOpModifier->op) + { +#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return + CASE(Mul, *); + CASE(Div, / ); + CASE(Mod, %); + CASE(Add, +); + CASE(Sub, -); + CASE(Lsh, << ); + CASE(Rsh, >> ); + CASE(Eql, == ); + CASE(Neq, != ); + CASE(Greater, > ); + CASE(Less, < ); + CASE(Geq, >= ); + CASE(Leq, <= ); + CASE(BitAnd, &); + CASE(BitXor, ^); + CASE(BitOr, | ); + CASE(And, &&); + CASE(Or, || ); +#undef CASE + +#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitBinAssignExpr(outerPrec, kEOp_##NAME, #OP, callExpr); return + CASE(Assign, =); + CASE(AddAssign, +=); + CASE(SubAssign, -=); + CASE(MulAssign, *=); + CASE(DivAssign, /=); + CASE(ModAssign, %=); + CASE(LshAssign, <<=); + CASE(RshAssign, >>=); + CASE(OrAssign, |=); + CASE(AndAssign, &=); + CASE(XorAssign, ^=); +#undef CASE + + case IntrinsicOp::Sequence: EmitBinExpr(outerPrec, kEOp_Comma, ",", callExpr); return; + +#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return + CASE(Pos, +); + CASE(Neg, -); + CASE(Not, !); + CASE(BitNot, ~); +#undef CASE + +#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Prefix, #OP, "", callExpr); return + CASE(PreInc, ++); + CASE(PreDec, --); +#undef CASE + +#define CASE(NAME, OP) case IntrinsicOp::NAME: EmitUnaryAssignExpr(outerPrec, kEOp_Postfix, "", #OP, callExpr); return + CASE(PostInc, ++); + CASE(PostDec, --); +#undef CASE + + case IntrinsicOp::InnerProduct_Vector_Vector: + // HLSL allows `mul()` to be used as a synonym for `dot()`, + // so we need to translate to `dot` for GLSL + if (context->shared->target == CodeGenTarget::GLSL) + { + Emit("dot("); + EmitExpr(callExpr->Arguments[0]); + Emit(", "); + EmitExpr(callExpr->Arguments[1]); + Emit(")"); + return; + } + break; + + case IntrinsicOp::InnerProduct_Matrix_Matrix: + case IntrinsicOp::InnerProduct_Matrix_Vector: + case IntrinsicOp::InnerProduct_Vector_Matrix: + // HLSL exposes these with the `mul()` function, while GLSL uses ordinary + // `operator*`. + // + // The other critical detail here is that the way we handle matrix + // conventions requires that the operands to the product be swapped. + if (context->shared->target == CodeGenTarget::GLSL) + { + Emit("(("); + EmitExpr(callExpr->Arguments[1]); + Emit(") * ("); + EmitExpr(callExpr->Arguments[0]); + Emit("))"); + return; + } + break; + + default: + break; + } + } } else if (auto overloadedExpr = funcExpr.As<OverloadedExpr>()) { @@ -3877,7 +3894,13 @@ emitDeclImpl(decl, nullptr); { if(auto decoration = inst->findDecoration<IRHighLevelDeclDecoration>()) { - return getText(decoration->decl->getName()); + auto decl = decoration->decl; + if (auto reflectionNameMod = decl->FindModifier<ParameterBlockReflectionName>()) + { + return getText(reflectionNameMod->nameAndLoc.name); + } + + return getText(decl->getName()); } StringBuilder sb; @@ -3891,9 +3914,11 @@ emitDeclImpl(decl, nullptr); enum class Flavor { Simple, + Ptr, }; Flavor flavor; + IRDeclaratorInfo* next; String const* name; }; @@ -3910,9 +3935,36 @@ emitDeclImpl(decl, nullptr); emit(" "); emit(*declarator->name); break; + + case IRDeclaratorInfo::Flavor::Ptr: + emit("*"); + emitDeclarator(context, declarator->next); + break; + } + } + + void emitIRSimpleValue( + EmitContext* context, + IRInst* inst) + { + switch(inst->op) + { + case kIROp_IntLit: + emit(((IRConstant*) inst)->u.intVal); + break; + + case kIROp_FloatLit: + emit(((IRConstant*) inst)->u.floatVal); + break; + + default: + SLANG_UNIMPLEMENTED_X("val case for emit"); + break; } + } + void emitIRSimpleType( EmitContext* context, IRType* type) @@ -3928,29 +3980,56 @@ emitDeclImpl(decl, nullptr); #undef CASE - default: - SLANG_UNIMPLEMENTED_X("type case for emit"); + + case kIROp_VectorType: + emitIRVectorType(context, (IRVectorType*) type); break; - } - } + case kIROp_StructType: + emit(getName(type)); + break; - void emitIRSimpleValue( - EmitContext* context, - IRInst* inst) - { - switch(inst->op) - { - case kIROp_IntLit: - emit(((IRConstant*) inst)->u.intVal); + case kIROp_TextureType: + { + auto textureType = (IRTextureType*) type; + + // TODO: actually look at the flavor and emit the right name + emit("Texture2D"); + + emit("<"); + emitIRType(context, textureType->getElementType(), nullptr); + emit(">"); + } break; - case kIROp_FloatLit: - emit(((IRConstant*) inst)->u.floatVal); + case kIROp_ConstantBufferType: + { + auto tt = (IRConstantBufferType*) type; + emit("ConstantBuffer<"); + emitIRType(context, tt->getElementType(), nullptr); + emit(">"); + } + break; + + case kIROp_TextureBufferType: + { + auto tt = (IRTextureBufferType*) type; + emit("ConstantBuffer<"); + emitIRType(context, tt->getElementType(), nullptr); + emit(">"); + } break; + case kIROp_SamplerType: + { + // TODO: actually look at the flavor and emit the right name + emit("SamplerState"); + } + break; + + default: - SLANG_UNIMPLEMENTED_X("val case for emit"); + SLANG_UNIMPLEMENTED_X("type case for emit"); break; } @@ -3974,14 +4053,15 @@ emitDeclImpl(decl, nullptr); { switch( type->op ) { - case kIROp_VectorType: - emitIRVectorType(context, (IRVectorType*) type); - emitDeclarator(context, declarator); - break; + case kIROp_PtrType: + { + auto ptrType = (IRPtrType*) type; - case kIROp_StructType: - emit(getName(type)); - emitDeclarator(context, declarator); + IRDeclaratorInfo ptrDeclarator; + ptrDeclarator.flavor = IRDeclaratorInfo::Flavor::Ptr; + ptrDeclarator.next = declarator; + emitIRType(context, ptrType->getValueType(), &ptrDeclarator); + } break; default: @@ -4010,11 +4090,82 @@ emitDeclImpl(decl, nullptr); emitIRType(context, type, (IRDeclaratorInfo*) nullptr); } + bool shouldFoldIRInstIntoUseSites( + EmitContext* context, + IRInst* inst) + { + // Certain opcodes should always be folded in + switch( inst->op ) + { + default: + break; + + case kIROp_IntLit: + case kIROp_FloatLit: + case kIROp_FieldAddress: + return true; + } + + // Certain *types* will usually want to be folded in + auto type = inst->getType(); + switch (type->op) + { + case kIROp_ConstantBufferType: + case kIROp_TextureBufferType: + // TODO: we need to be careful here, because + // HLSL shader model 6 allows these as explicit + // types. + return true; + + default: + break; + } + + // By default we will *not* fold things into their use sites. + return false; + } + + bool isDerefBaseImplicit( + EmitContext* context, + IRInst* inst) + { + auto type = inst->getType(); + switch (type->op) + { + case kIROp_ConstantBufferType: + case kIROp_TextureBufferType: + // TODO: we need to be careful here, because + // HLSL shader model 6 allows these as explicit + // types. + return true; + + default: + break; + } + + return false; + } + + + void emitIROperand( EmitContext* context, IRInst* inst) { - emit(getName(inst)); + if( shouldFoldIRInstIntoUseSites(context, inst) ) + { + emit("("); + emitIRInstExpr(context, inst); + emit(")"); + return; + } + + switch(inst->op) + { + default: + emit(getName(inst)); + break; + } } void emitIRArgs( @@ -4037,28 +4188,49 @@ emitDeclImpl(decl, nullptr); EmitContext* context, IRInst* inst) { - emitIRType(context, inst->getType(), getName(inst)); + auto type = inst->getType(); + if(!type || type->op == kIROp_VoidType) + return; + + emitIRType(context, type, getName(inst)); emit(" = "); } - void emitIRInst( + void emitIRInstExpr( EmitContext* context, IRInst* inst) { - // TODO: need to be able to `switch` on the IR opcode here, - // so there is some work to be done. switch(inst->op) { - case kIROp_Param: - // Don't emit parameters, since they are declared as part of the function. + case kIROp_IntLit: + { + auto irConst = (IRConstant*) inst; + emit(irConst->u.intVal); + } + break; + + case kIROp_FloatLit: + { + auto irConst = (IRConstant*) inst; + emit(irConst->u.floatVal); + } break; case kIROp_Construct: // Simple constructor call - emitIRInstResultDecl(context, inst); - emitIRType(context, inst->getType()); - emitIRArgs(context, inst); - emit(";\n"); + if( inst->getArgCount() == 2 ) + { + // Need to emit as cast for HLSL + emit("("); + emitIRType(context, inst->getType()); + emit(") "); + emitIROperand(context, inst->getArg(1)); + } + else + { + emitIRType(context, inst->getType()); + emitIRArgs(context, inst); + } break; case kIROp_FieldExtract: @@ -4067,14 +4239,95 @@ emitDeclImpl(decl, nullptr); IRFieldExtract* fieldExtract = (IRFieldExtract*) inst; - emitIRInstResultDecl(context, inst); emitIROperand(context, fieldExtract->getBase()); emit("."); emit(getName(fieldExtract->getField())); - emit(";\n"); } break; + case kIROp_FieldAddress: + { + // Extract field "address" from aggregate + + IRFieldAddress* ii = (IRFieldAddress*) inst; + + if (!isDerefBaseImplicit(context, ii->getBase())) + { + emitIROperand(context, ii->getBase()); + emit("."); + } + + emit(getName(ii->getField())); + } + break; + + case kIROp_Intrinsic_Add: + emitIROperand(context, inst->getArg(1)); + emit(" + "); + emitIROperand(context, inst->getArg(2)); + break; + + + case kIROp_Intrinsic_sample_t_s_u: + emitIROperand(context, inst->getArg(1)); + emit(".Sample("); + emitIROperand(context, inst->getArg(2)); + emit(", "); + emitIROperand(context, inst->getArg(3)); + emit(")"); + break; + + case kIROp_Load: + // TODO: this logic will really only work for a simple variable reference... + emitIROperand(context, inst->getArg(1)); + break; + + case kIROp_Call: + { + emitIROperand(context, inst->getArg(1)); + emit("("); + UInt argCount = inst->getArgCount() - 2; + for( UInt aa = 0; aa < argCount; ++aa ) + { + if(aa != 0) emit(", "); + emitIROperand(context, inst->getArg(aa + 2)); + } + emit(")"); + } + break; + + default: + emit("/* uhandled */"); + break; + } + } + + void emitIRInst( + EmitContext* context, + IRInst* inst) + { + if (shouldFoldIRInstIntoUseSites(context, inst)) + { + return; + } + + switch(inst->op) + { + default: + emitIRInstResultDecl(context, inst); + emitIRInstExpr(context, inst); + emit(";\n"); + break; + + case kIROp_Param: + // Don't emit parameters, since they are declared as part of the function. + break; + + case kIROp_FieldAddress: + // skip during code emit, since it should be + // folded into use site(s) + break; + case kIROp_ReturnVoid: emit("return;\n"); break; @@ -4084,10 +4337,6 @@ emitDeclImpl(decl, nullptr); emitIROperand(context, ((IRReturnVal*) inst)->getVal()); emit(";\n"); break; - - default: - emit("// uhandled\n"); - break; } } @@ -4102,6 +4351,17 @@ emitDeclImpl(decl, nullptr); } } + void emitIRLayoutSemantics( + EmitContext* context, + IRInst* inst) + { + auto decoration = inst->findDecoration<IRLayoutDecoration>(); + if (decoration) + { + emitHLSLRegisterSemantics((VarLayout*) decoration->layout); + } + } + void emitIRFunc( EmitContext* context, IRFunc* func) @@ -4172,6 +4432,70 @@ emitDeclImpl(decl, nullptr); emit("};\n"); } + void emitIRParameterBlock( + EmitContext* context, + IRVar* varDecl, + IRUniformBufferType* type) + { + emit("cbuffer "); + emit(getName(varDecl)); + emitIRLayoutSemantics(context, varDecl); + emit("\n{\n"); + + auto elementType = type->getElementType(); + switch( elementType->op ) + { + case kIROp_StructType: + { + auto structType = (IRStructDecl*) elementType; + for(auto ff = structType->getFirstField(); ff; ff = ff->getNextField()) + { + auto fieldType = ff->getFieldType(); + emitIRType(context, fieldType, getName(ff)); + + emitIRSemantics(context, ff); + + emit(";\n"); + } + } + break; + + default: + emit("/* unexpected */"); + break; + } + + emit("}\n"); + } + + void emitIRVar( + EmitContext* context, + IRVar* varDecl) + { + auto allocatedType = varDecl->getType(); + auto varType = ((IRPtrType*) allocatedType)->getValueType(); + + switch( varType->op ) + { + case kIROp_ConstantBufferType: + case kIROp_TextureBufferType: + emitIRParameterBlock(context, varDecl, (IRUniformBufferType*) varType); + return; + + default: + break; + } + + + emitIRType(context, varType, getName(varDecl)); + + emitIRSemantics(context, varDecl); + + emitIRLayoutSemantics(context, varDecl); + + emit(";\n"); + } + void emitIRGlobalInst( EmitContext* context, IRInst* inst) @@ -4188,6 +4512,10 @@ emitDeclImpl(decl, nullptr); emitIRStruct(context, (IRStructDecl*) inst); break; + case kIROp_Var: + emitIRVar(context, (IRVar*) inst); + break; + default: break; } @@ -4320,8 +4648,8 @@ String emitEntryPoint( // // We'll try to detect the cases here: // -#if 0 - if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING )) + if((translationUnit->compileRequest->compileFlags & SLANG_COMPILE_FLAG_USE_IR) + && !(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING )) { // This seems to be case (3), because the user is asking for full // checking, and so we can assume we understand the code fully. @@ -4332,20 +4660,14 @@ String emitEntryPoint( auto lowered = lowerEntryPointToIR(entryPoint, programLayout, target); - dumpIR(lowered); +// dumpIR(lowered); // TODO: do we want to emit directly from IR, or translate the // IR back into AST for emission? visitor.emitIRModule(&context, lowered); - - throw 99; - } - else if( -#else - if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ) || -#endif + else if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING ) || translationUnit->compileRequest->loadedModulesList.Count() != 0) { // The user has `import`ed some Slang modules, and so we are in case (2) |
