Commit 0edc2b19fe

Andrew Kelley <superjoe30@gmail.com>
2017-04-05 01:47:22
support module level assembly
closes #256
1 parent d97285e
src/all_types.hpp
@@ -1460,6 +1460,8 @@ struct CodeGen {
     ConstExprValue const_void_val;
 
     ConstExprValue panic_msg_vals[PanicMsgIdCount];
+
+    Buf global_asm;
 };
 
 enum VarLinkage {
src/codegen.cpp
@@ -73,6 +73,8 @@ CodeGen *codegen_create(Buf *root_source_dir, const ZigTarget *target) {
     g->is_test_build = false;
     g->want_h_file = true;
 
+    buf_resize(&g->global_asm, 0);
+
     // reserve index 0 to indicate no error
     g->error_decls.append(nullptr);
 
@@ -3725,6 +3727,10 @@ static void do_code_gen(CodeGen *g) {
     }
     assert(!g->errors.length);
 
+    if (buf_len(&g->global_asm) != 0) {
+        LLVMSetModuleInlineAsm(g->module, buf_ptr(&g->global_asm));
+    }
+
     ZigLLVMDIBuilderFinalize(g->dbuilder);
 
     if (g->verbose) {
src/ir.cpp
@@ -9896,13 +9896,30 @@ static TypeTableEntry *ir_analyze_instruction_slice_type(IrAnalyze *ira,
 static TypeTableEntry *ir_analyze_instruction_asm(IrAnalyze *ira, IrInstructionAsm *asm_instruction) {
     assert(asm_instruction->base.source_node->type == NodeTypeAsmExpr);
 
+    AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
+
+    bool global_scope = (scope_fn_entry(asm_instruction->base.scope) == nullptr);
+    if (global_scope) {
+        if (asm_expr->output_list.length != 0 || asm_expr->input_list.length != 0 ||
+            asm_expr->clobber_list.length != 0)
+        {
+            ir_add_error(ira, &asm_instruction->base,
+                buf_sprintf("global assembly cannot have inputs, outputs, or clobbers"));
+            return ira->codegen->builtin_types.entry_invalid;
+        }
+
+        buf_append_char(&ira->codegen->global_asm, '\n');
+        buf_append_buf(&ira->codegen->global_asm, asm_expr->asm_template);
+
+        ir_build_const_from(ira, &asm_instruction->base);
+        return ira->codegen->builtin_types.entry_void;
+    }
+
     if (!ir_emit_global_runtime_side_effect(ira, &asm_instruction->base))
         return ira->codegen->builtin_types.entry_invalid;
 
     // TODO validate the output types and variable types
 
-    AstNodeAsmExpr *asm_expr = &asm_instruction->base.source_node->data.asm_expr;
-
     IrInstruction **input_list = allocate<IrInstruction *>(asm_expr->input_list.length);
     IrInstruction **output_types = allocate<IrInstruction *>(asm_expr->output_list.length);
 
test/cases/asm.zig
@@ -0,0 +1,23 @@
+const assert = @import("std").debug.assert;
+
+comptime {
+    if (@compileVar("arch") == Arch.x86_64) {
+        asm volatile (
+            \\.globl aoeu;
+            \\.type aoeu, @function;
+            \\.set aoeu, derp;
+        );
+    }
+}
+
+test "module level assembly" {
+    if (@compileVar("arch") == Arch.x86_64) {
+        assert(aoeu() == 1234);
+    }
+}
+
+extern fn aoeu() -> i32;
+
+export fn derp() -> i32 {
+    return 1234;
+}
test/run_tests.cpp
@@ -1834,6 +1834,20 @@ fn foo(e: error) -> u2 {
 }
 export fn entry() -> usize { @sizeOf(@typeOf(foo)) }
     )SOURCE", 1, ".tmp_source.zig:5:14: error: too many error values to fit in 'u2'");
+
+    add_compile_fail_case("asm at compile time", R"SOURCE(
+comptime {
+    doSomeAsm();
+}
+
+fn doSomeAsm() {
+    asm volatile (
+        \\.globl aoeu;
+        \\.type aoeu, @function;
+        \\.set aoeu, derp;
+    );
+}
+    )SOURCE", 1, ".tmp_source.zig:7:5: error: unable to evaluate constant expression");
 }
 
 //////////////////////////////////////////////////////////////////////////////
test/self_hosted.zig
@@ -1,5 +1,6 @@
 comptime {
     _ = @import("cases/array.zig");
+    _ = @import("cases/asm.zig");
     _ = @import("cases/atomics.zig");
     _ = @import("cases/bool.zig");
     _ = @import("cases/cast.zig");