mirror of
https://github.com/ziglang/zig.git
synced 2026-02-14 13:30:45 +00:00
Merge branch 'windows-alignment'
This commit is contained in:
commit
402b03c4a9
@ -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;
|
||||
|
||||
|
||||
@ -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) {
|
||||
|
||||
71
src/ir.cpp
71
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:
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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);
|
||||
|
||||
@ -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;
|
||||
}
|
||||
|
||||
@ -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);
|
||||
}
|
||||
|
||||
@ -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");
|
||||
}
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user