Skip to content

Commit 05b4864

Browse files
authored
Merge pull request #1549 from Shaikh-Ubaid/wasm_x64_globals3
WASM_X64: Support for globals
2 parents 069b613 + e209b01 commit 05b4864

File tree

4 files changed

+178
-116
lines changed

4 files changed

+178
-116
lines changed

integration_tests/CMakeLists.txt

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -346,7 +346,7 @@ RUN(NAME bindc_06 LABELS llvm c
346346
EXTRAFILES bindc_06b.c)
347347
RUN(NAME test_generics_01 LABELS cpython llvm c)
348348
RUN(NAME test_cmath LABELS cpython llvm c)
349-
RUN(NAME test_complex_01 LABELS cpython llvm c wasm)
349+
RUN(NAME test_complex_01 LABELS cpython llvm c wasm wasm_x64)
350350
RUN(NAME test_complex_02 LABELS cpython llvm c)
351351
RUN(NAME test_max_min LABELS cpython llvm c)
352352
RUN(NAME test_global LABELS cpython llvm c)

src/libasr/codegen/wasm_to_x64.cpp

Lines changed: 146 additions & 115 deletions
Original file line numberDiff line numberDiff line change
@@ -60,9 +60,7 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
6060
}
6161

6262
void visit_Unreachable() {}
63-
6463
void visit_EmtpyBlockType() {}
65-
6664
void visit_Drop() { m_a.asm_pop_r64(X64Reg::rax); }
6765

6866
void call_imported_function(uint32_t func_idx) {
@@ -210,19 +208,58 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
210208
m_a.add_label(".else_" + label);
211209
}
212210

211+
void visit_GlobalGet(uint32_t globalidx) {
212+
std::string loc = "global_" + std::to_string(globalidx);
213+
std::string var_type = var_type_to_string[globals[globalidx].type];
214+
215+
X64Reg base = X64Reg::rbx;
216+
m_a.asm_mov_r64_label(X64Reg::rbx, loc);
217+
if (var_type == "i32" || var_type == "i64") {
218+
m_a.asm_mov_r64_m64(X64Reg::rax, &base, nullptr, 1, 0);
219+
m_a.asm_push_r64(X64Reg::rax);
220+
} else if (var_type == "f32" || var_type == "f64") {
221+
m_a.asm_movsd_r64_m64(X64FReg::xmm0, &base, nullptr, 1, 0);
222+
m_a.asm_sub_r64_imm32(X64Reg::rsp, 8); // create space for value to be fetched
223+
X64Reg stack_top = X64Reg::rsp;
224+
m_a.asm_movsd_m64_r64(&stack_top, nullptr, 1, 0, X64FReg::xmm0);
225+
} else {
226+
throw AssemblerError("WASM_X64: Var type not supported");
227+
}
228+
}
229+
230+
void visit_GlobalSet(uint32_t globalidx) {
231+
if (globals[globalidx].mut == 0) {
232+
throw AssemblerError("Attempt to modify unmutable global variable");
233+
}
234+
235+
std::string loc = "global_" + std::to_string(globalidx);
236+
std::string var_type = var_type_to_string[globals[globalidx].type];
237+
238+
X64Reg base = X64Reg::rbx;
239+
m_a.asm_mov_r64_label(X64Reg::rbx, loc);
240+
if (var_type == "i32" || var_type == "i64") {
241+
m_a.asm_pop_r64(X64Reg::rax);
242+
m_a.asm_mov_m64_r64(&base, nullptr, 1, 0, X64Reg::rax);
243+
} else if (var_type == "f32" || var_type == "f64") {
244+
X64Reg stack_top = X64Reg::rsp;
245+
m_a.asm_movsd_r64_m64(X64FReg::xmm0, &stack_top, nullptr, 1, 0);
246+
m_a.asm_add_r64_imm32(X64Reg::rsp, 8); // deallocate space
247+
m_a.asm_movsd_m64_r64(&base, nullptr, 1, 0, X64FReg::xmm0);
248+
} else {
249+
throw AssemblerError("WASM_X64: Var type not supported");
250+
}
251+
}
252+
213253
void visit_LocalGet(uint32_t localidx) {
214254
X64Reg base = X64Reg::rbp;
215255
auto cur_func_param_type = func_types[type_indices[cur_func_idx]];
216256
int no_of_params = (int)cur_func_param_type.param_types.size();
217257
if ((int)localidx < no_of_params) {
218258
std::string var_type = var_type_to_string[cur_func_param_type.param_types[localidx]];
219-
if (var_type == "i32") {
220-
m_a.asm_mov_r64_m64(X64Reg::rax, &base, nullptr, 1, 8 * (2 + no_of_params - (int)localidx - 1));
221-
m_a.asm_push_r64(X64Reg::rax);
222-
} else if (var_type == "i64") {
259+
if (var_type == "i32" || var_type == "i64") {
223260
m_a.asm_mov_r64_m64(X64Reg::rax, &base, nullptr, 1, 8 * (2 + no_of_params - (int)localidx - 1));
224261
m_a.asm_push_r64(X64Reg::rax);
225-
} else if (var_type == "f64") {
262+
} else if (var_type == "f32" || var_type == "f64") {
226263
m_a.asm_sub_r64_imm32(X64Reg::rsp, 8); // create space for value to be fetched
227264
m_a.asm_movsd_r64_m64(X64FReg::xmm0, &base, nullptr, 1, 8 * (2 + no_of_params - (int)localidx - 1));
228265
X64Reg stack_top = X64Reg::rsp;
@@ -233,13 +270,10 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
233270
} else {
234271
localidx -= no_of_params;
235272
std::string var_type = var_type_to_string[codes[cur_func_idx].locals[localidx].type];
236-
if (var_type == "i32") {
273+
if (var_type == "i32" || var_type == "i64") {
237274
m_a.asm_mov_r64_m64(X64Reg::rax, &base, nullptr, 1, -8 * (1 + (int)localidx));
238275
m_a.asm_push_r64(X64Reg::rax);
239-
} else if (var_type == "i64") {
240-
m_a.asm_mov_r64_m64(X64Reg::rax, &base, nullptr, 1, -8 * (1 + (int)localidx));
241-
m_a.asm_push_r64(X64Reg::rax);
242-
} else if (var_type == "f64") {
276+
} else if (var_type == "f32" || var_type == "f64") {
243277
m_a.asm_sub_r64_imm32(X64Reg::rsp, 8); // create space for value to be fetched
244278
m_a.asm_movsd_r64_m64(X64FReg::xmm0, &base, nullptr, 1, -8 * (1 + (int)localidx));
245279
X64Reg stack_top = X64Reg::rsp;
@@ -256,13 +290,10 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
256290
int no_of_params = (int)cur_func_param_type.param_types.size();
257291
if ((int)localidx < no_of_params) {
258292
std::string var_type = var_type_to_string[cur_func_param_type.param_types[localidx]];
259-
if (var_type == "i32") {
260-
m_a.asm_pop_r64(X64Reg::rax);
261-
m_a.asm_mov_m64_r64(&base, nullptr, 1, 8 * (2 + no_of_params - (int)localidx - 1), X64Reg::rax);
262-
} else if (var_type == "i64") {
293+
if (var_type == "i32" || var_type == "i64") {
263294
m_a.asm_pop_r64(X64Reg::rax);
264295
m_a.asm_mov_m64_r64(&base, nullptr, 1, 8 * (2 + no_of_params - (int)localidx - 1), X64Reg::rax);
265-
} else if (var_type == "f64") {
296+
} else if (var_type == "f32" || var_type == "f64") {
266297
X64Reg stack_top = X64Reg::rsp;
267298
m_a.asm_movsd_r64_m64(X64FReg::xmm0, &stack_top, nullptr, 1, 0);
268299
m_a.asm_movsd_m64_r64(&base, nullptr, 1, 8 * (2 + no_of_params - (int)localidx - 1), X64FReg::xmm0);
@@ -273,13 +304,10 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
273304
} else {
274305
localidx -= no_of_params;
275306
std::string var_type = var_type_to_string[codes[cur_func_idx].locals[localidx].type];
276-
if (var_type == "i32") {
307+
if (var_type == "i32" || var_type == "i64") {
277308
m_a.asm_pop_r64(X64Reg::rax);
278309
m_a.asm_mov_m64_r64(&base, nullptr, 1, -8 * (1 + (int)localidx), X64Reg::rax);
279-
} else if (var_type == "i64") {
280-
m_a.asm_pop_r64(X64Reg::rax);
281-
m_a.asm_mov_m64_r64(&base, nullptr, 1, -8 * (1 + (int)localidx), X64Reg::rax);
282-
} else if (var_type == "f64") {
310+
} else if (var_type == "f32" || var_type == "f64") {
283311
X64Reg stack_top = X64Reg::rsp;
284312
m_a.asm_movsd_r64_m64(X64FReg::xmm0, &stack_top, nullptr, 1, 0);
285313
m_a.asm_movsd_m64_r64(&base, nullptr, 1, -8 * (1 + (int)localidx), X64FReg::xmm0);
@@ -290,95 +318,30 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
290318
}
291319
}
292320

293-
void visit_I32Const(int32_t value) {
294-
m_a.asm_mov_r64_imm64(X64Reg::rax, labs((int64_t)value));
295-
if (value < 0) m_a.asm_neg_r64(X64Reg::rax);
296-
m_a.asm_push_r64(X64Reg::rax);
297-
}
298-
299-
template<typename F>
300-
void handleI32Opt(F && f) {
301-
m_a.asm_pop_r64(X64Reg::rbx);
302-
m_a.asm_pop_r64(X64Reg::rax);
303-
f();
304-
m_a.asm_push_r64(X64Reg::rax);
305-
}
306-
307-
void visit_I32Add() {
308-
handleI32Opt([&](){ m_a.asm_add_r64_r64(X64Reg::rax, X64Reg::rbx);});
309-
}
310-
void visit_I32Sub() {
311-
handleI32Opt([&](){ m_a.asm_sub_r64_r64(X64Reg::rax, X64Reg::rbx);});
312-
}
313-
void visit_I32Mul() {
314-
handleI32Opt([&](){ m_a.asm_mul_r64(X64Reg::rbx);});
315-
}
316-
void visit_I32DivS() {
317-
handleI32Opt([&](){
318-
m_a.asm_mov_r64_imm64(X64Reg::rdx, 0);
319-
m_a.asm_div_r64(X64Reg::rbx);
320-
});
321-
}
322-
323-
void visit_I32And() {
324-
handleI32Opt([&](){ m_a.asm_and_r64_r64(X64Reg::rax, X64Reg::rbx);});
325-
}
326-
327-
void visit_I32Or() {
328-
handleI32Opt([&](){ m_a.asm_or_r64_r64(X64Reg::rax, X64Reg::rbx);});
329-
}
330-
331-
void visit_I32Xor() {
332-
handleI32Opt([&](){ m_a.asm_xor_r64_r64(X64Reg::rax, X64Reg::rbx);});
333-
}
334-
335-
void visit_I32Shl() {
336-
m_a.asm_pop_r64(X64Reg::rcx);
337-
m_a.asm_pop_r64(X64Reg::rax);
338-
m_a.asm_shl_r64_cl(X64Reg::rax);
339-
m_a.asm_push_r64(X64Reg::rax);
340-
}
341-
void visit_I32ShrS() {
342-
m_a.asm_pop_r64(X64Reg::rcx);
343-
m_a.asm_pop_r64(X64Reg::rax);
344-
m_a.asm_sar_r64_cl(X64Reg::rax);
345-
m_a.asm_push_r64(X64Reg::rax);
346-
}
347-
348-
void visit_I32Eqz() {
349-
m_a.asm_mov_r64_imm64(X64Reg::rax, 0);
350-
m_a.asm_push_r64(X64Reg::rax);
351-
handle_I32Compare<&X86Assembler::asm_je_label>();
352-
}
321+
void visit_I32Const(int32_t value) { visit_I64Const(int64_t(value)); }
353322

354-
using JumpFn = void(X86Assembler::*)(const std::string&);
355-
template<JumpFn T>
356-
void handle_I32Compare() {
357-
std::string label = std::to_string(offset);
358-
m_a.asm_pop_r64(X64Reg::rbx);
359-
m_a.asm_pop_r64(X64Reg::rax);
360-
// `rax` and `rbx` contain the left and right operands, respectively
361-
m_a.asm_cmp_r64_r64(X64Reg::rax, X64Reg::rbx);
323+
void visit_I32Add() { visit_I64Add(); }
324+
void visit_I32Sub() { visit_I64Sub(); }
325+
void visit_I32Mul() { visit_I64Mul(); }
326+
void visit_I32DivS() { visit_I64DivS(); }
362327

363-
(m_a.*T)(".compare_1" + label);
328+
void visit_I32And() { visit_I64And(); }
329+
void visit_I32Or() { visit_I64Or(); }
330+
void visit_I32Xor() { visit_I64Xor(); }
331+
void visit_I32Shl() { visit_I64Shl(); }
332+
void visit_I32ShrS() { visit_I64ShrS(); }
364333

365-
// if the `compare` condition in `true`, jump to compare_1
366-
// and assign `1` else assign `0`
367-
m_a.asm_push_imm8(0);
368-
m_a.asm_jmp_label(".compare.end_" + label);
369-
m_a.add_label(".compare_1" + label);
370-
m_a.asm_push_imm8(1);
371-
m_a.add_label(".compare.end_" + label);
372-
}
334+
void visit_I32Eqz() { visit_I64Eqz(); }
335+
void visit_I32Eq() { visit_I64Eq(); }
336+
void visit_I32GtS() { visit_I64GtS(); }
337+
void visit_I32GeS() { visit_I64GeS(); }
338+
void visit_I32LtS() { visit_I64LtS(); }
339+
void visit_I32LeS() { visit_I64LeS(); }
340+
void visit_I32Ne() { visit_I64Ne(); }
373341

374-
void visit_I32Eq() { handle_I32Compare<&X86Assembler::asm_je_label>(); }
375-
void visit_I32GtS() { handle_I32Compare<&X86Assembler::asm_jg_label>(); }
376-
void visit_I32GeS() { handle_I32Compare<&X86Assembler::asm_jge_label>(); }
377-
void visit_I32LtS() { handle_I32Compare<&X86Assembler::asm_jl_label>(); }
378-
void visit_I32LeS() { handle_I32Compare<&X86Assembler::asm_jle_label>(); }
379-
void visit_I32Ne() { handle_I32Compare<&X86Assembler::asm_jne_label>(); }
342+
void visit_I32WrapI64() { } // empty, since i32's and i64's are considered similar currently.
380343

381-
void visit_I64Const(int32_t value) {
344+
void visit_I64Const(int64_t value) {
382345
m_a.asm_mov_r64_imm64(X64Reg::rax, labs((int64_t)value));
383346
if (value < 0) m_a.asm_neg_r64(X64Reg::rax);
384347
m_a.asm_push_r64(X64Reg::rax);
@@ -427,10 +390,6 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
427390
m_a.asm_push_r64(X64Reg::rdx);
428391
}
429392

430-
void visit_I32WrapI64() {
431-
// empty, since i32's and i64's are considered similar currently.
432-
}
433-
434393
void visit_I64Store(uint32_t /*mem_align*/, uint32_t /*mem_offset*/) {
435394
m_a.asm_pop_r64(X64Reg::rbx);
436395
m_a.asm_pop_r64(X64Reg::rax);
@@ -458,6 +417,7 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
458417
handle_I64Compare<&X86Assembler::asm_je_label>();
459418
}
460419

420+
using JumpFn = void(X86Assembler::*)(const std::string&);
461421
template<JumpFn T>
462422
void handle_I64Compare() {
463423
std::string label = std::to_string(offset);
@@ -492,15 +452,17 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
492452
m_a.asm_push_r64(X64Reg::rax);
493453
}
494454

495-
void visit_I64ExtendI32S() {
496-
// empty, since all i32's are already considered as i64's currently.
497-
}
455+
void visit_I64ExtendI32S() { } // empty, since all i32's are already considered as i64's currently.
498456

499457
std::string float_to_str(double z) {
500458
std::string float_str = "";
501-
for (auto ch:std::to_string(z)) {
459+
std::ostringstream strs;
460+
strs << z;
461+
for (auto ch:strs.str()) {
502462
if (ch == '-') {
503463
float_str += "neg_";
464+
} else if (ch == '+') {
465+
float_str += "_plus_";
504466
} else if (ch == '.') {
505467
float_str += "_dot_";
506468
} else {
@@ -577,8 +539,47 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
577539
m_a.asm_movsd_m64_r64(&stack_top, nullptr, 1, 0, X64FReg::xmm0); // store float on integer stack top;
578540
}
579541

542+
void visit_F64ConvertI32S() { visit_F64ConvertI64S(); } // I32's considered as I64's currently
543+
void visit_F64PromoteF32() { } // F32's considered as F64's currently
544+
545+
void visit_F64Neg() {
546+
visit_F64Const(double(-1.0));
547+
visit_F64Mul();
548+
}
549+
550+
void visit_F64Sqrt() {
551+
X64Reg stack_top = X64Reg::rsp;
552+
// load operand into floating-point register
553+
m_a.asm_movsd_r64_m64(X64FReg::xmm1, &stack_top, nullptr, 1, 0);
554+
m_a.asm_add_r64_imm32(X64Reg::rsp, 8); // pop the argument
555+
556+
m_a.asm_sqrtsd_r64_r64(X64FReg::xmm0, X64FReg::xmm1); // perform sqrt operation
557+
558+
m_a.asm_sub_r64_imm32(X64Reg::rsp, 8); // decrement stack and create space
559+
m_a.asm_movsd_m64_r64(&stack_top, nullptr, 1, 0, X64FReg::xmm0); // store the result on stack top;
560+
}
561+
562+
563+
void visit_F32Const(float z) { visit_F64Const(double(z)); }
564+
565+
void visit_F32Add() { visit_F64Add(); }
566+
void visit_F32Sub() { visit_F64Sub(); }
567+
void visit_F32Mul() { visit_F64Mul(); }
568+
void visit_F32Div() { visit_F64Div(); }
569+
570+
void visit_F32Eq() { visit_F64Eq(); }
571+
void visit_F32Gt() { visit_F64Gt(); }
572+
void visit_F32Ge() { visit_F64Ge(); }
573+
void visit_F32Lt() { visit_F64Lt(); }
574+
void visit_F32Le() { visit_F64Le(); }
575+
void visit_F32Ne() { visit_F64Ne(); }
576+
577+
void visit_F32ConvertI64S() { visit_F64ConvertI32S(); }
578+
void visit_F32Neg() { visit_F64Neg(); }
579+
void visit_F32Sqrt() { visit_F64Sqrt(); }
580+
580581
void gen_x64_bytes() {
581-
emit_elf64_header(m_a);
582+
emit_elf64_header(m_a, 7U);
582583

583584
// declare compile-time strings
584585
std::string base_memory = " "; /* in wasm backend, memory starts after 4 bytes*/
@@ -614,6 +615,36 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
614615
emit_double_const(m_a, d.first, d.second);
615616
}
616617

618+
for (size_t i = 0; i < globals.size(); i++) {
619+
uint32_t tmp_offset = globals[i].insts_start_idx;
620+
wasm::read_b8(wasm_bytes, tmp_offset); // read byte for i32/i64/f32/f64.const
621+
622+
std::string global_loc = "global_" + std::to_string(i);
623+
switch (globals[i].type) {
624+
case 0x7F: {
625+
int32_t val = wasm::read_i32(wasm_bytes, offset);
626+
emit_i64_const(m_a, global_loc, val);
627+
break;
628+
}
629+
case 0x7E: {
630+
int64_t val = wasm::read_i64(wasm_bytes, offset);
631+
emit_i64_const(m_a, global_loc, val);
632+
break;
633+
}
634+
case 0x7D: {
635+
float val = wasm::read_f32(wasm_bytes, offset);
636+
emit_double_const(m_a, global_loc, val);
637+
break;
638+
}
639+
case 0x7C: {
640+
double val = wasm::read_f64(wasm_bytes, offset);
641+
emit_double_const(m_a, global_loc, val);
642+
break;
643+
}
644+
default: throw CodeGenError("decode_global_section: Unsupport global type"); break;
645+
}
646+
}
647+
617648
emit_elf64_footer(m_a);
618649
}
619650
};

src/libasr/codegen/x86_assembler.cpp

Lines changed: 16 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -109,6 +109,22 @@ void emit_data_string(X86Assembler &a, const std::string &label,
109109
a.asm_db_imm8(s.c_str(), s.size());
110110
}
111111

112+
void emit_i32_const(X86Assembler &a, const std::string &label,
113+
const int32_t z) {
114+
uint8_t encoded_i32[sizeof(z)];
115+
std::memcpy(&encoded_i32, &z, sizeof(z));
116+
a.add_label(label);
117+
a.asm_db_imm8(encoded_i32, sizeof(z));
118+
}
119+
120+
void emit_i64_const(X86Assembler &a, const std::string &label,
121+
const int64_t z) {
122+
uint8_t encoded_i64[sizeof(z)];
123+
std::memcpy(&encoded_i64, &z, sizeof(z));
124+
a.add_label(label);
125+
a.asm_db_imm8(encoded_i64, sizeof(z));
126+
}
127+
112128
void emit_float_const(X86Assembler &a, const std::string &label,
113129
const float z) {
114130
uint8_t encoded_float[sizeof(z)];

0 commit comments

Comments
 (0)