From ad0e581c48dfbbab413cce57df6f3b11aaefaeec Mon Sep 17 00:00:00 2001 From: Seth Brenith Date: Wed, 10 Mar 2021 11:24:38 -0800 Subject: [PATCH] [torque] Add option for printing Torque IR While working on the Torque compiler, I've sometimes found it difficult to understand Torque's intermediate representation and how it corresponds to the output. In this change, I propose adding a build flag that instructs Torque to emit comments describing its IR, interspersed in the generated code. This is particularly useful for seeing the stack management instructions (Peek, Poke, and DeleteRange) which don't emit any corresponding C++ code. Bug: v8:7793 Change-Id: I24bdec47da76c9bd751b928d3cd92aa513dc6593 Reviewed-on: https://chromium-review.googlesource.com/c/v8/v8/+/2748040 Reviewed-by: Nico Hartmann Commit-Queue: Seth Brenith Cr-Commit-Position: refs/heads/master@{#73352} --- BUILD.gn | 6 ++ src/torque/global-context.cc | 1 + src/torque/global-context.h | 3 + src/torque/instructions.cc | 106 +++++++++++++++++++++ src/torque/instructions.h | 141 ++++++++++++++++++++++++++++ src/torque/torque-code-generator.cc | 9 +- src/torque/torque-code-generator.h | 6 ++ src/torque/torque-compiler.cc | 3 + src/torque/torque-compiler.h | 3 + src/torque/torque.cc | 2 + 10 files changed, 278 insertions(+), 2 deletions(-) diff --git a/BUILD.gn b/BUILD.gn index b713df86b8..2c930a1cd9 100644 --- a/BUILD.gn +++ b/BUILD.gn @@ -252,6 +252,9 @@ declare_args() { # file generation v8_verify_torque_generation_invariance = false + # Generate comments describing the Torque intermediate representation. + v8_annotate_torque_ir = false + # Disable all snapshot compression. v8_enable_snapshot_compression = true @@ -1537,6 +1540,9 @@ template("run_torque") { "-v8-root", rebase_path(".", root_build_dir), ] + if (v8_annotate_torque_ir) { + args += [ "-annotate-ir" ] + } if (defined(invoker.args)) { args += invoker.args } diff --git a/src/torque/global-context.cc b/src/torque/global-context.cc index 35ddb1d2e2..a70e8ec41f 100644 --- a/src/torque/global-context.cc +++ b/src/torque/global-context.cc @@ -14,6 +14,7 @@ DEFINE_CONTEXTUAL_VARIABLE(TargetArchitecture) GlobalContext::GlobalContext(Ast ast) : collect_language_server_data_(false), force_assert_statements_(false), + annotate_ir_(false), ast_(std::move(ast)) { CurrentScope::Scope current_scope(nullptr); CurrentSourcePosition::Scope current_source_position( diff --git a/src/torque/global-context.h b/src/torque/global-context.h index cd6ddef8b2..403502b67b 100644 --- a/src/torque/global-context.h +++ b/src/torque/global-context.h @@ -54,6 +54,8 @@ class GlobalContext : public ContextualClass { static bool force_assert_statements() { return Get().force_assert_statements_; } + static void SetAnnotateIR() { Get().annotate_ir_ = true; } + static bool annotate_ir() { return Get().annotate_ir_; } static Ast* ast() { return &Get().ast_; } static std::string MakeUniqueName(const std::string& base) { return base + "_" + std::to_string(Get().fresh_ids_[base]++); @@ -106,6 +108,7 @@ class GlobalContext : public ContextualClass { private: bool collect_language_server_data_; bool force_assert_statements_; + bool annotate_ir_; Namespace* default_namespace_; Ast ast_; std::vector> declarables_; diff --git a/src/torque/instructions.cc b/src/torque/instructions.cc index ea7676ea44..52f0f81976 100644 --- a/src/torque/instructions.cc +++ b/src/torque/instructions.cc @@ -129,6 +129,11 @@ DefinitionLocation NamespaceConstantInstruction::GetValueDefinition( return DefinitionLocation::Instruction(this, index); } +std::ostream& operator<<(std::ostream& os, + const NamespaceConstantInstruction& instruction) { + return os << "NamespaceConstant " << instruction.constant->external_name(); +} + void InstructionBase::InvalidateTransientTypes( Stack* stack) const { auto current = stack->begin(); @@ -183,6 +188,22 @@ DefinitionLocation CallIntrinsicInstruction::GetValueDefinition( return DefinitionLocation::Instruction(this, index); } +std::ostream& operator<<(std::ostream& os, + const CallIntrinsicInstruction& instruction) { + os << "CallIntrinsic " << instruction.intrinsic->ReadableName(); + if (!instruction.specialization_types.empty()) { + os << "<"; + PrintCommaSeparatedList( + os, instruction.specialization_types, + [](const Type* type) -> const Type& { return *type; }); + os << ">"; + } + os << "("; + PrintCommaSeparatedList(os, instruction.constexpr_arguments); + os << ")"; + return os; +} + void CallCsaMacroInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { std::vector parameter_types = @@ -243,6 +264,18 @@ DefinitionLocation CallCsaMacroInstruction::GetValueDefinition( return DefinitionLocation::Instruction(this, index); } +std::ostream& operator<<(std::ostream& os, + const CallCsaMacroInstruction& instruction) { + os << "CallCsaMacro " << instruction.macro->ReadableName(); + os << "("; + PrintCommaSeparatedList(os, instruction.constexpr_arguments); + os << ")"; + if (instruction.catch_block) { + os << ", catch block " << (*instruction.catch_block)->id(); + } + return os; +} + void CallCsaMacroAndBranchInstruction::TypeInstruction( Stack* stack, ControlFlowGraph* cfg) const { std::vector parameter_types = @@ -363,6 +396,26 @@ CallCsaMacroAndBranchInstruction::GetExceptionObjectDefinition() const { return DefinitionLocation::Instruction(this, GetValueDefinitionCount()); } +std::ostream& operator<<(std::ostream& os, + const CallCsaMacroAndBranchInstruction& instruction) { + os << "CallCsaMacroAndBranch " << instruction.macro->ReadableName(); + os << "("; + PrintCommaSeparatedList(os, instruction.constexpr_arguments); + os << ")"; + if (instruction.return_continuation) { + os << ", return continuation " << (*instruction.return_continuation)->id(); + } + if (!instruction.label_blocks.empty()) { + os << ", label blocks "; + PrintCommaSeparatedList(os, instruction.label_blocks, + [](Block* block) { return block->id(); }); + } + if (instruction.catch_block) { + os << ", catch block " << (*instruction.catch_block)->id(); + } + return os; +} + void CallBuiltinInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { std::vector argument_types = stack->PopMany(argc); @@ -447,6 +500,19 @@ DefinitionLocation CallBuiltinPointerInstruction::GetValueDefinition( return DefinitionLocation::Instruction(this, index); } +std::ostream& operator<<(std::ostream& os, + const CallBuiltinInstruction& instruction) { + os << "CallBuiltin " << instruction.builtin->ReadableName() + << ", argc: " << instruction.argc; + if (instruction.is_tailcall) { + os << ", is_tailcall"; + } + if (instruction.catch_block) { + os << ", catch block " << (*instruction.catch_block)->id(); + } + return os; +} + void CallRuntimeInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { std::vector argument_types = stack->PopMany(argc); @@ -507,6 +573,19 @@ CallRuntimeInstruction::GetExceptionObjectDefinition() const { return DefinitionLocation::Instruction(this, GetValueDefinitionCount()); } +std::ostream& operator<<(std::ostream& os, + const CallRuntimeInstruction& instruction) { + os << "CallRuntime " << instruction.runtime_function->ReadableName() + << ", argc: " << instruction.argc; + if (instruction.is_tailcall) { + os << ", is_tailcall"; + } + if (instruction.catch_block) { + os << ", catch block " << (*instruction.catch_block)->id(); + } + return os; +} + void BranchInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { const Type* condition_type = stack->Pop(); @@ -524,6 +603,12 @@ void BranchInstruction::RecomputeDefinitionLocations( if_false->MergeInputDefinitions(*locations, worklist); } +std::ostream& operator<<(std::ostream& os, + const BranchInstruction& instruction) { + return os << "Branch true: " << instruction.if_true->id() + << ", false: " << instruction.if_false->id(); +} + void ConstexprBranchInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { if_true->SetInputTypes(*stack); @@ -536,6 +621,13 @@ void ConstexprBranchInstruction::RecomputeDefinitionLocations( if_false->MergeInputDefinitions(*locations, worklist); } +std::ostream& operator<<(std::ostream& os, + const ConstexprBranchInstruction& instruction) { + return os << "ConstexprBranch " << instruction.condition + << ", true: " << instruction.if_true->id() + << ", false: " << instruction.if_false->id(); +} + void GotoInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { destination->SetInputTypes(*stack); @@ -546,6 +638,10 @@ void GotoInstruction::RecomputeDefinitionLocations( destination->MergeInputDefinitions(*locations, worklist); } +std::ostream& operator<<(std::ostream& os, const GotoInstruction& instruction) { + return os << "Goto " << instruction.destination->id(); +} + void GotoExternalInstruction::TypeInstruction(Stack* stack, ControlFlowGraph* cfg) const { if (variable_names.size() != stack->Size()) { @@ -693,6 +789,16 @@ DefinitionLocation MakeLazyNodeInstruction::GetValueDefinition() const { return DefinitionLocation::Instruction(this, 0); } +std::ostream& operator<<(std::ostream& os, + const MakeLazyNodeInstruction& instruction) { + os << "MakeLazyNode " << instruction.macro->ReadableName() << ", " + << *instruction.result_type; + for (const std::string& arg : instruction.constexpr_arguments) { + os << ", " << arg; + } + return os; +} + bool CallRuntimeInstruction::IsBlockTerminator() const { return is_tailcall || runtime_function->signature().return_type == TypeOracle::GetNeverType(); diff --git a/src/torque/instructions.h b/src/torque/instructions.h index 85fd7f897c..88736a4ace 100644 --- a/src/torque/instructions.h +++ b/src/torque/instructions.h @@ -288,6 +288,15 @@ struct PeekInstruction : InstructionBase { base::Optional widened_type; }; +inline std::ostream& operator<<(std::ostream& os, + const PeekInstruction& instruction) { + os << "Peek " << instruction.slot; + if (instruction.widened_type) { + os << ", " << **instruction.widened_type; + } + return os; +} + struct PokeInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() @@ -298,6 +307,15 @@ struct PokeInstruction : InstructionBase { base::Optional widened_type; }; +inline std::ostream& operator<<(std::ostream& os, + const PokeInstruction& instruction) { + os << "Poke " << instruction.slot; + if (instruction.widened_type) { + os << ", " << **instruction.widened_type; + } + return os; +} + // Preserve the top {preserved_slots} number of slots, and delete // {deleted_slots} number or slots below. struct DeleteRangeInstruction : InstructionBase { @@ -307,6 +325,11 @@ struct DeleteRangeInstruction : InstructionBase { StackRange range; }; +inline std::ostream& operator<<(std::ostream& os, + const DeleteRangeInstruction& instruction) { + return os << "DeleteRange " << instruction.range; +} + struct PushUninitializedInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit PushUninitializedInstruction(const Type* type) : type(type) {} @@ -316,6 +339,11 @@ struct PushUninitializedInstruction : InstructionBase { const Type* type; }; +inline std::ostream& operator<<( + std::ostream& os, const PushUninitializedInstruction& instruction) { + return os << "PushUninitialized " << *instruction.type; +} + struct PushBuiltinPointerInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() PushBuiltinPointerInstruction(std::string external_name, const Type* type) @@ -329,6 +357,13 @@ struct PushBuiltinPointerInstruction : InstructionBase { const Type* type; }; +inline std::ostream& operator<<( + std::ostream& os, const PushBuiltinPointerInstruction& instruction) { + return os << "PushBuiltinPointer " + << StringLiteralQuote(instruction.external_name) << ", " + << *instruction.type; +} + struct NamespaceConstantInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit NamespaceConstantInstruction(NamespaceConstant* constant) @@ -340,6 +375,9 @@ struct NamespaceConstantInstruction : InstructionBase { NamespaceConstant* constant; }; +std::ostream& operator<<(std::ostream& os, + const NamespaceConstantInstruction& instruction); + struct LoadReferenceInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit LoadReferenceInstruction(const Type* type) : type(type) {} @@ -349,12 +387,22 @@ struct LoadReferenceInstruction : InstructionBase { const Type* type; }; +inline std::ostream& operator<<(std::ostream& os, + const LoadReferenceInstruction& instruction) { + return os << "LoadReference " << *instruction.type; +} + struct StoreReferenceInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit StoreReferenceInstruction(const Type* type) : type(type) {} const Type* type; }; +inline std::ostream& operator<<(std::ostream& os, + const StoreReferenceInstruction& instruction) { + return os << "StoreReference " << *instruction.type; +} + // Pops a bitfield struct; pushes a bitfield value extracted from it. struct LoadBitFieldInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() @@ -368,6 +416,12 @@ struct LoadBitFieldInstruction : InstructionBase { BitField bit_field; }; +inline std::ostream& operator<<(std::ostream& os, + const LoadBitFieldInstruction& instruction) { + return os << "LoadBitField " << *instruction.bit_field_struct_type << ", " + << instruction.bit_field.name_and_type.name; +} + // Pops a bitfield value and a bitfield struct; pushes a new bitfield struct // containing the updated value. struct StoreBitFieldInstruction : InstructionBase { @@ -386,6 +440,16 @@ struct StoreBitFieldInstruction : InstructionBase { bool starts_as_zero; }; +inline std::ostream& operator<<(std::ostream& os, + const StoreBitFieldInstruction& instruction) { + os << "StoreBitField " << *instruction.bit_field_struct_type << ", " + << instruction.bit_field.name_and_type.name; + if (instruction.starts_as_zero) { + os << ", starts_as_zero"; + } + return os; +} + struct CallIntrinsicInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() CallIntrinsicInstruction(Intrinsic* intrinsic, @@ -403,6 +467,9 @@ struct CallIntrinsicInstruction : InstructionBase { std::vector constexpr_arguments; }; +std::ostream& operator<<(std::ostream& os, + const CallIntrinsicInstruction& instruction); + struct CallCsaMacroInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() CallCsaMacroInstruction(Macro* macro, @@ -424,6 +491,9 @@ struct CallCsaMacroInstruction : InstructionBase { base::Optional catch_block; }; +std::ostream& operator<<(std::ostream& os, + const CallCsaMacroInstruction& instruction); + struct CallCsaMacroAndBranchInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() CallCsaMacroAndBranchInstruction(Macro* macro, @@ -458,6 +528,9 @@ struct CallCsaMacroAndBranchInstruction : InstructionBase { base::Optional catch_block; }; +std::ostream& operator<<(std::ostream& os, + const CallCsaMacroAndBranchInstruction& instruction); + struct MakeLazyNodeInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() MakeLazyNodeInstruction(Macro* macro, const Type* result_type, @@ -473,6 +546,9 @@ struct MakeLazyNodeInstruction : InstructionBase { std::vector constexpr_arguments; }; +std::ostream& operator<<(std::ostream& os, + const MakeLazyNodeInstruction& instruction); + struct CallBuiltinInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return is_tailcall; } @@ -496,6 +572,9 @@ struct CallBuiltinInstruction : InstructionBase { base::Optional catch_block; }; +std::ostream& operator<<(std::ostream& os, + const CallBuiltinInstruction& instruction); + struct CallBuiltinPointerInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return is_tailcall; } @@ -511,6 +590,16 @@ struct CallBuiltinPointerInstruction : InstructionBase { size_t argc; }; +inline std::ostream& operator<<( + std::ostream& os, const CallBuiltinPointerInstruction& instruction) { + os << "CallBuiltinPointer " << *instruction.type + << ", argc: " << instruction.argc; + if (instruction.is_tailcall) { + os << ", is_tailcall"; + } + return os; +} + struct CallRuntimeInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override; @@ -535,6 +624,9 @@ struct CallRuntimeInstruction : InstructionBase { base::Optional catch_block; }; +std::ostream& operator<<(std::ostream& os, + const CallRuntimeInstruction& instruction); + struct BranchInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } @@ -550,6 +642,9 @@ struct BranchInstruction : InstructionBase { Block* if_false; }; +std::ostream& operator<<(std::ostream& os, + const BranchInstruction& instruction); + struct ConstexprBranchInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } @@ -567,6 +662,9 @@ struct ConstexprBranchInstruction : InstructionBase { Block* if_false; }; +std::ostream& operator<<(std::ostream& os, + const ConstexprBranchInstruction& instruction); + struct GotoInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } @@ -579,6 +677,8 @@ struct GotoInstruction : InstructionBase { Block* destination; }; +std::ostream& operator<<(std::ostream& os, const GotoInstruction& instruction); + struct GotoExternalInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() bool IsBlockTerminator() const override { return true; } @@ -592,6 +692,15 @@ struct GotoExternalInstruction : InstructionBase { std::vector variable_names; }; +inline std::ostream& operator<<(std::ostream& os, + const GotoExternalInstruction& instruction) { + os << "GotoExternal " << instruction.destination; + for (const std::string& name : instruction.variable_names) { + os << ", " << name; + } + return os; +} + struct ReturnInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit ReturnInstruction(size_t count) : count(count) {} @@ -600,6 +709,11 @@ struct ReturnInstruction : InstructionBase { size_t count; // How many values to return. }; +inline std::ostream& operator<<(std::ostream& os, + const ReturnInstruction& instruction) { + return os << "Return count: " << instruction.count; +} + struct PrintConstantStringInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit PrintConstantStringInstruction(std::string message) @@ -608,17 +722,39 @@ struct PrintConstantStringInstruction : InstructionBase { std::string message; }; +inline std::ostream& operator<<( + std::ostream& os, const PrintConstantStringInstruction& instruction) { + return os << "PrintConstantString " + << StringLiteralQuote(instruction.message); +} + struct AbortInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() enum class Kind { kDebugBreak, kUnreachable, kAssertionFailure }; bool IsBlockTerminator() const override { return kind != Kind::kDebugBreak; } explicit AbortInstruction(Kind kind, std::string message = "") : kind(kind), message(std::move(message)) {} + static const char* KindToString(Kind kind) { + switch (kind) { + case Kind::kDebugBreak: + return "kDebugBreak"; + case Kind::kUnreachable: + return "kUnreachable"; + case Kind::kAssertionFailure: + return "kAssertionFailure"; + } + } Kind kind; std::string message; }; +inline std::ostream& operator<<(std::ostream& os, + const AbortInstruction& instruction) { + return os << "Abort " << AbortInstruction::KindToString(instruction.kind) + << ", " << StringLiteralQuote(instruction.message); +} + struct UnsafeCastInstruction : InstructionBase { TORQUE_INSTRUCTION_BOILERPLATE() explicit UnsafeCastInstruction(const Type* destination_type) @@ -629,6 +765,11 @@ struct UnsafeCastInstruction : InstructionBase { const Type* destination_type; }; +inline std::ostream& operator<<(std::ostream& os, + const UnsafeCastInstruction& instruction) { + return os << "UnsafeCast " << *instruction.destination_type; +} + } // namespace torque } // namespace internal } // namespace v8 diff --git a/src/torque/torque-code-generator.cc b/src/torque/torque-code-generator.cc index 46763be468..9108d9a731 100644 --- a/src/torque/torque-code-generator.cc +++ b/src/torque/torque-code-generator.cc @@ -4,6 +4,8 @@ #include "src/torque/torque-code-generator.h" +#include "src/torque/global-context.h" + namespace v8 { namespace internal { namespace torque { @@ -31,8 +33,11 @@ void TorqueCodeGenerator::EmitInstruction(const Instruction& instruction, #endif switch (instruction.kind()) { -#define ENUM_ITEM(T) \ - case InstructionKind::k##T: \ +#define ENUM_ITEM(T) \ + case InstructionKind::k##T: \ + if (GlobalContext::annotate_ir()) { \ + EmitIRAnnotation(instruction.Cast(), stack); \ + } \ return EmitInstruction(instruction.Cast(), stack); TORQUE_INSTRUCTION_LIST(ENUM_ITEM) #undef ENUM_ITEM diff --git a/src/torque/torque-code-generator.h b/src/torque/torque-code-generator.h index ddbd5309c9..ed9e70f377 100644 --- a/src/torque/torque-code-generator.h +++ b/src/torque/torque-code-generator.h @@ -74,6 +74,12 @@ class TorqueCodeGenerator { void EmitInstruction(const Instruction& instruction, Stack* stack); + template + void EmitIRAnnotation(const T& instruction, Stack* stack) { + out() << " // " << instruction + << ", starting stack size: " << stack->Size() << "\n"; + } + #define EMIT_INSTRUCTION_DECLARATION(T) \ void EmitInstruction(const T& instruction, Stack* stack); TORQUE_BACKEND_AGNOSTIC_INSTRUCTION_LIST(EMIT_INSTRUCTION_DECLARATION) diff --git a/src/torque/torque-compiler.cc b/src/torque/torque-compiler.cc index 6da18dd526..64bad91cab 100644 --- a/src/torque/torque-compiler.cc +++ b/src/torque/torque-compiler.cc @@ -53,6 +53,9 @@ void CompileCurrentAst(TorqueCompilerOptions options) { if (options.force_assert_statements) { GlobalContext::SetForceAssertStatements(); } + if (options.annotate_ir) { + GlobalContext::SetAnnotateIR(); + } TargetArchitecture::Scope target_architecture(options.force_32bit_output); TypeOracle::Scope type_oracle; CurrentScope::Scope current_namespace(GlobalContext::GetDefaultNamespace()); diff --git a/src/torque/torque-compiler.h b/src/torque/torque-compiler.h index df81d60d3e..0e8f3b42ae 100644 --- a/src/torque/torque-compiler.h +++ b/src/torque/torque-compiler.h @@ -30,6 +30,9 @@ struct TorqueCompilerOptions { // architectures. Note that this does not needed in Chromium/V8 land, since we // always build with the same bit width as the target architecture. bool force_32bit_output = false; + + // Adds extra comments in output that show Torque intermediate representation. + bool annotate_ir = false; }; struct TorqueCompilerResult { diff --git a/src/torque/torque.cc b/src/torque/torque.cc index ad7551f8aa..4e71c43014 100644 --- a/src/torque/torque.cc +++ b/src/torque/torque.cc @@ -34,6 +34,8 @@ int WrappedMain(int argc, const char** argv) { options.v8_root = std::string(argv[++i]); } else if (argument == "-m32") { options.force_32bit_output = true; + } else if (argument == "-annotate-ir") { + options.annotate_ir = true; } else { // Otherwise it's a .tq file. Remember it for compilation. files.emplace_back(std::move(argument));