From ad3539574f52634c51523cfec1747e7565ad8876 Mon Sep 17 00:00:00 2001 From: Tim Foley Date: Wed, 6 Sep 2017 14:56:28 -0700 Subject: Replace old notion of "intrinsic" operations The code previously had an enumerated type for "intrinsic" operations, and allowed functions to be marked `__intrinsic_op(...)` to indicate the operation they map to. The nature of the IR meant that each of these intrinsic ops had to have a corresponding IR opcode, but the `enum` types weren't the same. This change cleans things up a bit by deciding that the `__intrinsic_op(...)` modifier names an actual IR opcode, and so the `IntrinsicOp` enum is gone. The biggest source of complexity here is that there are certain operations that need to be "intrinsic"-ish for the purposes of the current AST-based translation path, because we need them to round-trip from source to AST and back. Right now this is being handled by defining a bunch of "pseudo-ops" which can be used in the `__intrinsic_op` modifier, but which are *not* meant to be represented in the IR. Currently I don't actually handle this during IR generation. In the long run, once we are using IR for everything that needs cross-compilation, we should be able to eliminate the pseudo-ops in favor of just having these be ordinary (inline) functions defined in the stdlib (e.g., the `+=` operator can just have a direct definition). There was a second category of modifier that gets a little caught up in this, which is the `__intrinsic` modifier, which got used in two ways: 1. A function marked `__intrinsic(glsl, ...)` had what I call a "target intrinsic" modifier, which specified how to lower it for a specific target (e.g., GLSL). 2. A function just marked `__intrinsic` was supposed to be a marker for "this function shouldn't be emitted in the output, because the implementation is expected to be provided" The latter category of function should really be an `__intrinsic_op`, so I translated all those uses. I added a tiny bit of sugar so that `__intrinsic_op` without an explicit opcode will look up an opcode based on the name of the function being called, so that an operation like `sin` can automatically be plumbed through to an equivalent IR op. (The first category is a stopgap for the AST-based cross-compilation, and will hopefully be replaced by something better as we get the IR-based path working). Getting the switch from `__intrinsic` to `__intrinsic_op` working required shuffling around some code in `emit.cpp` that handles looking up those modifiers and emitting builtin operations appropriately during cross-compilation. Depending on where we go with things, a possible extension of this approach is to allow multiple operands to `__intrinsic_op` so that the first specifies the opcode, and then the rest are literal arguments to specify "sub-ops." This could help us handle stuff like texture-fetch operations without an explosion in the number of opcodes. I still need to think about whether this is a good idea or not. --- source/slang/ir.h | 80 ++++++++++++++++++++++++++++++++++++++++++++++++++----- 1 file changed, 74 insertions(+), 6 deletions(-) (limited to 'source/slang/ir.h') diff --git a/source/slang/ir.h b/source/slang/ir.h index aa7b6a045..51755f89f 100644 --- a/source/slang/ir.h +++ b/source/slang/ir.h @@ -7,13 +7,36 @@ // similar in spirit to LLVM (but much simpler). // -#include "type-layout.h" +#include "../core/basic.h" -// We need the definition of `BaseType` which currently belongs to the AST -#include "syntax.h" namespace Slang { +// TODO(tfoley): We should ditch this enumeration +// and just use the IR opcodes that represent these +// types directly. The one major complication there +// is that the order of the enum values currently +// matters, since it determines promotion rank. +// We either need to keep that restriction, or +// look up promotion rank by some other means. +// +enum class BaseType +{ + // Note(tfoley): These are ordered in terms of promotion rank, so be vareful when messing with this + + Void = 0, + Bool, + Int, + UInt, + UInt64, + Half, + Float, + Double, +}; + + +class Layout; + struct IRFunc; struct IRInst; struct IRModule; @@ -29,14 +52,59 @@ enum : IROpFlags kIROpFlag_Parent = 1 << 0, }; -enum IROp : uint16_t +enum IROp : int16_t { - #define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) \ kIROp_##ID, #include "ir-inst-defs.h" + + kIROpCount, + + // We use the negative range of opcode values + // to encode "pseudo" instructions that should + // not appear in valid IR. + + kIRPseduoOp_FirstPseudo = -1000, + +#define INST(ID, MNEMONIC, ARG_COUNT, FLAGS) /* empty */ +#define PSEUDO_INST(ID) kIRPseudoOp_##ID, + +#include "ir-inst-defs.h" + + kIROp_Invalid = -1, + +}; + +#if 0 +enum IRPseudoOp +{ + kIRPseudoOp_Pos = -1000, + kIRPseudoOp_PreInc, + kIRPseudoOp_PreDec, + kIRPseudoOp_PostInc, + kIRPseudoOp_PostDec, + kIRPseudoOp_Sequence, + kIRPseudoOp_AddAssign, + kIRPseudoOp_SubAssign, + kIRPseudoOp_MulAssign, + kIRPseudoOp_DivAssign, + kIRPseudoOp_ModAssign, + kIRPseudoOp_AndAssign, + kIRPseudoOp_OrAssign, + kIRPseudoOp_XorAssign , + kIRPseudoOp_LshAssign, + kIRPseudoOp_RshAssign, + kIRPseudoOp_Assign, + kIRPseudoOp_BitNot, + kIRPseudoOp_And, + kIRPseudoOp_Or, + + kIROp_Invalid = -1, }; +#endif + +IROp findIROp(char const* name); // A logical operation/opcode in the IR struct IROpInfo @@ -448,7 +516,7 @@ struct IRBuilder IRInst* emitIntrinsicInst( IRType* type, - IntrinsicOp intrinsicOp, + IROp op, UInt argCount, IRValue* const* args); -- cgit v1.2.3