From ad19484792dcc5a1fb90720614830c66c4b9712d Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 17 Aug 2017 13:40:50 -0700 Subject: Add some dummy logic to print IR to HLSL - Change IR instructions to just hold an integer opcode instead of a pointer to the "info" structure - Externalize definition of IR instructions to a header file, and use the "X macro" approach to allow generating different definitions - Add notion of function types to the IR, so that we can easily query the result type of a function - Add some convenience accesors to allow walking the IR in a strongly-typed manner (e.g., iterate over the parameters of a function) - TODO: these should really be changed to assert the type of things, as least in debug builds - Add very basic logic to `emit.cpp` so that it can walk the generated IR and start printing it back as HLSL - This isn't meant to be usable as-is, but it is a step toward where we need to go --- source/slang/ir.h | 73 ++++++++++++++++++++++++++++++++++++++++++++++++++++--- 1 file changed, 69 insertions(+), 4 deletions(-) (limited to 'source/slang/ir.h') diff --git a/source/slang/ir.h b/source/slang/ir.h index 0a0c8d612..e583997fd 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -27,6 +27,15 @@ enum : IROpFlags kIROpFlag_Parent = 1 << 0, }; +enum IROp : uint16_t +{ + +#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) \ + kIROp_##ID, + +#include "ir-inst-defs.h" +}; + // A logical operation/opcode in the IR struct IROpInfo { @@ -67,7 +76,7 @@ typedef uint32_t IRInstID; struct IRInst { // The operation that this value represents - IROpInfo const* op; + IROp op; // A unique ID to represent the op when printing // (or zero to indicate that the value of this @@ -93,7 +102,19 @@ struct IRInst // The type of this value IRUse type; + IRType* getType() { return (IRType*) type.usedValue; } + + UInt getArgCount() + { + return argCount; + } + IRUse* getArgs(); + + IRInst* getArg(UInt index) + { + return getArgs()[index].usedValue; + } }; typedef IRInst IRValue; @@ -127,15 +148,40 @@ struct IRVectorType : IRType { IRUse elementType; IRUse elementCount; + + IRType* getElementType() { return (IRType*) elementType.usedValue; } + IRInst* getElementCount() { return elementCount.usedValue; } }; struct IRStructType : IRType -{}; +{ + + UInt getFieldCount() { return getArgCount() - 1; } + IRType* getFieldType(UInt index) { return (IRType*) getArg(index + 1); } +}; + +struct IRFuncType : IRType +{ + IRUse resultType; + // parameter tyeps are varargs... + + IRType* getResultType() { return (IRType*) resultType.usedValue; } + UInt getParamCount() + { + return getArgCount() - 2; + } + IRType* getParamType(UInt index) + { + return (IRType*) getArg(2 + index); + } +}; struct IRFieldExtract : IRInst { IRUse base; UInt fieldIndex; + + IRInst* getBase() { return base.usedValue; } }; // A instruction that ends a basic block (usually because of control flow) @@ -148,6 +194,8 @@ struct IRReturn : IRTerminatorInst struct IRReturnVal : IRReturn { IRUse val; + + IRInst* getVal() { return val.usedValue; } }; struct IRReturnVoid : IRReturn @@ -174,14 +222,16 @@ struct IRBlock : IRParentInst // Note that in a valid program, every block must end with // a "terminator" instruction, so these should be non-NULL, // and `last` should actually be an `IRTerminatorInst`. - IRInst* firstChild; - IRInst* lastChild; + + IRBlock* getPrevBlock() { return (IRBlock*) prevInst; } + IRBlock* getNextBlock() { return (IRBlock*) nextInst; } }; // A function parameter is represented by an instruction // in the entry block of a function. struct IRParam : IRInst { + IRParam* getNextParam(); }; // A function is a parent to zero or more blocks of instructions. @@ -190,6 +240,16 @@ struct IRParam : IRInst // an instruction (e.g., a call). struct IRFunc : IRParentInst { + IRFuncType* getType() { return (IRFuncType*) type.usedValue; } + + IRType* getResultType() { return getType()->getResultType(); } + UInt getParamCount() { return getType()->getParamCount(); } + IRType* getParamType(UInt index) { return getType()->getParamType(index); } + + IRBlock* getFirstBlock() { return (IRBlock*) firstChild; } + IRBlock* getLastBlock() { return (IRBlock*) lastChild; } + + IRParam* getFirstParam(); }; // A module is a parent to functions, global variables, types, etc. @@ -252,6 +312,11 @@ struct IRBuilder UInt fieldCount, IRType* const* fieldTypes); + IRType* getFuncType( + UInt paramCount, + IRType* const* paramTypes, + IRType* resultType); + IRValue* getBoolValue(bool value); IRValue* getIntValue(IRType* type, IRIntegerValue value); IRValue* getFloatValue(IRType* type, IRFloatingPointValue value); -- cgit v1.2.3 From 95348fdb623509eb22c04d4c7c19af8228c5a533 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 17 Aug 2017 14:17:29 -0700 Subject: [ir] Represent fields more direclty Previously, a `StructType` was an ordinary instruction that took a variable number of types are operands, representing the types of fields. This ends up being inconvenient for a few reasons: - To add decorations to the fields, you'd end up having to decorate the struct type instead (SPIR-V has this problem) - You need to compute field indices during lowering, when you might prefer to defer that until later - The get/set field operations now need an index, which needs to be an explicit operand, which means a magic numeric literal floating around to represent the index The new approach fixes for the first two of these, and at least makes the last one a bit nicer. A `StructDecl` is now a parent instruction, and its sub-instructions represent the fields of the type - each field is an explicit instruction of type `StructField`. The operation to extract a field takes a direct reference the struct field, so everything is quite explicit. --- source/slang/emit.cpp | 22 +++++++++------------ source/slang/ir-inst-defs.h | 7 ++++--- source/slang/ir.cpp | 32 +++++++++++++++--------------- source/slang/ir.h | 37 +++++++++++++++++++++-------------- source/slang/lower-to-ir.cpp | 46 +++++++++++++++++--------------------------- 5 files changed, 71 insertions(+), 73 deletions(-) (limited to 'source/slang/ir.h') diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 35020249b..1f0436d8a 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -4016,8 +4016,8 @@ emitDeclImpl(decl, nullptr); emitIRInstResultDecl(context, inst); emitIROperand(context, fieldExtract->getBase()); - emit(".field"); - emit(fieldExtract->fieldIndex); + emit("."); + emit(getName(fieldExtract->getField())); emit(";\n"); } break; @@ -4087,20 +4087,16 @@ emitDeclImpl(decl, nullptr); void emitIRStruct( EmitContext* context, - IRStructType* structType) + IRStructDecl* structType) { emit("struct "); emit(getName(structType)); emit("\n{\n"); - auto fieldCount = structType->getFieldCount(); - for( UInt ff = 0; ff < fieldCount; ++ff ) - { - auto fieldType = structType->getFieldType(ff); - - String fieldName = "field"; - fieldName.append(ff); - emitIRType(context, fieldType, fieldName); + for(auto ff = structType->getFirstField(); ff; ff = ff->getNextField()) + { + auto fieldType = ff->getFieldType(); + emitIRType(context, fieldType, getName(ff)); emit(";\n"); } emit("};\n"); @@ -4119,7 +4115,7 @@ emitDeclImpl(decl, nullptr); break; case kIROp_StructType: - emitIRStruct(context, (IRStructType*) inst); + emitIRStruct(context, (IRStructDecl*) inst); break; default: @@ -4254,7 +4250,7 @@ String emitEntryPoint( // // We'll try to detect the cases here: // -#if 0 +#if 1 if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING )) { // This seems to be case (3), because the user is asking for full diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h index 8445e5a1e..d594d2755 100644 --- a/source/slang/ir-inst-defs.h +++ b/source/slang/ir-inst-defs.h @@ -14,7 +14,7 @@ INST(BoolType, type.bool, 0, 0) INST(Float32Type, type.f32, 0, 0) INST(Int32Type, type.i32, 0, 0) INST(UInt32Type, type.u32, 0, 0) -INST(StructType, type.struct, 0, 0) +INST(StructType, type.struct, 0, PARENT) INST(FuncType, func_type, 0, 0) INST(IntLit, integer_constant, 0, 0) @@ -26,9 +26,10 @@ INST(Module, module, 0, PARENT) INST(Func, func, 0, PARENT) INST(Block, block, 0, PARENT) -INST(Param, param, 0, 0) +INST(Param, param, 0, 0) +INST(StructField, field, 0, 0) -INST(FieldExtract, get_field, 1, 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 d2c3f5ba5..b6af6aabb 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -622,20 +622,23 @@ namespace Slang getTypeType()); } - IRType* IRBuilder::getStructType( - UInt fieldCount, - IRType* const* fieldTypes) + IRStructDecl* IRBuilder::createStructType() { - auto inst = createInstWithTrailingArgs( + return createInst( this, kIROp_StructType, - getTypeType(), - fieldCount, - (IRValue* const*)fieldTypes); - addInst(inst); - return inst; + getTypeType()); } + IRStructField* IRBuilder::createStructField(IRType* fieldType) + { + return createInst( + this, + kIROp_StructField, + fieldType); + } + + IRType* IRBuilder::getFuncType( UInt paramCount, IRType* const* paramTypes, @@ -753,17 +756,16 @@ namespace Slang } IRInst* IRBuilder::emitFieldExtract( - IRType* type, - IRValue* base, - UInt fieldIndex) + IRType* type, + IRValue* base, + IRStructField* field) { auto inst = createInst( this, kIROp_FieldExtract, type, - base); - - inst->fieldIndex = fieldIndex; + base, + field); addInst(inst); return inst; diff --git a/source/slang/ir.h b/source/slang/ir.h index e583997fd..319340024 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -153,13 +153,6 @@ struct IRVectorType : IRType IRInst* getElementCount() { return elementCount.usedValue; } }; -struct IRStructType : IRType -{ - - UInt getFieldCount() { return getArgCount() - 1; } - IRType* getFieldType(UInt index) { return (IRType*) getArg(index + 1); } -}; - struct IRFuncType : IRType { IRUse resultType; @@ -176,12 +169,14 @@ struct IRFuncType : IRType } }; +struct IRStructField; struct IRFieldExtract : IRInst { IRUse base; - UInt fieldIndex; + 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) @@ -212,6 +207,20 @@ struct IRParentInst : IRInst IRInst* lastChild; }; +struct IRStructField : IRInst +{ + IRType* getFieldType() { return (IRType*) type.usedValue; } + + IRStructField* getNextField() { return (IRStructField*) nextInst; } +}; + +struct IRStructDecl : IRParentInst +{ + IRStructField* getFirstField() { return (IRStructField*) firstChild; } + IRStructField* getLastField() { return (IRStructField*) lastChild; } +}; + + // A basic block is a parent instruction that adds the constraint // that all the children need to be "ordinary" instructions (so // no function declarations, or nested blocks). We also expect @@ -308,9 +317,9 @@ struct IRBuilder IRType* getTypeType(); IRType* getVoidType(); IRType* getBlockType(); - IRType* getStructType( - UInt fieldCount, - IRType* const* fieldTypes); + + IRStructDecl* createStructType(); + IRStructField* createStructField(IRType* fieldType); IRType* getFuncType( UInt paramCount, @@ -343,9 +352,9 @@ struct IRBuilder IRType* type); IRInst* emitFieldExtract( - IRType* type, - IRValue* base, - UInt fieldIndex); + IRType* type, + IRValue* base, + IRStructField* field); IRInst* emitReturn( IRValue* val); diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index ed8ed3318..5d0a40072 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -394,7 +394,7 @@ struct ExprLoweringVisitor : ExprVisitor LoweredValInfo extractField( LoweredTypeInfo fieldType, LoweredValInfo base, - UInt fieldIndex) + LoweredValInfo field) { switch (base.flavor) { @@ -405,7 +405,7 @@ struct ExprLoweringVisitor : ExprVisitor getBuilder()->emitFieldExtract( getSimpleType(fieldType), irBase, - fieldIndex)); + (IRStructField*) getSimpleVal(field))); } break; @@ -424,22 +424,8 @@ struct ExprLoweringVisitor : ExprVisitor { // Okay, easy enough: we have a reference to a field of a struct type... - // HACK: for now just scan the decl to find the right index. - // TODO: we need to deal with the fact that the struct might get - // tuple-ified. - // - UInt index = 0; - for (auto fieldDecl : getMembersOfType(fieldDeclRef.GetParent().As())) - { - if (fieldDecl == fieldDeclRef.getDecl()) - { - break; - } - - index++; - } - - return extractField(loweredType, loweredBase, index); + auto loweredField = ensureDecl(context, fieldDeclRef); + return extractField(loweredType, loweredBase, loweredField); } SLANG_UNIMPLEMENTED_X("codegen for subscript expression"); @@ -559,20 +545,27 @@ struct DeclLoweringVisitor : DeclVisitor // User-defined aggregate type: need to translate into // a corresponding IR aggregate type. - List fieldTypes; - List irFieldTypes; + auto builder = getBuilder(); + IRStructDecl* irStruct = builder->createStructType(); for (auto fieldDecl : decl->GetFields()) { + // TODO: need to track relationship to original fields... + // TODO: need to be prepared to deal with tuple-ness of fields here auto fieldType = lowerType(context, fieldDecl->getType()); - fieldTypes.Add(fieldType); - switch (fieldType.flavor) { case LoweredTypeInfo::Flavor::Simple: - irFieldTypes.Add(fieldType.type); + { + auto irField = builder->createStructField(getSimpleType(fieldType)); + builder->addInst(irStruct, irField); + + context->shared->declValues.Add( + DeclRef(fieldDecl, nullptr), + LoweredValInfo::simple(irField)); + } break; default: @@ -580,13 +573,10 @@ struct DeclLoweringVisitor : DeclVisitor } } - // TODO: need to track relationship to original fields... - IRType* irStructType = getBuilder()->getStructType( - irFieldTypes.Count(), - &irFieldTypes[0]); + builder->addInst(irStruct); - return LoweredValInfo::simple(irStructType); + return LoweredValInfo::simple(irStruct); } LoweredValInfo visitFunctionDeclBase(FunctionDeclBase* decl) -- cgit v1.2.3 From ec8175c1f0afe3f7758f70da240aba03a791c3a9 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 17 Aug 2017 14:51:09 -0700 Subject: [ir] Add support for "decorations" on instructions The terminology here is similar to SPIR-V. For right now the only decoration exposed is a fairly brute-force one that just points back to a high-level declaration so that we can look up info on it that might affect how we print output. --- source/slang/emit.cpp | 24 ++++++++++++++++++- source/slang/ir.cpp | 38 ++++++++++++++++++++++++++++-- source/slang/ir.h | 55 ++++++++++++++++++++++++++++++++++++++++++++ source/slang/lower-to-ir.cpp | 5 ++++ 4 files changed, 119 insertions(+), 3 deletions(-) (limited to 'source/slang/ir.h') diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index 1f0436d8a..6440c63a3 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -3827,6 +3827,11 @@ emitDeclImpl(decl, nullptr); String getName(IRInst* inst) { + if(auto decoration = inst->findDecoration()) + { + return getText(decoration->decl->getName()); + } + StringBuilder sb; sb << "_S"; sb << inst->id; @@ -4038,6 +4043,17 @@ emitDeclImpl(decl, nullptr); } } + void emitIRSemantics( + EmitContext* context, + IRInst* inst) + { + auto decoration = inst->findDecoration(); + if( decoration ) + { + EmitSemantics(decoration->decl); + } + } + void emitIRFunc( EmitContext* context, IRFunc* func) @@ -4061,6 +4077,9 @@ emitDeclImpl(decl, nullptr); } emit(")"); + + emitIRSemantics(context, func); + // TODO: encode declaration vs. definition bool isDefinition = true; if(isDefinition) @@ -4097,6 +4116,9 @@ emitDeclImpl(decl, nullptr); { auto fieldType = ff->getFieldType(); emitIRType(context, fieldType, getName(ff)); + + emitIRSemantics(context, ff); + emit(";\n"); } emit("};\n"); @@ -4250,7 +4272,7 @@ String emitEntryPoint( // // We'll try to detect the cases here: // -#if 1 +#if 0 if(!(translationUnit->compileFlags & SLANG_COMPILE_FLAG_NO_CHECKING )) { // This seems to be case (3), because the user is asking for full diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index b6af6aabb..8e4f789ef 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -46,6 +46,16 @@ namespace Slang return &type; } + IRDecoration* IRInst::findDecorationImpl(IRDecorationOp decorationOp) + { + for( auto dd = firstDecoration; dd; dd = dd->next ) + { + if(dd->op == decorationOp) + return dd; + } + return nullptr; + } + // IRParam* IRFunc::getFirstParam() @@ -129,8 +139,6 @@ namespace Slang IRValue* inst = (IRInst*) malloc(size); memset(inst, 0, size); - IRUse* instArgs = inst->getArgs(); - auto module = builder->getModule(); if (!module || (type && type->op == kIROp_VoidType)) { @@ -793,6 +801,32 @@ namespace Slang return inst; } + IRDecoration* IRBuilder::addDecorationImpl( + IRInst* inst, + UInt decorationSize, + IRDecorationOp op) + { + auto decoration = (IRDecoration*) malloc(decorationSize); + memset(decoration, 0, decorationSize); + + decoration->op = op; + + decoration->next = inst->firstDecoration; + inst->firstDecoration = decoration; + + return decoration; + } + + IRHighLevelDeclDecoration* IRBuilder::addHighLevelDeclDecoration(IRInst* inst, Decl* decl) + { + auto decoration = addDecoration(inst, kIRDecorationOp_HighLevelDecl); + decoration->decl = decl; + return decoration; + } + + // + + struct IRDumpContext { FILE* file; diff --git a/source/slang/ir.h b/source/slang/ir.h index 319340024..fcebf5d15 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -69,6 +69,22 @@ struct IRUse void init(IRInst* user, IRInst* usedValue); }; +enum IRDecorationOp : uint16_t +{ + kIRDecorationOp_HighLevelDecl, +}; + +// A "decoration" that gets applied to an instruction. +// These usually don't affect semantics, but are useful +// for preserving high-level source information. +struct IRDecoration +{ + // Next decoration attached to the same instruction + IRDecoration* next; + + IRDecorationOp op; +}; + typedef uint32_t IRInstID; // In the IR, almost *everything* is an instruction, @@ -99,6 +115,18 @@ struct IRInst // The first use of this value (start of a linked list) IRUse* firstUse; + // The linked list of decorations attached to this instruction + IRDecoration* firstDecoration; + + IRDecoration* findDecorationImpl(IRDecorationOp op); + + template + T* findDecoration() + { + return (T*) findDecorationImpl(IRDecorationOp(T::kDecorationOp)); + } + + // The type of this value IRUse type; @@ -117,8 +145,22 @@ struct IRInst } }; +// This type alias exists because I waffled on the name for a bit. +// All existing uses of `IRValue` should move to `IRInst` typedef IRInst IRValue; +class Decl; + +// Associates an IR-level decoration with a source declaration +// in the high-level AST, that can be used to extract +// additional information that informs code emission. +struct IRHighLevelDeclDecoration : IRDecoration +{ + enum { kDecorationOp = kIRDecorationOp_HighLevelDecl }; + + Decl* decl; +}; + typedef long long IRIntegerValue; typedef double IRFloatingPointValue; @@ -360,6 +402,19 @@ struct IRBuilder IRValue* val); IRInst* emitReturn(); + + IRDecoration* addDecorationImpl( + IRInst* inst, + UInt decorationSize, + IRDecorationOp op); + + template + T* addDecoration(IRInst* inst, IRDecorationOp op) + { + return (T*) addDecorationImpl(inst, sizeof(T), op); + } + + IRHighLevelDeclDecoration* addHighLevelDeclDecoration(IRInst* inst, Decl* decl); }; void dumpIR(IRModule* module); diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index 5d0a40072..781209dce 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -562,6 +562,8 @@ struct DeclLoweringVisitor : DeclVisitor auto irField = builder->createStructField(getSimpleType(fieldType)); builder->addInst(irStruct, irField); + builder->addHighLevelDeclDecoration(irField, fieldDecl); + context->shared->declValues.Add( DeclRef(fieldDecl, nullptr), LoweredValInfo::simple(irField)); @@ -573,6 +575,7 @@ struct DeclLoweringVisitor : DeclVisitor } } + builder->addHighLevelDeclDecoration(irStruct, decl); builder->addInst(irStruct); @@ -624,6 +627,8 @@ struct DeclLoweringVisitor : DeclVisitor lowerStmt(subContext, decl->Body); + getBuilder()->addHighLevelDeclDecoration(irFunc, decl); + getBuilder()->addInst(irFunc); return LoweredValInfo::simple(irFunc); -- cgit v1.2.3