From f1bd02e6f46821415d96f54f6a3258159ba5a9c5 Mon Sep 17 00:00:00 2001 From: Andrew Kelley Date: Mon, 2 Oct 2017 22:00:42 -0400 Subject: [PATCH] add @setAlignStack builtin --- src/all_types.hpp | 10 ++++++ src/codegen.cpp | 11 +++--- src/ir.cpp | 71 +++++++++++++++++++++++++++++++++++++++ src/ir_print.cpp | 9 +++++ src/link.cpp | 2 +- std/special/bootstrap.zig | 9 +++++ test/cases/align.zig | 18 ++++++++++ test/compile_errors.zig | 33 ++++++++++++++++++ 8 files changed, 158 insertions(+), 5 deletions(-) diff --git a/src/all_types.hpp b/src/all_types.hpp index 05df479beb..bbb4f43497 100644 --- a/src/all_types.hpp +++ b/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; diff --git a/src/codegen.cpp b/src/codegen.cpp index 18d19e39f0..6a0e7e12f4 100644 --- a/src/codegen.cpp +++ b/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) { diff --git a/src/ir.cpp b/src/ir.cpp index 422c40b678..570783872f 100644 --- a/src/ir.cpp +++ 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 static T *ir_create_instruction(IrBuilder *irb, Scope *scope, AstNode *source_node) { T *special_instruction = allocate(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(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: diff --git a/src/ir_print.cpp b/src/ir_print.cpp index fccf11038a..63ccd76ef0 100644 --- a/src/ir_print.cpp +++ b/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"); } diff --git a/src/link.cpp b/src/link.cpp index 75ec651942..bd17ff1ea4 100644 --- a/src/link.cpp +++ b/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); diff --git a/std/special/bootstrap.zig b/std/special/bootstrap.zig index d2554fd8df..3067406431 100644 --- a/std/special/bootstrap.zig +++ b/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); diff --git a/test/cases/align.zig b/test/cases/align.zig index c655b1b593..98d307964c 100644 --- a/test/cases/align.zig +++ b/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); +} diff --git a/test/compile_errors.zig b/test/compile_errors.zig index fc74cc1bf0..cbde0e9b99 100644 --- a/test/compile_errors.zig +++ b/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"); }