add @setAlignStack builtin

master
Andrew Kelley 2017-10-02 22:00:42 -04:00
parent c180ef86af
commit f1bd02e6f4
8 changed files with 158 additions and 5 deletions

View File

@ -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;

View File

@ -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) {

View File

@ -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:

View File

@ -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");
}

View File

@ -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);

View File

@ -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);

View File

@ -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);
}

View File

@ -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");
}