From 5900f32fff9970b4221ce7fb7e94133e387ff9de Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Thu, 31 Aug 2017 12:47:39 -0700 Subject: Continue work on IR-based codegen This gets us far enough that we can convert a single test case to use the IR, under the new `-use-ir` flag. Getting this merged into mainline will at least ensure that we keep the IR path working in a minimal fashion, even when we have to add functionality the existing AST-based path There is definitely some clutter here from keeping both IR-based and AST-based translation around, but I don't want to have a long-lived branch for the IR that gets further and further away from the `master` branch that is actually getting used and tested. Summary of changes: - Add pointer types and basic `load` operation to be able to handle variable declarations - Add basic `call` instruction type - Add simple address math for field reference in l-value - Always add IR for referenced decls to global scope - Add notion of "intrinsic" type modifier, which maps a type declaration directly to an IR opcode (plus optional literal operands to handle things like texture/sampler flavor) - Improve printing of IR instructions, types, operands - Add constant-buffer type to IR - Allow any instruction to be detected as "should be folded into use sites" and use this to tag things of constant-buffer type - Also add logic for implicit base on member expressions, to handle references to `cbuffer` members - Add connection back to original decl to IR variables (including global shader parameters...) - Use reflection name instead of true name when emitting HLSL from IR (so that we can match HLSL output) - Make IR include decorations for type layout - Re-use existing emit logic for HLSL semantics to output `register` semantics for IR-based code - Make IR-based codegen be an option we can enable from the command line - It still isn't on by default (it can barely manage a trivial shader), but it seems better to enable it always instead of putting it under an `#ifdef` - Fix up how we check for intrinsic operations suring AST-based cross compilation so that adding new intrinsic ops for the IR won't break codegen. --- source/slang/lower-to-ir.cpp | 255 +++++++++++++++++++++++++++++++++++++++---- 1 file changed, 236 insertions(+), 19 deletions(-) (limited to 'source/slang/lower-to-ir.cpp') 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 : ValVisitordeclRef); + return getSimpleVal(loweredFunc)->getType(); + } + + void addGenericArgs(List* 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() ) + { + auto builder = getBuilder(); + auto intType = builder->getBaseType(BaseType::Int); + // + List 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 : ValVisitorgetVectorType(irElementType, irElementCount); } - }; LoweredValInfo lowerVal( @@ -306,7 +370,8 @@ struct ExprLoweringVisitor : ExprVisitor 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 } } - LoweredValInfo lowerIntrinsicCall( - InvokeExpr* expr, - IntrinsicOp intrinsicOp) + // Collect the lowered arguments for a call expression + void addCallArgs( + InvokeExpr* expr, + List* 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() ) + { + // 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 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 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 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 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 } 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 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 { - 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 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(decl, nullptr), + varVal); + + return varVal; + } + LoweredValInfo visitAggTypeDecl(AggTypeDecl* decl) { // User-defined aggregate type: need to translate into @@ -621,7 +819,7 @@ struct DeclLoweringVisitor : DeclVisitor auto irFuncType = getBuilder()->getFuncType( paramTypes.Count(), - ¶mTypes[0], + paramTypes.Buffer(), irResultType); irFunc->type.init(irFunc, irFuncType); @@ -637,9 +835,11 @@ struct DeclLoweringVisitor : DeclVisitor 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 layout; + auto globalScopeLayout = shared->programLayout->globalScopeLayout; + if (auto globalParameterBlockLayout = globalScopeLayout.As()) + { + globalScopeLayout = globalParameterBlockLayout->elementTypeLayout; + } + if (auto globalStructTypeLayout = globalScopeLayout.As()) + { + 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( -- cgit v1.2.3