Skip to content

Commit a7b33cd

Browse files
authored
Merge pull request #1285 from Shaikh-Ubaid/wasm_x86_exit_code
WASM_X86: Support exit code
2 parents 7597043 + ad6c486 commit a7b33cd

File tree

4 files changed

+60
-21
lines changed

4 files changed

+60
-21
lines changed

integration_tests/CMakeLists.txt

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -180,8 +180,8 @@ RUN(NAME bindc_02 LABELS llvm c)
180180
RUN(NAME bindc_04 LABELS llvm)
181181
RUN(NAME exit_01 LABELS cpython llvm c)
182182
RUN(NAME exit_02 FAIL LABELS cpython llvm c)
183-
RUN(NAME exit_01b LABELS cpython llvm c wasm)
184-
RUN(NAME exit_02b FAIL LABELS cpython llvm c wasm)
183+
RUN(NAME exit_01b LABELS cpython llvm c wasm wasm_x86)
184+
RUN(NAME exit_02b FAIL LABELS cpython llvm c wasm wasm_x86)
185185
RUN(NAME exit_02c FAIL LABELS cpython llvm c)
186186

187187
# Test all four backends
@@ -233,8 +233,8 @@ RUN(NAME test_dict_03 LABELS cpython llvm)
233233
RUN(NAME test_dict_04 LABELS cpython llvm)
234234
RUN(NAME test_dict_05 LABELS cpython llvm)
235235
RUN(NAME test_for_loop LABELS cpython llvm c)
236-
RUN(NAME modules_01 LABELS cpython llvm wasm)
237-
RUN(NAME modules_02 LABELS cpython llvm wasm)
236+
RUN(NAME modules_01 LABELS cpython llvm wasm wasm_x86)
237+
RUN(NAME modules_02 LABELS cpython llvm wasm wasm_x86)
238238
RUN(NAME test_import_01 LABELS cpython llvm)
239239
RUN(NAME test_import_02 LABELS cpython llvm)
240240
RUN(NAME test_math LABELS cpython llvm)

src/libasr/codegen/wasm_to_x86.cpp

Lines changed: 37 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -11,6 +11,22 @@ namespace LFortran {
1111

1212
namespace wasm {
1313

14+
/*
15+
16+
This X86Visitor uses stack to pass arguments and return values from functions.
17+
Since in X86, instructions operate on registers (and not on stack),
18+
for every instruction we pop elements from top of stack and store them into
19+
registers. After operating on the registers, the result value is then
20+
pushed back onto the stack.
21+
22+
One of the reasons to use stack to pass function arguments is that,
23+
it allows us to define and call functions with any number of parameters.
24+
As registers are limited in number, if we use them to pass function arugments,
25+
the number of arguments we could pass to a function would get limited by
26+
the number of registers available with the CPU.
27+
28+
*/
29+
1430
class X86Visitor : public WASMDecoder<X86Visitor>,
1531
public WASM_INSTS_VISITOR::BaseWASMVisitor<X86Visitor> {
1632
public:
@@ -28,6 +44,8 @@ class X86Visitor : public WASMDecoder<X86Visitor>,
2844
wasm_bytes.from_pointer_n(code.data(), code.size());
2945
}
3046

47+
void visit_Unreachable() {}
48+
3149
void visit_Return() {}
3250

3351
void call_imported_function(uint32_t func_index) {
@@ -70,7 +88,7 @@ class X86Visitor : public WASMDecoder<X86Visitor>,
7088
break;
7189
}
7290
case 6: { // set_exit_code
73-
m_a.asm_call_label("exit");
91+
m_a.asm_jmp_label("exit");
7492
break;
7593
}
7694
default: {
@@ -160,6 +178,11 @@ class X86Visitor : public WASMDecoder<X86Visitor>,
160178
}
161179
}
162180

181+
void visit_I32Eqz() {
182+
m_a.asm_push_imm32(0U);
183+
handle_I32Compare("Eq");
184+
}
185+
163186
void visit_I32Const(int32_t value) {
164187
m_a.asm_push_imm32(value);
165188
// if (value < 0) {
@@ -199,31 +222,30 @@ class X86Visitor : public WASMDecoder<X86Visitor>,
199222
}
200223

201224
void handle_I32Compare(const std::string &compare_op) {
202-
unique_id.push_back(std::to_string(offset));
225+
std::string label = std::to_string(offset);
203226
m_a.asm_pop_r32(X86Reg::ebx);
204227
m_a.asm_pop_r32(X86Reg::eax);
205228
m_a.asm_cmp_r32_r32(X86Reg::eax, X86Reg::ebx);
206229
if (compare_op == "Eq") {
207-
m_a.asm_je_label(".compare_1" + unique_id.back());
230+
m_a.asm_je_label(".compare_1" + label);
208231
} else if (compare_op == "Gt") {
209-
m_a.asm_jg_label(".compare_1" + unique_id.back());
232+
m_a.asm_jg_label(".compare_1" + label);
210233
} else if (compare_op == "GtE") {
211-
m_a.asm_jge_label(".compare_1" + unique_id.back());
234+
m_a.asm_jge_label(".compare_1" + label);
212235
} else if (compare_op == "Lt") {
213-
m_a.asm_jl_label(".compare_1" + unique_id.back());
236+
m_a.asm_jl_label(".compare_1" + label);
214237
} else if (compare_op == "LtE") {
215-
m_a.asm_jle_label(".compare_1" + unique_id.back());
238+
m_a.asm_jle_label(".compare_1" + label);
216239
} else if (compare_op == "NotEq") {
217-
m_a.asm_jne_label(".compare_1" + unique_id.back());
240+
m_a.asm_jne_label(".compare_1" + label);
218241
} else {
219242
throw CodeGenError("Comparison operator not implemented");
220243
}
221-
m_a.asm_mov_r32_imm32(X86Reg::eax, 0);
222-
m_a.asm_jmp_label(".compare.end_" + unique_id.back());
223-
m_a.add_label(".compare_1" + unique_id.back());
224-
m_a.asm_mov_r32_imm32(X86Reg::eax, 1);
225-
m_a.add_label(".compare.end_" + unique_id.back());
226-
m_a.asm_push_r32(X86Reg::eax);
244+
m_a.asm_push_imm8(0);
245+
m_a.asm_jmp_label(".compare.end_" + label);
246+
m_a.add_label(".compare_1" + label);
247+
m_a.asm_push_imm8(1);
248+
m_a.add_label(".compare.end_" + label);
227249
}
228250

229251
void visit_I32Eq() { handle_I32Compare("Eq"); }
@@ -238,7 +260,7 @@ class X86Visitor : public WASMDecoder<X86Visitor>,
238260

239261
// Add runtime library functions
240262
emit_print_int(m_a, "print_i32");
241-
emit_exit(m_a, "exit", 0);
263+
emit_exit2(m_a, "exit");
242264

243265
// declare compile-time strings
244266
for (uint32_t i = 0; i < data_segments.size(); i++) {

src/libasr/codegen/x86_assembler.cpp

Lines changed: 11 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -94,6 +94,15 @@ void emit_exit(X86Assembler &a, const std::string &name,
9494
a.asm_int_imm8(0x80); // syscall
9595
}
9696

97+
void emit_exit2(X86Assembler &a, const std::string &name)
98+
{
99+
a.add_label(name);
100+
// void exit();
101+
a.asm_mov_r32_imm32(LFortran::X86Reg::eax, 1); // sys_exit
102+
a.asm_pop_r32(X86Reg::ebx); // exit code on stack, move to register
103+
a.asm_int_imm8(0x80); // syscall
104+
}
105+
97106
void emit_data_string(X86Assembler &a, const std::string &label,
98107
const std::string &s)
99108
{
@@ -145,11 +154,11 @@ void emit_print_int(X86Assembler &a, const std::string &name)
145154
a.asm_je_label(".print");
146155
// jmp .loop
147156
a.asm_jmp_label(".loop");
148-
157+
149158
a.add_label(".print");
150159
// cmp esi, 0
151160
a.asm_cmp_r32_imm8(X86Reg::esi, 0);
152-
// jz end
161+
// jz end
153162
a.asm_je_label(".end");
154163
// dec esi
155164
a.asm_dec_r32(X86Reg::esi);

src/libasr/codegen/x86_assembler.h

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -815,6 +815,14 @@ void emit_elf32_footer(X86Assembler &a);
815815

816816
void emit_exit(X86Assembler &a, const std::string &name,
817817
uint32_t exit_code);
818+
819+
// this is similar to emit_exit() but takes the argument (i.e. exit code)
820+
// from top of stack. To call this exit2, one needs to jump to it
821+
// instead of call it. (Because calling pushes the instruction address and
822+
// base pointer value (ebp) of previous function and thus makes the
823+
// exit code parameter less reachable)
824+
void emit_exit2(X86Assembler &a, const std::string &name);
825+
818826
void emit_data_string(X86Assembler &a, const std::string &label,
819827
const std::string &s);
820828
void emit_print(X86Assembler &a, const std::string &msg_label,

0 commit comments

Comments
 (0)