Skip to content

Commit

Permalink
Merge pull request lcompilers#1379 from Shaikh-Ubaid/wasm_x64_print_ints
Browse files Browse the repository at this point in the history
WASM_X64: Support printing integers
  • Loading branch information
certik committed Dec 23, 2022
2 parents eaf2099 + 836d82e commit 7aaedcc
Show file tree
Hide file tree
Showing 4 changed files with 112 additions and 15 deletions.
2 changes: 1 addition & 1 deletion integration_tests/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -215,7 +215,7 @@ RUN(NAME exit_02c FAIL LABELS cpython llvm c)

# Test all four backends
RUN(NAME print_01 LABELS cpython llvm c wasm) # wasm not yet supports sep and end keywords
RUN(NAME print_03 LABELS x86 c wasm_x86) # simple test case specifically for x86 and wasm_x86
RUN(NAME print_03 LABELS x86 c wasm wasm_x86 wasm_x64) # simple test case specifically for x86, wasm_x86 and wasm_x64
RUN(NAME print_04 LABELS cpython llvm c)

# CPython and LLVM
Expand Down
18 changes: 6 additions & 12 deletions src/libasr/codegen/wasm_to_x64.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -50,14 +50,8 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
void call_imported_function(uint32_t func_idx) {
switch (func_idx) {
case 0: { // print_i32
std::cerr << "Call to print_i32() will be printed as exit code\n";

// Currently, for print we are setting the value to be printed
// as the exit code. Later we can access/view this value from console
// using: echo $?
m_a.asm_pop_r64(X64Reg::rdi); // get exit code from stack top
m_a.asm_mov_r64_imm64(X64Reg::rax, 60); // sys_exit
m_a.asm_syscall(); // syscall
m_a.asm_call_label("print_i64");
m_a.asm_pop_r64(X64Reg::r15); // pop the passed argument
break;
}
case 1: { // print_i64
Expand Down Expand Up @@ -185,10 +179,8 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
}

void visit_I32Const(int32_t value) {
// direct addition of imm64 to stack is not available with us yet
// so temporarily using a combination of instructions
// TODO: Update this once we have support for push_imm64()
m_a.asm_mov_r64_imm64(X64Reg::rax, value);
m_a.asm_mov_r64_imm64(X64Reg::rax, labs((int64_t)value));
if (value < 0) m_a.asm_neg_r64(X64Reg::rax);
m_a.asm_push_r64(X64Reg::rax);

// TODO: Following seems/is hackish. Fix/Improve it.
Expand Down Expand Up @@ -225,9 +217,11 @@ class X64Visitor : public WASMDecoder<X64Visitor>,
{ // Initialize/Modify values of entities
exports.back().name = "_start"; // Update _lcompilers_main() to _start
label_to_str["string_newline"] = "\n";
label_to_str["string_neg"] = "-"; // - symbol for printing negative ints
}

emit_elf64_header(m_a);
emit_print_int_64(m_a, "print_i64");

// declare compile-time strings
for (uint32_t i = 0; i < data_segments.size(); i++) {
Expand Down
54 changes: 54 additions & 0 deletions src/libasr/codegen/x86_assembler.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -286,4 +286,58 @@ void emit_print_64(X86Assembler &a, const std::string &msg_label, uint64_t size)
a.asm_syscall();
}

void emit_print_int_64(X86Assembler &a, const std::string &name)
{
// void print_int_64(uint64_t i);
a.add_label(name);
// Initialize stack
a.asm_push_r64(X64Reg::rbp);
a.asm_mov_r64_r64(X64Reg::rbp, X64Reg::rsp);

X64Reg base = X64Reg::rbp;
a.asm_mov_r64_m64(X64Reg::r8, &base, nullptr, 1, 16); // mov r8, [rbp+16] // argument "i"
a.asm_mov_r64_imm64(X64Reg::r9, 0); // r9 holds count of digits

// if num >= 0 then print it
a.asm_cmp_r64_imm8(X64Reg::r8, 0);
a.asm_jge_label("_print_i64_loop_initialize");

// print "-" and then negate the integer
emit_print_64(a, "string_neg", 1);
a.asm_neg_r64(X64Reg::r8);

a.add_label("_print_i64_loop_initialize");
a.asm_mov_r64_r64(X64Reg::rax, X64Reg::r8); // rax as quotient
a.asm_mov_r64_imm64(X64Reg::r10, 10); // 10 as divisor

a.add_label("_print_i64_loop");
a.asm_mov_r64_imm64(X64Reg::rdx, 0);
a.asm_div_r64(X64Reg::r10);
a.asm_add_r64_imm32(X64Reg::rdx, 48);
a.asm_push_r64(X64Reg::rdx);
a.asm_inc_r64(X64Reg::r9);
a.asm_cmp_r64_imm8(X64Reg::rax, 0);
a.asm_je_label("_print_i64_digit");
a.asm_jmp_label("_print_i64_loop");

a.add_label("_print_i64_digit");
a.asm_cmp_r64_imm8(X64Reg::r9, 0);
a.asm_je_label("_print_i64_end");
a.asm_dec_r64(X64Reg::r9);
{ // write() syscall
a.asm_mov_r64_imm64(X64Reg::rax, 1);
a.asm_mov_r64_imm64(X64Reg::rdi, 1);
a.asm_mov_r64_r64(X64Reg::rsi, X64Reg::rsp);
a.asm_mov_r64_imm64(X64Reg::rdx, 1);
a.asm_syscall();
}
a.asm_pop_r64(X64Reg::r15); // increment stack pointer by pop operation
a.asm_jmp_label("_print_i64_digit");

a.add_label("_print_i64_end");
// Restore stack
a.asm_mov_r64_r64(X64Reg::rsp, X64Reg::rbp);
a.asm_pop_r64(X64Reg::rbp);
a.asm_ret();
}
} // namespace LFortran
53 changes: 51 additions & 2 deletions src/libasr/codegen/x86_assembler.h
Original file line number Diff line number Diff line change
Expand Up @@ -650,14 +650,32 @@ class X86Assembler {
EMIT("jge " + label);
}

void asm_inc_r64(X64Reg r64) {
X86Reg r32 = X86Reg(r64 & 7);
m_code.push_back(m_al, rex(1, 0, 0, r64 >> 3));
m_code.push_back(m_al, 0xFF);
modrm_sib_disp(m_code, m_al,
X86Reg::eax, &r32, nullptr, 1, 0, false);
EMIT("inc " + r2s(r64));
}

void asm_inc_r32(X86Reg r32) {
m_code.push_back(m_al, 0x40+r32);
EMIT("inc " + r2s(r32));
}

void asm_dec_r64(X64Reg r64) {
X86Reg r32 = X86Reg(r64 & 7);
m_code.push_back(m_al, rex(1, 0, 0, r64 >> 3));
m_code.push_back(m_al, 0xFF);
modrm_sib_disp(m_code, m_al,
X86Reg::ecx, &r32, nullptr, 1, 0, false);
EMIT("dec " + r2s(r64));
}

void asm_dec_r32(X86Reg r32) {
m_code.push_back(m_al, 0x48+r32);
EMIT("inc " + r2s(r32));
EMIT("dec " + r2s(r32));
}

void asm_inc_m32(X86Reg *base, X86Reg *index, uint8_t scale, int32_t disp) {
Expand Down Expand Up @@ -848,6 +866,16 @@ class X86Assembler {
EMIT("sar " + r2s(r32) + ", " + i2s(imm8));
}

void asm_cmp_r64_imm8(X64Reg r64, uint8_t imm8) {
X86Reg r32 = X86Reg(r64 & 7);
m_code.push_back(m_al, rex(1, 0, 0, r64 >> 3));
m_code.push_back(m_al, 0x83);
modrm_sib_disp(m_code, m_al,
X86Reg::edi, &r32, nullptr, 1, 0, false);
m_code.push_back(m_al, imm8);
EMIT("cmp " + r2s(r64) + ", " + i2s(imm8));
}

void asm_cmp_r32_imm8(X86Reg r32, uint8_t imm8) {
m_code.push_back(m_al, 0x83);
modrm_sib_disp(m_code, m_al,
Expand Down Expand Up @@ -961,7 +989,7 @@ class X86Assembler {

void asm_add_r64_r64(X64Reg s64, X64Reg r64) {
X86Reg r32 = X86Reg(r64 & 7), s32 = X86Reg(s64 & 7);
m_code.push_back(m_al, rex(1, s64 >> 3, 0, r64 >> 3));
m_code.push_back(m_al, rex(1, r64 >> 3, 0, s64 >> 3));
m_code.push_back(m_al, 0x01);
modrm_sib_disp(m_code, m_al,
r32, &s32, nullptr, 1, 0, false);
Expand All @@ -983,6 +1011,17 @@ class X86Assembler {
EMIT("add " + r2s(r32) + ", " + i2s(imm8));
}

// Only 'ADD r/m64, imm32' is available in assembly
void asm_add_r64_imm32(X64Reg r64, uint32_t imm32) {
X86Reg r32 = X86Reg(r64 & 7);
m_code.push_back(m_al, rex(1, 0, 0, r64 >> 3));
m_code.push_back(m_al, 0x81);
modrm_sib_disp(m_code, m_al,
X86Reg::eax, &r32, nullptr, 1, 0, false);
push_back_uint32(m_code, m_al, imm32);
EMIT("add " + r2s(r64) + ", " + i2s(imm32));
}

void asm_add_r32_imm32(X86Reg r32, uint32_t imm32) {
m_code.push_back(m_al, 0x81);
modrm_sib_disp(m_code, m_al,
Expand Down Expand Up @@ -1023,6 +1062,15 @@ class X86Assembler {
EMIT("div " + r2s(r32));
}

void asm_neg_r64(X64Reg r64) {
X86Reg r32 = X86Reg(r64 & 7);
m_code.push_back(m_al, rex(1, 0, 0, r64 >> 3));
m_code.push_back(m_al, 0xF7);
modrm_sib_disp(m_code, m_al,
X86Reg::ebx, &r32, nullptr, 1, 0, false);
EMIT("neg " + r2s(r64));
}

void asm_neg_r32(X86Reg r32) {
m_code.push_back(m_al, 0xF7);
modrm_sib_disp(m_code, m_al,
Expand Down Expand Up @@ -1088,6 +1136,7 @@ void emit_elf64_footer(X86Assembler &a);
void emit_exit_64(X86Assembler &a, std::string label, int exit_code);

void emit_print_64(X86Assembler &a, const std::string &msg_label, uint64_t size);
void emit_print_int_64(X86Assembler &a, const std::string &name);
} // namespace LFortran

#endif // LFORTRAN_CODEGEN_X86_ASSEMBER_H

0 comments on commit 7aaedcc

Please sign in to comment.