Skip to content

Commit

Permalink
WebAssembly: update syntax
Browse files Browse the repository at this point in the history
Summary:
Follow the same syntax as for the spec repo. Both have evolved slightly
independently and need to converge again.

This, along with wasmate changes, allows me to do the following:

  echo "int add(int a, int b) { return a + b; }" > add.c
  ./out/bin/clang -O2 -S --target=wasm32-unknown-unknown add.c -o add.wack
  ./experimental/prototype-wasmate/wasmate.py add.wack > add.wast
  ./sexpr-wasm-prototype/out/sexpr-wasm add.wast -o add.wasm
  ./sexpr-wasm-prototype/third_party/v8-native-prototype/v8/v8/out/Release/d8 -e "print(WASM.instantiateModule(readbuffer('add.wasm'), {print:print}).add(42, 1337));"

As you'd expect, the d8 shell prints out the right value.

Reviewers: sunfish

Subscribers: jfb, llvm-commits, dschuff

Differential Revision: http://reviews.llvm.org/D13712

llvm-svn: 250480
  • Loading branch information
jfbastien committed Oct 16, 2015
1 parent ad5e0ce commit 1d20a5e
Show file tree
Hide file tree
Showing 23 changed files with 973 additions and 825 deletions.
90 changes: 59 additions & 31 deletions llvm/lib/Target/WebAssembly/WebAssemblyAsmPrinter.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -42,11 +42,12 @@ namespace {

class WebAssemblyAsmPrinter final : public AsmPrinter {
const WebAssemblyInstrInfo *TII;
const MachineRegisterInfo *MRI;
unsigned NumArgs;

public:
WebAssemblyAsmPrinter(TargetMachine &TM, std::unique_ptr<MCStreamer> Streamer)
: AsmPrinter(TM, std::move(Streamer)), TII(nullptr) {}
: AsmPrinter(TM, std::move(Streamer)), TII(nullptr), MRI(nullptr) {}

private:
const char *getPassName() const override {
Expand All @@ -64,6 +65,7 @@ class WebAssemblyAsmPrinter final : public AsmPrinter {
bool runOnMachineFunction(MachineFunction &MF) override {
const auto &Subtarget = MF.getSubtarget<WebAssemblySubtarget>();
TII = Subtarget.getInstrInfo();
MRI = &MF.getRegInfo();
NumArgs = MF.getInfo<WebAssemblyFunctionInfo>()->getNumArguments();
return AsmPrinter::runOnMachineFunction(MF);
}
Expand All @@ -78,10 +80,11 @@ class WebAssemblyAsmPrinter final : public AsmPrinter {

void EmitInstruction(const MachineInstr *MI) override;

std::string getRegTypeName(unsigned RegNo) 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
Expand All @@ -90,19 +93,33 @@ class WebAssemblyAsmPrinter final : public AsmPrinter {
// 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(); }

std::string WebAssemblyAsmPrinter::getRegTypeName(unsigned RegNo) const {
const TargetRegisterClass *TRC = MRI->getRegClass(RegNo);
for (MVT T : {MVT::i32, MVT::i64, MVT::f32, MVT::f64})
if (TRC->hasType(T))
return EVT(T).getEVTString();
DEBUG(errs() << "Unknown type for register number: " << RegNo);
llvm_unreachable("Unknown register type");
return "?";
}

std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
static const size_t BufBytes = 128;
char buf[BufBytes];
Expand All @@ -120,19 +137,21 @@ std::string WebAssemblyAsmPrinter::toString(const APFloat &FP) {
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 {
Expand Down Expand Up @@ -189,26 +208,36 @@ void WebAssemblyAsmPrinter::EmitJumpTableInfo() {
void WebAssemblyAsmPrinter::EmitFunctionBodyStart() {
const Function *F = MF->getFunction();
Type *Rt = F->getReturnType();
SmallString<128> Str;
raw_svector_ostream OS(Str);
bool First = true;

if (!Rt->isVoidTy() || !F->arg_empty()) {
SmallString<128> Str;
raw_svector_ostream OS(Str);
bool First = true;
for (const Argument &A : F->args()) {
OS << (First ? "" : "\n") << "\t"
".param "
<< toString(A.getType());
OS << (First ? "" : "\n") << "\t.param " << toString(A.getType());
First = false;
}
if (!Rt->isVoidTy()) {
OS << (First ? "" : "\n") << "\t"
".result "
<< toString(Rt);
OS << (First ? "" : "\n") << "\t.result " << toString(Rt);
First = false;
}
OutStreamer->EmitRawText(OS.str());
}

bool FirstVReg = true;
for (unsigned Idx = 0, IdxE = MRI->getNumVirtRegs(); Idx != IdxE; ++Idx) {
unsigned VReg = TargetRegisterInfo::index2VirtReg(Idx);
if (!MRI->use_empty(VReg)) {
if (FirstVReg) {
OS << (First ? "" : "\n") << "\t.local ";
First = false;
}
OS << (FirstVReg ? "" : ", ") << getRegTypeName(VReg);
FirstVReg = false;
}
}

if (!First)
OutStreamer->EmitRawText(OS.str());
AsmPrinter::EmitFunctionBodyStart();
}

Expand All @@ -225,7 +254,7 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {

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
Expand All @@ -235,7 +264,7 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
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();
Expand Down Expand Up @@ -263,7 +292,7 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
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();
Expand All @@ -288,9 +317,8 @@ void WebAssemblyAsmPrinter::EmitInstruction(const MachineInstr *MI) {
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());
}
}
Expand Down
64 changes: 36 additions & 28 deletions llvm/test/CodeGen/WebAssembly/call.ll
Original file line number Diff line number Diff line change
Expand Up @@ -15,39 +15,43 @@ declare void @void_nullary()

; CHECK-LABEL: call_i32_nullary:
; CHECK-NEXT: .result i32
; CHECK-NEXT: .local i32
; CHECK-NEXT: call $i32_nullary
; CHECK-NEXT: set_local @0, pop
; CHECK-NEXT: return @0
; CHECK-NEXT: set_local 0, pop
; CHECK-NEXT: return (get_local 0)
define i32 @call_i32_nullary() {
%r = call i32 @i32_nullary()
ret i32 %r
}

; CHECK-LABEL: call_i64_nullary:
; CHECK-NEXT: .result i64
; CHECK-NEXT: .local i64
; CHECK-NEXT: call $i64_nullary
; CHECK-NEXT: set_local @0, pop
; CHECK-NEXT: return @0
; CHECK-NEXT: set_local 0, pop
; CHECK-NEXT: return (get_local 0)
define i64 @call_i64_nullary() {
%r = call i64 @i64_nullary()
ret i64 %r
}

; CHECK-LABEL: call_float_nullary:
; CHECK-NEXT: .result f32
; CHECK-NEXT: .local f32
; CHECK-NEXT: call $float_nullary
; CHECK-NEXT: set_local @0, pop
; CHECK-NEXT: return @0
; CHECK-NEXT: set_local 0, pop
; CHECK-NEXT: return (get_local 0)
define float @call_float_nullary() {
%r = call float @float_nullary()
ret float %r
}

; CHECK-LABEL: call_double_nullary:
; CHECK-NEXT: .result f64
; CHECK-NEXT: .local f64
; CHECK-NEXT: call $double_nullary
; CHECK-NEXT: set_local @0, pop
; CHECK-NEXT: return @0
; CHECK-NEXT: set_local 0, pop
; CHECK-NEXT: return (get_local 0)
define double @call_double_nullary() {
%r = call double @double_nullary()
ret double %r
Expand All @@ -64,11 +68,12 @@ define void @call_void_nullary() {
; CHECK-LABEL: call_i32_unary:
; CHECK-NEXT: .param i32
; CHECK-NEXT: .result i32
; CHECK-NEXT: @0
; CHECK-NEXT: set_local @1, pop
; CHECK-NEXT: call $i32_unary, @1
; CHECK-NEXT: set_local @2, pop
; CHECK-NEXT: return @2
; CHECK-NEXT: .local i32, i32
; CHECK-NEXT: get_local 0
; CHECK-NEXT: set_local 1, pop
; CHECK-NEXT: call $i32_unary, (get_local 1)
; CHECK-NEXT: set_local 2, pop
; CHECK-NEXT: return (get_local 2)
define i32 @call_i32_unary(i32 %a) {
%r = call i32 @i32_unary(i32 %a)
ret i32 %r
Expand All @@ -78,23 +83,25 @@ define i32 @call_i32_unary(i32 %a) {
; CHECK-NEXT: .param i32
; CHECK-NEXT: .param i32
; CHECK-NEXT: .result i32
; CHECK-NEXT: @1
; CHECK-NEXT: set_local @2, pop
; CHECK-NEXT: @0
; CHECK-NEXT: set_local @3, pop
; CHECK-NEXT: call $i32_binary, @3, @2
; CHECK-NEXT: set_local @4, pop
; CHECK-NEXT: return @4
; CHECK-NEXT: .local i32, i32, i32
; CHECK-NEXT: get_local 1
; CHECK-NEXT: set_local 2, pop
; CHECK-NEXT: get_local 0
; CHECK-NEXT: set_local 3, pop
; CHECK-NEXT: call $i32_binary, (get_local 3), (get_local 2)
; CHECK-NEXT: set_local 4, pop
; CHECK-NEXT: return (get_local 4)
define i32 @call_i32_binary(i32 %a, i32 %b) {
%r = call i32 @i32_binary(i32 %a, i32 %b)
ret i32 %r
}

; CHECK-LABEL: call_indirect_void:
; CHECK-NEXT: .param i32
; CHECK-NEXT: @0
; CHECK-NEXT: set_local @1, pop
; CHECK-NEXT: call_indirect @1
; CHECK-NEXT: .local i32
; CHECK-NEXT: get_local 0
; CHECK-NEXT: set_local 1, pop
; CHECK-NEXT: call_indirect (get_local 1)
; CHECK-NEXT: return
define void @call_indirect_void(void ()* %callee) {
call void %callee()
Expand All @@ -104,11 +111,12 @@ define void @call_indirect_void(void ()* %callee) {
; CHECK-LABEL: call_indirect_i32:
; CHECK-NEXT: .param i32
; CHECK-NEXT: .result i32
; CHECK-NEXT: @0
; CHECK-NEXT: set_local @1, pop
; CHECK-NEXT: call_indirect @1
; CHECK-NEXT: set_local @2, pop
; CHECK-NEXT: return @2
; CHECK-NEXT: .local i32, i32
; CHECK-NEXT: get_local 0
; CHECK-NEXT: set_local 1, pop
; CHECK-NEXT: call_indirect (get_local 1)
; CHECK-NEXT: set_local 2, pop
; CHECK-NEXT: return (get_local 2)
define i32 @call_indirect_i32(i32 ()* %callee) {
%t = call i32 %callee()
ret i32 %t
Expand Down
Loading

0 comments on commit 1d20a5e

Please sign in to comment.