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/parser.cpp | 52 +++++++++++++++++++++++++++++++++++-------------- 1 file changed, 37 insertions(+), 15 deletions(-) (limited to 'source/slang/parser.cpp') diff --git a/source/slang/parser.cpp b/source/slang/parser.cpp index 9c0d692e6..8ea41641d 100644 --- a/source/slang/parser.cpp +++ b/source/slang/parser.cpp @@ -3766,29 +3766,51 @@ namespace Slang { RefPtr modifier = new IntrinsicOpModifier(); - parser->ReadToken(TokenType::LParent); - if (parser->LookAheadToken(TokenType::IntegerLiteral)) - { - modifier->op = (IntrinsicOp)StringToInt(parser->ReadToken().Content); - } - else + // We allow a few difference forms here: + // + // First, we can specify the intrinsic op `enum` value directly: + // + // __intrinsic_op() + // + // Second, we can specify the operation by name: + // + // __intrinsic_op() + // + // Finally, we can leave off the specification, so that the + // op name will be derived fromthe function name: + // + // __intrinsic_op + // + if (AdvanceIf(parser, TokenType::LParent)) { - modifier->opToken = parser->ReadToken(TokenType::Identifier); + if (AdvanceIf(parser, TokenType::OpSub)) + { + modifier->op = IROp(-StringToInt(parser->ReadToken().Content)); + } + else if (parser->LookAheadToken(TokenType::IntegerLiteral)) + { + modifier->op = IROp(StringToInt(parser->ReadToken().Content)); + } + else + { + modifier->opToken = parser->ReadToken(TokenType::Identifier); - modifier->op = findIntrinsicOp(modifier->opToken.Content.Buffer()); + modifier->op = findIROp(modifier->opToken.Content.Buffer()); - if (modifier->op == IntrinsicOp::Unknown) - { - parser->sink->diagnose(modifier->opToken, Diagnostics::unimplemented, "unknown intrinsic op"); + if (modifier->op == kIROp_Invalid) + { + parser->sink->diagnose(modifier->opToken, Diagnostics::unimplemented, "unknown intrinsic op"); + } } + + parser->ReadToken(TokenType::RParent); } - parser->ReadToken(TokenType::RParent); return modifier; } - static RefPtr parseIntrinsicModifier(Parser* parser, void* /*userData*/) + static RefPtr parseTargetIntrinsicModifier(Parser* parser, void* /*userData*/) { auto modifier = new TargetIntrinsicModifier(); @@ -4027,8 +4049,8 @@ namespace Slang MODIFIER(layout, parseLayoutModifier); - MODIFIER(__intrinsic_op, parseIntrinsicOpModifier); - MODIFIER(__intrinsic, parseIntrinsicModifier); + MODIFIER(__intrinsic_op, parseIntrinsicOpModifier); + MODIFIER(__target_intrinsic, parseTargetIntrinsicModifier); MODIFIER(__glsl_extension, parseGLSLExtensionModifier); MODIFIER(__glsl_version, parseGLSLVersionModifier); -- cgit v1.2.3