diff options
| author | Yong He <yonghe@outlook.com> | 2022-08-10 15:37:19 -0700 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-08-10 15:37:19 -0700 |
| commit | a083a37ee58dc48d92cf2b844466a295eb3e643e (patch) | |
| tree | 69028f3d9f92fa7e2085c76bc234e1949ef645c5 | |
| parent | 88f04c29244af23c1cdd472d8d1ae3e5a650494e (diff) | |
Add `none` literal that is convertible to `Optional`. (#2356)
* Add `none` literal that is convertible to `Optional`.
* Fix cpu code gen.
* Include vk and cpu test for is-as operator test.
* Inline comparison operators.
Co-authored-by: Yong He <yhe@nvidia.com>
21 files changed, 343 insertions, 3 deletions
diff --git a/build/visual-studio/slang/slang.vcxproj b/build/visual-studio/slang/slang.vcxproj index 7e4144240..39e5f9083 100644 --- a/build/visual-studio/slang/slang.vcxproj +++ b/build/visual-studio/slang/slang.vcxproj @@ -345,6 +345,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClInclude Include="..\..\..\source\slang\slang-ir-augment-make-existential.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-bind-existentials.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-byte-address-legalize.h" />
+ <ClInclude Include="..\..\..\source\slang\slang-ir-cleanup-void.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-clone.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-collect-global-uniforms.h" />
<ClInclude Include="..\..\..\source\slang\slang-ir-com-interface.h" />
@@ -508,6 +509,7 @@ IF EXIST ..\..\..\external\slang-glslang\bin\windows-aarch64\release\slang-glsla <ClCompile Include="..\..\..\source\slang\slang-ir-augment-make-existential.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-bind-existentials.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-byte-address-legalize.cpp" />
+ <ClCompile Include="..\..\..\source\slang\slang-ir-cleanup-void.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-clone.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-collect-global-uniforms.cpp" />
<ClCompile Include="..\..\..\source\slang\slang-ir-com-interface.cpp" />
diff --git a/build/visual-studio/slang/slang.vcxproj.filters b/build/visual-studio/slang/slang.vcxproj.filters index 27589ffee..739a6a442 100644 --- a/build/visual-studio/slang/slang.vcxproj.filters +++ b/build/visual-studio/slang/slang.vcxproj.filters @@ -132,6 +132,9 @@ <ClInclude Include="..\..\..\source\slang\slang-ir-byte-address-legalize.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="..\..\..\source\slang\slang-ir-cleanup-void.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="..\..\..\source\slang\slang-ir-clone.h">
<Filter>Header Files</Filter>
</ClInclude>
@@ -617,6 +620,9 @@ <ClCompile Include="..\..\..\source\slang\slang-ir-byte-address-legalize.cpp">
<Filter>Source Files</Filter>
</ClCompile>
+ <ClCompile Include="..\..\..\source\slang\slang-ir-cleanup-void.cpp">
+ <Filter>Source Files</Filter>
+ </ClCompile>
<ClCompile Include="..\..\..\source\slang\slang-ir-clone.cpp">
<Filter>Source Files</Filter>
</ClCompile>
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang index 616511b01..d912fe8ce 100644 --- a/source/slang/core.meta.slang +++ b/source/slang/core.meta.slang @@ -349,6 +349,12 @@ struct NullPtr { }; +__magic_type(NoneType) +__intrinsic_type($(kIROp_VoidType)) +struct __none_t +{ +}; + __generic<T> __magic_type(PtrType) __intrinsic_type($(kIROp_PtrType)) @@ -472,8 +478,36 @@ struct Optional __intrinsic_op($(kIROp_GetOptionalValue)) get; } + + __implicit_conversion($(kConversionCost_ValToOptional)) + __intrinsic_op($(kIROp_MakeOptionalValue)) + __init(T val); }; +__generic<T> +[__unsafeForceInlineEarly] +bool operator==(Optional<T> val, __none_t noneVal) +{ + return !val.hasValue; +} +__generic<T> +[__unsafeForceInlineEarly] +bool operator!=(Optional<T> val, __none_t noneVal) +{ + return val.hasValue; +} +__generic<T> +[__unsafeForceInlineEarly] +bool operator==(__none_t noneVal, Optional<T> val) +{ + return !val.hasValue; +} +__generic<T> +[__unsafeForceInlineEarly] +bool operator!=(__none_t noneVal, Optional<T> val) +{ + return val.hasValue; +} __magic_type(StringType) __intrinsic_type($(kIROp_StringType)) diff --git a/source/slang/slang-ast-builder.cpp b/source/slang/slang-ast-builder.cpp index 2acdc26d3..fa8051171 100644 --- a/source/slang/slang-ast-builder.cpp +++ b/source/slang/slang-ast-builder.cpp @@ -131,6 +131,16 @@ Type* SharedASTBuilder::getNullPtrType() return m_nullPtrType; } +Type* SharedASTBuilder::getNoneType() +{ + if (!m_noneType) + { + auto noneTypeDecl = findMagicDecl("NoneType"); + m_noneType = DeclRefType::create(m_astBuilder, makeDeclRef<Decl>(noneTypeDecl)); + } + return m_noneType; +} + SharedASTBuilder::~SharedASTBuilder() { // Release built in types.. diff --git a/source/slang/slang-ast-builder.h b/source/slang/slang-ast-builder.h index e62e92a7b..d363cb8f6 100644 --- a/source/slang/slang-ast-builder.h +++ b/source/slang/slang-ast-builder.h @@ -33,6 +33,8 @@ public: Type* getDynamicType(); /// Get the NullPtr type Type* getNullPtrType(); + /// Get the NullPtr type + Type* getNoneType(); const ReflectClassInfo* findClassInfo(Name* name); SyntaxClass<NodeBase> findSyntaxClass(Name* name); @@ -74,6 +76,7 @@ protected: Type* m_enumTypeType = nullptr; Type* m_dynamicType = nullptr; Type* m_nullPtrType = nullptr; + Type* m_noneType = nullptr; Type* m_builtinTypes[Index(BaseType::CountOf)]; @@ -139,6 +142,7 @@ public: Type* getBottomType() { return m_sharedASTBuilder->m_bottomType; } Type* getStringType() { return m_sharedASTBuilder->getStringType(); } Type* getNullPtrType() { return m_sharedASTBuilder->getNullPtrType(); } + Type* getNoneType() { return m_sharedASTBuilder->getNoneType(); } Type* getEnumTypeType() { return m_sharedASTBuilder->getEnumTypeType(); } // Construct the type `Ptr<valueType>`, where `Ptr` diff --git a/source/slang/slang-ast-expr.h b/source/slang/slang-ast-expr.h index 2dfd937a4..6f10f2d82 100644 --- a/source/slang/slang-ast-expr.h +++ b/source/slang/slang-ast-expr.h @@ -101,6 +101,11 @@ class NullPtrLiteralExpr : public LiteralExpr SLANG_AST_CLASS(NullPtrLiteralExpr) }; +class NoneLiteralExpr : public LiteralExpr +{ + SLANG_AST_CLASS(NoneLiteralExpr) +}; + class StringLiteralExpr : public LiteralExpr { SLANG_AST_CLASS(StringLiteralExpr) diff --git a/source/slang/slang-ast-iterator.h b/source/slang/slang-ast-iterator.h index 8461ff7a3..233ce9a17 100644 --- a/source/slang/slang-ast-iterator.h +++ b/source/slang/slang-ast-iterator.h @@ -46,6 +46,10 @@ struct ASTIterator { iterator->maybeDispatchCallback(expr); } + void visitNoneLiteralExpr(NoneLiteralExpr* expr) + { + iterator->maybeDispatchCallback(expr); + } void visitIntegerLiteralExpr(IntegerLiteralExpr* expr) { iterator->maybeDispatchCallback(expr); diff --git a/source/slang/slang-ast-support-types.h b/source/slang/slang-ast-support-types.h index f74abc08f..00a4a482c 100644 --- a/source/slang/slang-ast-support-types.h +++ b/source/slang/slang-ast-support-types.h @@ -88,6 +88,9 @@ namespace Slang // Conversion that is lossless and keeps the "kind" of the value the same kConversionCost_RankPromotion = 150, + kConversionCost_NoneToOptional = 150, + kConversionCost_ValToOptional = 150, + kConversionCost_NullPtrToPtr = 150, // Conversions that are lossless, but change "kind" kConversionCost_UnsignedToSignedPromotion = 200, diff --git a/source/slang/slang-ast-type.h b/source/slang/slang-ast-type.h index 7b94cbe6d..0ee0fce2c 100644 --- a/source/slang/slang-ast-type.h +++ b/source/slang/slang-ast-type.h @@ -529,6 +529,11 @@ class PtrTypeBase : public BuiltinType Type* getValueType(); }; +class NoneType : public BuiltinType +{ + SLANG_AST_CLASS(NoneType) +}; + class NullPtrType : public BuiltinType { SLANG_AST_CLASS(NullPtrType) diff --git a/source/slang/slang-check-conversion.cpp b/source/slang/slang-check-conversion.cpp index 6290acbd9..4bcf07b2b 100644 --- a/source/slang/slang-check-conversion.cpp +++ b/source/slang/slang-check-conversion.cpp @@ -773,13 +773,28 @@ namespace Slang { if (outCost) { - *outCost = kConversionCost_None; + *outCost = kConversionCost_NullPtrToPtr; } if (outToExpr) *outToExpr = fromExpr; return true; } - + // none_t can be cast into any Optional<T> type. + if (as<NoneType>(fromType) && as<OptionalType>(toType)) + { + if (outCost) + { + *outCost = kConversionCost_NoneToOptional; + } + if (outToExpr) + { + auto resultExpr = getASTBuilder()->create<MakeOptionalExpr>(); + resultExpr->loc = fromExpr->loc; + resultExpr->type = toType; + *outToExpr = resultExpr; + } + return true; + } // If we are casting to an interface type, then that will succeed // if the "from" type conforms to the interface. // diff --git a/source/slang/slang-check-expr.cpp b/source/slang/slang-check-expr.cpp index 714eba9a3..cb75e3078 100644 --- a/source/slang/slang-check-expr.cpp +++ b/source/slang/slang-check-expr.cpp @@ -822,6 +822,12 @@ namespace Slang return expr; } + Expr* SemanticsExprVisitor::visitNoneLiteralExpr(NoneLiteralExpr* expr) + { + expr->type = m_astBuilder->getNoneType(); + return expr; + } + Expr* SemanticsExprVisitor::visitIntegerLiteralExpr(IntegerLiteralExpr* expr) { // The expression might already have a type, determined by its suffix. diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h index df713d80f..345652263 100644 --- a/source/slang/slang-check-impl.h +++ b/source/slang/slang-check-impl.h @@ -1697,6 +1697,7 @@ namespace Slang Expr* visitIncompleteExpr(IncompleteExpr* expr); Expr* visitBoolLiteralExpr(BoolLiteralExpr* expr); Expr* visitNullPtrLiteralExpr(NullPtrLiteralExpr* expr); + Expr* visitNoneLiteralExpr(NoneLiteralExpr* expr); Expr* visitIntegerLiteralExpr(IntegerLiteralExpr* expr); Expr* visitFloatingPointLiteralExpr(FloatingPointLiteralExpr* expr); Expr* visitStringLiteralExpr(StringLiteralExpr* expr); diff --git a/source/slang/slang-emit.cpp b/source/slang/slang-emit.cpp index da8739a49..f3e78e48f 100644 --- a/source/slang/slang-emit.cpp +++ b/source/slang/slang-emit.cpp @@ -8,6 +8,7 @@ #include "slang-ir-bind-existentials.h" #include "slang-ir-byte-address-legalize.h" #include "slang-ir-collect-global-uniforms.h" +#include "slang-ir-cleanup-void.h" #include "slang-ir-dce.h" #include "slang-ir-dll-export.h" #include "slang-ir-dll-import.h" @@ -745,6 +746,8 @@ Result linkAndOptimizeIR( #endif validateIRModuleIfEnabled(codeGenContext, irModule); + cleanUpVoidType(irModule); + // Lower all bit_cast operations on complex types into leaf-level // bit_cast on basic types. lowerBitCast(targetRequest, irModule); diff --git a/source/slang/slang-ir-cleanup-void.cpp b/source/slang/slang-ir-cleanup-void.cpp new file mode 100644 index 000000000..ac520c1d5 --- /dev/null +++ b/source/slang/slang-ir-cleanup-void.cpp @@ -0,0 +1,159 @@ +// slang-ir-cleanup-void.cpp + +#include "slang-ir-cleanup-void.h" +#include "slang-ir.h" +#include "slang-ir-insts.h" + +namespace Slang +{ + struct CleanUpVoidContext + { + IRModule* module; + + SharedIRBuilder sharedBuilderStorage; + + List<IRInst*> workList; + HashSet<IRInst*> workListSet; + + void addToWorkList( + IRInst* inst) + { + for (auto ii = inst->getParent(); ii; ii = ii->getParent()) + { + if (as<IRGeneric>(ii)) + return; + } + + if (workListSet.Contains(inst)) + return; + + workList.add(inst); + workListSet.Add(inst); + } + + void processInst(IRInst* inst) + { + switch (inst->getOp()) + { + case kIROp_Call: + { + // Remove void argument. + auto call = as<IRCall>(inst); + List<IRInst*> newArgs; + for (UInt i = 0; i < call->getArgCount(); i++) + { + auto arg = call->getArg(i); + if (arg->getDataType() && arg->getDataType()->getOp() == kIROp_VoidType) + { + continue; + } + newArgs.add(arg); + } + if (newArgs.getCount() != (Index)call->getArgCount()) + { + IRBuilder builder(&sharedBuilderStorage); + builder.setInsertBefore(call); + auto newCall = builder.emitCallInst(call->getFullType(), call->getCallee(), newArgs); + call->replaceUsesWith(newCall); + call->removeAndDeallocate(); + inst = newCall; + } + } + break; + case kIROp_Func: + { + // Remove void parameter. + List<IRParam*> paramsToRemove; + auto func = as<IRFunc>(inst); + for (auto param : func->getParams()) + { + if (param->getDataType()->getOp() == kIROp_VoidType) + { + paramsToRemove.add(param); + } + } + IRBuilder builder(&sharedBuilderStorage); + builder.setInsertBefore(func); + for (auto param : paramsToRemove) + { + auto voidVal = builder.getVoidValue(); + param->replaceUsesWith(voidVal); + param->removeAndDeallocate(); + } + } + break; + case kIROp_FuncType: + { + auto funcType = as<IRFuncType>(inst); + List<IRInst*> newOperands; + for (UInt i = 1; i < funcType->getOperandCount(); i++) + { + auto operand = funcType->getOperand(i); + if (operand->getOp() == kIROp_VoidType) + { + continue; + } + newOperands.add(operand); + } + if (newOperands.getCount() != (Index)funcType->getParamCount()) + { + IRBuilder builder(&sharedBuilderStorage); + builder.setInsertBefore(funcType); + auto newFuncType = builder.getFuncType(newOperands.getCount(), (IRType**)newOperands.getBuffer(), funcType->getResultType()); + if (newFuncType != funcType) + { + funcType->replaceUsesWith(newFuncType); + funcType->removeAndDeallocate(); + } + inst = newFuncType; + } + } + break; + case kIROp_StructType: + { + // TODO: cleanup void fields. + } + break; + default: + break; + } + + // TODO: If inst has void type, all uses of it should be replaced with void val. + // We should do this only for a subset of opcodes known to be safe. + + } + + void processModule() + { + SharedIRBuilder* sharedBuilder = &sharedBuilderStorage; + sharedBuilder->init(module); + + // Deduplicate equivalent types. + sharedBuilder->deduplicateAndRebuildGlobalNumberingMap(); + + addToWorkList(module->getModuleInst()); + + while (workList.getCount() != 0) + { + IRInst* inst = workList.getLast(); + + workList.removeLast(); + workListSet.Remove(inst); + + processInst(inst); + + for (auto child = inst->getLastChild(); child; child = child->getPrevInst()) + { + addToWorkList(child); + } + } + } + }; + + void cleanUpVoidType(IRModule* module) + { + CleanUpVoidContext context; + context.module = module; + context.processModule(); + } +} diff --git a/source/slang/slang-ir-cleanup-void.h b/source/slang/slang-ir-cleanup-void.h new file mode 100644 index 000000000..b6c6fc5f7 --- /dev/null +++ b/source/slang/slang-ir-cleanup-void.h @@ -0,0 +1,15 @@ +// slang-ir-cleanup-void.h +#pragma once + +#include "slang-ir.h" + +namespace Slang +{ + struct IRModule; + class DiagnosticSink; + + /// Cleanup all uses of void and void types. + void cleanUpVoidType( + IRModule* module); + +} diff --git a/source/slang/slang-language-server-completion.cpp b/source/slang/slang-language-server-completion.cpp index 4ae5bcb37..ac3cdefc0 100644 --- a/source/slang/slang-language-server-completion.cpp +++ b/source/slang/slang-language-server-completion.cpp @@ -31,7 +31,7 @@ static const char* kStmtKeywords[] = { "extension", "associatedtype", "this", "namespace", "This", "using", "__generic", "__exported", "import", "enum", "break", "continue", "discard", "defer", "cbuffer", "tbuffer", "func", "is", - "as", "nullptr", "true", "false"}; + "as", "nullptr", "none", "true", "false"}; static const char* hlslSemanticNames[] = { "register", diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp index aef60c9d9..c7fbfda77 100644 --- a/source/slang/slang-lower-to-ir.cpp +++ b/source/slang/slang-lower-to-ir.cpp @@ -3365,6 +3365,11 @@ struct ExprLoweringVisitorBase : ExprVisitor<Derived, LoweredValInfo> return LoweredValInfo::simple(context->irBuilder->getPtrValue(nullptr)); } + LoweredValInfo visitNoneLiteralExpr(NoneLiteralExpr*) + { + return LoweredValInfo::simple(context->irBuilder->getVoidValue()); + } + LoweredValInfo visitIntegerLiteralExpr(IntegerLiteralExpr* expr) { auto type = lowerType(context, expr->type); diff --git a/source/slang/slang-parser.cpp b/source/slang/slang-parser.cpp index 4baa7211e..7447859c7 100644 --- a/source/slang/slang-parser.cpp +++ b/source/slang/slang-parser.cpp @@ -5031,6 +5031,11 @@ namespace Slang return parser->astBuilder->create<NullPtrLiteralExpr>(); } + static NodeBase* parseNoneExpr(Parser* parser, void* /*userData*/) + { + return parser->astBuilder->create<NoneLiteralExpr>(); + } + static NodeBase* parseTryExpr(Parser* parser, void* /*userData*/) { auto tryExpr = parser->astBuilder->create<TryExpr>(); @@ -6587,6 +6592,7 @@ namespace Slang _makeParseExpr("true", parseTrueExpr), _makeParseExpr("false", parseFalseExpr), _makeParseExpr("nullptr", parseNullPtrExpr), + _makeParseExpr("none", parseNoneExpr), _makeParseExpr("try", parseTryExpr), _makeParseExpr("__TaggedUnion", parseTaggedUnionType), _makeParseExpr("__jvp", parseJVPDifferentiate) diff --git a/tests/language-feature/interfaces/is-as-dynamic.slang b/tests/language-feature/interfaces/is-as-dynamic.slang index 4499db53a..1b163361a 100644 --- a/tests/language-feature/interfaces/is-as-dynamic.slang +++ b/tests/language-feature/interfaces/is-as-dynamic.slang @@ -3,6 +3,8 @@ // Test that `is` and `as` operators works as intended in dynamic dispatch. //TEST(compute):COMPARE_COMPUTE: -shaderobj +//TEST(compute):COMPARE_COMPUTE_EX:-slang -vk -compute +//TEST(compute):COMPARE_COMPUTE_EX:-slang -cpu -compute //TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer RWStructuredBuffer<int> outputBuffer; diff --git a/tests/language-feature/types/optional.slang b/tests/language-feature/types/optional.slang new file mode 100644 index 000000000..ae1711cd7 --- /dev/null +++ b/tests/language-feature/types/optional.slang @@ -0,0 +1,53 @@ +// optional.slang + +// Test that `Optional` construction and conversion works. + +//TEST(compute):COMPARE_COMPUTE_EX:-slang -compute +//TEST(compute):COMPARE_COMPUTE_EX:-slang -vk -compute +//TEST(compute):COMPARE_COMPUTE_EX:-slang -cpu -compute + +//TEST_INPUT:ubuffer(data=[0 0 0 0], stride=4):out,name=outputBuffer +RWStructuredBuffer<int> outputBuffer; + +[anyValueSize(8)] +interface IFoo +{ + int method(); +} + +//TEST_INPUT: type_conformance Impl1:IFoo = 0 +struct Impl1 : IFoo +{ + int data; + int method() { return data; } +} + +Optional<Impl1> getVal(bool shouldHaveVal) +{ + if (shouldHaveVal) + { + Impl1 val; + val.data = 1; + return val; + } + return none; +} + +[numthreads(1, 1, 1)] +void computeMain(uint3 dispatchThreadID: SV_DispatchThreadID) +{ + let v1 = getVal(true); + let v2 = getVal(false); + int result = 0; + if (v1.hasValue) + result += v1.value.data; + if (v2.hasValue) + result += v2.value.data; + outputBuffer[0] = result; + if (v1 != none) + result += v1.value.data; + if (!(v2 == none)) + result += v2.value.data; + outputBuffer[1] = result; + +} diff --git a/tests/language-feature/types/optional.slang.expected.txt b/tests/language-feature/types/optional.slang.expected.txt new file mode 100644 index 000000000..1191247b6 --- /dev/null +++ b/tests/language-feature/types/optional.slang.expected.txt @@ -0,0 +1,2 @@ +1 +2 |
