diff options
Diffstat (limited to 'source/slang')
| -rw-r--r-- | source/slang/check.cpp | 32 | ||||
| -rw-r--r-- | source/slang/emit.cpp | 72 | ||||
| -rw-r--r-- | source/slang/expr-defs.h | 5 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang | 21 | ||||
| -rw-r--r-- | source/slang/hlsl.meta.slang.cpp | 22 | ||||
| -rw-r--r-- | source/slang/ir-inst-defs.h | 4 | ||||
| -rw-r--r-- | source/slang/ir.cpp | 16 | ||||
| -rw-r--r-- | source/slang/ir.h | 17 | ||||
| -rw-r--r-- | source/slang/lower-to-ir.cpp | 570 | ||||
| -rw-r--r-- | source/slang/lower.cpp | 23 | ||||
| -rw-r--r-- | source/slang/parser.cpp | 4 |
11 files changed, 678 insertions, 108 deletions
diff --git a/source/slang/check.cpp b/source/slang/check.cpp index ce9b2de55..e22db4186 100644 --- a/source/slang/check.cpp +++ b/source/slang/check.cpp @@ -146,13 +146,26 @@ namespace Slang { if (baseExpr) { - auto expr = new MemberExpr(); - expr->loc = loc; - expr->BaseExpression = baseExpr; - expr->name = declRef.GetName(); - expr->type = GetTypeForDeclRef(declRef); - expr->declRef = declRef; - return expr; + if (baseExpr->type->As<TypeType>()) + { + auto expr = new StaticMemberExpr(); + expr->loc = loc; + expr->BaseExpression = baseExpr; + expr->name = declRef.GetName(); + expr->type = GetTypeForDeclRef(declRef); + expr->declRef = declRef; + return expr; + } + else + { + auto expr = new MemberExpr(); + expr->loc = loc; + expr->BaseExpression = baseExpr; + expr->name = declRef.GetName(); + expr->type = GetTypeForDeclRef(declRef); + expr->declRef = declRef; + return expr; + } } else { @@ -4978,6 +4991,11 @@ namespace Slang } } + RefPtr<Expr> visitStaticMemberExpr(StaticMemberExpr* expr) + { + SLANG_UNEXPECTED("should not occur in unchecked AST"); + return expr; + } RefPtr<Expr> visitMemberExpr(MemberExpr * expr) { diff --git a/source/slang/emit.cpp b/source/slang/emit.cpp index a0cf8eb19..f6c63dff6 100644 --- a/source/slang/emit.cpp +++ b/source/slang/emit.cpp @@ -2274,6 +2274,44 @@ struct EmitVisitor if(needClose) Emit(")"); } + void visitStaticMemberExpr(StaticMemberExpr* memberExpr, ExprEmitArg const& arg) + { + auto prec = kEOp_Postfix; + auto outerPrec = arg.outerPrec; + bool needClose = MaybeEmitParens(outerPrec, prec); + + // TODO(tfoley): figure out a good way to reference + // declarations that might be generic and/or might + // not be generated as lexically nested declarations... + + // TODO(tfoley): also, probably need to special case + // this for places where we are using a built-in... + + auto base = memberExpr->BaseExpression; + if (IsBaseExpressionImplicit(base)) + { + // don't emit the base expression + } + else + { + EmitExprWithPrecedence(memberExpr->BaseExpression, leftSide(outerPrec, prec)); + Emit("."); + } + + if (!memberExpr->declRef) + { + // This case arises when checking didn't find anything, but we were + // in "rewrite" mode so we blazed ahead anyway. + emitName(memberExpr->name); + } + else + { + emit(memberExpr->declRef.GetName()); + } + + if(needClose) Emit(")"); + } + void visitMemberExpr(MemberExpr* memberExpr, ExprEmitArg const& arg) { auto prec = kEOp_Postfix; @@ -4033,6 +4071,15 @@ emitDeclImpl(decl, nullptr); } break; + case kIROp_readWriteStructuredBufferType: + { + auto tt = (IRBufferType*) type; + emit("RWStructuredBuffer<"); + emitIRType(context, tt->getElementType(), nullptr); + emit(">"); + } + break; + case kIROp_SamplerType: { // TODO: actually look at the flavor and emit the right name @@ -4308,6 +4355,13 @@ emitDeclImpl(decl, nullptr); emitIROperand(context, inst->getArg(1)); break; + case kIROp_Store: + // TODO: this logic will really only work for a simple variable reference... + emitIROperand(context, inst->getArg(1)); + emit(" = "); + emitIROperand(context, inst->getArg(2)); + break; + case kIROp_Call: { emitIROperand(context, inst->getArg(1)); @@ -4322,6 +4376,13 @@ emitDeclImpl(decl, nullptr); } break; + case kIROp_BufferLoad: + emitIROperand(context, inst->getArg(1)); + emit("["); + emitIROperand(context, inst->getArg(2)); + emit("]"); + break; + default: emit("/* uhandled */"); break; @@ -4345,6 +4406,17 @@ emitDeclImpl(decl, nullptr); emit(";\n"); break; + case kIROp_Var: + { + auto ptrType = inst->getType(); + auto valType = ((IRPtrType*)ptrType)->getValueType(); + + auto name = getName(inst); + emitIRType(context, valType, name); + emit(";\n"); + } + break; + case kIROp_Param: // Don't emit parameters, since they are declared as part of the function. break; diff --git a/source/slang/expr-defs.h b/source/slang/expr-defs.h index 5821bddc3..81e8d9275 100644 --- a/source/slang/expr-defs.h +++ b/source/slang/expr-defs.h @@ -90,6 +90,11 @@ SYNTAX_CLASS(MemberExpr, DeclRefExpr) SYNTAX_FIELD(RefPtr<Expr>, BaseExpression) END_SYNTAX_CLASS() +// Member looked up on a type, rather than a value +SYNTAX_CLASS(StaticMemberExpr, DeclRefExpr) + SYNTAX_FIELD(RefPtr<Expr>, BaseExpression) +END_SYNTAX_CLASS() + SYNTAX_CLASS(SwizzleExpr, Expr) SYNTAX_FIELD(RefPtr<Expr>, base) FIELD(int, elementCount) diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang index 3b4b85b91..7c7d3fda0 100644 --- a/source/slang/hlsl.meta.slang +++ b/source/slang/hlsl.meta.slang @@ -38,7 +38,8 @@ __generic<T> __magic_type(HLSLStructuredBufferType) struct StructuredBuffer __intrinsic_op T Load(int location); __intrinsic_op T Load(int location, out uint status); - __intrinsic_op __subscript(uint index) -> T; + __intrinsic_op(bufferLoad) + __subscript(uint index) -> T; }; __generic<T> __magic_type(HLSLConsumeStructuredBufferType) struct ConsumeStructuredBuffer @@ -173,7 +174,13 @@ __magic_type(HLSLRWByteAddressBufferType) struct RWByteAddressBuffer uint4 value); }; -__generic<T> __magic_type(HLSLRWStructuredBufferType) struct RWStructuredBuffer +__generic<T> +__magic_type(HLSLRWStructuredBufferType) +__intrinsic_type(${{ + // TODO: we really need a simple way to write an "expression splice" + sb << kIROp_readWriteStructuredBufferType; +}}) +struct RWStructuredBuffer { __intrinsic_op uint DecrementCounter(); @@ -186,7 +193,15 @@ __generic<T> __magic_type(HLSLRWStructuredBufferType) struct RWStructuredBuffer __intrinsic_op T Load(int location); __intrinsic_op T Load(int location, out uint status); - __intrinsic_op __subscript(uint index) -> T { get; set; } + __intrinsic_op + __subscript(uint index) -> T + { + __intrinsic_op(bufferLoad) + get; + + __intrinsic_op(bufferStore) + set; + } }; __generic<T> __magic_type(HLSLPointStreamType) struct PointStream diff --git a/source/slang/hlsl.meta.slang.cpp b/source/slang/hlsl.meta.slang.cpp index e9e2277e6..a3744f620 100644 --- a/source/slang/hlsl.meta.slang.cpp +++ b/source/slang/hlsl.meta.slang.cpp @@ -38,7 +38,8 @@ sb << "\n"; sb << " __intrinsic_op T Load(int location);\n"; sb << " __intrinsic_op T Load(int location, out uint status);\n"; sb << "\n"; -sb << " __intrinsic_op __subscript(uint index) -> T;\n"; +sb << " __intrinsic_op(bufferLoad)\n"; +sb << " __subscript(uint index) -> T;\n"; sb << "};\n"; sb << "\n"; sb << "__generic<T> __magic_type(HLSLConsumeStructuredBufferType) struct ConsumeStructuredBuffer\n"; @@ -173,7 +174,14 @@ sb << " uint address,\n"; sb << " uint4 value);\n"; sb << "};\n"; sb << "\n"; -sb << "__generic<T> __magic_type(HLSLRWStructuredBufferType) struct RWStructuredBuffer\n"; +sb << "__generic<T>\n"; +sb << "__magic_type(HLSLRWStructuredBufferType)\n"; +sb << "__intrinsic_type("; + + // TODO: we really need a simple way to write an "expression splice" + sb << kIROp_readWriteStructuredBufferType; +sb << ")\n"; +sb << "struct RWStructuredBuffer\n"; sb << "{\n"; sb << " __intrinsic_op uint DecrementCounter();\n"; sb << "\n"; @@ -186,7 +194,15 @@ sb << "\n"; sb << " __intrinsic_op T Load(int location);\n"; sb << " __intrinsic_op T Load(int location, out uint status);\n"; sb << "\n"; -sb << " __intrinsic_op __subscript(uint index) -> T { get; set; }\n"; +sb << "\t__intrinsic_op\n"; +sb << "\t__subscript(uint index) -> T\n"; +sb << "\t{\n"; +sb << "\t\t__intrinsic_op(bufferLoad)\n"; +sb << "\t\tget;\n"; +sb << "\n"; +sb << "\t\t__intrinsic_op(bufferStore)\n"; +sb << "\t\tset;\n"; +sb << "\t}\n"; sb << "};\n"; sb << "\n"; sb << "__generic<T> __magic_type(HLSLPointStreamType) struct PointStream\n"; diff --git a/source/slang/ir-inst-defs.h b/source/slang/ir-inst-defs.h index 15a082aba..0ed66064a 100644 --- a/source/slang/ir-inst-defs.h +++ b/source/slang/ir-inst-defs.h @@ -28,6 +28,7 @@ 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(readWriteStructuredBufferType, readWriteStructuredBufferType, 1, 0) INST(IntLit, integer_constant, 0, 0) INST(FloatLit, float_constant, 0, 0) @@ -46,6 +47,9 @@ INST(Var, var, 0, 0) INST(Load, load, 1, 0) INST(Store, store, 2, 0) +INST(BufferLoad, bufferLoad, 2, 0) +INST(BufferStore, bufferStore, 3, 0) + INST(FieldExtract, get_field, 2, 0) INST(FieldAddress, get_field_addr, 2, 0) diff --git a/source/slang/ir.cpp b/source/slang/ir.cpp index eadcd34d0..9e39579f6 100644 --- a/source/slang/ir.cpp +++ b/source/slang/ir.cpp @@ -883,6 +883,22 @@ namespace Slang return inst; } + IRInst* IRBuilder::emitStore( + IRValue* dstPtr, + IRValue* srcVal) + { + auto type = getVoidType(); + auto inst = createInst<IRStore>( + this, + kIROp_Store, + type, + dstPtr, + srcVal); + + addInst(inst); + return inst; + } + IRInst* IRBuilder::emitFieldExtract( IRType* type, IRValue* base, diff --git a/source/slang/ir.h b/source/slang/ir.h index 51755f89f..1561d15a7 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -307,6 +307,13 @@ struct IRTextureType : IRType IRType* getElementType() { return (IRType*) elementType.usedValue; } }; +struct IRBufferType : IRType +{ + IRUse elementType; + IRType* getElementType() { return (IRType*) elementType.usedValue; } +}; + + struct IRUniformBufferType : IRType { IRUse elementType; @@ -326,6 +333,12 @@ struct IRLoad : IRInst IRUse ptr; }; +struct IRStore : IRInst +{ + IRUse ptr; + IRUse val; +}; + struct IRStructField; struct IRFieldExtract : IRInst { @@ -541,6 +554,10 @@ struct IRBuilder IRInst* emitLoad( IRValue* ptr); + IRInst* emitStore( + IRValue* dstPtr, + IRValue* srcVal); + IRInst* emitFieldExtract( IRType* type, IRValue* base, diff --git a/source/slang/lower-to-ir.cpp b/source/slang/lower-to-ir.cpp index a8acadb59..4bdfa4438 100644 --- a/source/slang/lower-to-ir.cpp +++ b/source/slang/lower-to-ir.cpp @@ -8,6 +8,20 @@ namespace Slang { +struct BoundMemberInfo; + +struct SubscriptInfo : RefObject +{ + DeclRef<SubscriptDecl> declRef; +}; + +struct BoundSubscriptInfo : RefObject +{ + DeclRef<SubscriptDecl> declRef; + IRType* type; + List<IRValue*> args; +}; + struct LoweredValInfo { enum class Flavor @@ -15,11 +29,17 @@ struct LoweredValInfo None, Simple, Ptr, + BoundMember, + Subscript, + BoundSubscript, }; union { - IRValue* val; + IRValue* val; + BoundMemberInfo* boundMemberInfo; + SubscriptInfo* subscriptInfo; + BoundSubscriptInfo* boundSubscriptInfo; }; Flavor flavor; @@ -43,7 +63,57 @@ struct LoweredValInfo info.flavor = Flavor::Ptr; info.val = v; return info; - }}; + } + + static LoweredValInfo boundMember( + LoweredValInfo const& base, + LoweredValInfo const& member); + + static LoweredValInfo subscript( + SubscriptInfo* subscriptInfo); + + static LoweredValInfo boundSubscript( + BoundSubscriptInfo* boundSubscriptInfo); +}; + +struct BoundMemberInfo +{ + LoweredValInfo base; + LoweredValInfo member; +}; + +LoweredValInfo LoweredValInfo::boundMember( + LoweredValInfo const& base, + LoweredValInfo const& member) +{ + BoundMemberInfo* boundMember = new BoundMemberInfo(); + boundMember->base = base; + boundMember->member = member; + + LoweredValInfo info; + info.flavor = Flavor::BoundMember; + info.boundMemberInfo = boundMember; + return info; +} + +LoweredValInfo LoweredValInfo::subscript( + SubscriptInfo* subscriptInfo) +{ + LoweredValInfo info; + info.flavor = Flavor::Subscript; + info.subscriptInfo = subscriptInfo; + return info; +} + +LoweredValInfo LoweredValInfo::boundSubscript( + BoundSubscriptInfo* boundSubscriptInfo) +{ + LoweredValInfo info; + info.flavor = Flavor::BoundSubscript; + info.boundSubscriptInfo = boundSubscriptInfo; + return info; +} + struct SharedIRGenContext { @@ -52,6 +122,9 @@ struct SharedIRGenContext CodeGenTarget target; Dictionary<DeclRef<Decl>, LoweredValInfo> declValues; + + // Arrays we keep around strictly for memory-management purposes + List<RefPtr<BoundSubscriptInfo>> boundSubscripts; }; @@ -62,24 +135,149 @@ struct IRGenContext IRBuilder* irBuilder; }; -IRValue* getSimpleVal(LoweredValInfo lowered) +LoweredValInfo ensureDecl( + IRGenContext* context, + DeclRef<Decl> const& declRef); + + +IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered); + +IROp getIntrinsicOp( + Decl* decl, + IntrinsicOpModifier* intrinsicOpMod) { - switch(lowered.flavor) - { - case LoweredValInfo::Flavor::None: - return nullptr; + if (int(intrinsicOpMod->op) != 0) + return intrinsicOpMod->op; - case LoweredValInfo::Flavor::Simple: - return lowered.val; + // No specified modifier? Then we need to look it up + // based on the name of the declaration... + + auto name = decl->getName(); + auto nameText = getText(name); + + IROp op = findIROp(nameText.Buffer()); + assert(op != kIROp_Invalid); + return op; +} +// Given a `LoweredValInfo` for something callable, along with a +// bunch of arguments, emit an appropriate call to it. +LoweredValInfo emitCallToVal( + IRGenContext* context, + IRType* type, + LoweredValInfo funcVal, + UInt argCount, + IRValue* const* args) +{ + auto builder = context->irBuilder; + switch (funcVal.flavor) + { default: - SLANG_UNEXPECTED("unhandled value flavor"); - return nullptr; + return LoweredValInfo::simple( + builder->emitCallInst(type, getSimpleVal(context, funcVal), argCount, args)); + } +} + +// Given a `DeclRef` for something callable, along with a bunch of +// arguments, emit an appropriate call to it. +LoweredValInfo emitCallToDeclRef( + IRGenContext* context, + IRType* type, + DeclRef<Decl> funcDeclRef, + UInt argCount, + IRValue* const* args) +{ + auto builder = context->irBuilder; + + + if (auto subscriptDeclRef = funcDeclRef.As<SubscriptDecl>()) + { + // A reference to a subscript declaration is potentially a + // special case, if we have more than just a getter. + + DeclRef<GetterDecl> getterDeclRef; + bool justAGetter = true; + for (auto accessorDeclRef : getMembersOfType<AccessorDecl>(subscriptDeclRef)) + { + if (auto foundGetterDeclRef = accessorDeclRef.As<GetterDecl>()) + { + getterDeclRef = foundGetterDeclRef; + } + else + { + justAGetter = false; + break; + } + } + + if (!justAGetter) + { + // We can't perform an actual call right now, because + // this expression might appear in an r-value or l-value + // position (or *both* if it is being passed as an argument + // for an `in out` parameter!). + // + // Instead, we will construct a special-case value to + // represent the latent subscript operation (abstractly + // this is a reference to a storage location). + + // The abstract storage location will need to include + // all the arguments being passed to the subscript operation. + + RefPtr<BoundSubscriptInfo> boundSubscript = new BoundSubscriptInfo(); + boundSubscript->declRef = subscriptDeclRef; + boundSubscript->type = type; + boundSubscript->args.AddRange(args, argCount); + + context->shared->boundSubscripts.Add(boundSubscript); + + return LoweredValInfo::boundSubscript(boundSubscript); + } + + // Otherwise we are just call the getter, and so that + // is what we need to be emitting a call to... + if (getterDeclRef) + funcDeclRef = getterDeclRef; + } + + auto funcDecl = funcDeclRef.getDecl(); + if(auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>()) + { + auto op = getIntrinsicOp(funcDecl, intrinsicOpModifier); + + return LoweredValInfo::simple(builder->emitIntrinsicInst( + type, + op, + argCount, + args)); } + // TODO: handle target intrinsic modifier too... + + if( auto ctorDeclRef = funcDeclRef.As<ConstructorDecl>() ) + { + // HACK: we know all constructors are builtins for now, + // so we need to emit them as a call to the corresponding + // builtin operation. + return LoweredValInfo::simple(builder->emitConstructorInst(type, argCount, args)); + } + + // Fallback case is to emit an actual call. + LoweredValInfo funcVal = ensureDecl(context, funcDeclRef); + return emitCallToVal(context, type, funcVal, argCount, args); +} + +LoweredValInfo emitCallToDeclRef( + IRGenContext* context, + IRType* type, + DeclRef<Decl> funcDeclRef, + List<IRValue*> const& args) +{ + return emitCallToDeclRef(context, type, funcDeclRef, args.Count(), args.Buffer()); } IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered) { +top: switch(lowered.flavor) { case LoweredValInfo::Flavor::None: @@ -91,6 +289,26 @@ IRValue* getSimpleVal(IRGenContext* context, LoweredValInfo lowered) case LoweredValInfo::Flavor::Ptr: return context->irBuilder->emitLoad(lowered.val); + case LoweredValInfo::Flavor::BoundSubscript: + { + auto boundSubscriptInfo = lowered.boundSubscriptInfo; + auto builder = context->irBuilder; + + for (auto getter : getMembersOfType<GetterDecl>(boundSubscriptInfo->declRef)) + { + lowered = emitCallToDeclRef( + context, + boundSubscriptInfo->type, + getter, + boundSubscriptInfo->args); + goto top; + } + + SLANG_UNEXPECTED("subscript had no getter"); + return nullptr; + } + break; + default: SLANG_UNEXPECTED("unhandled value flavor"); return nullptr; @@ -148,7 +366,7 @@ IRValue* lowerSimpleVal( Val* val) { auto lowered = lowerVal(context, val); - return getSimpleVal(lowered); + return getSimpleVal(context, lowered); } LoweredTypeInfo lowerType( @@ -184,13 +402,19 @@ LoweredValInfo lowerExpr( IRGenContext* context, Expr* expr); +void assign( + IRGenContext* context, + LoweredValInfo const& left, + LoweredValInfo const& right); + void lowerStmt( IRGenContext* context, Stmt* stmt); -LoweredValInfo ensureDecl( - IRGenContext* context, - DeclRef<Decl> const& declRef); +LoweredValInfo lowerDecl( + IRGenContext* context, + DeclBase* decl, + Layout* layout); // @@ -222,7 +446,15 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower LoweredTypeInfo visitFuncType(FuncType* type) { LoweredValInfo loweredFunc = ensureDecl(context, type->declRef); - return getSimpleVal(loweredFunc)->getType(); + auto loweredFuncVal = getSimpleVal(context, loweredFunc); + + // HACK: deal with the case where the decl might not + // lower to anything, and so we don't have a type to + // work with. + if (!loweredFuncVal) + return LoweredTypeInfo(); + + return loweredFuncVal->getType(); } void addGenericArgs(List<IRValue*>* ioArgs, DeclRefBase declRef) @@ -232,7 +464,7 @@ struct ValLoweringVisitor : ValVisitor<ValLoweringVisitor, LoweredValInfo, Lower { for(auto aa : subs->args) { - (*ioArgs).Add(getSimpleVal(lowerVal(context, aa))); + (*ioArgs).Add(getSimpleVal(context, lowerVal(context, aa))); } subs = subs->outer; } @@ -380,7 +612,25 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> } } - // Collect the lowered arguments for a call expression + + // Add arguments that appeared directly in an argument list + // to the list of argument values for a call. + void addDirectCallArgs( + InvokeExpr* expr, + List<IRValue*>* ioArgs) + { + auto& irArgs = *ioArgs; + + for( auto arg : expr->Arguments ) + { + auto loweredArg = lowerExpr(context, arg); + addArgs(&irArgs, loweredArg); + } + } + + // Try to add "all" the arguments for a call to the argument list, + // including implicit arguments that come from (e.g.,) a member + // expression used to form the call. void addCallArgs( InvokeExpr* expr, List<IRValue*>* ioArgs) @@ -398,11 +648,7 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> addArgs(&irArgs, loweredBase); } - for( auto arg : expr->Arguments ) - { - auto loweredArg = lowerExpr(context, arg); - addArgs(&irArgs, loweredArg); - } + addDirectCallArgs(expr, ioArgs); } LoweredValInfo lowerIntrinsicCall( @@ -418,77 +664,78 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> return LoweredValInfo::simple(getBuilder()->emitIntrinsicInst(type, intrinsicOp, argCount, &irArgs[0])); } - LoweredValInfo lowerSimpleCall(InvokeExpr* expr) + void addFuncBaseArgs( + LoweredValInfo funcVal, + List<IRValue*>* ioArgs) { - auto type = lowerSimpleType(context, expr->type); - - auto loweredFunc = lowerExpr(context, expr->FunctionExpr); - - List<IRValue*> irArgs; - addCallArgs(expr, &irArgs); - UInt argCount = irArgs.Count(); - - return LoweredValInfo::simple(getBuilder()->emitCallInst(type, getSimpleVal(loweredFunc), argCount, irArgs.Buffer())); + switch (funcVal.flavor) + { + default: + return; + } } - IROp getIntrinsicOp( - Decl* decl, - IntrinsicOpModifier* intrinsicOpMod) + LoweredValInfo lowerSimpleCall(InvokeExpr* expr) { - if (int(intrinsicOpMod->op) != 0) - return intrinsicOpMod->op; - // No specified modifier? Then we need to look it up - // based on the name of the declaration... - - auto name = decl->getName(); - auto nameText = getText(name); - - IROp op = findIROp(nameText.Buffer()); - assert(op != kIROp_Invalid); - return op; } LoweredValInfo visitInvokeExpr(InvokeExpr* expr) { - // TODO: need to detect calls to builtins here, so that we can expand - // them as their own special opcodes... + auto type = lowerSimpleType(context, expr->type); - auto funcExpr = expr->FunctionExpr; - if( auto funcDeclRefExpr = funcExpr.As<DeclRefExpr>() ) - { - auto funcDeclRef = funcDeclRefExpr->declRef; - auto funcDecl = funcDeclRef.getDecl(); - if(auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>()) - { - auto op = getIntrinsicOp(funcDecl, intrinsicOpModifier); - return lowerIntrinsicCall(expr, op); - // - } - // TODO: handle target intrinsic modifier too... + // We are going to look at the syntactic form of + // the "function" expression, so that we can avoid + // a lot of complexity that would come from lowering + // it as a general expression first, and then trying + // to apply it. For example, given `obj.f(a,b)` we + // will try to detect that we are trying to compute + // something like `ObjType::f(obj, a, b)` (in pseudo-code), + // rather than trying to construct a meaningful + // intermediate value for `obj.f` first. + // + // Note that this doe not preclude having support + // for directly generating code from `obj.f` - it + // just may be that such usage is more complicated. - if( auto ctorDeclRef = funcDeclRef.As<ConstructorDecl>() ) - { - // HACK: we know all constructors are builtins for now, - // so we need to emit them as a call to the corresponding - // builtin operation. + // Along the way, we may end up collecting additional + // arguments that will be part of the call. + List<IRValue*> irArgs; - auto type = lowerSimpleType(context, expr->type); - List<IRValue*> irArgs; - for( auto arg : expr->Arguments ) - { - auto loweredArg = lowerExpr(context, arg); - addArgs(&irArgs, loweredArg); - } + auto funcExpr = expr->FunctionExpr; + if (auto memberFuncExpr = funcExpr.As<MemberExpr>()) + { + auto loweredBaseVal = lowerExpr(context, memberFuncExpr->BaseExpression); + addArgs(&irArgs, loweredBaseVal); - UInt argCount = irArgs.Count(); + auto funcDeclRef = memberFuncExpr->declRef; - return LoweredValInfo::simple(getBuilder()->emitConstructorInst(type, argCount, &irArgs[0])); - } + addDirectCallArgs(expr, &irArgs); + return emitCallToDeclRef(context, type, funcDeclRef, irArgs); + } + else if (auto staticMemberFuncExpr = funcExpr.As<StaticMemberExpr>()) + { + auto funcDeclRef = staticMemberFuncExpr->declRef; + addDirectCallArgs(expr, &irArgs); + return emitCallToDeclRef(context, type, funcDeclRef, irArgs); } + else if (auto varExpr = funcExpr.As<VarExpr>()) + { + auto funcDeclRef = varExpr->declRef; + addDirectCallArgs(expr, &irArgs); + return emitCallToDeclRef(context, type, funcDeclRef, irArgs); + } + + // The default case is to assume that we just have + // an ordinary expression, and can lower it as such. + LoweredValInfo funcVal = lowerExpr(context, expr->FunctionExpr); + + // Now we add any direct arguments from the call expression itself. + addDirectCallArgs(expr, &irArgs); - return lowerSimpleCall(expr); + // Delegate to the logic for invoking a value. + return emitCallToVal(context, type, funcVal, irArgs.Count(), irArgs.Buffer()); } LoweredValInfo visitIndexExpr(IndexExpr* expr) @@ -503,14 +750,14 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> { switch (base.flavor) { - case LoweredValInfo::Flavor::Simple: + default: { - IRValue* irBase = base.val; + IRValue* irBase = getSimpleVal(context, base); return LoweredValInfo::simple( getBuilder()->emitFieldExtract( getSimpleType(fieldType), irBase, - (IRStructField*) getSimpleVal(field))); + (IRStructField*) getSimpleVal(context, field))); } break; @@ -524,14 +771,17 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> getBuilder()->emitFieldAddress( getBuilder()->getPtrType(getSimpleType(fieldType)), irBasePtr, - (IRStructField*) getSimpleVal(field))); + (IRStructField*) getSimpleVal(context, field))); } break; - default: - SLANG_UNIMPLEMENTED_X("codegen for field extract"); } } + LoweredValInfo visitStaticMemberExpr(StaticMemberExpr* expr) + { + return ensureDecl(context, expr->declRef); + } + LoweredValInfo visitMemberExpr(MemberExpr* expr) { auto loweredType = lowerType(context, expr->type); @@ -545,6 +795,11 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> auto loweredField = ensureDecl(context, fieldDeclRef); return extractField(loweredType, loweredBase, loweredField); } + else if (auto callableDeclRef = declRef.As<CallableDecl>()) + { + auto loweredFunc = ensureDecl(context, callableDeclRef); + return LoweredValInfo::boundMember(loweredBase, loweredFunc); + } SLANG_UNIMPLEMENTED_X("codegen for subscript expression"); } @@ -613,7 +868,20 @@ struct ExprLoweringVisitor : ExprVisitor<ExprLoweringVisitor, LoweredValInfo> LoweredValInfo visitAssignExpr(AssignExpr* expr) { - SLANG_UNIMPLEMENTED_X("shared type expression during code generation"); + // Because our representation of lowered "values" + // can encompass l-values explicitly, we can + // lower assignment easily. We just lower the left- + // and right-hand sides, and then peform an assignment + // based on the resulting values. + // + auto leftVal = lowerExpr(context, expr->left); + auto rightVal = lowerExpr(context, expr->right); + assign(context, leftVal, rightVal); + + // The result value of the assignment expression is + // the value of the left-hand side (and it is expected + // to be an l-value). + return leftVal; } LoweredValInfo visitParenExpr(ParenExpr* expr) @@ -642,18 +910,61 @@ struct StmtLoweringVisitor : StmtVisitor<StmtLoweringVisitor> SLANG_UNIMPLEMENTED_X("stmt catch-all"); } + void visitExpressionStmt(ExpressionStmt* stmt) + { + // The statement evaluates an expression + // (for side effects, one assumes) and then + // discards the result. As such, we simply + // lower the expression, and don't use + // the result. + // + lowerExpr(context, stmt->Expression); + } + + void visitDeclStmt(DeclStmt* stmt) + { + // For now, we lower a declaration directly + // into the current context. + // + // TODO: We may want to consider whether + // nested type/function declarations should + // be lowered into the global scope during + // IR generation, or whether they should + // be lifted later (pushing capture analysis + // down to the IR). + // + lowerDecl(context, stmt->decl, nullptr); + } + + void visitSeqStmt(SeqStmt* stmt) + { + // To lower a sequence of statements, + // just lower each in order + for (auto ss : stmt->stmts) + { + lowerStmt(context, ss); + } + } + void visitBlockStmt(BlockStmt* stmt) { + // To lower a block (scope) statement, + // just lower its body. The IR doesn't + // need to reflect the scoping of the AST. lowerStmt(context, stmt->body); } void visitReturnStmt(ReturnStmt* stmt) { + // A `return` statement turns into a return + // instruction. If the statement had an argument + // expression, then we need to lower that to + // a value first, and then emit the resulting value. if( auto expr = stmt->Expression ) { auto loweredExpr = lowerExpr(context, expr); - getBuilder()->emitReturn(getSimpleVal(loweredExpr)); + getBuilder()->emitReturn(getSimpleVal(context, loweredExpr)); } else { @@ -676,7 +987,31 @@ void assign( LoweredValInfo const& left, LoweredValInfo const& right) { - SLANG_UNIMPLEMENTED_X("assignment"); + switch (left.flavor) + { + case LoweredValInfo::Flavor::Ptr: + switch (right.flavor) + { + case LoweredValInfo::Flavor::Simple: + case LoweredValInfo::Flavor::Ptr: + { + auto builder = context->irBuilder; + builder->emitStore( + left.val, + getSimpleVal(context, right)); + } + break; + + default: + SLANG_UNIMPLEMENTED_X("assignment"); + break; + } + break; + + default: + SLANG_UNIMPLEMENTED_X("assignment"); + break; + } } struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> @@ -704,6 +1039,30 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> SLANG_UNIMPLEMENTED_X("decl catch-all"); } + LoweredValInfo visitSubscriptDecl(SubscriptDecl* decl) + { + // A subscript operation may encompass one or more + // accessors, and these are what should actually + // get lowered (they are effectively functions). + + for (auto accessor : decl->getMembersOfType<AccessorDecl>()) + { + if (accessor->HasModifier<IntrinsicOpModifier>()) + continue; + + ensureDecl(context, makeDeclRef(accessor.Ptr())); + } + + // The subscript declaration itself won't correspond + // to anything in the lowered program, so we don't + // bother creating a representation here. + // + // Note: We may want to have a specific lowered value + // that can represent the combination of callables + // that make up the subscript operation. + return LoweredValInfo(); + } + LoweredValInfo visitVarDeclBase(VarDeclBase* decl) { // A user-defined variable declaration will usually turn into @@ -818,9 +1177,22 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> // set up sub context for generating our new function + CallableDecl* declForParameters = decl; + CallableDecl* declForReturnType = decl; + if (auto accessorDecl = dynamic_cast<AccessorDecl*>(decl)) + { + auto parentDecl = accessorDecl->ParentDecl; + if (auto subscriptDecl = dynamic_cast<SubscriptDecl*>(parentDecl)) + { + declForParameters = subscriptDecl; + declForReturnType = subscriptDecl; + } + } + + List<IRType*> paramTypes; - for( auto paramDecl : decl->GetParameters() ) + for( auto paramDecl : declForParameters->GetParameters() ) { IRType* irParamType = lowerSimpleType(context, paramDecl->getType()); paramTypes.Add(irParamType); @@ -834,7 +1206,27 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> subContext->shared->declValues.Add(paramDeclRef, irParamVal); } - auto irResultType = lowerSimpleType(context, decl->ReturnType); + auto irResultType = lowerSimpleType(context, declForReturnType->ReturnType); + + if (auto setterDecl = dynamic_cast<SetterDecl*>(decl)) + { + // We are lowering a "setter" accessor inside a subscript + // declaration, which means we don't want to *return* the + // stated return type of the subscript, but instead take + // it as a parameter. + // + IRType* irParamType = irResultType; + paramTypes.Add(irParamType); + IRParam* irParam = subBuilder->emitParam(irParamType); + + // TODO: we need some way to wire this up to the `newValue` + // or whatever name we give for that parameter inside + // the setter body. + + // Instead, a setter always returns `void` + // + irResultType = getBuilder()->getVoidType(); + } auto irFuncType = getBuilder()->getFuncType( paramTypes.Count(), @@ -854,7 +1246,7 @@ struct DeclLoweringVisitor : DeclVisitor<DeclLoweringVisitor, LoweredValInfo> LoweredValInfo lowerDecl( IRGenContext* context, - Decl* decl, + DeclBase* decl, Layout* layout) { DeclLoweringVisitor visitor; diff --git a/source/slang/lower.cpp b/source/slang/lower.cpp index f0d756a02..391221f47 100644 --- a/source/slang/lower.cpp +++ b/source/slang/lower.cpp @@ -1852,17 +1852,28 @@ struct LoweringVisitor return &shared->compileRequest->mSink; } + LoweredExpr visitStaticMemberExpr( + StaticMemberExpr* expr) + { + auto loweredBase = lowerExprOrTuple(expr->BaseExpression); + auto loweredDeclRef = translateDeclRef(expr->declRef); + + // TODO: we should probably support type-type members here. + + RefPtr<StaticMemberExpr> loweredExpr = new StaticMemberExpr(); + lowerExprCommon(loweredExpr, expr); + loweredExpr->BaseExpression = loweredBase.getExpr(); + loweredExpr->declRef = loweredDeclRef.As<Decl>(); + loweredExpr->name = expr->name; + + return LoweredExpr(loweredExpr); + } + LoweredExpr visitMemberExpr( MemberExpr* expr) { assert(expr->BaseExpression); auto loweredBase = lowerExprOrTuple(expr->BaseExpression); - - if( !loweredBase ) - { - loweredBase = lowerExprOrTuple(expr->BaseExpression); - } - assert(loweredBase); auto loweredDeclRef = translateDeclRef(expr->declRef); diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 8ea41641d..2381682aa 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -2121,6 +2121,8 @@ namespace Slang static RefPtr<AccessorDecl> parseAccessorDecl(Parser* parser) { + Modifiers modifiers = ParseModifiers(parser); + RefPtr<AccessorDecl> decl; if( AdvanceIf(parser, "get") ) { @@ -2136,6 +2138,8 @@ namespace Slang return nullptr; } + AddModifiers(decl, modifiers.first); + if( parser->tokenReader.PeekTokenType() == TokenType::LBrace ) { decl->Body = parser->ParseBlockStatement(); |
