1 //===-- WebAssemblyAsmPrinter.cpp - WebAssembly LLVM assembly writer ------===// 2 // 3 // The LLVM Compiler Infrastructure 4 // 5 // This file is distributed under the University of Illinois Open Source 6 // License. See LICENSE.TXT for details. 7 // 8 //===----------------------------------------------------------------------===// 9 /// 10 /// \file 11 /// \brief This file contains a printer that converts from our internal 12 /// representation of machine-dependent LLVM code to the WebAssembly assembly 13 /// language. 14 /// 15 //===----------------------------------------------------------------------===// 16 17 #include "WebAssembly.h" 18 #include "WebAssemblyMachineFunctionInfo.h" 19 #include "WebAssemblyRegisterInfo.h" 20 #include "WebAssemblySubtarget.h" 21 #include "InstPrinter/WebAssemblyInstPrinter.h" 22 #include "MCTargetDesc/WebAssemblyMCTargetDesc.h" 23 24 #include "llvm/ADT/SmallString.h" 25 #include "llvm/CodeGen/AsmPrinter.h" 26 #include "llvm/CodeGen/MachineConstantPool.h" 27 #include "llvm/CodeGen/MachineInstr.h" 28 #include "llvm/IR/DataLayout.h" 29 #include "llvm/IR/DebugInfo.h" 30 #include "llvm/MC/MCStreamer.h" 31 #include "llvm/MC/MCSymbol.h" 32 #include "llvm/Support/Debug.h" 33 #include "llvm/Support/TargetRegistry.h" 34 #include "llvm/Support/raw_ostream.h" 35 36 using namespace llvm; 37 38 #define DEBUG_TYPE "asm-printer" 39 40 namespace { 41 42 class WebAssemblyAsmPrinter final : public AsmPrinter { 43 bool hasAddr64; 44 const WebAssemblyInstrInfo *TII; 45 46 public: 47 WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer) 48 : AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {} 49 50 private: 51 const char *getPassName() const override { 52 return "WebAssembly Assembly Printer"; 53 } 54 55 //===------------------------------------------------------------------===// 56 // MachineFunctionPass Implementation. 57 //===------------------------------------------------------------------===// 58 59 void getAnalysisUsage(AnalysisUsage &AU) const override { 60 AsmPrinter::getAnalysisUsage(AU); 61 } 62 63 bool runOnMachineFunction(MachineFunction &MF) override { 64 const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>(); 65 hasAddr64 = Subtarget.hasAddr64(); 66 TII = Subtarget.getInstrInfo(); 67 return AsmPrinter::runOnMachineFunction(MF); 68 } 69 70 //===------------------------------------------------------------------===// 71 // AsmPrinter Implementation. 72 //===------------------------------------------------------------------===// 73 74 void EmitGlobalVariable(const GlobalVariable *GV) override; 75 76 void EmitConstantPool() override; 77 void EmitFunctionEntryLabel() override; 78 void EmitFunctionBodyStart() override; 79 void EmitFunctionBodyEnd() override; 80 81 void EmitInstruction(const MachineInstr *MI) override; 82 }; 83 84 } // end anonymous namespace 85 86 //===----------------------------------------------------------------------===// 87 // Helpers. 88 //===----------------------------------------------------------------------===// 89 90 // Untyped, lower-case version of the opcode's name matching the names 91 // WebAssembly opcodes are expected to have. The tablegen names are uppercase 92 // and suffixed with their type (after an underscore). 93 static SmallString<32> OpcodeName(const WebAssemblyInstrInfo *TII, 94 const MachineInstr *MI) { 95 std::string N(StringRef(TII->getName(MI->getOpcode())).lower()); 96 std::string::size_type End = N.rfind('_'); 97 End = std::string::npos == End ? N.length() : End; 98 return SmallString<32>(&N[0], &N[End]); 99 } 100 101 static std::string toSymbol(StringRef S) { return ("$" + S).str(); } 102 103 static std::string toString(const APFloat &FP) { 104 static const size_t BufBytes = 128; 105 char buf[BufBytes]; 106 if (FP.isNaN()) 107 assert((FP.bitwiseIsEqual(APFloat::getQNaN(FP.getSemantics())) || 108 FP.bitwiseIsEqual( 109 APFloat::getQNaN(FP.getSemantics(), /*Negative=*/true))) && 110 "convertToHexString handles neither SNaN nor NaN payloads"); 111 // Use C99's hexadecimal floating-point representation. 112 auto Written = FP.convertToHexString( 113 buf, /*hexDigits=*/0, /*upperCase=*/false, APFloat::rmNearestTiesToEven); 114 (void)Written; 115 assert(Written != 0); 116 assert(Written < BufBytes); 117 return buf; 118 } 119 120 static const char *toString(const Type *Ty, bool hasAddr64) { 121 switch (Ty->getTypeID()) { 122 default: break; 123 // Treat all pointers as the underlying integer into linear memory. 124 case Type::PointerTyID: return hasAddr64 ? "i64" : "i32"; 125 case Type::FloatTyID: return "f32"; 126 case Type::DoubleTyID: return "f64"; 127 case Type::IntegerTyID: 128 switch (Ty->getIntegerBitWidth()) { 129 case 8: return "i8"; 130 case 16: return "i16"; 131 case 32: return "i32"; 132 case 64: return "i64"; 133 default: break; 134 } 135 } 136 DEBUG(dbgs() << "Invalid type "; Ty->print(dbgs()); dbgs() << '\n'); 137 llvm_unreachable("invalid type"); 138 return "<invalid>"; 139 } 140 141 142 //===----------------------------------------------------------------------===// 143 // WebAssemblyAsmPrinter Implementation. 144 //===----------------------------------------------------------------------===// 145 146 void WebAssemblyAsmPrinter::EmitGlobalVariable(const GlobalVariable *GV) { 147 SmallString<128> Str; 148 raw_svector_ostream OS(Str); 149 StringRef Name = GV->getName(); 150 DEBUG(dbgs() << "Global " << Name << '\n'); 151 152 if (!GV->hasInitializer()) { 153 DEBUG(dbgs() << " Skipping declaration.\n"); 154 return; 155 } 156 157 // Check to see if this is a special global used by LLVM. 158 static const char *Ignored[] = {"llvm.used", "llvm.metadata"}; 159 for (const char *I : Ignored) 160 if (Name == I) 161 return; 162 // FIXME: Handle the following globals. 163 static const char *Unhandled[] = {"llvm.global_ctors", "llvm.global_dtors"}; 164 for (const char *U : Unhandled) 165 if (Name == U) 166 report_fatal_error("Unhandled global"); 167 if (Name.startswith("llvm.")) 168 report_fatal_error("Unknown LLVM-internal global"); 169 170 if (GV->isThreadLocal()) 171 report_fatal_error("TLS isn't yet supported by WebAssembly"); 172 173 const DataLayout &DL = getDataLayout(); 174 const Constant *Init = GV->getInitializer(); 175 if (isa<UndefValue>(Init)) 176 Init = Constant::getNullValue(Init->getType()); 177 unsigned Align = DL.getPrefTypeAlignment(Init->getType()); 178 179 switch (GV->getLinkage()) { 180 case GlobalValue::InternalLinkage: 181 case GlobalValue::PrivateLinkage: 182 break; 183 case GlobalValue::AppendingLinkage: 184 case GlobalValue::LinkOnceAnyLinkage: 185 case GlobalValue::LinkOnceODRLinkage: 186 case GlobalValue::WeakAnyLinkage: 187 case GlobalValue::WeakODRLinkage: 188 case GlobalValue::ExternalLinkage: 189 case GlobalValue::CommonLinkage: 190 report_fatal_error("Linkage types other than internal and private aren't " 191 "supported by WebAssembly yet"); 192 default: 193 llvm_unreachable("Unknown linkage type"); 194 return; 195 } 196 197 OS << "(global " << toSymbol(Name) << ' ' 198 << toString(Init->getType(), hasAddr64) << ' '; 199 if (const auto *C = dyn_cast<ConstantInt>(Init)) { 200 assert(C->getBitWidth() <= 64 && "Printing wider types unimplemented"); 201 OS << C->getZExtValue(); 202 } else if (const auto *C = dyn_cast<ConstantFP>(Init)) { 203 OS << toString(C->getValueAPF()); 204 } else { 205 assert(false && "Only integer and floating-point constants are supported"); 206 } 207 OS << ") ;; align " << Align; 208 OutStreamer->EmitRawText(OS.str()); 209 } 210 211 void WebAssemblyAsmPrinter::EmitConstantPool() { 212 assert(MF->getConstantPool()->getConstants().empty() && 213 "WebAssembly disables constant pools"); 214 } 215 216 void WebAssemblyAsmPrinter::EmitFunctionEntryLabel() { 217 SmallString<128> Str; 218 raw_svector_ostream OS(Str); 219 220 CurrentFnSym->redefineIfPossible(); 221 222 // The function label could have already been emitted if two symbols end up 223 // conflicting due to asm renaming. Detect this and emit an error. 224 if (CurrentFnSym->isVariable()) 225 report_fatal_error("'" + Twine(CurrentFnSym->getName()) + 226 "' is a protected alias"); 227 if (CurrentFnSym->isDefined()) 228 report_fatal_error("'" + Twine(CurrentFnSym->getName()) + 229 "' label emitted multiple times to assembly file"); 230 231 OS << "(func " << toSymbol(CurrentFnSym->getName()); 232 OutStreamer->EmitRawText(OS.str()); 233 } 234 235 void WebAssemblyAsmPrinter::EmitFunctionBodyStart() { 236 SmallString<128> Str; 237 raw_svector_ostream OS(Str); 238 const Function *F = MF->getFunction(); 239 const Type *Rt = F->getReturnType(); 240 if (!Rt->isVoidTy() || !F->arg_empty()) { 241 for (const Argument &A : F->args()) 242 OS << " (param " << toString(A.getType(), hasAddr64) << ')'; 243 if (!Rt->isVoidTy()) 244 OS << " (result " << toString(Rt, hasAddr64) << ')'; 245 OutStreamer->EmitRawText(OS.str()); 246 } 247 } 248 249 void WebAssemblyAsmPrinter::EmitFunctionBodyEnd() { 250 SmallString<128> Str; 251 raw_svector_ostream OS(Str); 252 OS << ") ;; end func " << toSymbol(CurrentFnSym->getName()); 253 OutStreamer->EmitRawText(OS.str()); 254 } 255 256 void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) { 257 DEBUG(dbgs() << "EmitInstruction: " << *MI << '\n'); 258 SmallString<128> Str; 259 raw_svector_ostream OS(Str); 260 261 unsigned NumDefs = MI->getDesc().getNumDefs(); 262 assert(NumDefs <= 1 && 263 "Instructions with multiple result values not implemented"); 264 265 OS << '\t'; 266 267 if (NumDefs != 0) { 268 const MachineOperand &MO = MI->getOperand(0); 269 unsigned Reg = MO.getReg(); 270 OS << "(setlocal @" << TargetRegisterInfo::virtReg2Index(Reg) << ' '; 271 } 272 273 if (MI->getOpcode() == WebAssembly::COPY) { 274 OS << '@' << TargetRegisterInfo::virtReg2Index(MI->getOperand(1).getReg()); 275 } else { 276 OS << '(' << OpcodeName(TII, MI); 277 for (const MachineOperand &MO : MI->uses()) 278 switch (MO.getType()) { 279 default: 280 llvm_unreachable("unexpected machine operand type"); 281 case MachineOperand::MO_Register: { 282 if (MO.isImplicit()) 283 continue; 284 unsigned Reg = MO.getReg(); 285 OS << " @" << TargetRegisterInfo::virtReg2Index(Reg); 286 } break; 287 case MachineOperand::MO_Immediate: { 288 OS << ' ' << MO.getImm(); 289 } break; 290 case MachineOperand::MO_FPImmediate: { 291 OS << ' ' << toString(MO.getFPImm()->getValueAPF()); 292 } break; 293 case MachineOperand::MO_GlobalAddress: { 294 OS << ' ' << toSymbol(MO.getGlobal()->getName()); 295 } break; 296 } 297 OS << ')'; 298 } 299 300 if (NumDefs != 0) 301 OS << ')'; 302 303 OutStreamer->EmitRawText(OS.str()); 304 } 305 306 // Force static initialization. 307 extern "C" void LLVMInitializeWebAssemblyAsmPrinter() { 308 RegisterAsmPrinter<WebAssemblyAsmPrinter> X(TheWebAssemblyTarget32); 309 RegisterAsmPrinter<WebAssemblyAsmPrinter> Y(TheWebAssemblyTarget64); 310 } 311