#include "molasses/parser_primitives.h" #include "molasses/generator_primitives.h" namespace molasses { std::string marshal(const std::string& target) { std::stringstream builder; bool is_first = true; for(char character : target) { if(isalpha(character)) { builder << character; } else if(isdigit(character) and is_first) { builder << "___" << (int)character << "___"; } else { builder << "__" << (int)character << "__"; } is_first = false; } return builder.str(); } std::string escape(const std::string& target) { std::stringstream builder; for(char character : target) { switch(character) { case 0: builder << "\\0"; break; case '\n': builder << "\\n"; break; case '"': builder << "\\\""; break; case '\t': builder << "\\t"; break; case '\'': builder << "\\'"; break; default: builder << character; } } return builder.str(); } parser_context register_integers(parser_context ctx) requires(architecture == architecture_t::x86_64_linux) { ctx.types.push_back(std::make_shared("i8", 1)); ctx.types.push_back(std::make_shared("i16", 2)); ctx.types.push_back(std::make_shared("i32", 4)); ctx.types.push_back(std::make_shared("i64", 8)); ctx.types.push_back(std::make_shared("u8", 1)); ctx.types.push_back(std::make_shared("u16", 2)); ctx.types.push_back(std::make_shared("u32", 4)); ctx.types.push_back(std::make_shared("u64", 8)); return ctx; } parser_context register_i32_operations(parser_context ctx) requires (architecture == architecture_t::x86_64_linux) { ctx.operations.emplace_back( std::make_shared( std::string{"+"}, std::vector({"i32", "i32"}), std::vector({"i32"}), std::vector({ " popq %rax\n", " popq %rbx\n", " addl %ebx, %eax\n", " andl $0xFFFFFFFF, %eax\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"+_i64"}, std::vector({"i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rbx\n", " addq %rbx, %rax\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"/%_i64"}, std::vector({"i64", "i64"}), std::vector({"i64", "i64"}), std::vector({ " popq %rax\n", " popq %rbx\n", " divq %rbx, %rax\n", " pushq %rax\n", " pushq %rdx\n" // TODO: this is actually unsigned division, so it needs improvements on negative numbers }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"u8-ptr_to_i64"}, std::vector({"u8 ptr"}), std::vector({"i64"}), std::vector({ }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"*"}, std::vector({"i32", "i32"}), std::vector({"i32"}), std::vector({ " popq %rax\n", " popq %rbx\n", " imull %ebx, %eax\n", " andl $0xFFFFFFFF, %eax\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"-"}, std::vector({"i32", "i32"}), std::vector({"i32"}), std::vector({ " popq %rax\n", " popq %rbx\n", " subl %ebx, %eax\n", " andl $0xFFFFFFFF, %eax\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"i32-to-i64"}, std::vector({"i32"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " movslq %eax, %rax\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"drop_i64"}, std::vector({"i64"}), std::vector({}), std::vector({ " popq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"syscall1"}, std::vector({"i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rdi\n", " syscall\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"syscall2"}, std::vector({"i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rdi\n", " popq %rsi\n", " syscall\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"syscall3"}, std::vector({"i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rdi\n", " popq %rsi\n", " popq %rdx\n", " syscall\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"syscall4"}, std::vector({"i64", "i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rdi\n", " popq %rsi\n", " popq %rdx\n", " popq %r10\n", " syscall\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"syscall5"}, std::vector({"i64", "i64", "i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rdi\n", " popq %rsi\n", " popq %rdx\n", " popq %r10\n", " popq %r8\n", " syscall\n", " pushq %rax\n" }) ) ); ctx.operations.emplace_back( std::make_shared( std::string{"syscall6"}, std::vector({"i64", "i64", "i64", "i64", "i64", "i64", "i64"}), std::vector({"i64"}), std::vector({ " popq %rax\n", " popq %rdi\n", " popq %rsi\n", " popq %rdx\n", " popq %r10\n", " popq %r8\n", " popq %r9\n", " syscall\n", " pushq %rax\n" }) ) ); return ctx; } std::vector generate_call(const std::string& target) requires (architecture == architecture_t::x86_64_linux) { return { " call "+marshal(target)+"\n", }; } std::vector generate_string(const symbol& representation, const std::string& string_value) requires (architecture == architecture_t::x86_64_linux) { return { "__EMITED_STRING_____"+std::to_string(representation.id)+"___:\n", " .asciz \""+escape(string_value)+"\"\n", }; } std::vector generate_push_string_ptr(const symbol& representation) { return { " pushq $__EMITED_STRING_____"+std::to_string(representation.id)+"___\n" }; } std::vector generate_push_int32(int32_t target) requires (architecture == architecture_t::x86_64_linux) { return { " pushq $" +std::to_string(target)+ "\n" }; } std::vector generate_push_int64(int64_t target) requires (architecture == architecture_t::x86_64_linux) { return { " pushq $" +std::to_string(target)+ "\n" }; } std::vector generate_label(const std::string& target) requires (architecture == architecture_t::x86_64_linux) { return { marshal(target)+":\n" }; } std::vector generate_return() requires (architecture == architecture_t::x86_64_linux) { return { " // Return to caller\n", " addq $-8, %r10\n", " pushq (%r10)\n", " retq\n" }; } std::vector generate_enter() requires (architecture == architecture_t::x86_64_linux) { return { " // Prepare the function stack\n", " popq (%r10)\n" " addq $8, %r10\n", }; } std::vector initialize_stack() requires (architecture == architecture_t::x86_64_linux) { std::vector operations = { "code:\n", " .skip 1000000\n", ".text\n", " .globl _start\n", "initialize_callstack:\n", " movq $9, %rax\n", " movq $0, %rdi\n", " movq $8192, %rsi\n", " movq $3, %rdx\n", " movq $34, %r10\n", " movq $-1, %r8\n", " movq $0, %r9\n", " syscall\n", " movq %rax, %r10\n", " retq\n", "_start:\n", " call initialize_callstack\n" }; for(const auto& op : generate_call("main")) { operations.push_back(op); } for(const auto& op : std::vector{ " movq $0, %rdi\n", " movq $60, %rax\n", " syscall\n" } ) { operations.push_back(op); } return operations; } }