commit 402b03c4a9d0a4d2c69cf5914c131f6efa4bc4c9 (tree)
parent c180ef86afee17e36135b653fb6256cff46f4e69
Author: Andrew Kelley <superjoe30@gmail.com>
Date: Tue, 3 Oct 2017 00:58:57 -0400
Merge branch 'windows-alignment'
Diffstat:
9 files changed, 188 insertions(+), 42 deletions(-)
diff --git 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
@@ -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");
@@ -866,7 +867,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
addLLVMFnAttr(fn_val, "noreturn");
addLLVMFnAttr(fn_val, "cold");
LLVMSetLinkage(fn_val, LLVMInternalLinkage);
- LLVMSetFunctionCallConv(fn_val, LLVMFastCallConv);
+ LLVMSetFunctionCallConv(fn_val, get_llvm_cc(g, CallingConventionUnspecified));
addLLVMFnAttr(fn_val, "nounwind");
if (g->build_mode == BuildModeDebug) {
ZigLLVMAddFunctionAttr(fn_val, "no-frame-pointer-elim", "true");
@@ -923,7 +924,8 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
- ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, LLVMFastCallConv, false, "");
+ ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
+ false, "");
LLVMBuildUnreachable(g->builder);
}
@@ -3379,6 +3381,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 +4787,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
@@ -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:
diff --git 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
@@ -423,7 +423,7 @@ static void construct_linker_job_coff(LinkJob *lj) {
if (g->have_winmain) {
lj->args.append("-ENTRY:WinMain");
} else {
- lj->args.append("-ENTRY:_start");
+ lj->args.append("-ENTRY:WinMainCRTStartup");
}
}
@@ -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
@@ -5,12 +5,13 @@ const root = @import("@root");
const std = @import("std");
const builtin = @import("builtin");
+const is_windows = builtin.os == builtin.Os.windows;
const want_main_symbol = builtin.link_libc;
-const want_start_symbol = !want_main_symbol;
+const want_start_symbol = !want_main_symbol and !is_windows;
+const want_WinMainCRTStartup = is_windows and !builtin.link_libc;
var argc_ptr: &usize = undefined;
-const is_windows = builtin.os == builtin.Os.windows;
export nakedcc fn _start() -> noreturn {
if (!want_start_symbol) {
@@ -18,10 +19,6 @@ export nakedcc fn _start() -> noreturn {
unreachable;
}
- if (is_windows) {
- windowsCallMainAndExit()
- }
-
switch (builtin.arch) {
builtin.Arch.x86_64 => {
argc_ptr = asm("lea (%%rsp), %[argc]": [argc] "=r" (-> &usize));
@@ -34,7 +31,13 @@ export nakedcc fn _start() -> noreturn {
posixCallMainAndExit()
}
-fn windowsCallMainAndExit() -> noreturn {
+export fn WinMainCRTStartup() -> noreturn {
+ if (!want_WinMainCRTStartup) {
+ @setGlobalLinkage(WinMainCRTStartup, builtin.GlobalLinkage.Internal);
+ unreachable;
+ }
+ @setAlignStack(16);
+
std.debug.user_main_fn = root.main;
root.main() %% std.os.windows.ExitProcess(1);
std.os.windows.ExitProcess(0);
diff --git a/std/special/compiler_rt/index.zig b/std/special/compiler_rt/index.zig
@@ -129,8 +129,9 @@ export nakedcc fn _chkstk() align(4) {
@setGlobalLinkage(_chkstk, strong_linkage);
asm volatile (
\\ push %%ecx
+ \\ push %%eax
\\ cmp $0x1000,%%eax
- \\ lea 8(%%esp),%%ecx // esp before calling this routine -> ecx
+ \\ lea 12(%%esp),%%ecx
\\ jb 1f
\\ 2:
\\ sub $0x1000,%%ecx
@@ -141,12 +142,8 @@ export nakedcc fn _chkstk() align(4) {
\\ 1:
\\ sub %%eax,%%ecx
\\ test %%ecx,(%%ecx)
- \\
- \\ lea 4(%%esp),%%eax // load pointer to the return address into eax
- \\ mov %%ecx,%%esp // install the new top of stack pointer into esp
- \\ mov -4(%%eax),%%ecx // restore ecx
- \\ push (%%eax) // push return address onto the stack
- \\ sub %%esp,%%eax // restore the original value in eax
+ \\ pop %%eax
+ \\ pop %%ecx
\\ ret
);
unreachable;
@@ -155,32 +152,33 @@ export nakedcc fn _chkstk() align(4) {
@setGlobalLinkage(_chkstk, builtin.GlobalLinkage.Internal);
}
+// TODO The implementation from compiler-rt causes crashes and
+// the implementation from disassembled ntdll seems to depend on
+// thread local storage. So we have given up this safety check
+// and simply have `ret`.
export nakedcc fn __chkstk() align(4) {
@setDebugSafety(this, false);
if (win64_nocrt) {
@setGlobalLinkage(__chkstk, strong_linkage);
asm volatile (
- \\ push %%rcx
- \\ cmp $0x1000,%%rax
- \\ lea 16(%%rsp),%%rcx // rsp before calling this routine -> rcx
- \\ jb 1f
- \\ 2:
- \\ sub $0x1000,%%rcx
- \\ test %%rcx,(%%rcx)
- \\ sub $0x1000,%%rax
- \\ cmp $0x1000,%%rax
- \\ ja 2b
- \\ 1:
- \\ sub %%rax,%%rcx
- \\ test %%rcx,(%%rcx)
- \\
- \\ lea 8(%%rsp),%%rax // load pointer to the return address into rax
- \\ mov %%rcx,%%rsp // install the new top of stack pointer into rsp
- \\ mov -8(%%rax),%%rcx // restore rcx
- \\ push (%%rax) // push return address onto the stack
- \\ sub %%rsp,%%rax // restore the original value in rax
- \\ ret
+ \\ push %%rcx
+ \\ push %%rax
+ \\ cmp $0x1000,%%rax
+ \\ lea 24(%%rsp),%%rcx
+ \\ jb 1f
+ \\2:
+ \\ sub $0x1000,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ sub $0x1000,%%rax
+ \\ cmp $0x1000,%%rax
+ \\ ja 2b
+ \\1:
+ \\ sub %%rax,%%rcx
+ \\ test %%rcx,(%%rcx)
+ \\ pop %%rax
+ \\ pop %%rcx
+ \\ ret
);
unreachable;
}
diff --git 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
@@ -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");
}