Index: lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp =================================================================== --- lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp +++ lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp @@ -40,13 +40,20 @@ namespace { +struct IdxType { + unsigned Idx; + MVT Type; +}; + class WebAssemblyAsmPrinter final : public AsmPrinter { const WebAssemblyInstrInfo *TII; + const MachineRegisterInfo *MRI; + std::vector LocalTypes; unsigned NumArgs; public: WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr Streamer) - : AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {} + : AsmPrinter(TM, std::move(Streamer)), TII(nullptr), MRI(nullptr) {} private: const char *getPassName() const override { @@ -64,6 +71,7 @@ bool runOnMachineFunction(MachineFunction &MF) override { const auto &Subtarget = MF.getSubtarget(); TII = Subtarget.getInstrInfo(); + MRI = &MF.getRegInfo(); NumArgs = MF.getInfo()->getNumArguments(); return AsmPrinter::runOnMachineFunction(MF); } @@ -75,13 +83,15 @@ void EmitJumpTableInfo() override; void EmitConstantPool() override; void EmitFunctionBodyStart() override; + void EmitFunctionBodyEnd() override; void EmitInstruction(const MachineInstr *MI) override; + MVT getRegType(const MachineOperand &MO) const; static std::string toString(const APFloat &APF); const char *toString(Type *Ty) const; - std::string regToString(unsigned RegNo); - std::string argToString(unsigned ArgNo); + std::string regToString(const MachineOperand &MO); + std::string argToString(const MachineOperand &MO); }; } // end anonymous namespace @@ -90,19 +100,35 @@ // Helpers. //===----------------------------------------------------------------------===// -// Untyped, lower-case version of the opcode's name matching the names -// WebAssembly opcodes are expected to have. The tablegen names are uppercase -// and suffixed with their type (after an underscore). -static SmallString<32> OpcodeName(const WebAssemblyInstrInfo *TII, - const MachineInstr *MI) { +// Operand type (if any), followed by the lower-case version of the opcode's +// name matching the names WebAssembly opcodes are expected to have. The +// tablegen names are uppercase and suffixed with their type (after an +// underscore). +static std::string OpcodeName(const WebAssemblyInstrInfo *TII, + const MachineInstr *MI) { std::string N(StringRef(TII->getName(MI->getOpcode())).lower()); - std::string::size_type End = N.rfind('_'); - End = std::string::npos == End ? N.length() : End; - return SmallString<32>(&N[0], &N[End]); + std::string::size_type Len = N.length(); + std::string::size_type Under = N.rfind('_'); + bool HasType = std::string::npos != Under; + std::string::size_type NameEnd = HasType ? Under : Len; + std::string Name(&N[0], &N[NameEnd]); + return HasType ? (std::string(&N[NameEnd + 1], &N[Len]) + '.' + Name) : Name; } static std::string toSymbol(StringRef S) { return ("$" + S).str(); } +MVT WebAssemblyAsmPrinter::getRegType(const MachineOperand &MO) const { + unsigned RegNo = MO.getReg(); + const TargetRegisterClass *TRC = MRI->getRegClass(RegNo); + static const MVT Types[] = {MVT::i32, MVT::i64, MVT::f32, MVT::f64}; + for (MVT T : Types) + if (TRC->hasType(T)) + return T; + DEBUG(errs() << "Unknown type: " << MO); + llvm_unreachable("Unknown register type"); + return MVT::isVoid; +} + std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) { static const size_t BufBytes = 128; char buf[BufBytes]; @@ -120,19 +146,21 @@ return buf; } -std::string WebAssemblyAsmPrinter::regToString(unsigned RegNo) { +std::string WebAssemblyAsmPrinter::regToString(const MachineOperand &MO) { + unsigned RegNo = MO.getReg(); if (TargetRegisterInfo::isPhysicalRegister(RegNo)) return WebAssemblyInstPrinter::getRegisterName(RegNo); // WebAssembly arguments and local variables are in the same index space, and // there are no explicit varargs, so we just add the number of arguments to // the virtual register number to get the local variable number. - return '@' + utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs); + return utostr(TargetRegisterInfo::virtReg2Index(RegNo) + NumArgs); } -std::string WebAssemblyAsmPrinter::argToString(unsigned ArgNo) { +std::string WebAssemblyAsmPrinter::argToString(const MachineOperand &MO) { + unsigned ArgNo = MO.getImm(); // Same as above, but we don't need to add NumArgs here. - return '@' + utostr(ArgNo); + return utostr(ArgNo); } const char *WebAssemblyAsmPrinter::toString(Type *Ty) const { @@ -212,6 +240,27 @@ AsmPrinter::EmitFunctionBodyStart(); } +void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() { + if (!LocalTypes.empty()) { + std::sort(LocalTypes.begin(), LocalTypes.end(), + [](const IdxType &L, const IdxType &R) { return L.Idx < R.Idx; }); + DEBUG(for (size_t I = 0; I < LocalTypes.size(); ++I) assert( + LocalTypes[I].Idx == I && "each local should be declared once");); + SmallString<128> Str; + raw_svector_ostream OS(Str); + bool First = true; + OS << "\t.local "; + for (const auto &IT : LocalTypes) { + OS << (First ? "" : ", ") << EVT(IT.Type).getEVTString(); + First = false; + } + OS << '\n'; + OutStreamer->EmitRawText(OS.str()); + LocalTypes.clear(); + } + AsmPrinter::EmitFunctionBodyEnd(); +} + void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); SmallString<128> Str; @@ -225,7 +274,7 @@ switch (MI->getOpcode()) { case TargetOpcode::COPY: - OS << regToString(MI->getOperand(1).getReg()); + OS << "get_local " << regToString(MI->getOperand(1)); break; case WebAssembly::GLOBAL: // TODO: wasm64 @@ -235,7 +284,7 @@ case WebAssembly::ARGUMENT_I64: case WebAssembly::ARGUMENT_F32: case WebAssembly::ARGUMENT_F64: - OS << argToString(MI->getOperand(1).getImm()); + OS << "get_local " << argToString(MI->getOperand(1)); break; case WebAssembly::Immediate_I32: OS << "i32.const " << MI->getOperand(1).getImm(); @@ -263,7 +312,7 @@ default: llvm_unreachable("unexpected machine operand type"); case MachineOperand::MO_Register: - OS << regToString(MO.getReg()); + OS << "get_local " << regToString(MO); break; case MachineOperand::MO_Immediate: OS << MO.getImm(); @@ -288,10 +337,10 @@ if (NumDefs != 0) { SmallString<128> Str; raw_svector_ostream OS(Str); - OS << "\t" "set_local " - << regToString(MI->getOperand(0).getReg()) << ", " - "pop"; + const MachineOperand &Operand = MI->getOperand(0); + OS << "\tset_local " << regToString(Operand) << ", pop"; OutStreamer->EmitRawText(OS.str()); + LocalTypes.push_back({Operand.getReg(), getRegType(Operand)}); } } Index: test/CodeGen/WebAssembly/func.ll =================================================================== --- test/CodeGen/WebAssembly/func.ll +++ test/CodeGen/WebAssembly/func.ll @@ -15,8 +15,9 @@ ; CHECK-LABEL: f1: ; CHECK-NEXT: .result i32{{$}} ; CHECK-NEXT: i32.const 0{{$}} -; CHECK-NEXT: set_local @0, pop{{$}} -; CHECK-NEXT: return @0{{$}} +; CHECK-NEXT: set_local 0, pop{{$}} +; CHECK-NEXT: i32.return get_local 0{{$}} +; CHECK-NEXT: .local i32{{$}} ; CHECK: .size f1, define i32 @f1() { ret i32 0 @@ -27,8 +28,9 @@ ; CHECK-NEXT: .param f32{{$}} ; CHECK-NEXT: .result i32{{$}} ; CHECK-NEXT: i32.const 0{{$}} -; CHECK-NEXT: set_local @2, pop{{$}} -; CHECK-NEXT: return @2{{$}} +; CHECK-NEXT: set_local 2, pop{{$}} +; CHECK-NEXT: i32.return get_local 2{{$}} +; CHECK-NEXT: .local i32{{$}} ; CHECK: .size f2, define i32 @f2(i32 %p1, float %p2) { ret i32 0 @@ -37,7 +39,8 @@ ; CHECK-LABEL: f3: ; CHECK-NEXT: .param i32{{$}} ; CHECK-NEXT: .param f32{{$}} -; CHECK-NEXT: return{{$}} +; CHECK-NEXT: void.return{{$}} +; CHECK-NOT: .local ; CHECK: .size f3, define void @f3(i32 %p1, float %p2) { ret void