Commit f1bd02e6f4

Andrew Kelley <superjoe30@gmail.com>
2017-10-03 04:00:42
add @setAlignStack builtin
1 parent c180ef8
src/all_types.hpp
@@ -1191,6 +1191,8 @@ struct FnTableEntry {
     Buf *section_name;
     AstNode *set_global_linkage_node;
     GlobalLinkageId linkage;
+    AstNode *set_alignstack_node;
+    uint32_t alignstack_value;
 };
 
 uint32_t fn_table_entry_hash(FnTableEntry*);
@@ -1254,6 +1256,7 @@ enum BuiltinFnId {
     BuiltinFnIdSetEvalBranchQuota,
     BuiltinFnIdAlignCast,
     BuiltinFnIdOpaqueType,
+    BuiltinFnIdSetAlignStack,
 };
 
 struct BuiltinFnEntry {
@@ -1860,6 +1863,7 @@ enum IrInstructionId {
     IrInstructionIdPtrTypeOf,
     IrInstructionIdAlignCast,
     IrInstructionIdOpaqueType,
+    IrInstructionIdSetAlignStack,
 };
 
 struct IrInstruction {
@@ -2654,6 +2658,12 @@ struct IrInstructionOpaqueType {
     IrInstruction base;
 };
 
+struct IrInstructionSetAlignStack {
+    IrInstruction base;
+
+    IrInstruction *align_bytes;
+};
+
 static const size_t slice_ptr_index = 0;
 static const size_t slice_len_index = 1;
 
src/codegen.cpp
@@ -410,6 +410,9 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
             addLLVMFnAttr(fn_table_entry->llvm_value, "noinline");
             break;
         case FnInlineAuto:
+            if (fn_table_entry->alignstack_value != 0) {
+                addLLVMFnAttr(fn_table_entry->llvm_value, "noinline");
+            }
             break;
     }
 
@@ -452,10 +455,8 @@ static LLVMValueRef fn_llvm_value(CodeGen *g, FnTableEntry *fn_table_entry) {
         }
     }
 
-    if (g->zig_target.os == ZigLLVM_Win32 && g->zig_target.arch.arch == ZigLLVM_x86_64 &&
-        fn_type->data.fn.fn_type_id.cc != CallingConventionNaked)
-    {
-        addLLVMFnAttrInt(fn_table_entry->llvm_value, "alignstack", 16);
+    if (fn_table_entry->alignstack_value != 0) {
+        addLLVMFnAttrInt(fn_table_entry->llvm_value, "alignstack", fn_table_entry->alignstack_value);
     }
 
     addLLVMFnAttr(fn_table_entry->llvm_value, "nounwind");
@@ -3379,6 +3380,7 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
         case IrInstructionIdSetEvalBranchQuota:
         case IrInstructionIdPtrTypeOf:
         case IrInstructionIdOpaqueType:
+        case IrInstructionIdSetAlignStack:
             zig_unreachable();
         case IrInstructionIdReturn:
             return ir_render_return(g, executable, (IrInstructionReturn *)instruction);
@@ -4784,6 +4786,7 @@ static void define_builtin_fns(CodeGen *g) {
     create_builtin_fn(g, BuiltinFnIdSetEvalBranchQuota, "setEvalBranchQuota", 1);
     create_builtin_fn(g, BuiltinFnIdAlignCast, "alignCast", 2);
     create_builtin_fn(g, BuiltinFnIdOpaqueType, "OpaqueType", 0);
+    create_builtin_fn(g, BuiltinFnIdSetAlignStack, "setAlignStack", 1);
 }
 
 static const char *bool_to_str(bool b) {
src/ir.cpp
@@ -563,6 +563,10 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionOpaqueType *) {
     return IrInstructionIdOpaqueType;
 }
 
+static constexpr IrInstructionId ir_instruction_id(IrInstructionSetAlignStack *) {
+    return IrInstructionIdSetAlignStack;
+}
+
 template<typename T>
 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) {
     T *special_instruction = allocate<T>(1);
@@ -2248,6 +2252,17 @@ static IrInstruction *ir_build_opaque_type(IrBuilder *irb, Scope *scope, AstNode
     return &instruction->base;
 }
 
+static IrInstruction *ir_build_set_align_stack(IrBuilder *irb, Scope *scope, AstNode *source_node,
+        IrInstruction *align_bytes)
+{
+    IrInstructionSetAlignStack *instruction = ir_build_instruction<IrInstructionSetAlignStack>(irb, scope, source_node);
+    instruction->align_bytes = align_bytes;
+
+    ir_ref_instruction(align_bytes, irb->current_basic_block);
+
+    return &instruction->base;
+}
+
 static IrInstruction *ir_instruction_br_get_dep(IrInstructionBr *instruction, size_t index) {
     return nullptr;
 }
@@ -2970,6 +2985,13 @@ static IrInstruction *ir_instruction_opaquetype_get_dep(IrInstructionOpaqueType
     return nullptr;
 }
 
+static IrInstruction *ir_instruction_setalignstack_get_dep(IrInstructionSetAlignStack *instruction, size_t index) {
+    switch (index) {
+        case 0: return instruction->align_bytes;
+        default: return nullptr;
+    }
+}
+
 static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t index) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -3170,6 +3192,8 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
             return ir_instruction_aligncast_get_dep((IrInstructionAlignCast *) instruction, index);
         case IrInstructionIdOpaqueType:
             return ir_instruction_opaquetype_get_dep((IrInstructionOpaqueType *) instruction, index);
+        case IrInstructionIdSetAlignStack:
+            return ir_instruction_setalignstack_get_dep((IrInstructionSetAlignStack *) instruction, index);
     }
     zig_unreachable();
 }
@@ -4596,6 +4620,15 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
             }
         case BuiltinFnIdOpaqueType:
             return ir_build_opaque_type(irb, scope, node);
+        case BuiltinFnIdSetAlignStack:
+            {
+                AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
+                IrInstruction *arg0_value = ir_gen_node(irb, arg0_node, scope);
+                if (arg0_value == irb->codegen->invalid_instruction)
+                    return arg0_value;
+
+                return ir_build_set_align_stack(irb, scope, node, arg0_value);
+            }
     }
     zig_unreachable();
 }
@@ -15264,6 +15297,41 @@ static TypeTableEntry *ir_analyze_instruction_opaque_type(IrAnalyze *ira, IrInst
     return ira->codegen->builtin_types.entry_type;
 }
 
+static TypeTableEntry *ir_analyze_instruction_set_align_stack(IrAnalyze *ira, IrInstructionSetAlignStack *instruction) {
+    uint32_t align_bytes;
+    IrInstruction *align_bytes_inst = instruction->align_bytes->other;
+    if (!ir_resolve_align(ira, align_bytes_inst, &align_bytes))
+        return ira->codegen->builtin_types.entry_invalid;
+
+    FnTableEntry *fn_entry = exec_fn_entry(ira->new_irb.exec);
+    if (fn_entry == nullptr) {
+        ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack outside function"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+    if (fn_entry->type_entry->data.fn.fn_type_id.cc == CallingConventionNaked) {
+        ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in naked function"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    if (fn_entry->fn_inline == FnInlineAlways) {
+        ir_add_error(ira, &instruction->base, buf_sprintf("@setAlignStack in inline function"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    if (fn_entry->set_alignstack_node != nullptr) {
+        ErrorMsg *msg = ir_add_error_node(ira, instruction->base.source_node,
+            buf_sprintf("alignstack set twice"));
+        add_error_note(ira->codegen, msg, fn_entry->set_alignstack_node, buf_sprintf("first set here"));
+        return ira->codegen->builtin_types.entry_invalid;
+    }
+
+    fn_entry->set_alignstack_node = instruction->base.source_node;
+    fn_entry->alignstack_value = align_bytes;
+
+    ir_build_const_from(ira, &instruction->base);
+    return ira->codegen->builtin_types.entry_void;
+}
+
 static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstruction *instruction) {
     switch (instruction->id) {
         case IrInstructionIdInvalid:
@@ -15452,6 +15520,8 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
             return ir_analyze_instruction_align_cast(ira, (IrInstructionAlignCast *)instruction);
         case IrInstructionIdOpaqueType:
             return ir_analyze_instruction_opaque_type(ira, (IrInstructionOpaqueType *)instruction);
+        case IrInstructionIdSetAlignStack:
+            return ir_analyze_instruction_set_align_stack(ira, (IrInstructionSetAlignStack *)instruction);
     }
     zig_unreachable();
 }
@@ -15564,6 +15634,7 @@ bool ir_has_side_effects(IrInstruction *instruction) {
         case IrInstructionIdPanic:
         case IrInstructionIdSetEvalBranchQuota:
         case IrInstructionIdPtrTypeOf:
+        case IrInstructionIdSetAlignStack:
             return true;
         case IrInstructionIdPhi:
         case IrInstructionIdUnOp:
src/ir_print.cpp
@@ -948,6 +948,12 @@ static void ir_print_opaque_type(IrPrint *irp, IrInstructionOpaqueType *instruct
     fprintf(irp->f, "@OpaqueType()");
 }
 
+static void ir_print_set_align_stack(IrPrint *irp, IrInstructionSetAlignStack *instruction) {
+    fprintf(irp->f, "@setAlignStack(");
+    ir_print_other_instruction(irp, instruction->align_bytes);
+    fprintf(irp->f, ")");
+}
+
 static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
     ir_print_prefix(irp, instruction);
     switch (instruction->id) {
@@ -1247,6 +1253,9 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
         case IrInstructionIdOpaqueType:
             ir_print_opaque_type(irp, (IrInstructionOpaqueType *)instruction);
             break;
+        case IrInstructionIdSetAlignStack:
+            ir_print_set_align_stack(irp, (IrInstructionSetAlignStack *)instruction);
+            break;
     }
     fprintf(irp->f, "\n");
 }
src/link.cpp
@@ -846,7 +846,7 @@ void codegen_link(CodeGen *g, const char *out_file) {
         buf_resize(&lj.out_file, 0);
     }
 
-    if (g->verbose) {
+    if (g->verbose || g->verbose_ir) {
         fprintf(stderr, "\nOptimization:\n");
         fprintf(stderr, "---------------\n");
         LLVMDumpModule(g->module);
std/special/bootstrap.zig
@@ -19,6 +19,14 @@ export nakedcc fn _start() -> noreturn {
     }
 
     if (is_windows) {
+        if (builtin.arch == builtin.Arch.x86_64) {
+            // Align the stack pointer to 16 bytes.
+            asm volatile (
+                \\ and    $0xfffffffffffffff0,%%rsp
+                \\ sub    $0x10,%%rsp
+                :::"rsp"
+            );
+        }
         windowsCallMainAndExit()
     }
 
@@ -35,6 +43,7 @@ export nakedcc fn _start() -> noreturn {
 }
 
 fn windowsCallMainAndExit() -> noreturn {
+    @setAlignStack(16);
     std.debug.user_main_fn = root.main;
     root.main() %% std.os.windows.ExitProcess(1);
     std.os.windows.ExitProcess(0);
test/cases/align.zig
@@ -1,4 +1,5 @@
 const assert = @import("std").debug.assert;
+const builtin = @import("builtin");
 
 var foo: u8 align(4) = 100;
 
@@ -180,3 +181,20 @@ fn testIndex(smaller: &align(2) u32, index: usize, comptime T: type) {
 fn testIndex2(ptr: &align(4) u8, index: usize, comptime T: type) {
     assert(@typeOf(&ptr[index]) == T);
 }
+
+
+test "alignstack" {
+    fnWithAlignedStack();
+}
+
+fn fnWithAlignedStack() {
+    @setAlignStack(1024);
+    const stack_address = if (builtin.arch == builtin.Arch.x86_64) {
+        asm volatile ("" :[rsp] "={rsp}"(-> usize))
+    } else if (builtin.arch == builtin.Arch.i386) {
+        asm volatile ("" :[esp] "={esp}"(-> usize))
+    } else {
+        return;
+    };
+    assert(stack_address % 1024 == 0);
+}
test/compile_errors.zig
@@ -2153,4 +2153,37 @@ pub fn addCases(cases: &tests.CompileErrorContext) {
         \\}
     ,
         ".tmp_source.zig:14:17: error: use of undeclared identifier 'HeaderValue'");
+
+    cases.add("@setAlignStack outside function",
+        \\comptime {
+        \\    @setAlignStack(16);
+        \\}
+    ,
+        ".tmp_source.zig:2:5: error: @setAlignStack outside function");
+
+    cases.add("@setAlignStack in naked function",
+        \\export nakedcc fn entry() {
+        \\    @setAlignStack(16);
+        \\}
+    ,
+        ".tmp_source.zig:2:5: error: @setAlignStack in naked function");
+
+    cases.add("@setAlignStack in inline function",
+        \\export fn entry() {
+        \\    foo();
+        \\}
+        \\inline fn foo() {
+        \\    @setAlignStack(16);
+        \\}
+    ,
+        ".tmp_source.zig:5:5: error: @setAlignStack in inline function");
+
+    cases.add("@setAlignStack set twice",
+        \\export fn entry() {
+        \\    @setAlignStack(16);
+        \\    @setAlignStack(16);
+        \\}
+    ,
+        ".tmp_source.zig:3:5: error: alignstack set twice",
+        ".tmp_source.zig:2:5: note: first set here");
 }