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 | |
| 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')
| -rw-r--r-- | source/slang/emit.cpp | 614 | ||||
| -rw-r--r-- | source/slang/intrinsic-defs.h | 4 | ||||
| -rw-r--r-- | source/slang/ir-inst-defs.h | 16 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 143 | ||||
| -rw-r--r-- | source/slang/ir.h | 92 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 255 | ||||
| -rw-r--r-- | source/slang/modifier-defs.h | 14 | ||||
| -rw-r--r-- | source/slang/options.cpp | 4 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 15 | ||||
| -rw-r--r-- | source/slang/slang-stdlib.cpp | 31 |
10 files changed, 1011 insertions, 177 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) diff --git a/source/slang/intrinsic-defs.h b/source/slang/intrinsic-defs.h index a272bd51c..336f1eff1 100644 --- a/source/slang/intrinsic-defs.h +++ b/source/slang/intrinsic-defs.h @@ -68,6 +68,10 @@ INTRINSIC(InnerProduct_Vector_Matrix) INTRINSIC(InnerProduct_Matrix_Vector) INTRINSIC(InnerProduct_Matrix_Matrix) +// Texture sampling operation of the form `t.Sample(s,u)` +INTRINSIC(sample_t_s_u) + + diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h index d594d2755..2548dc22a 100644 --- a/source/slang/ir-inst-defs.h +++ b/source/slang/ir-inst-defs.h @@ -16,11 +16,17 @@ INST(Int32Type, type.i32, 0, 0) INST(UInt32Type, type.u32, 0, 0) INST(StructType, type.struct, 0, PARENT) INST(FuncType, func_type, 0, 0) +INST(PtrType, ptr_type, 1, 0) +INST(TextureType, texture_type, 2, 0) +INST(SamplerType, sampler_type, 1, 0) +INST(ConstantBufferType, constant_buffer_type, 1, 0) +INST(TextureBufferType, texture_buffer_type, 1, 0) INST(IntLit, integer_constant, 0, 0) INST(FloatLit, float_constant, 0, 0) -INST(Construct, construct, 0, 0) +INST(Construct, construct, 0, 0) +INST(Call, call, 1, 0) INST(Module, module, 0, PARENT) INST(Func, func, 0, PARENT) @@ -28,8 +34,14 @@ INST(Block, block, 0, PARENT) INST(Param, param, 0, 0) INST(StructField, field, 0, 0) +INST(Var, var, 0, 0) + +INST(Load, load, 1, 0) +INST(Store, store, 2, 0) + +INST(FieldExtract, get_field, 2, 0) +INST(FieldAddress, get_field_addr, 2, 0) -INST(FieldExtract, get_field, 2, 0) INST(ReturnVal, return_val, 1, 0) INST(ReturnVoid, return_void, 1, 0) diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index 8e4f789ef..3f07a685f 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -365,8 +365,10 @@ namespace Slang UInt size, IROp op, IRType* type, - UInt argCount, - IRValue* const* args) + UInt fixedArgCount, + IRValue* const* fixedArgs, + UInt varArgCount = 0, + IRValue* const* varArgs = nullptr) { // First, we need to pick a good insertion point // for the instruction, which we do by looking @@ -378,9 +380,14 @@ namespace Slang { parent = joinParentInstsForInsertion(parent, type->parent); } - for( UInt aa = 0; aa < argCount; ++aa ) + for( UInt aa = 0; aa < fixedArgCount; ++aa ) { - auto arg = args[aa]; + auto arg = fixedArgs[aa]; + parent = joinParentInstsForInsertion(parent, arg->parent); + } + for( UInt aa = 0; aa < varArgCount; ++aa ) + { + auto arg = varArgs[aa]; parent = joinParentInstsForInsertion(parent, arg->parent); } @@ -405,7 +412,7 @@ namespace Slang // way: we will construct a temporary instruction and // then use it to look up in a cache of instructions. - IRInst* keyInst = createInstImpl(builder, size, op, type, argCount ,args); + IRInst* keyInst = createInstImpl(builder, size, op, type, fixedArgCount, fixedArgs, varArgCount, varArgs); keyInst->parent = parent; IRInstKey key; @@ -452,6 +459,27 @@ namespace Slang static T* findOrEmitInst( IRBuilder* builder, IROp op, + IRType* type, + UInt fixedArgCount, + IRValue* const* fixedArgs, + UInt varArgCount, + IRValue* const* varArgs) + { + return (T*) findOrEmitInstImpl( + builder, + sizeof(T) + varArgCount * sizeof(IRUse), + op, + type, + fixedArgCount, + fixedArgs, + varArgCount, + varArgs); + } + + template<typename T> + static T* findOrEmitInst( + IRBuilder* builder, + IROp op, IRType* type) { return (T*) findOrEmitInstImpl( @@ -630,6 +658,22 @@ namespace Slang getTypeType()); } + IRType* IRBuilder::getIntrinsicType( + IROp op, + UInt argCount, + IRValue* const* args) + { + return findOrEmitInst<IRType>( + this, + op, + getTypeType(), + 0, + nullptr, + argCount, + args); + } + + IRStructDecl* IRBuilder::createStructType() { return createInst<IRStructDecl>( @@ -652,6 +696,7 @@ namespace Slang IRType* const* paramTypes, IRType* resultType) { + // TODO: need to unique things here! auto inst = createInstWithTrailingArgs<IRFuncType>( this, kIROp_FuncType, @@ -664,6 +709,19 @@ namespace Slang return inst; } + IRType* IRBuilder::getPtrType( + IRType* valueType) + { + auto inst = findOrEmitInst<IRPtrType>( + this, + kIROp_PtrType, + getTypeType(), + 1, + (IRValue* const*) &valueType); + return inst; + } + + IRValue* IRBuilder::getBoolValue(bool value) { SLANG_UNIMPLEMENTED_X("IR"); @@ -689,6 +747,24 @@ namespace Slang &value); } + IRInst* IRBuilder::emitCallInst( + IRType* type, + IRValue* func, + UInt argCount, + IRValue* const* args) + { + auto inst = createInstWithTrailingArgs<IRCall>( + this, + kIROp_Call, + type, + 1, + &func, + argCount, + args); + addInst(inst); + return inst; + } + IRInst* IRBuilder::emitIntrinsicInst( IRType* type, IntrinsicOp intrinsicOp, @@ -763,6 +839,40 @@ namespace Slang return inst; } + IRVar* IRBuilder::emitVar( + IRType* type) + { + auto allocatedType = getPtrType(type); + auto inst = createInst<IRVar>( + this, + kIROp_Var, + allocatedType); + addInst(inst); + return inst; + } + + IRInst* IRBuilder::emitLoad( + IRValue* ptr) + { + auto ptrType = ptr->getType(); + if( ptrType->op != kIROp_PtrType ) + { + // Bad! + return nullptr; + } + + auto valueType = ((IRPtrType*) ptrType)->getValueType(); + + auto inst = createInst<IRLoad>( + this, + kIROp_Load, + valueType, + ptr); + + addInst(inst); + return inst; + } + IRInst* IRBuilder::emitFieldExtract( IRType* type, IRValue* base, @@ -779,6 +889,22 @@ namespace Slang return inst; } + IRInst* IRBuilder::emitFieldAddress( + IRType* type, + IRValue* base, + IRStructField* field) + { + auto inst = createInst<IRFieldAddress>( + this, + kIROp_FieldAddress, + type, + base, + field); + + addInst(inst); + return inst; + } + IRInst* IRBuilder::emitReturn( IRValue* val) { @@ -824,6 +950,13 @@ namespace Slang return decoration; } + IRLayoutDecoration* IRBuilder::addLayoutDecoration(IRInst* inst, Layout* layout) + { + auto decoration = addDecoration<IRLayoutDecoration>(inst); + decoration->layout = layout; + return decoration; + } + // diff --git a/source/slang/ir.h b/source/slang/ir.h index fcebf5d15..aa7b6a045 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -7,6 +7,8 @@ // similar in spirit to LLVM (but much simpler). // +#include "type-layout.h" + // We need the definition of `BaseType` which currently belongs to the AST #include "syntax.h" @@ -72,6 +74,7 @@ struct IRUse enum IRDecorationOp : uint16_t { kIRDecorationOp_HighLevelDecl, + kIRDecorationOp_Layout, }; // A "decoration" that gets applied to an instruction. @@ -161,6 +164,15 @@ struct IRHighLevelDeclDecoration : IRDecoration Decl* decl; }; +// Associates an IR-level decoration with a source layout +struct IRLayoutDecoration : IRDecoration +{ + enum { kDecorationOp = kIRDecorationOp_Layout }; + + Layout* layout; +}; + + typedef long long IRIntegerValue; typedef double IRFloatingPointValue; @@ -211,6 +223,41 @@ struct IRFuncType : IRType } }; +struct IRPtrType : IRType +{ + IRUse valueType; + + IRType* getValueType() { return (IRType*) valueType.usedValue; } +}; + +struct IRTextureType : IRType +{ + IRUse flavor; + IRUse elementType; + + IRIntegerValue getFlavor() { return ((IRConstant*) flavor.usedValue)->u.intVal; } + IRType* getElementType() { return (IRType*) elementType.usedValue; } +}; + +struct IRUniformBufferType : IRType +{ + IRUse elementType; + IRType* getElementType() { return (IRType*) elementType.usedValue; } +}; + +struct IRConstantBufferType : IRUniformBufferType {}; +struct IRTextureBufferType : IRUniformBufferType {}; + +struct IRCall : IRInst +{ + IRUse func; +}; + +struct IRLoad : IRInst +{ + IRUse ptr; +}; + struct IRStructField; struct IRFieldExtract : IRInst { @@ -221,6 +268,16 @@ struct IRFieldExtract : IRInst IRStructField* getField() { return (IRStructField*) field.usedValue; } }; +struct IRFieldAddress : IRInst +{ + IRUse base; + IRUse field; + + IRInst* getBase() { return base.usedValue; } + IRStructField* getField() { return (IRStructField*) field.usedValue; } +}; + + // A instruction that ends a basic block (usually because of control flow) struct IRTerminatorInst : IRInst {}; @@ -285,6 +342,9 @@ struct IRParam : IRInst IRParam* getNextParam(); }; +struct IRVar : IRInst +{}; + // A function is a parent to zero or more blocks of instructions. // // A function is itself a value, so that it can be a direct operand of @@ -360,6 +420,11 @@ struct IRBuilder IRType* getVoidType(); IRType* getBlockType(); + IRType* getIntrinsicType( + IROp op, + UInt argCount, + IRValue* const* args); + IRStructDecl* createStructType(); IRStructField* createStructField(IRType* fieldType); @@ -368,10 +433,19 @@ struct IRBuilder IRType* const* paramTypes, IRType* resultType); + IRType* getPtrType( + IRType* valueType); + IRValue* getBoolValue(bool value); IRValue* getIntValue(IRType* type, IRIntegerValue value); IRValue* getFloatValue(IRType* type, IRFloatingPointValue value); + IRInst* emitCallInst( + IRType* type, + IRValue* func, + UInt argCount, + IRValue* const* args); + IRInst* emitIntrinsicInst( IRType* type, IntrinsicOp intrinsicOp, @@ -393,11 +467,22 @@ struct IRBuilder IRParam* emitParam( IRType* type); + IRVar* emitVar( + IRType* type); + + IRInst* emitLoad( + IRValue* ptr); + IRInst* emitFieldExtract( IRType* type, IRValue* base, IRStructField* field); + IRInst* emitFieldAddress( + IRType* type, + IRValue* basePtr, + IRStructField* field); + IRInst* emitReturn( IRValue* val); @@ -414,7 +499,14 @@ struct IRBuilder return (T*) addDecorationImpl(inst, sizeof(T), op); } + template<typename T> + T* addDecoration(IRInst* inst) + { + return (T*) addDecorationImpl(inst, sizeof(T), IRDecorationOp(T::kDecorationOp)); + } + IRHighLevelDeclDecoration* addHighLevelDeclDecoration(IRInst* inst, Decl* decl); + IRLayoutDecoration* addLayoutDecoration(IRInst* inst, Layout* layout); }; void dumpIR(IRModule* module); diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 781209dce..d4cac0337 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -14,6 +14,7 @@ struct LoweredValInfo { None, Simple, + Ptr, }; union @@ -35,7 +36,14 @@ struct LoweredValInfo info.val = v; return info; } -}; + + static LoweredValInfo ptr(IRValue* v) + { + LoweredValInfo info; + info.flavor = Flavor::Ptr; + info.val = v; + return info; + }}; struct SharedIRGenContext { @@ -70,6 +78,25 @@ IRValue* getSimpleVal(LoweredValInfo lowered) } } +IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered) +{ + switch(lowered.flavor) + { + case LoweredValInfo::Flavor::None: + return nullptr; + + case LoweredValInfo::Flavor::Simple: + return lowered.val; + + case LoweredValInfo::Flavor::Ptr: + return context->irBuilder->emitLoad(lowered.val); + + default: + SLANG_UNEXPECTED("unhandled value flavor"); + return nullptr; + } +} + struct LoweredTypeInfo { enum class Flavor @@ -192,8 +219,46 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower SLANG_UNIMPLEMENTED_X("type lowering"); } + LoweredTypeInfo visitFuncType(FuncType* type) + { + LoweredValInfo loweredFunc = ensureDecl(context, type->declRef); + return getSimpleVal(loweredFunc)->getType(); + } + + void addGenericArgs(List<IRValue*>* ioArgs, DeclRefBase declRef) + { + auto subs = declRef.substitutions; + while(subs) + { + for(auto aa : subs->args) + { + (*ioArgs).Add(getSimpleVal(lowerVal(context, aa))); + } + subs = subs->outer; + } + } + LoweredTypeInfo visitDeclRefType(DeclRefType* type) { + // We need to detect builtin/intrinsic types here, since they should map to custom modifiers + // We need to catch builtin/intrinsic types here + if( auto intrinsicTypeMod = type->declRef.getDecl()->FindModifier<IntrinsicTypeModifier>() ) + { + auto builder = getBuilder(); + auto intType = builder->getBaseType(BaseType::Int); + // + List<IRValue*> irArgs; + for( auto val : intrinsicTypeMod->irOperands ) + { + irArgs.Add(builder->getIntValue(intType, val)); + } + + addGenericArgs(&irArgs, type->declRef); + + auto irType = getBuilder()->getIntrinsicType(IROp(intrinsicTypeMod->irOp), irArgs.Count(), irArgs.Buffer()); + return LoweredTypeInfo(irType); + } + // Catch-all for user-defined type references LoweredValInfo loweredDeclRef = ensureDecl(context, type->declRef); @@ -222,7 +287,6 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower return getBuilder()->getVectorType(irElementType, irElementCount); } - }; LoweredValInfo lowerVal( @@ -306,7 +370,8 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> switch( argInfo.flavor ) { case LoweredValInfo::Flavor::Simple: - args.Add(getSimpleVal(argInfo)); + case LoweredValInfo::Flavor::Ptr: + args.Add(getSimpleVal(context, argInfo)); break; default: @@ -315,19 +380,39 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> } } - LoweredValInfo lowerIntrinsicCall( - InvokeExpr* expr, - IntrinsicOp intrinsicOp) + // Collect the lowered arguments for a call expression + void addCallArgs( + InvokeExpr* expr, + List<IRValue*>* ioArgs) { - auto type = lowerSimpleType(context, expr->type); + auto& irArgs = *ioArgs; + + // TODO: should unwrap any layers of identity expressions around this... + if( auto baseMemberExpr = expr->FunctionExpr.As<MemberExpr>() ) + { + // This call took the form of a member function call, so + // we need to correctly add the `this` argument as + // an explicit argument. + // + auto loweredBase = lowerExpr(context, baseMemberExpr->BaseExpression); + addArgs(&irArgs, loweredBase); + } - List<IRValue*> irArgs; for( auto arg : expr->Arguments ) { auto loweredArg = lowerExpr(context, arg); addArgs(&irArgs, loweredArg); } + } + + LoweredValInfo lowerIntrinsicCall( + InvokeExpr* expr, + IntrinsicOp intrinsicOp) + { + auto type = lowerSimpleType(context, expr->type); + List<IRValue*> irArgs; + addCallArgs(expr, &irArgs); UInt argCount = irArgs.Count(); return LoweredValInfo::simple(getBuilder()->emitIntrinsicInst(type, intrinsicOp, argCount, &irArgs[0])); @@ -335,14 +420,15 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> LoweredValInfo lowerSimpleCall(InvokeExpr* expr) { + auto type = lowerSimpleType(context, expr->type); + auto loweredFunc = lowerExpr(context, expr->FunctionExpr); - for( auto arg : expr->Arguments ) - { - auto loweredArg = lowerExpr(context, arg); - } + List<IRValue*> irArgs; + addCallArgs(expr, &irArgs); + UInt argCount = irArgs.Count(); - SLANG_UNIMPLEMENTED_X("codegen for invoke expression"); + return LoweredValInfo::simple(getBuilder()->emitCallInst(type, getSimpleVal(loweredFunc), argCount, irArgs.Buffer())); } LoweredValInfo visitInvokeExpr(InvokeExpr* expr) @@ -409,6 +495,19 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> } break; + case LoweredValInfo::Flavor::Ptr: + { + // We are "extracting" a field from an lvalue address, + // which means we should just compute an lvalue + // representing the field address. + IRValue* irBasePtr = base.val; + return LoweredValInfo::ptr( + getBuilder()->emitFieldAddress( + getBuilder()->getPtrType(getSimpleType(fieldType)), + irBasePtr, + (IRStructField*) getSimpleVal(field))); + } + break; default: SLANG_UNIMPLEMENTED_X("codegen for field extract"); } @@ -438,7 +537,39 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> LoweredValInfo visitDerefExpr(DerefExpr* expr) { - SLANG_UNIMPLEMENTED_X("codegen for deref expression"); + auto loweredType = lowerType(context, expr->type); + auto loweredBase = lowerExpr(context, expr->base); + + // TODO: handle tupel-type for `base` + + // The type of the lowered base must by some kind of pointer, + // in order for a dereference to make senese, so we just + // need to extract the value type from that pointer here. + // + auto loweredBaseVal = getSimpleVal(context, loweredBase); + auto loweredBaseType = loweredBaseVal->getType(); + switch( loweredBaseType->op ) + { + case kIROp_PtrType: + // TODO: should we enumerate these explicitly? + case kIROp_ConstantBufferType: + case kIROp_TextureBufferType: + // Note that we do *not* perform an actual `load` operation + // here, but rather just use the pointer value to construct + // an appropriate `LoweredValInfo` representing the underlying + // dereference. + // + // This is important so that an expression like `&((*foo).bar)` + // (which is desugared from `&foo->bar`) can be handled; such + // an expression does *not* perform a dereference at runtime, + // and is just a bit of pointer math. + // + return LoweredValInfo::ptr(loweredBaseVal); + + default: + SLANG_UNIMPLEMENTED_X("codegen for deref expression"); + return LoweredValInfo(); + } } LoweredValInfo visitTypeCastExpr(TypeCastExpr* expr) @@ -521,15 +652,29 @@ void lowerStmt( return visitor.dispatch(stmt); } +void assign( + IRGenContext* context, + LoweredValInfo const& left, + LoweredValInfo const& right) +{ + SLANG_UNIMPLEMENTED_X("assignment"); +} + struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> { - IRGenContext* context; + IRGenContext* context; + Layout* layout; IRBuilder* getBuilder() { return context->irBuilder; } + Layout* getLayout() + { + return layout; + } + LoweredValInfo visitDeclBase(DeclBase* decl) { SLANG_UNIMPLEMENTED_X("decl catch-all"); @@ -540,6 +685,59 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> SLANG_UNIMPLEMENTED_X("decl catch-all"); } + LoweredValInfo visitVarDeclBase(VarDeclBase* decl) + { + // A user-defined variable declaration will usually turn into + // an `alloca` operation for the variable's storage, + // plus some code to initialize it and then store to the variable. + // + // TODO: we may want to special-case things when the variable's + // type, qualifiers, or context mark it as something that can't + // be mutable (or even do some limited dataflow pass to check + // which variables ever get assigned) so that we can directly + // emit an SSA value in this common case. + // + + auto varType = lowerType(context, decl->getType()); + + LoweredValInfo varVal; + + switch( varType.flavor ) + { + case LoweredTypeInfo::Flavor::Simple: + { + auto irAlloc = getBuilder()->emitVar(getSimpleType(varType)); + + getBuilder()->addHighLevelDeclDecoration(irAlloc, decl); + + if (getLayout()) + { + getBuilder()->addLayoutDecoration(irAlloc, getLayout()); + } + + + varVal = LoweredValInfo::ptr(irAlloc); + } + break; + + default: + SLANG_UNIMPLEMENTED_X("struct field type"); + } + + if( auto initExpr = decl->initExpr ) + { + auto initVal = lowerExpr(context, initExpr); + + assign(context, varVal, initVal); + } + + context->shared->declValues.Add( + DeclRef<VarDeclBase>(decl, nullptr), + varVal); + + return varVal; + } + LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl) { // User-defined aggregate type: need to translate into @@ -621,7 +819,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> auto irFuncType = getBuilder()->getFuncType( paramTypes.Count(), - ¶mTypes[0], + paramTypes.Buffer(), irResultType); irFunc->type.init(irFunc, irFuncType); @@ -637,9 +835,11 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> LoweredValInfo lowerDecl( IRGenContext* context, - Decl* decl) + Decl* decl, + Layout* layout) { DeclLoweringVisitor visitor; + visitor.layout = layout; visitor.context = context; return visitor.dispatch(decl); } @@ -658,9 +858,26 @@ LoweredValInfo ensureDecl( // from the declaration reference, so that they can be // applied correctly to the declaration itself... + IRBuilder subIRBuilder; + subIRBuilder.shared = context->irBuilder->shared; + subIRBuilder.parentInst = subIRBuilder.shared->module; + IRGenContext subContext = *context; - result = lowerDecl(context, declRef.getDecl()); + subContext.irBuilder = &subIRBuilder; + + RefPtr<VarLayout> layout; + auto globalScopeLayout = shared->programLayout->globalScopeLayout; + if (auto globalParameterBlockLayout = globalScopeLayout.As<ParameterBlockTypeLayout>()) + { + globalScopeLayout = globalParameterBlockLayout->elementTypeLayout; + } + if (auto globalStructTypeLayout = globalScopeLayout.As<StructTypeLayout>()) + { + globalStructTypeLayout->mapVarToLayout.TryGetValue(declRef.getDecl(), layout); + } + + result = lowerDecl(&subContext, declRef.getDecl(), layout); shared->declValues[declRef] = result; @@ -699,7 +916,7 @@ static void lowerEntryPointToIR( // TODO: entry point lowering is probably *not* just like lowering a function... - lowerDecl(context, entryPointFunc); + lowerDecl(context, entryPointFunc, entryPointLayout); } IRModule* lowerEntryPointToIR( diff --git a/source/slang/modifier-defs.h b/source/slang/modifier-defs.h index 597d59d9a..d7d0391e4 100644 --- a/source/slang/modifier-defs.h +++ b/source/slang/modifier-defs.h @@ -209,6 +209,20 @@ SYNTAX_CLASS(MagicTypeModifier, Modifier) FIELD(uint32_t, tag) END_SYNTAX_CLASS() +// A modifier applied to declarations of builtin types to indicate how they +// should be lowered to the IR. +// +// TODO: This should really subsume `BuiltinTypeModifier` and +// `MagicTypeModifier` so that we don't have to apply all of them. +SYNTAX_CLASS(IntrinsicTypeModifier, Modifier) + // The IR opcode to use when constructing a type + FIELD(uint32_t, irOp) + + // Additional literal opreands to provide when creating instances. + // (e.g., for a texture type this passes in shape/mutability info) + FIELD(List<uint32_t>, irOperands) +END_SYNTAX_CLASS() + // Modifiers that affect the storage layout for matrices SIMPLE_SYNTAX_CLASS(MatrixLayoutModifier, Modifier) diff --git a/source/slang/options.cpp b/source/slang/options.cpp index 0e329ccd2..669ab1827 100644 --- a/source/slang/options.cpp +++ b/source/slang/options.cpp @@ -258,6 +258,10 @@ struct OptionsParser { flags |= SLANG_COMPILE_FLAG_SPLIT_MIXED_TYPES; } + else if(argStr == "-use-ir" ) + { + flags |= SLANG_COMPILE_FLAG_USE_IR; + } else if (argStr == "-backend" || argStr == "-target") { String name = tryReadCommandLineArgument(arg, &argCursor, argEnd); diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 7da309d29..9c0d692e6 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -3916,6 +3916,20 @@ namespace Slang return modifier; } + static RefPtr<RefObject> parseIntrinsicTypeModifier(Parser* parser, void* /*userData*/) + { + RefPtr<IntrinsicTypeModifier> modifier = new IntrinsicTypeModifier(); + parser->ReadToken(TokenType::LParent); + modifier->irOp = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); + while( AdvanceIf(parser, TokenType::Comma) ) + { + auto operand = uint32_t(StringToInt(parser->ReadToken(TokenType::IntegerLiteral).Content)); + modifier->irOperands.Add(operand); + } + parser->ReadToken(TokenType::RParent); + + return modifier; + } static RefPtr<RefObject> parseImplicitConversionModifier(Parser* parser, void* /*userData*/) { RefPtr<ImplicitConversionModifier> modifier = new ImplicitConversionModifier(); @@ -4020,6 +4034,7 @@ namespace Slang MODIFIER(__builtin_type, parseBuiltinTypeModifier); MODIFIER(__magic_type, parseMagicTypeModifier); + MODIFIER(__intrinsic_type, parseIntrinsicTypeModifier); MODIFIER(__implicit_conversion, parseImplicitConversionModifier); #undef MODIFIER diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp index 89fcd0800..afcca4d7f 100644 --- a/source/slang/slang-stdlib.cpp +++ b/source/slang/slang-stdlib.cpp @@ -1,6 +1,7 @@ // slang-stdlib.cpp #include "compiler.h" +#include "ir.h" #include "syntax.h" #define STRINGIZE(x) STRINGIZE2(x) @@ -1393,8 +1394,15 @@ namespace Slang // Declare additional built-in generic types // EMIT_LINE_DIRECTIVE(); - sb << "__generic<T> __magic_type(ConstantBuffer) struct ConstantBuffer {};\n"; - sb << "__generic<T> __magic_type(TextureBuffer) struct TextureBuffer {};\n"; + + + sb << "__generic<T>\n"; + sb << "__intrinsic_type(" << kIROp_ConstantBufferType << ")\n"; + sb << "__magic_type(ConstantBuffer) struct ConstantBuffer {};\n"; + + sb << "__generic<T>\n"; + sb << "__intrinsic_type(" << kIROp_TextureBufferType << ")\n"; + sb << "__magic_type(TextureBuffer) struct TextureBuffer {};\n"; static const char* kComponentNames[]{ "x", "y", "z", "w" }; @@ -1527,8 +1535,15 @@ namespace Slang // Declare built-in texture and sampler types - sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ") struct SamplerState {};"; - sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerComparisonState) << ") struct SamplerComparisonState {};"; + + + sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerState) << ")\n"; + sb << "__intrinsic_type(" << kIROp_SamplerType << ", " << int(SamplerStateType::Flavor::SamplerState) << ")\n"; + sb << "struct SamplerState {};"; + + sb << "__magic_type(SamplerState," << int(SamplerStateType::Flavor::SamplerComparisonState) << ")\n"; + sb << "__intrinsic_type(" << kIROp_SamplerType << ", " << int(SamplerStateType::Flavor::SamplerComparisonState) << ")\n"; + sb << "struct SamplerComparisonState {};"; // TODO(tfoley): Need to handle `RW*` variants of texture types as well... static const struct { @@ -1582,7 +1597,9 @@ namespace Slang // TODO: allow for multisample count to come in as well... sb << "__generic<T = float4> "; - sb << "__magic_type(Texture," << int(flavor) << ") struct "; + sb << "__magic_type(Texture," << int(flavor) << ")\n"; + sb << "__intrinsic_type(" << kIROp_TextureType << ", " << flavor << ")\n"; + sb << "struct "; sb << kBaseTextureAccessLevels[accessLevel].name; sb << name; if (isMultisample) sb << "MS"; @@ -1787,6 +1804,10 @@ namespace Slang // `Sample()` sb << "__intrinsic(glsl, \"texture($p, $1)\")\n"; + + // TODO: only enable if IR is being used? + sb << "__intrinsic_op(sample_t_s_u)\n"; + sb << "__intrinsic\n"; sb << "T Sample(SamplerState s, "; sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location);\n"; |
