Skip to content

Commit 16b6436

Browse files
committed
WASM_X86: Define wasm_to_x86 backend
1 parent d72728b commit 16b6436

File tree

3 files changed

+287
-0
lines changed

3 files changed

+287
-0
lines changed

src/libasr/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -23,6 +23,7 @@ set(SRC
2323
codegen/asr_to_x86.cpp
2424
codegen/asr_to_wasm.cpp
2525
codegen/wasm_to_wat.cpp
26+
codegen/wasm_to_x86.cpp
2627
codegen/wasm_utils.cpp
2728

2829
pass/param_to_const.cpp

src/libasr/codegen/wasm_to_x86.cpp

Lines changed: 135 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,135 @@
1+
#include <fstream>
2+
#include <chrono>
3+
#include <iomanip>
4+
5+
#include <libasr/assert.h>
6+
#include <libasr/codegen/wasm_decoder.h>
7+
#include <libasr/codegen/wasm_to_x86.h>
8+
#include <libasr/codegen/x86_assembler.h>
9+
10+
namespace LFortran {
11+
12+
namespace wasm {
13+
14+
class X86Generator : public WASMDecoder<X86Generator> {
15+
public:
16+
X86Generator(Allocator &al, diag::Diagnostics &diagonostics)
17+
: WASMDecoder(al, diagonostics) {}
18+
19+
void gen_x86_bytes(X86Assembler &m_a) {
20+
emit_elf32_header(m_a);
21+
22+
// Add runtime library functions
23+
emit_print_int(m_a, "print_i32");
24+
emit_exit(m_a, "exit", 0);
25+
26+
for (uint32_t i = 0; i < type_indices.size(); i++) {
27+
if (i < type_indices.size() - 1U) {
28+
m_a.add_label(exports[i].name);
29+
} else {
30+
m_a.add_label("_start");
31+
}
32+
33+
{
34+
// Initialize the stack
35+
m_a.asm_push_r32(X86Reg::ebp);
36+
m_a.asm_mov_r32_r32(X86Reg::ebp, X86Reg::esp);
37+
38+
// Initialze space for local variables to zero
39+
m_a.asm_mov_r32_imm32(X86Reg::eax, 0U);
40+
for (uint32_t j = 0; j < codes.p[i].locals.size(); j++) {
41+
for (uint32_t k = 0; k < codes.p[i].locals.p[j].count;
42+
k++) {
43+
m_a.asm_push_r32(X86Reg::eax);
44+
}
45+
}
46+
47+
WASM_INSTS_VISITOR::X86Visitor v =
48+
WASM_INSTS_VISITOR::X86Visitor(
49+
wasm_bytes, codes.p[i].insts_start_index, m_a,
50+
func_types, imports, type_indices, exports, codes,
51+
data_segments, i);
52+
53+
v.decode_instructions();
54+
55+
// Restore stack
56+
m_a.asm_mov_r32_r32(X86Reg::esp, X86Reg::ebp);
57+
m_a.asm_pop_r32(X86Reg::ebp);
58+
m_a.asm_ret();
59+
}
60+
}
61+
62+
emit_elf32_footer(m_a);
63+
}
64+
};
65+
66+
} // namespace wasm
67+
68+
Result<int> wasm_to_x86(Vec<uint8_t> &wasm_bytes, Allocator &al,
69+
const std::string &filename, bool time_report,
70+
diag::Diagnostics &diagnostics) {
71+
int time_decode_wasm = 0;
72+
int time_gen_x86_bytes = 0;
73+
int time_save = 0;
74+
int time_verify = 0;
75+
76+
X86Assembler m_a(al);
77+
78+
wasm::X86Generator x86_generator(al, diagnostics);
79+
x86_generator.wasm_bytes.from_pointer_n(wasm_bytes.data(),
80+
wasm_bytes.size());
81+
82+
{
83+
auto t1 = std::chrono::high_resolution_clock::now();
84+
try {
85+
x86_generator.decode_wasm();
86+
} catch (const CodeGenError &e) {
87+
diagnostics.diagnostics.push_back(e.d);
88+
return Error();
89+
}
90+
auto t2 = std::chrono::high_resolution_clock::now();
91+
time_decode_wasm =
92+
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)
93+
.count();
94+
}
95+
96+
{
97+
auto t1 = std::chrono::high_resolution_clock::now();
98+
x86_generator.gen_x86_bytes(m_a);
99+
auto t2 = std::chrono::high_resolution_clock::now();
100+
time_gen_x86_bytes =
101+
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)
102+
.count();
103+
}
104+
105+
{
106+
auto t1 = std::chrono::high_resolution_clock::now();
107+
m_a.verify();
108+
auto t2 = std::chrono::high_resolution_clock::now();
109+
time_verify = std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count();
110+
}
111+
112+
{
113+
auto t1 = std::chrono::high_resolution_clock::now();
114+
m_a.save_binary(filename);
115+
auto t2 = std::chrono::high_resolution_clock::now();
116+
time_save =
117+
std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1)
118+
.count();
119+
}
120+
121+
if (time_report) {
122+
std::cout << "Codegen Time report:" << std::endl;
123+
std::cout << "Decode wasm: " << std::setw(5) << time_decode_wasm
124+
<< std::endl;
125+
std::cout << "Generate asm: " << std::setw(5) << time_gen_x86_bytes
126+
<< std::endl;
127+
std::cout << "Verify: " << std::setw(5) << time_verify << std::endl;
128+
std::cout << "Save: " << std::setw(5) << time_save << std::endl;
129+
int total = time_decode_wasm + time_gen_x86_bytes + time_verify + time_save;
130+
std::cout << "Total: " << std::setw(5) << total << std::endl;
131+
}
132+
return 0;
133+
}
134+
135+
} // namespace LFortran

src/libasr/codegen/wasm_to_x86.h

Lines changed: 151 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,151 @@
1+
#ifndef LFORTRAN_WASM_TO_X86_H
2+
#define LFORTRAN_WASM_TO_X86_H
3+
4+
#include <libasr/wasm_visitor.h>
5+
#include <libasr/codegen/x86_assembler.h>
6+
7+
namespace LFortran {
8+
9+
namespace WASM_INSTS_VISITOR {
10+
11+
class X86Visitor : public BaseWASMVisitor<X86Visitor> {
12+
public:
13+
X86Assembler &m_a;
14+
Vec<wasm::FuncType> func_types;
15+
Vec<wasm::Import> imports;
16+
Vec<uint32_t> type_indices;
17+
Vec<wasm::Export> exports;
18+
Vec<wasm::Code> func_codes;
19+
Vec<wasm::Data> data_segments;
20+
uint32_t cur_func_idx;
21+
22+
X86Visitor(Vec<uint8_t> &code, uint32_t offset, X86Assembler &m_a,
23+
Vec<wasm::FuncType> &func_types, Vec<wasm::Import> &imports,
24+
Vec<uint32_t> &type_indices, Vec<wasm::Export> &exports,
25+
Vec<wasm::Code> &func_codes, Vec<wasm::Data> &data_segments,
26+
uint32_t cur_func_idx)
27+
: BaseWASMVisitor(code, offset), m_a(m_a) {
28+
this->func_types.from_pointer_n(func_types.p, func_types.size());
29+
this->imports.from_pointer_n(imports.p, imports.size());
30+
this->type_indices.from_pointer_n(type_indices.p, type_indices.size());
31+
this->exports.from_pointer_n(exports.p, exports.size());
32+
this->func_codes.from_pointer_n(func_codes.p, func_codes.size());
33+
this->data_segments.from_pointer_n(data_segments.p,
34+
data_segments.size());
35+
this->cur_func_idx = cur_func_idx;
36+
}
37+
38+
void visit_Return() {}
39+
40+
void visit_Call(uint32_t func_index) {
41+
if (func_index <= 6U) {
42+
// call to imported functions
43+
if (func_index == 0) {
44+
m_a.asm_call_label("print_i32");
45+
} else if (func_index == 5) {
46+
// currently ignoring print_buf
47+
} else if (func_index == 6) {
48+
m_a.asm_call_label("exit");
49+
} else {
50+
std::cerr << "Call to imported function with index " +
51+
std::to_string(func_index) +
52+
" not yet supported";
53+
}
54+
} else {
55+
m_a.asm_call_label(exports[func_index - 7].name);
56+
}
57+
58+
// Pop the passed function arguments
59+
wasm::FuncType func_type = func_types[type_indices[func_index]];
60+
for (uint32_t i = 0; i < func_type.param_types.size(); i++) {
61+
m_a.asm_pop_r32(X86Reg::eax);
62+
}
63+
64+
// Adjust the return values of the called function
65+
X86Reg base = X86Reg::esp;
66+
for (uint32_t i = 0; i < func_type.result_types.size(); i++) {
67+
// take value into eax
68+
m_a.asm_mov_r32_m32(
69+
X86Reg::eax, &base, nullptr, 1,
70+
-(4 * (func_type.param_types.size() + 2 +
71+
func_codes[func_index].locals.size() + 1)));
72+
73+
// push eax value onto stack
74+
m_a.asm_push_r32(X86Reg::eax);
75+
}
76+
}
77+
78+
void visit_LocalGet(uint32_t localidx) {
79+
X86Reg base = X86Reg::ebp;
80+
int no_of_params =
81+
(int)func_types[type_indices[cur_func_idx]].param_types.size();
82+
if ((int)localidx < no_of_params) {
83+
m_a.asm_mov_r32_m32(X86Reg::eax, &base, nullptr, 1,
84+
(4 * localidx + 8));
85+
m_a.asm_push_r32(X86Reg::eax);
86+
} else {
87+
m_a.asm_mov_r32_m32(X86Reg::eax, &base, nullptr, 1,
88+
-(4 * ((int)localidx - no_of_params + 1)));
89+
m_a.asm_push_r32(X86Reg::eax);
90+
}
91+
}
92+
void visit_LocalSet(uint32_t localidx) {
93+
X86Reg base = X86Reg::ebp;
94+
int no_of_params =
95+
(int)func_types[type_indices[cur_func_idx]].param_types.size();
96+
if ((int)localidx < no_of_params) {
97+
m_a.asm_pop_r32(X86Reg::eax);
98+
m_a.asm_mov_m32_r32(&base, nullptr, 1, (4 * localidx + 8),
99+
X86Reg::eax);
100+
} else {
101+
m_a.asm_pop_r32(X86Reg::eax);
102+
m_a.asm_mov_m32_r32(&base, nullptr, 1,
103+
-(4 * ((int)localidx - no_of_params + 1)),
104+
X86Reg::eax);
105+
}
106+
}
107+
108+
void visit_I32Const(int32_t value) {
109+
m_a.asm_push_imm32(value);
110+
// if (value < 0) {
111+
// m_a.asm_pop_r32(X86Reg::eax);
112+
// m_a.asm_neg_r32(X86Reg::eax);
113+
// m_a.asm_push_r32(X86Reg::eax);
114+
// }
115+
}
116+
117+
void visit_I32Add() {
118+
m_a.asm_pop_r32(X86Reg::ebx);
119+
m_a.asm_pop_r32(X86Reg::eax);
120+
m_a.asm_add_r32_r32(X86Reg::eax, X86Reg::ebx);
121+
m_a.asm_push_r32(X86Reg::eax);
122+
}
123+
void visit_I32Sub() {
124+
m_a.asm_pop_r32(X86Reg::ebx);
125+
m_a.asm_pop_r32(X86Reg::eax);
126+
m_a.asm_sub_r32_r32(X86Reg::eax, X86Reg::ebx);
127+
m_a.asm_push_r32(X86Reg::eax);
128+
}
129+
void visit_I32Mul() {
130+
m_a.asm_pop_r32(X86Reg::ebx);
131+
m_a.asm_pop_r32(X86Reg::eax);
132+
m_a.asm_mul_r32(X86Reg::ebx);
133+
m_a.asm_push_r32(X86Reg::eax);
134+
}
135+
void visit_I32DivS() {
136+
m_a.asm_pop_r32(X86Reg::ebx);
137+
m_a.asm_pop_r32(X86Reg::eax);
138+
m_a.asm_div_r32(X86Reg::ebx);
139+
m_a.asm_push_r32(X86Reg::eax);
140+
}
141+
};
142+
143+
} // namespace WASM_INSTS_VISITOR
144+
145+
Result<int> wasm_to_x86(Vec<uint8_t> &wasm_bytes, Allocator &al,
146+
const std::string &filename, bool time_report,
147+
diag::Diagnostics &diagnostics);
148+
149+
} // namespace LFortran
150+
151+
#endif // LFORTRAN_WASM_TO_X86_H

0 commit comments

Comments
 (0)