From ed2032f17aa8bc2dbcb96611e6a0bd6dec36ae6a Mon Sep 17 00:00:00 2001 From: Alex Lorenz Date: Thu, 16 Jul 2015 23:37:45 +0000 Subject: [PATCH] MIR Serialization: Serialize the frame index machine operands. Reviewers: Duncan P. N. Exon Smith git-svn-id: https://llvm.org/svn/llvm-project/llvm/trunk@242487 91177308-0d34-0410-b5e6-96231b3b80d8 --- lib/CodeGen/MIRParser/MILexer.cpp | 33 +++++++++ lib/CodeGen/MIRParser/MILexer.h | 3 + lib/CodeGen/MIRParser/MIParser.cpp | 43 ++++++++++++ lib/CodeGen/MIRParser/MIParser.h | 2 + lib/CodeGen/MIRParser/MIRParser.cpp | 23 ++++--- lib/CodeGen/MIRPrinter.cpp | 68 ++++++++++++++++--- ...ack-object-operand-name-mismatch-error.mir | 35 ++++++++++ .../CodeGen/MIR/X86/stack-object-operands.mir | 48 +++++++++++++ .../MIR/X86/undefined-fixed-stack-object.mir | 40 +++++++++++ .../MIR/X86/undefined-stack-object.mir | 32 +++++++++ 10 files changed, 307 insertions(+), 20 deletions(-) create mode 100644 test/CodeGen/MIR/X86/stack-object-operand-name-mismatch-error.mir create mode 100644 test/CodeGen/MIR/X86/stack-object-operands.mir create mode 100644 test/CodeGen/MIR/X86/undefined-fixed-stack-object.mir create mode 100644 test/CodeGen/MIR/X86/undefined-stack-object.mir diff --git a/lib/CodeGen/MIRParser/MILexer.cpp b/lib/CodeGen/MIRParser/MILexer.cpp index 9babb3b0a3d..53c393da45d 100644 --- a/lib/CodeGen/MIRParser/MILexer.cpp +++ b/lib/CodeGen/MIRParser/MILexer.cpp @@ -128,10 +128,39 @@ static Cursor maybeLexIndex(Cursor C, MIToken &Token, StringRef Rule, return C; } +static Cursor maybeLexIndexAndName(Cursor C, MIToken &Token, StringRef Rule, + MIToken::TokenKind Kind) { + if (!C.remaining().startswith(Rule) || !isdigit(C.peek(Rule.size()))) + return None; + auto Range = C; + C.advance(Rule.size()); + auto NumberRange = C; + while (isdigit(C.peek())) + C.advance(); + StringRef Number = NumberRange.upto(C); + unsigned StringOffset = Rule.size() + Number.size(); + if (C.peek() == '.') { + C.advance(); + ++StringOffset; + while (isIdentifierChar(C.peek())) + C.advance(); + } + Token = MIToken(Kind, Range.upto(C), APSInt(Number), StringOffset); + return C; +} + static Cursor maybeLexJumpTableIndex(Cursor C, MIToken &Token) { return maybeLexIndex(C, Token, "%jump-table.", MIToken::JumpTableIndex); } +static Cursor maybeLexStackObject(Cursor C, MIToken &Token) { + return maybeLexIndexAndName(C, Token, "%stack.", MIToken::StackObject); +} + +static Cursor maybeLexFixedStackObject(Cursor C, MIToken &Token) { + return maybeLexIndex(C, Token, "%fixed-stack.", MIToken::FixedStackObject); +} + static Cursor lexVirtualRegister(Cursor C, MIToken &Token) { auto Range = C; C.advance(); // Skip '%' @@ -228,6 +257,10 @@ StringRef llvm::lexMIToken( return R.remaining(); if (Cursor R = maybeLexJumpTableIndex(C, Token)) return R.remaining(); + if (Cursor R = maybeLexStackObject(C, Token)) + return R.remaining(); + if (Cursor R = maybeLexFixedStackObject(C, Token)) + return R.remaining(); if (Cursor R = maybeLexRegister(C, Token)) return R.remaining(); if (Cursor R = maybeLexGlobalValue(C, Token)) diff --git a/lib/CodeGen/MIRParser/MILexer.h b/lib/CodeGen/MIRParser/MILexer.h index b1c05226158..03b4d486f98 100644 --- a/lib/CodeGen/MIRParser/MILexer.h +++ b/lib/CodeGen/MIRParser/MILexer.h @@ -48,6 +48,8 @@ struct MIToken { Identifier, NamedRegister, MachineBasicBlock, + StackObject, + FixedStackObject, NamedGlobalValue, GlobalValue, @@ -97,6 +99,7 @@ public: bool hasIntegerValue() const { return Kind == IntegerLiteral || Kind == MachineBasicBlock || + Kind == StackObject || Kind == FixedStackObject || Kind == GlobalValue || Kind == VirtualRegister || Kind == JumpTableIndex; } diff --git a/lib/CodeGen/MIRParser/MIParser.cpp b/lib/CodeGen/MIRParser/MIParser.cpp index 62a3fbe1e5f..b2931189578 100644 --- a/lib/CodeGen/MIRParser/MIParser.cpp +++ b/lib/CodeGen/MIRParser/MIParser.cpp @@ -17,8 +17,10 @@ #include "llvm/AsmParser/SlotMapping.h" #include "llvm/CodeGen/MachineBasicBlock.h" #include "llvm/CodeGen/MachineFunction.h" +#include "llvm/CodeGen/MachineFrameInfo.h" #include "llvm/CodeGen/MachineInstr.h" #include "llvm/CodeGen/MachineInstrBuilder.h" +#include "llvm/IR/Instructions.h" #include "llvm/IR/Module.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/SourceMgr.h" @@ -87,6 +89,8 @@ public: bool parseImmediateOperand(MachineOperand &Dest); bool parseMBBReference(MachineBasicBlock *&MBB); bool parseMBBOperand(MachineOperand &Dest); + bool parseStackObjectOperand(MachineOperand &Dest); + bool parseFixedStackObjectOperand(MachineOperand &Dest); bool parseGlobalAddressOperand(MachineOperand &Dest); bool parseJumpTableIndexOperand(MachineOperand &Dest); bool parseMachineOperand(MachineOperand &Dest); @@ -439,6 +443,41 @@ bool MIParser::parseMBBOperand(MachineOperand &Dest) { return false; } +bool MIParser::parseStackObjectOperand(MachineOperand &Dest) { + assert(Token.is(MIToken::StackObject)); + unsigned ID; + if (getUnsigned(ID)) + return true; + auto ObjectInfo = PFS.StackObjectSlots.find(ID); + if (ObjectInfo == PFS.StackObjectSlots.end()) + return error(Twine("use of undefined stack object '%stack.") + Twine(ID) + + "'"); + StringRef Name; + if (const auto *Alloca = + MF.getFrameInfo()->getObjectAllocation(ObjectInfo->second)) + Name = Alloca->getName(); + if (!Token.stringValue().empty() && Token.stringValue() != Name) + return error(Twine("the name of the stack object '%stack.") + Twine(ID) + + "' isn't '" + Token.stringValue() + "'"); + lex(); + Dest = MachineOperand::CreateFI(ObjectInfo->second); + return false; +} + +bool MIParser::parseFixedStackObjectOperand(MachineOperand &Dest) { + assert(Token.is(MIToken::FixedStackObject)); + unsigned ID; + if (getUnsigned(ID)) + return true; + auto ObjectInfo = PFS.FixedStackObjectSlots.find(ID); + if (ObjectInfo == PFS.FixedStackObjectSlots.end()) + return error(Twine("use of undefined fixed stack object '%fixed-stack.") + + Twine(ID) + "'"); + lex(); + Dest = MachineOperand::CreateFI(ObjectInfo->second); + return false; +} + bool MIParser::parseGlobalAddressOperand(MachineOperand &Dest) { switch (Token.kind()) { case MIToken::NamedGlobalValue: { @@ -498,6 +537,10 @@ bool MIParser::parseMachineOperand(MachineOperand &Dest) { return parseImmediateOperand(Dest); case MIToken::MachineBasicBlock: return parseMBBOperand(Dest); + case MIToken::StackObject: + return parseStackObjectOperand(Dest); + case MIToken::FixedStackObject: + return parseFixedStackObjectOperand(Dest); case MIToken::GlobalValue: case MIToken::NamedGlobalValue: return parseGlobalAddressOperand(Dest); diff --git a/lib/CodeGen/MIRParser/MIParser.h b/lib/CodeGen/MIRParser/MIParser.h index 405e637267f..b34b7626eb8 100644 --- a/lib/CodeGen/MIRParser/MIParser.h +++ b/lib/CodeGen/MIRParser/MIParser.h @@ -29,6 +29,8 @@ class SourceMgr; struct PerFunctionMIParsingState { DenseMap MBBSlots; DenseMap VirtualRegisterSlots; + DenseMap FixedStackObjectSlots; + DenseMap StackObjectSlots; DenseMap JumpTableSlots; }; diff --git a/lib/CodeGen/MIRParser/MIRParser.cpp b/lib/CodeGen/MIRParser/MIRParser.cpp index fb1878febda..ca46de44740 100644 --- a/lib/CodeGen/MIRParser/MIRParser.cpp +++ b/lib/CodeGen/MIRParser/MIRParser.cpp @@ -109,7 +109,9 @@ public: DenseMap &VirtualRegisterSlots); bool initializeFrameInfo(const Function &F, MachineFrameInfo &MFI, - const yaml::MachineFunction &YamlMF); + const yaml::MachineFunction &YamlMF, + DenseMap &StackObjectSlots, + DenseMap &FixedStackObjectSlots); bool initializeJumpTableInfo(MachineFunction &MF, const yaml::MachineJumpTable &YamlJTI, @@ -268,7 +270,8 @@ bool MIRParserImpl::initializeMachineFunction(MachineFunction &MF) { if (initializeRegisterInfo(MF, MF.getRegInfo(), YamlMF, PFS.VirtualRegisterSlots)) return true; - if (initializeFrameInfo(*MF.getFunction(), *MF.getFrameInfo(), YamlMF)) + if (initializeFrameInfo(*MF.getFunction(), *MF.getFrameInfo(), YamlMF, + PFS.StackObjectSlots, PFS.FixedStackObjectSlots)) return true; const auto &F = *MF.getFunction(); @@ -375,9 +378,11 @@ bool MIRParserImpl::initializeRegisterInfo( return false; } -bool MIRParserImpl::initializeFrameInfo(const Function &F, - MachineFrameInfo &MFI, - const yaml::MachineFunction &YamlMF) { +bool MIRParserImpl::initializeFrameInfo( + const Function &F, MachineFrameInfo &MFI, + const yaml::MachineFunction &YamlMF, + DenseMap &StackObjectSlots, + DenseMap &FixedStackObjectSlots) { const yaml::MachineFrameInfo &YamlMFI = YamlMF.FrameInfo; MFI.setFrameAddressIsTaken(YamlMFI.IsFrameAddressTaken); MFI.setReturnAddressIsTaken(YamlMFI.IsReturnAddressTaken); @@ -403,8 +408,8 @@ bool MIRParserImpl::initializeFrameInfo(const Function &F, else ObjectIdx = MFI.CreateFixedSpillStackObject(Object.Size, Object.Offset); MFI.setObjectAlignment(ObjectIdx, Object.Alignment); - // TODO: Store the mapping between fixed object IDs and object indices to - // parse fixed stack object references correctly. + // TODO: Report an error when objects are redefined. + FixedStackObjectSlots.insert(std::make_pair(Object.ID, ObjectIdx)); } // Initialize the ordinary frame objects. @@ -428,8 +433,8 @@ bool MIRParserImpl::initializeFrameInfo(const Function &F, Object.Size, Object.Alignment, Object.Type == yaml::MachineStackObject::SpillSlot, Alloca); MFI.setObjectOffset(ObjectIdx, Object.Offset); - // TODO: Store the mapping between object IDs and object indices to parse - // stack object references correctly. + // TODO: Report an error when objects are redefined. + StackObjectSlots.insert(std::make_pair(Object.ID, ObjectIdx)); } return false; } diff --git a/lib/CodeGen/MIRPrinter.cpp b/lib/CodeGen/MIRPrinter.cpp index 50715e9f127..a701ffdce6a 100644 --- a/lib/CodeGen/MIRPrinter.cpp +++ b/lib/CodeGen/MIRPrinter.cpp @@ -32,11 +32,34 @@ using namespace llvm; namespace { +/// This structure describes how to print out stack object references. +struct FrameIndexOperand { + std::string Name; + unsigned ID; + bool IsFixed; + + FrameIndexOperand(StringRef Name, unsigned ID, bool IsFixed) + : Name(Name.str()), ID(ID), IsFixed(IsFixed) {} + + /// Return an ordinary stack object reference. + static FrameIndexOperand create(StringRef Name, unsigned ID) { + return FrameIndexOperand(Name, ID, /*IsFixed=*/false); + } + + /// Return a fixed stack object reference. + static FrameIndexOperand createFixed(unsigned ID) { + return FrameIndexOperand("", ID, /*IsFixed=*/true); + } +}; + /// This class prints out the machine functions using the MIR serialization /// format. class MIRPrinter { raw_ostream &OS; DenseMap RegisterMaskIds; + /// Maps from stack object indices to operand indices which will be used when + /// printing frame index machine operands. + DenseMap StackObjectOperandMapping; public: MIRPrinter(raw_ostream &OS) : OS(OS) {} @@ -63,14 +86,18 @@ class MIPrinter { raw_ostream &OS; ModuleSlotTracker &MST; const DenseMap &RegisterMaskIds; + const DenseMap &StackObjectOperandMapping; public: MIPrinter(raw_ostream &OS, ModuleSlotTracker &MST, - const DenseMap &RegisterMaskIds) - : OS(OS), MST(MST), RegisterMaskIds(RegisterMaskIds) {} + const DenseMap &RegisterMaskIds, + const DenseMap &StackObjectOperandMapping) + : OS(OS), MST(MST), RegisterMaskIds(RegisterMaskIds), + StackObjectOperandMapping(StackObjectOperandMapping) {} void print(const MachineInstr &MI); void printMBBReference(const MachineBasicBlock &MBB); + void printStackObjectReference(int FrameIndex); void print(const MachineOperand &Op, const TargetRegisterInfo *TRI); }; @@ -182,7 +209,7 @@ void MIRPrinter::convertStackObjects(yaml::MachineFunction &MF, continue; yaml::FixedMachineStackObject YamlObject; - YamlObject.ID = ID++; + YamlObject.ID = ID; YamlObject.Type = MFI.isSpillSlotObjectIndex(I) ? yaml::FixedMachineStackObject::SpillSlot : yaml::FixedMachineStackObject::DefaultType; @@ -192,8 +219,8 @@ void MIRPrinter::convertStackObjects(yaml::MachineFunction &MF, YamlObject.IsImmutable = MFI.isImmutableObjectIndex(I); YamlObject.IsAliased = MFI.isAliasedObjectIndex(I); MF.FixedStackObjects.push_back(YamlObject); - // TODO: Store the mapping between fixed object IDs and object indices to - // print the fixed stack object references correctly. + StackObjectOperandMapping.insert( + std::make_pair(I, FrameIndexOperand::createFixed(ID++))); } // Process ordinary stack objects. @@ -203,7 +230,7 @@ void MIRPrinter::convertStackObjects(yaml::MachineFunction &MF, continue; yaml::MachineStackObject YamlObject; - YamlObject.ID = ID++; + YamlObject.ID = ID; if (const auto *Alloca = MFI.getObjectAllocation(I)) YamlObject.Name.Value = Alloca->hasName() ? Alloca->getName() : ""; @@ -217,8 +244,8 @@ void MIRPrinter::convertStackObjects(yaml::MachineFunction &MF, YamlObject.Alignment = MFI.getObjectAlignment(I); MF.StackObjects.push_back(YamlObject); - // TODO: Store the mapping between object IDs and object indices to print - // the stack object references correctly. + StackObjectOperandMapping.insert(std::make_pair( + I, FrameIndexOperand::create(YamlObject.Name.Value, ID++))); } } @@ -233,7 +260,8 @@ void MIRPrinter::convert(ModuleSlotTracker &MST, Entry.ID = ID++; for (const auto *MBB : Table.MBBs) { raw_string_ostream StrOS(Str); - MIPrinter(StrOS, MST, RegisterMaskIds).printMBBReference(*MBB); + MIPrinter(StrOS, MST, RegisterMaskIds, StackObjectOperandMapping) + .printMBBReference(*MBB); Entry.Blocks.push_back(StrOS.str()); Str.clear(); } @@ -257,7 +285,8 @@ void MIRPrinter::convert(ModuleSlotTracker &MST, for (const auto *SuccMBB : MBB.successors()) { std::string Str; raw_string_ostream StrOS(Str); - MIPrinter(StrOS, MST, RegisterMaskIds).printMBBReference(*SuccMBB); + MIPrinter(StrOS, MST, RegisterMaskIds, StackObjectOperandMapping) + .printMBBReference(*SuccMBB); YamlMBB.Successors.push_back(StrOS.str()); } // Print the live in registers. @@ -274,7 +303,7 @@ void MIRPrinter::convert(ModuleSlotTracker &MST, std::string Str; for (const auto &MI : MBB) { raw_string_ostream StrOS(Str); - MIPrinter(StrOS, MST, RegisterMaskIds).print(MI); + MIPrinter(StrOS, MST, RegisterMaskIds, StackObjectOperandMapping).print(MI); YamlMBB.Instructions.push_back(StrOS.str()); Str.clear(); } @@ -327,6 +356,20 @@ void MIPrinter::printMBBReference(const MachineBasicBlock &MBB) { } } +void MIPrinter::printStackObjectReference(int FrameIndex) { + auto ObjectInfo = StackObjectOperandMapping.find(FrameIndex); + assert(ObjectInfo != StackObjectOperandMapping.end() && + "Invalid frame index"); + const FrameIndexOperand &Operand = ObjectInfo->second; + if (Operand.IsFixed) { + OS << "%fixed-stack." << Operand.ID; + return; + } + OS << "%stack." << Operand.ID; + if (!Operand.Name.empty()) + OS << '.' << Operand.Name; +} + void MIPrinter::print(const MachineOperand &Op, const TargetRegisterInfo *TRI) { switch (Op.getType()) { case MachineOperand::MO_Register: @@ -350,6 +393,9 @@ void MIPrinter::print(const MachineOperand &Op, const TargetRegisterInfo *TRI) { case MachineOperand::MO_MachineBasicBlock: printMBBReference(*Op.getMBB()); break; + case MachineOperand::MO_FrameIndex: + printStackObjectReference(Op.getIndex()); + break; case MachineOperand::MO_JumpTableIndex: OS << "%jump-table." << Op.getIndex(); // TODO: Print target flags. diff --git a/test/CodeGen/MIR/X86/stack-object-operand-name-mismatch-error.mir b/test/CodeGen/MIR/X86/stack-object-operand-name-mismatch-error.mir new file mode 100644 index 00000000000..a6ffb3b93e0 --- /dev/null +++ b/test/CodeGen/MIR/X86/stack-object-operand-name-mismatch-error.mir @@ -0,0 +1,35 @@ +# RUN: not llc -march=x86-64 -start-after machine-sink -stop-after machine-sink -o /dev/null %s 2>&1 | FileCheck %s +# This test ensures that an error is reported when an stack object reference +# uses a different name then the stack object definition. + +--- | + + define i32 @test(i32 %a) { + entry: + %b = alloca i32 + store i32 %a, i32* %b + %c = load i32, i32* %b + ret i32 %c + } + +... +--- +name: test +isSSA: true +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } +frameInfo: + maxAlignment: 4 +stack: + - { id: 0, name: b, size: 4, alignment: 4 } +body: + - id: 0 + name: entry + instructions: + - '%0 = COPY %edi' + # CHECK: [[@LINE+1]]:18: the name of the stack object '%stack.0' isn't 'x' + - 'MOV32mr %stack.0.x, 1, _, 0, _, %0' + - '%eax = COPY %0' + - 'RETQ %eax' +... diff --git a/test/CodeGen/MIR/X86/stack-object-operands.mir b/test/CodeGen/MIR/X86/stack-object-operands.mir new file mode 100644 index 00000000000..b1c078eb6a3 --- /dev/null +++ b/test/CodeGen/MIR/X86/stack-object-operands.mir @@ -0,0 +1,48 @@ +# RUN: llc -march=x86 -start-after machine-sink -stop-after machine-sink -o /dev/null %s | FileCheck %s +# This test ensures that the MIR parser parses stack object machine operands +# correctly. + +--- | + + define i32 @test(i32 %a) { + entry: + %b = alloca i32 + %0 = alloca i32 + store i32 %a, i32* %b + store i32 2, i32* %0 + %c = load i32, i32* %b + ret i32 %c + } + +... +--- +name: test +isSSA: true +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } + - { id: 1, class: gr32 } +frameInfo: + maxAlignment: 4 +fixedStack: + - { id: 0, offset: 0, size: 4, isImmutable: true, isAliased: false } +stack: + - { id: 0, name: b, size: 4, alignment: 4 } + - { id: 1, size: 4, alignment: 4 } +body: + # CHECK: name: entry + # CHECK: instructions: + # CHECK-NEXT: - '%0 = MOV32rm %fixed-stack.0, 1, _, 0, _' + # CHECK-NEXT: - 'MOV32mr %stack.0.b, 1, _, 0, _, %0' + # CHECK-NEXT: - 'MOV32mi %stack.1, 1, _, 0, _, 2' + # CHECK-NEXT: - '%1 = MOV32rm %stack.0.b, 1, _, 0, _' + - id: 0 + name: entry + instructions: + - '%0 = MOV32rm %fixed-stack.0, 1, _, 0, _' + - 'MOV32mr %stack.0.b, 1, _, 0, _, %0' + - 'MOV32mi %stack.1, 1, _, 0, _, 2' + - '%1 = MOV32rm %stack.0, 1, _, 0, _' + - '%eax = COPY %1' + - 'RETL %eax' +... diff --git a/test/CodeGen/MIR/X86/undefined-fixed-stack-object.mir b/test/CodeGen/MIR/X86/undefined-fixed-stack-object.mir new file mode 100644 index 00000000000..4a2fc80d52a --- /dev/null +++ b/test/CodeGen/MIR/X86/undefined-fixed-stack-object.mir @@ -0,0 +1,40 @@ +# RUN: not llc -march=x86-64 -start-after machine-sink -stop-after machine-sink -o /dev/null %s 2>&1 | FileCheck %s +--- | + + define i32 @test(i32 %a) { + entry: + %b = alloca i32 + %0 = alloca i32 + store i32 %a, i32* %b + store i32 2, i32* %0 + %c = load i32, i32* %b + ret i32 %c + } + +... +--- +name: test +isSSA: true +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } + - { id: 1, class: gr32 } +frameInfo: + maxAlignment: 4 +fixedStack: + - { id: 0, offset: 0, size: 4, isImmutable: true, isAliased: false } +stack: + - { id: 0, name: b, size: 4, alignment: 4 } + - { id: 1, size: 4, alignment: 4 } +body: + - id: 0 + name: entry + instructions: + # CHECK: [[@LINE+1]]:23: use of undefined fixed stack object '%fixed-stack.11' + - '%0 = MOV32rm %fixed-stack.11, 1, _, 0, _' + - 'MOV32mr %stack.0, 1, _, 0, _, %0' + - 'MOV32mi %stack.1, 1, _, 0, _, 2' + - '%1 = MOV32rm %stack.0, 1, _, 0, _' + - '%eax = COPY %1' + - 'RETL %eax' +... diff --git a/test/CodeGen/MIR/X86/undefined-stack-object.mir b/test/CodeGen/MIR/X86/undefined-stack-object.mir new file mode 100644 index 00000000000..bec703a2d11 --- /dev/null +++ b/test/CodeGen/MIR/X86/undefined-stack-object.mir @@ -0,0 +1,32 @@ +# RUN: not llc -march=x86-64 -start-after machine-sink -stop-after machine-sink -o /dev/null %s 2>&1 | FileCheck %s +--- | + + define i32 @test(i32 %a) { + entry: + %b = alloca i32 + store i32 %a, i32* %b + %c = load i32, i32* %b + ret i32 %c + } + +... +--- +name: test +isSSA: true +tracksRegLiveness: true +registers: + - { id: 0, class: gr32 } +frameInfo: + maxAlignment: 4 +stack: + - { id: 0, name: b, size: 4, alignment: 4 } +body: + - id: 0 + name: entry + instructions: + - '%0 = COPY %edi' + # CHECK: [[@LINE+1]]:18: use of undefined stack object '%stack.2' + - 'MOV32mr %stack.2, 1, _, 0, _, %0' + - '%eax = COPY %0' + - 'RETQ %eax' +... -- 2.34.1