add @setAlignStack builtin
parent
c180ef86af
commit
f1bd02e6f4
|
@ -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");
|
||||
|
@ -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) {
|
||||
|
|
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");
|
||||
}
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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);
|
||||
|
|
|
@ -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…
Reference in New Issue