summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
authorTim Foley <tfoleyNV@users.noreply.github.com>2019-11-22 18:54:38 -0800
committerGitHub <noreply@github.com>2019-11-22 18:54:38 -0800
commitc6393465795e700a68458ff618041104f89ed42b (patch)
treea58ce1a580c8c3aa108265703ba88571e780c9af
parent63dcbe50d40afc15196eeca6ba0b04657adf6953 (diff)
Clean up the concept of "pseudo ops" (#1136)
* Clean up the concept of "pseudo ops" Built-in functions in the Slang standard library can be marked with `__intrinsic_op(...)` to indicate that they should not lower to functions in the IR, and that instead call sites to those functions should be translated directly to the IR. There are two cases where `__intrinsic_op(...)` gets used: 1. In the case where the argument to `__intrinsic_op(...)` is an actual IR instruction opcode, the IR lowering logic directly translates a call into an instruction with the given opcode. The arguments to the call become the operands of the instruction. 2. In the case where the argument to `__intrinsic_op(...)` is one of a set of "pseudo" instruction opcodes, the IR lowering logic directly handles the lowering to IR with dedicated code. The operands to the call might be handled differently depending on the kind of operation. The compound operators like `+=` are the most important example of these "pseudo" instructions. It doesn't make sense to handle them as true function calls (although that would work semantically), nor does it make sense to have a single IR instruction with such complicated semantics. An earlier version of the compiler used the same enumeration for both the true IR instruction opcodes and these "pseudo" opcodes, with the simple constraint that the pseudo opcodes were all negative while the real opcodes were positive. That design got changed up over a few refactorings, and because there was never a good explanation in the code itself of what "pseudo" opcodes were, we eventually ended up in a place where the in-memory and serialized IR encodings included logic to try to deal with the possibility of these "pseudo" opcodes, even though the entire design of the lowering pass meant that they'd never appear in generated IR. This change tries to clean up the mess in a few ways: * The terminology is now that these are "compound" intrinsic ops, to differentiate them from the more common case of intrinsic ops that map one-to-one to IR instructions. * The declaration of the compound intrinsic ops is no longer in a file related to the IR, and doesn't use the `IR` naming prefix, so somebody looking at the IR opcodes cannot become confused and think the compound ops are allowed there. * The IR encoding in memory and when serialized is updated to not account for or worry about the possibility of "pseudo" ops. * The compound ops are declared in such a way that ensures their enumerant values are all negative, so that they are yet again trivially disjoint from the true IR opcodes. A more drastic change might have split `__intrinsic_op` into two different modifier types: one for the trivial single-instruction case and one for the compound case. Doing this would make the change more invasive, though, because there are places in the meta-code that generates the standard library that intentionally handle both single-instruction and compound ops (because built-in operators can translate to either case). * fixup: missing file * cleanups based on review feedback
-rw-r--r--source/slang/core.meta.slang14
-rw-r--r--source/slang/core.meta.slang.h25
-rw-r--r--source/slang/hlsl.meta.slang20
-rw-r--r--source/slang/hlsl.meta.slang.h20
-rw-r--r--source/slang/slang-check-impl.h2
-rw-r--r--source/slang/slang-compound-intrinsics.h81
-rw-r--r--source/slang/slang-ir-inst-defs.h28
-rw-r--r--source/slang/slang-ir-serialize.cpp7
-rw-r--r--source/slang/slang-ir.cpp30
-rw-r--r--source/slang/slang-ir.h39
-rw-r--r--source/slang/slang-lower-to-ir.cpp83
-rw-r--r--source/slang/slang-modifier-defs.h15
-rw-r--r--source/slang/slang-stdlib.cpp35
-rw-r--r--source/slang/slang-syntax.h2
-rw-r--r--source/slang/slang.vcxproj5
-rw-r--r--source/slang/slang.vcxproj.filters3
16 files changed, 234 insertions, 175 deletions
diff --git a/source/slang/core.meta.slang b/source/slang/core.meta.slang
index 763bd4a78..7785239a2 100644
--- a/source/slang/core.meta.slang
+++ b/source/slang/core.meta.slang
@@ -58,8 +58,16 @@ interface __FlagsEnumType : __EnumType
{
};
-__generic<T,U> __intrinsic_op(Sequence) U operator,(T left, U right);
+// The "comma operator" is effectively just a generic function that returns its second
+// argument. The left-to-right evaluation order guaranteed by Slang then ensures that
+// `left` is evaluated before `right`.
+//
+__generic<T,U> __intrinsic_op($(kCompoundIntrinsicOp_Sequence)) U operator,(T left, U right);
+// The ternary `?:` operator does not short-circuit in HLSL, and Slang continues to
+// follow that definition, so that this operator is effectively just an ordinary
+// function, rather than a special-case piece of syntax.
+//
__generic<T> __intrinsic_op(select) T operator?:(bool condition, T ifTrue, T ifFalse);
__generic<T, let N : int> __intrinsic_op(select) vector<T,N> operator?:(vector<bool,N> condition, vector<T,N> ifTrue, vector<T,N> ifFalse);
@@ -956,7 +964,6 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"$ctextureGrad($p, $2, $3, $4)$z\")\n";
-// sb << "__intrinsic_op(sampleGrad)\n";
sb << "T SampleGrad(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, ";
@@ -966,7 +973,6 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
if( baseShape != TextureFlavor::Shape::ShapeCube )
{
sb << "__target_intrinsic(glsl, \"$ctextureGradOffset($p, $2, $3, $4, $5)$z\")\n";
-// sb << "__intrinsic_op(sampleGrad)\n";
sb << "T SampleGrad(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, ";
@@ -1158,7 +1164,7 @@ for (auto op : binaryOps)
switch (op.opCode)
{
case kIROp_Mul:
- case kIRPseudoOp_MulAssign:
+ case kCompoundIntrinsicOp_MulAssign:
break;
default:
diff --git a/source/slang/core.meta.slang.h b/source/slang/core.meta.slang.h
index 7cb729722..c681a5f6b 100644
--- a/source/slang/core.meta.slang.h
+++ b/source/slang/core.meta.slang.h
@@ -58,8 +58,19 @@ SLANG_RAW("interface __FlagsEnumType : __EnumType\n")
SLANG_RAW("{\n")
SLANG_RAW("};\n")
SLANG_RAW("\n")
-SLANG_RAW("__generic<T,U> __intrinsic_op(Sequence) U operator,(T left, U right);\n")
+SLANG_RAW("// The \"comma operator\" is effectively just a generic function that returns its second\n")
+SLANG_RAW("// argument. The left-to-right evaluation order guaranteed by Slang then ensures that\n")
+SLANG_RAW("// `left` is evaluated before `right`.\n")
+SLANG_RAW("//\n")
+SLANG_RAW("__generic<T,U> __intrinsic_op(")
+SLANG_SPLICE(kCompoundIntrinsicOp_Sequence
+)
+SLANG_RAW(") U operator,(T left, U right);\n")
SLANG_RAW("\n")
+SLANG_RAW("// The ternary `?:` operator does not short-circuit in HLSL, and Slang continues to\n")
+SLANG_RAW("// follow that definition, so that this operator is effectively just an ordinary\n")
+SLANG_RAW("// function, rather than a special-case piece of syntax.\n")
+SLANG_RAW("//\n")
SLANG_RAW("__generic<T> __intrinsic_op(select) T operator?:(bool condition, T ifTrue, T ifFalse);\n")
SLANG_RAW("__generic<T, let N : int> __intrinsic_op(select) vector<T,N> operator?:(vector<bool,N> condition, vector<T,N> ifTrue, vector<T,N> ifFalse);\n")
SLANG_RAW("\n")
@@ -142,7 +153,7 @@ for (int tt = 0; tt < kBaseTypeCount; ++tt)
// TODO: should this cover the full gamut of integer types?
case BaseType::Int:
case BaseType::UInt:
-SLANG_RAW("#line 145 \"core.meta.slang\"")
+SLANG_RAW("#line 153 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW(" __generic<T:__EnumType>\n")
SLANG_RAW(" __init(T value);\n")
@@ -158,7 +169,7 @@ SLANG_RAW(" __init(T value);\n")
// Declare built-in pointer type
// (eventually we can have the traditional syntax sugar for this)
-SLANG_RAW("#line 160 \"core.meta.slang\"")
+SLANG_RAW("#line 168 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T>\n")
@@ -220,7 +231,7 @@ sb << " __init(T value);\n";
sb << " __init(vector<T,N> value);\n";
sb << "};\n";
-SLANG_RAW("#line 206 \"core.meta.slang\"")
+SLANG_RAW("#line 214 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("__generic<T = float, let R : int = 4, let C : int = 4>\n")
@@ -974,7 +985,6 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
sb << "__target_intrinsic(glsl, \"$ctextureGrad($p, $2, $3, $4)$z\")\n";
-// sb << "__intrinsic_op(sampleGrad)\n";
sb << "T SampleGrad(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, ";
@@ -984,7 +994,6 @@ for (int tt = 0; tt < kBaseTextureTypeCount; ++tt)
if( baseShape != TextureFlavor::Shape::ShapeCube )
{
sb << "__target_intrinsic(glsl, \"$ctextureGradOffset($p, $2, $3, $4, $5)$z\")\n";
-// sb << "__intrinsic_op(sampleGrad)\n";
sb << "T SampleGrad(SamplerState s, ";
sb << "float" << kBaseTextureTypes[tt].coordCount + isArray << " location, ";
sb << "float" << kBaseTextureTypes[tt].coordCount << " gradX, ";
@@ -1176,7 +1185,7 @@ for (auto op : binaryOps)
switch (op.opCode)
{
case kIROp_Mul:
- case kIRPseudoOp_MulAssign:
+ case kCompoundIntrinsicOp_MulAssign:
break;
default:
@@ -1207,7 +1216,7 @@ for (auto op : binaryOps)
sb << "__intrinsic_op(" << int(op.opCode) << ") matrix<" << resultType << ",N,M> operator" << op.opName << "(" << leftQual << "matrix<" << leftType << ",N,M> left, " << rightType << " right);\n";
}
}
-SLANG_RAW("#line 1192 \"core.meta.slang\"")
+SLANG_RAW("#line 1198 \"core.meta.slang\"")
SLANG_RAW("\n")
SLANG_RAW("\n")
SLANG_RAW("// Operators to apply to `enum` types\n")
diff --git a/source/slang/hlsl.meta.slang b/source/slang/hlsl.meta.slang
index 986152e92..7c88e530f 100644
--- a/source/slang/hlsl.meta.slang
+++ b/source/slang/hlsl.meta.slang
@@ -393,13 +393,13 @@ __target_intrinsic(glsl, "uintBitsToFloat")
vector<float,N> asfloat(vector<uint,N> x);
// No op
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
float asfloat(float x);
__generic<let N : int>
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
vector<float,N> asfloat(vector<float,N> x);
__generic<let N : int, let M : int>
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
matrix<float,N,M> asfloat(matrix<float,N,M> x);
// Pass thru to HLSL
@@ -430,13 +430,13 @@ __target_intrinsic(glsl, "ivec$N0($0)")
vector<int,N> asint(vector<uint,N> x);
// No op
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
int asint(int x);
__generic<let N : int>
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
vector<int,N> asint(vector<int,N> x);
__generic<let N : int, let M : int>
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
matrix<int,N,M> asint(matrix<int,N,M> x);
// Pass thru HLSL
@@ -473,13 +473,13 @@ __target_intrinsic(glsl, "uvec$N0($0)")
vector<uint,N> asuint(vector<int,N> x);
// No op
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
uint asuint(uint x);
__generic<let N : int>
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
vector<uint,N> asuint(vector<uint,N> x);
__generic<let N : int, let M : int>
-__intrinsic_op($(kIRPseudoOp_Pos))
+__intrinsic_op($(kCompoundIntrinsicOp_Pos))
matrix<uint,N,M> asuint(matrix<uint,N,M> x);
// Pass thru HLSL
@@ -1431,7 +1431,7 @@ for(auto op : binaryOps)
continue;
case kIROp_Mul:
- case kIRPseudoOp_MulAssign:
+ case kCompoundIntrinsicOp_MulAssign:
break;
}
diff --git a/source/slang/hlsl.meta.slang.h b/source/slang/hlsl.meta.slang.h
index 95e07658f..db0fc2285 100644
--- a/source/slang/hlsl.meta.slang.h
+++ b/source/slang/hlsl.meta.slang.h
@@ -443,19 +443,19 @@ SLANG_RAW("vector<float,N> asfloat(vector<uint,N> x);\n")
SLANG_RAW("\n")
SLANG_RAW("// No op\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("float asfloat(float x);\n")
SLANG_RAW("__generic<let N : int>\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("vector<float,N> asfloat(vector<float,N> x);\n")
SLANG_RAW("__generic<let N : int, let M : int>\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("matrix<float,N,M> asfloat(matrix<float,N,M> x);\n")
@@ -489,19 +489,19 @@ SLANG_RAW("vector<int,N> asint(vector<uint,N> x);\n")
SLANG_RAW("\n")
SLANG_RAW("// No op\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("int asint(int x);\n")
SLANG_RAW("__generic<let N : int>\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("vector<int,N> asint(vector<int,N> x);\n")
SLANG_RAW("__generic<let N : int, let M : int>\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("matrix<int,N,M> asint(matrix<int,N,M> x);\n")
@@ -541,19 +541,19 @@ SLANG_RAW("vector<uint,N> asuint(vector<int,N> x);\n")
SLANG_RAW("\n")
SLANG_RAW("// No op\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("uint asuint(uint x);\n")
SLANG_RAW("__generic<let N : int>\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("vector<uint,N> asuint(vector<uint,N> x);\n")
SLANG_RAW("__generic<let N : int, let M : int>\n")
SLANG_RAW("__intrinsic_op(")
-SLANG_SPLICE(kIRPseudoOp_Pos
+SLANG_SPLICE(kCompoundIntrinsicOp_Pos
)
SLANG_RAW(")\n")
SLANG_RAW("matrix<uint,N,M> asuint(matrix<uint,N,M> x);\n")
@@ -1507,7 +1507,7 @@ for(auto op : binaryOps)
continue;
case kIROp_Mul:
- case kIRPseudoOp_MulAssign:
+ case kCompoundIntrinsicOp_MulAssign:
break;
}
diff --git a/source/slang/slang-check-impl.h b/source/slang/slang-check-impl.h
index 7027c4671..f026f0ed1 100644
--- a/source/slang/slang-check-impl.h
+++ b/source/slang/slang-check-impl.h
@@ -80,7 +80,7 @@ namespace Slang
struct OperatorOverloadCacheKey
{
- IROp operatorName;
+ intptr_t operatorName;
BasicTypeKey args[2];
bool operator == (OperatorOverloadCacheKey key)
{
diff --git a/source/slang/slang-compound-intrinsics.h b/source/slang/slang-compound-intrinsics.h
new file mode 100644
index 000000000..4a0b1a109
--- /dev/null
+++ b/source/slang/slang-compound-intrinsics.h
@@ -0,0 +1,81 @@
+// slang-compound-intrinsics.h
+#pragma once
+
+// Intrinsic functions in the Slang standard library are marked
+// with the `__intrinsic_op(...)` modifier. Many of these map
+// one-to-one to instruction opcodes in the Slang IR, and the
+// argument to `__intrinsic_op(...)` is the IR instruction
+// opcode in that case.
+//
+// In other cases, we have intrinsic operations like the `+=` or
+// `&&` operator that either need to map to multiple IR instructions
+// (or more generally, a number of instructions not equal to one),
+// or otherwise have complications thake one-to-one lowering
+// not possible.
+//
+// We refer to these as "compound" intrinsic ops, since the common
+// case is that they represent a composition of multiple instructions.
+//
+// In order to not conflict with the opcodes of any IR instructions,
+// these compound intrinsic ops will all be identified by *negative*
+// integer opcodes.
+
+// We start by defining an "X-macro" that lists all the compound
+// intrinsic ops we support.
+
+#define FOREACH_COMPOUND_INTRINSIC_OP(M) \
+ M(Pos) \
+ M(PreInc) \
+ M(PreDec) \
+ M(PostInc) \
+ M(PostDec) \
+ M(Sequence) \
+ M(AddAssign) \
+ M(SubAssign) \
+ M(MulAssign) \
+ M(DivAssign) \
+ M(IRemAssign) \
+ M(FRemAssign) \
+ M(AndAssign) \
+ M(OrAssign) \
+ M(XorAssign ) \
+ M(LshAssign) \
+ M(RshAssign) \
+ M(Assign) \
+ M(And) \
+ M(Or) \
+ /* end */
+
+// We will use a simple type alias to capture the fact that
+// a 32-bit integer is sufficient to represent compound
+// intrinsic ops (as negative values) plus IR opcode values
+// for single-instruction intrinsics (as non-negative values)
+//
+typedef int32_t IntrinsicOp;
+
+// Next we use an enumeration declaration as an implementation
+// detail, to associate each of the above cases with a (positive)
+// integer.
+//
+enum class _CompoundIntrinsicOpVal : IntrinsicOp
+{
+#define DECLARE_COMPOUND_INTRINSIC_OP_VAL(NAME) NAME,
+ FOREACH_COMPOUND_INTRINSIC_OP(DECLARE_COMPOUND_INTRINSIC_OP_VAL)
+#undef DECLARE_COMPOUND_INTRINSIC_OP_VAL
+};
+
+// Finally, we define a second enumeration that takes the values
+// from the first and performs a bitwise negation on them, which
+// guarantees we get strictly negative values.
+
+ /// Compound/complex intrinsic operations, which do not map to a single IR instruction.
+ ///
+ /// All of the values of this enumeration are guaranteed to be negative, and thus
+ /// cannot conflict with any valid value of type `IROp`
+ ///
+enum CompoundIntrinsicOp : IntrinsicOp
+{
+#define DECLARE_COMPOUND_INTRINSIC_OP(NAME) kCompoundIntrinsicOp_##NAME = ~IntrinsicOp(_CompoundIntrinsicOpVal::NAME),
+ FOREACH_COMPOUND_INTRINSIC_OP(DECLARE_COMPOUND_INTRINSIC_OP)
+#undef DECLARE_COMPOUND_INTRINSIC_OP
+};
diff --git a/source/slang/slang-ir-inst-defs.h b/source/slang/slang-ir-inst-defs.h
index 00ac21e70..4caa8acf0 100644
--- a/source/slang/slang-ir-inst-defs.h
+++ b/source/slang/slang-ir-inst-defs.h
@@ -8,10 +8,6 @@
#define INST_RANGE(BASE, FIRST, LAST) /* empty */
#endif
-#ifndef PSEUDO_INST
-#define PSEUDO_INST(ID) /* empty */
-#endif
-
#define PARENT kIROpFlag_Parent
#define USE_OTHER kIROpFlag_UseOther
@@ -516,30 +512,6 @@ INST_RANGE(Layout, VarLayout, EntryPointLayout)
INST_RANGE(LayoutResourceInfoAttr, TypeSizeAttr, VarOffsetAttr)
INST_RANGE(Attr, PendingLayoutAttr, VarOffsetAttr)
-PSEUDO_INST(Pos)
-PSEUDO_INST(PreInc)
-
-PSEUDO_INST(PreDec)
-PSEUDO_INST(PostInc)
-PSEUDO_INST(PostDec)
-PSEUDO_INST(Sequence)
-PSEUDO_INST(AddAssign)
-PSEUDO_INST(SubAssign)
-PSEUDO_INST(MulAssign)
-PSEUDO_INST(DivAssign)
-PSEUDO_INST(IRemAssign)
-PSEUDO_INST(FRemAssign)
-PSEUDO_INST(AndAssign)
-PSEUDO_INST(OrAssign)
-PSEUDO_INST(XorAssign )
-PSEUDO_INST(LshAssign)
-PSEUDO_INST(RshAssign)
-PSEUDO_INST(Assign)
-PSEUDO_INST(And)
-PSEUDO_INST(Or)
-
-
-#undef PSEUDO_INST
#undef PARENT
#undef USE_OTHER
#undef INST_RANGE
diff --git a/source/slang/slang-ir-serialize.cpp b/source/slang/slang-ir-serialize.cpp
index 1b734c6b2..fe997cca8 100644
--- a/source/slang/slang-ir-serialize.cpp
+++ b/source/slang/slang-ir-serialize.cpp
@@ -12,13 +12,13 @@ namespace Slang {
static bool _isTextureTypeBase(IROp opIn)
{
- const int op = (kIROpMeta_PseudoOpMask & opIn);
+ const int op = (kIROpMeta_OpMask & opIn);
return op >= kIROp_FirstTextureTypeBase && op <= kIROp_LastTextureTypeBase;
}
static bool _isConstant(IROp opIn)
{
- const int op = (kIROpMeta_PseudoOpMask & opIn);
+ const int op = (kIROpMeta_OpMask & opIn);
return op >= kIROp_FirstConstant && op <= kIROp_LastConstant;
}
@@ -293,9 +293,6 @@ Result IRSerialWriter::write(IRModule* module, SourceManager* sourceManager, Opt
{
IRInst* srcInst = m_insts[i];
Ser::Inst& dstInst = m_serialData->m_insts[i];
-
- // Can't be any pseudo ops
- SLANG_ASSERT(!isPseudoOp(srcInst->op));
dstInst.m_op = uint8_t(srcInst->op & kIROpMeta_OpMask);
dstInst.m_payloadType = PayloadType::Empty;
diff --git a/source/slang/slang-ir.cpp b/source/slang/slang-ir.cpp
index a30445682..a8f42c326 100644
--- a/source/slang/slang-ir.cpp
+++ b/source/slang/slang-ir.cpp
@@ -10,9 +10,6 @@ namespace Slang
{
struct IRSpecContext;
-
- SLANG_COMPILE_TIME_ASSERT(kIROpCount < kIRPseudoOp_First);
-
IRInst* cloneGlobalValueWithLinkage(
IRSpecContext* context,
IRInst* originalVal,
@@ -39,31 +36,14 @@ namespace Slang
{ kIROp_##ID, { #MNEMONIC, ARG_COUNT, FLAGS, } },
#include "slang-ir-inst-defs.h"
- // Pseudo ops
-#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) /* empty */
-#define PSEUDO_INST(ID) \
- { kIRPseudoOp_##ID, { #ID, 0, 0 } },
-
- // First is 'invalid'
+ // Invalid op sentinel value comes after all the valid ones
{ kIROp_Invalid,{ "invalid", 0, 0 } },
- // Then all the other psuedo ops
-#include "slang-ir-inst-defs.h"
-
};
IROpInfo getIROpInfo(IROp opIn)
{
- const int op = opIn & kIROpMeta_PseudoOpMask;
- if ((op & kIROpMeta_IsPseudoOp) && op < kIRPseudoOp_LastPlusOne)
- {
- // It's a pseudo op
- const int index = op - kIRPseudoOp_First;
- // Pseudo ops start from kIROpcount
- const auto& entry = kIROps[kIROpCount + index];
- SLANG_ASSERT(entry.op == op);
- return entry.info;
- }
- else if (op < kIROpCount)
+ const int op = opIn & kIROpMeta_OpMask;
+ if (op < kIROpCount)
{
// It's a main op
const auto& entry = kIROps[op];
@@ -4460,8 +4440,8 @@ namespace Slang
return false;
}
- const IROp opA = IROp(a->op & kIROpMeta_PseudoOpMask);
- const IROp opB = IROp(b->op & kIROpMeta_PseudoOpMask);
+ const IROp opA = IROp(a->op & kIROpMeta_OpMask);
+ const IROp opB = IROp(b->op & kIROpMeta_OpMask);
if (opA != opB)
{
diff --git a/source/slang/slang-ir.h b/source/slang/slang-ir.h
index fbb560a60..9bdc7c3c9 100644
--- a/source/slang/slang-ir.h
+++ b/source/slang/slang-ir.h
@@ -41,36 +41,27 @@ enum : IROpFlags
/* Bit usage of IROp is a follows
- MainOp | Pseudo | Other
-Bit range: 0-7 | 8 | Remaining bits
+ MainOp | Other
+Bit range: 0-7 | Remaining bits
-If an instruction is 'pseudo' (ie shouldn't appear in output IR), then the Pseudo bit is set - and 'Invalid' falls into
-this category as well as all pseudo ops.
For doing range checks (for example for doing isa tests), the value is masked by kIROpMeta_OpMask, such that the Other bits don't interfere.
The other bits can be used for storage for anything that needs to identify as a different 'op' or 'type'. It is currently
used currently for storing the TextureFlavor of a IRResourceTypeBase derived types for example.
+
+TODO: We should eliminate the use of the "other" bits so that the entire value/state
+of an instruction is manifest in its opcode, operands, and children.
*/
enum IROp : int32_t
{
#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) \
kIROp_##ID,
-
#include "slang-ir-inst-defs.h"
+ /// The total number of valid opcodes
kIROpCount,
- // We use the range 0x100 to 0x1ff set for pseudo/non main codes
- // Instructions that should not appear in valid IR.
-
- kIROp_Invalid = 0x100, ///< If bit set, then in pseudo/not normal space
- kIRPseudoOp_First = kIROp_Invalid,
-
-#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) /* empty */
-#define PSEUDO_INST(ID) kIRPseudoOp_##ID,
-
- kIRPseudoOp_LastPlusOne,
-
-#include "slang-ir-inst-defs.h"
+ /// An invalid opcode used to represent a missing or unknown opcode value.
+ kIROp_Invalid = kIROpCount,
#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) /* empty */
#define INST_RANGE(BASE, FIRST, LAST) \
@@ -83,16 +74,10 @@ enum IROp : int32_t
/* IROpMeta describe values for layout of IROp, as well as values for accessing aspects of IROp bits. */
enum IROpMeta
{
- kIROpMeta_OtherShift = 9, ///< Number of bits for op/pseudo ops (shift right by this to get the other bits)
- kIROpMeta_PseudoOpMask = (int32_t(1) << kIROpMeta_OtherShift) - 1, ///< Mask for ops including pseudo ops
- kIROpMeta_OpMask = 0xff, ///< Mask for just ops
- kIrOpMeta_OtherMask = ~kIROpMeta_PseudoOpMask, ///< Mask for bits that can be used for other purposes than 'op' ('other' bits)
- kIROpMeta_IsPseudoOp = kIROp_Invalid, ///< 'And' with op, if set, the op is a pseudo op
+ kIROpMeta_OtherShift = 8, ///< Number of bits for op (shift right by this to get the other bits)
+ kIROpMeta_OpMask = 0xff, ///< Mask for just opcode
};
-// True if op is pseudo (or invalid which is 'pseudo-like' at least in as so far as current behavior)
-SLANG_FORCE_INLINE bool isPseudoOp(IROp op) { return (op & kIROpMeta_IsPseudoOp) != 0; }
-
IROp findIROp(const UnownedStringSlice& name);
// A logical operation/opcode in the IR
@@ -602,8 +587,8 @@ typename IRInstList<T>::Iterator IRInstList<T>::end()
// Types
-#define IR_LEAF_ISA(NAME) static bool isaImpl(IROp op) { return (kIROpMeta_PseudoOpMask & op) == kIROp_##NAME; }
-#define IR_PARENT_ISA(NAME) static bool isaImpl(IROp opIn) { const int op = (kIROpMeta_PseudoOpMask & opIn); return op >= kIROp_First##NAME && op <= kIROp_Last##NAME; }
+#define IR_LEAF_ISA(NAME) static bool isaImpl(IROp op) { return (kIROpMeta_OpMask & op) == kIROp_##NAME; }
+#define IR_PARENT_ISA(NAME) static bool isaImpl(IROp opIn) { const int op = (kIROpMeta_OpMask & opIn); return op >= kIROp_First##NAME && op <= kIROp_Last##NAME; }
#define SIMPLE_IR_TYPE(NAME, BASE) struct IR##NAME : IR##BASE { IR_LEAF_ISA(NAME) };
#define SIMPLE_IR_PARENT_TYPE(NAME, BASE) struct IR##NAME : IR##BASE { IR_PARENT_ISA(NAME) };
diff --git a/source/slang/slang-lower-to-ir.cpp b/source/slang/slang-lower-to-ir.cpp
index ac02e1dfd..2a04b53b2 100644
--- a/source/slang/slang-lower-to-ir.cpp
+++ b/source/slang/slang-lower-to-ir.cpp
@@ -5,6 +5,7 @@
#include "slang-check.h"
#include "slang-ir.h"
+#include "slang-compound-intrinsics.h"
#include "slang-ir-constexpr.h"
#include "slang-ir-dce.h"
#include "slang-ir-insts.h"
@@ -478,12 +479,13 @@ LoweredValInfo emitDeclRef(
IRInst* getSimpleVal(IRGenContext* context, LoweredValInfo lowered);
-IROp getIntrinsicOp(
+int32_t getIntrinsicOp(
Decl* decl,
IntrinsicOpModifier* intrinsicOpMod)
{
- if (int(intrinsicOpMod->op) != 0)
- return intrinsicOpMod->op;
+ int32_t op = intrinsicOpMod->op;
+ if(op != 0)
+ return op;
// No specified modifier? Then we need to look it up
// based on the name of the declaration...
@@ -491,9 +493,10 @@ IROp getIntrinsicOp(
auto name = decl->getName();
auto nameText = getUnownedStringSliceText(name);
- IROp op = findIROp(nameText);
- SLANG_ASSERT(op != kIROp_Invalid);
- return op;
+ IROp irOp = findIROp(nameText);
+ SLANG_ASSERT(irOp != kIROp_Invalid);
+ SLANG_ASSERT(int32_t(irOp) >= 0);
+ return int32_t(irOp);
}
// Given a `LoweredValInfo` for something callable, along with a
@@ -713,16 +716,23 @@ LoweredValInfo emitCallToDeclRef(
auto funcDecl = funcDeclRef.getDecl();
if(auto intrinsicOpModifier = funcDecl->FindModifier<IntrinsicOpModifier>())
{
- auto op = getIntrinsicOp(funcDecl, intrinsicOpModifier);
-
- if (isPseudoOp(op))
+ // An intrinsic op either maps to a single IR instruction
+ // (in the case where the opcode value is >= 0), or to
+ // a `CompountIntrinsicOp` (in the case where it is < 0).
+ //
+ auto intrinsicOp = getIntrinsicOp(funcDecl, intrinsicOpModifier);
+ if(intrinsicOp < 0)
{
- switch (op)
+ // We have a compound op, which requires special-case
+ // handling to generate zero or more IR instructions.
+ //
+ auto compoundOp = CompoundIntrinsicOp(intrinsicOp);
+ switch (compoundOp)
{
- case kIRPseudoOp_Pos:
+ case kCompoundIntrinsicOp_Pos:
return LoweredValInfo::simple(args[0]);
- case kIRPseudoOp_Sequence:
+ case kCompoundIntrinsicOp_Sequence:
// The main effect of "operator comma" is to enforce
// sequencing of its operands, but Slang already
// implements a strictly left-to-right evaluation
@@ -734,44 +744,49 @@ LoweredValInfo emitCallToDeclRef(
#define CASE(COMPOUND, OP) \
case COMPOUND: return emitCompoundAssignOp(context, type, OP, argCount, args)
- CASE(kIRPseudoOp_AddAssign, kIROp_Add);
- CASE(kIRPseudoOp_SubAssign, kIROp_Sub);
- CASE(kIRPseudoOp_MulAssign, kIROp_Mul);
- CASE(kIRPseudoOp_DivAssign, kIROp_Div);
- CASE(kIRPseudoOp_IRemAssign,kIROp_IRem);
- CASE(kIRPseudoOp_FRemAssign,kIROp_FRem);
- CASE(kIRPseudoOp_AndAssign, kIROp_BitAnd);
- CASE(kIRPseudoOp_OrAssign, kIROp_BitOr);
- CASE(kIRPseudoOp_XorAssign, kIROp_BitXor);
- CASE(kIRPseudoOp_LshAssign, kIROp_Lsh);
- CASE(kIRPseudoOp_RshAssign, kIROp_Rsh);
+ CASE(kCompoundIntrinsicOp_AddAssign, kIROp_Add);
+ CASE(kCompoundIntrinsicOp_SubAssign, kIROp_Sub);
+ CASE(kCompoundIntrinsicOp_MulAssign, kIROp_Mul);
+ CASE(kCompoundIntrinsicOp_DivAssign, kIROp_Div);
+ CASE(kCompoundIntrinsicOp_IRemAssign,kIROp_IRem);
+ CASE(kCompoundIntrinsicOp_FRemAssign,kIROp_FRem);
+ CASE(kCompoundIntrinsicOp_AndAssign, kIROp_BitAnd);
+ CASE(kCompoundIntrinsicOp_OrAssign, kIROp_BitOr);
+ CASE(kCompoundIntrinsicOp_XorAssign, kIROp_BitXor);
+ CASE(kCompoundIntrinsicOp_LshAssign, kIROp_Lsh);
+ CASE(kCompoundIntrinsicOp_RshAssign, kIROp_Rsh);
#undef CASE
#define CASE(COMPOUND, OP) \
case COMPOUND: return emitPrefixIncDecOp(context, type, OP, argCount, args)
- CASE(kIRPseudoOp_PreInc, kIROp_Add);
- CASE(kIRPseudoOp_PreDec, kIROp_Sub);
+ CASE(kCompoundIntrinsicOp_PreInc, kIROp_Add);
+ CASE(kCompoundIntrinsicOp_PreDec, kIROp_Sub);
#undef CASE
#define CASE(COMPOUND, OP) \
case COMPOUND: return emitPostfixIncDecOp(context, type, OP, argCount, args)
- CASE(kIRPseudoOp_PostInc, kIROp_Add);
- CASE(kIRPseudoOp_PostDec, kIROp_Sub);
+ CASE(kCompoundIntrinsicOp_PostInc, kIROp_Add);
+ CASE(kCompoundIntrinsicOp_PostDec, kIROp_Sub);
#undef CASE
default:
SLANG_UNIMPLEMENTED_X("IR pseudo-op");
UNREACHABLE_RETURN(LoweredValInfo());
}
}
-
- return LoweredValInfo::simple(builder->emitIntrinsicInst(
- type,
- op,
- argCount,
- args));
+ else
+ {
+ // The intrinsic op maps to a single IR instruction,
+ // so we will emit an instruction with the chosen
+ // opcode, and the arguments to the call as its operands.
+ //
+ return LoweredValInfo::simple(builder->emitIntrinsicInst(
+ type,
+ IROp(intrinsicOp),
+ argCount,
+ args));
+ }
}
- // TODO: handle target intrinsic modifier too...
if( auto ctorDeclRef = funcDeclRef.as<ConstructorDecl>() )
{
diff --git a/source/slang/slang-modifier-defs.h b/source/slang/slang-modifier-defs.h
index fb3292e23..bfbb51c1e 100644
--- a/source/slang/slang-modifier-defs.h
+++ b/source/slang/slang-modifier-defs.h
@@ -33,11 +33,20 @@ SIMPLE_MODIFIER(GloballyCoherent)
//
SYNTAX_CLASS(IntrinsicOpModifier, Modifier)
- // token that names the intrinsic op
+ // Token that names the intrinsic op.
FIELD(Token, opToken)
- // The opcode for the intrinsic operation
- FIELD_INIT(IROp, op, kIROp_Nop)
+ // The opcode for the intrinsic operation.
+ //
+ // If greather than or equal to zero, then `op`
+ // is an `IROp` and directly identifies an IR
+ // instruction that the intrinsic should translate to.
+ //
+ // If less than zero, then `op` is a `CompoundIntrinsicOp`
+ // which maps to zero or more IR instructions using
+ // special-case logic in the IR lowering phase.
+ //
+ FIELD_INIT(IntrinsicOp, op, 0)
END_SYNTAX_CLASS()
// A modifier that marks something as an intrinsic function,
diff --git a/source/slang/slang-stdlib.cpp b/source/slang/slang-stdlib.cpp
index c1a9d59d2..26a7dbb65 100644
--- a/source/slang/slang-stdlib.cpp
+++ b/source/slang/slang-stdlib.cpp
@@ -1,6 +1,7 @@
// slang-stdlib.cpp
#include "slang-compiler.h"
+#include "slang-compound-intrinsics.h"
#include "slang-ir.h"
#include "slang-syntax.h"
@@ -198,17 +199,17 @@ namespace Slang
}
}
- struct OpInfo { int32_t opCode; char const* opName; unsigned flags; };
+ struct OpInfo { IntrinsicOp opCode; char const* opName; unsigned flags; };
static const OpInfo unaryOps[] = {
- { kIRPseudoOp_Pos, "+", ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_Pos, "+", ARITHMETIC_MASK },
{ kIROp_Neg, "-", ARITHMETIC_MASK },
{ kIROp_Not, "!", BOOL_MASK | BOOL_RESULT },
{ kIROp_BitNot, "~", INT_MASK },
- { kIRPseudoOp_PreInc, "++", ARITHMETIC_MASK | ASSIGNMENT },
- { kIRPseudoOp_PreDec, "--", ARITHMETIC_MASK | ASSIGNMENT },
- { kIRPseudoOp_PostInc, "++", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
- { kIRPseudoOp_PostDec, "--", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
+ { kCompoundIntrinsicOp_PreInc, "++", ARITHMETIC_MASK | ASSIGNMENT },
+ { kCompoundIntrinsicOp_PreDec, "--", ARITHMETIC_MASK | ASSIGNMENT },
+ { kCompoundIntrinsicOp_PostInc, "++", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
+ { kCompoundIntrinsicOp_PostDec, "--", ARITHMETIC_MASK | ASSIGNMENT | POSTFIX },
};
static const OpInfo binaryOps[] = {
@@ -231,17 +232,17 @@ namespace Slang
{ kIROp_Less, "<", ARITHMETIC_MASK | BOOL_RESULT },
{ kIROp_Geq, ">=", ARITHMETIC_MASK | BOOL_RESULT },
{ kIROp_Leq, "<=", ARITHMETIC_MASK | BOOL_RESULT },
- { kIRPseudoOp_AddAssign, "+=", ASSIGNMENT | ARITHMETIC_MASK },
- { kIRPseudoOp_SubAssign, "-=", ASSIGNMENT | ARITHMETIC_MASK },
- { kIRPseudoOp_MulAssign, "*=", ASSIGNMENT | ARITHMETIC_MASK },
- { kIRPseudoOp_DivAssign, "/=", ASSIGNMENT | ARITHMETIC_MASK },
- { kIRPseudoOp_IRemAssign, "%=", ASSIGNMENT | INT_MASK },
- { kIRPseudoOp_FRemAssign, "%=", ASSIGNMENT | FLOAT_MASK },
- { kIRPseudoOp_AndAssign, "&=", ASSIGNMENT | LOGICAL_MASK },
- { kIRPseudoOp_OrAssign, "|=", ASSIGNMENT | LOGICAL_MASK },
- { kIRPseudoOp_XorAssign, "^=", ASSIGNMENT | LOGICAL_MASK },
- { kIRPseudoOp_LshAssign, "<<=", ASSIGNMENT | INT_MASK },
- { kIRPseudoOp_RshAssign, ">>=", ASSIGNMENT | INT_MASK },
+ { kCompoundIntrinsicOp_AddAssign, "+=", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_SubAssign, "-=", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_MulAssign, "*=", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_DivAssign, "/=", ASSIGNMENT | ARITHMETIC_MASK },
+ { kCompoundIntrinsicOp_IRemAssign, "%=", ASSIGNMENT | INT_MASK },
+ { kCompoundIntrinsicOp_FRemAssign, "%=", ASSIGNMENT | FLOAT_MASK },
+ { kCompoundIntrinsicOp_AndAssign, "&=", ASSIGNMENT | LOGICAL_MASK },
+ { kCompoundIntrinsicOp_OrAssign, "|=", ASSIGNMENT | LOGICAL_MASK },
+ { kCompoundIntrinsicOp_XorAssign, "^=", ASSIGNMENT | LOGICAL_MASK },
+ { kCompoundIntrinsicOp_LshAssign, "<<=", ASSIGNMENT | INT_MASK },
+ { kCompoundIntrinsicOp_RshAssign, ">>=", ASSIGNMENT | INT_MASK },
};
String Session::getCoreLibraryCode()
diff --git a/source/slang/slang-syntax.h b/source/slang/slang-syntax.h
index 0dd70b1f8..86cc4b902 100644
--- a/source/slang/slang-syntax.h
+++ b/source/slang/slang-syntax.h
@@ -2,7 +2,7 @@
#define SLANG_SYNTAX_H
#include "../core/slang-basic.h"
-#include "slang-ir.h"
+#include "slang-compound-intrinsics.h"
#include "slang-lexer.h"
#include "slang-profile.h"
#include "slang-type-system-shared.h"
diff --git a/source/slang/slang.vcxproj b/source/slang/slang.vcxproj
index fc1f3c421..3d3fa4375 100644
--- a/source/slang/slang.vcxproj
+++ b/source/slang/slang.vcxproj
@@ -193,6 +193,7 @@
<ClInclude Include="slang-check-impl.h" />
<ClInclude Include="slang-check.h" />
<ClInclude Include="slang-compiler.h" />
+ <ClInclude Include="slang-compound-intrinsics.h" />
<ClInclude Include="slang-decl-defs.h" />
<ClInclude Include="slang-diagnostic-defs.h" />
<ClInclude Include="slang-diagnostics.h" />
@@ -337,7 +338,7 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-generate" %(Identity)</Command>
- <Outputs>%(Identity).h</Outputs>
+ <Outputs>../../core.meta.slang.h</Outputs>
<Message>slang-generate %(Identity)</Message>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-generate.exe</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-generate.exe</AdditionalInputs>
@@ -350,7 +351,7 @@
<Command Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">"../../bin/windows-x64/debug/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">"../../bin/windows-x86/release/slang-generate" %(Identity)</Command>
<Command Condition="'$(Configuration)|$(Platform)'=='Release|x64'">"../../bin/windows-x64/release/slang-generate" %(Identity)</Command>
- <Outputs>%(Identity).h</Outputs>
+ <Outputs>../../hlsl.meta.slang.h</Outputs>
<Message>slang-generate %(Identity)</Message>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">../../bin/windows-x86/debug/slang-generate.exe</AdditionalInputs>
<AdditionalInputs Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">../../bin/windows-x64/debug/slang-generate.exe</AdditionalInputs>
diff --git a/source/slang/slang.vcxproj.filters b/source/slang/slang.vcxproj.filters
index 754fc8aa6..3764a6f11 100644
--- a/source/slang/slang.vcxproj.filters
+++ b/source/slang/slang.vcxproj.filters
@@ -30,6 +30,9 @@
<ClInclude Include="slang-compiler.h">
<Filter>Header Files</Filter>
</ClInclude>
+ <ClInclude Include="slang-compound-intrinsics.h">
+ <Filter>Header Files</Filter>
+ </ClInclude>
<ClInclude Include="slang-decl-defs.h">
<Filter>Header Files</Filter>
</ClInclude>