Skip to content

Commit

Permalink
add higher level arg-parsing API + misc. changes
Browse files Browse the repository at this point in the history
 * add @noInlineCall - see #640
   This fixes a crash in --release-safe and --release-fast modes
   where the optimizer inlines everything into _start and
   clobbers the command line argument data.
   If we were able to verify that the user's code never reads
   command line args, we could leave off this "no inline"
   attribute.
 * add i29 and u29 primitive types. u29 is the type of alignment,
   so it makes sense to be a primitive.
   probably in the future we'll make any `i` or `u` followed by
   digits into a primitive.
 * add `aligned` functions to Allocator interface
 * add `os.argsAlloc` and `os.argsFree` so that you can get
   a `[]const []u8`, do whatever arg parsing you want, and then free
   it. For now this uses the other API under the hood, but it could
   be reimplemented to do a single allocation.
 * add tests to make sure command line argument parsing works.
  • Loading branch information
andrewrk committed Dec 6, 2017
1 parent 249cb2a commit 62c25af
Show file tree
Hide file tree
Showing 13 changed files with 249 additions and 58 deletions.
5 changes: 3 additions & 2 deletions src/all_types.hpp
Expand Up @@ -1270,6 +1270,7 @@ enum BuiltinFnId {
BuiltinFnIdFieldParentPtr,
BuiltinFnIdOffsetOf,
BuiltinFnIdInlineCall,
BuiltinFnIdNoInlineCall,
BuiltinFnIdTypeId,
BuiltinFnIdShlExact,
BuiltinFnIdShrExact,
Expand Down Expand Up @@ -1439,7 +1440,7 @@ struct CodeGen {

struct {
TypeTableEntry *entry_bool;
TypeTableEntry *entry_int[2][11]; // [signed,unsigned][2,3,4,5,6,7,8,16,32,64,128]
TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128]
TypeTableEntry *entry_c_int[CIntTypeCount];
TypeTableEntry *entry_c_longdouble;
TypeTableEntry *entry_c_void;
Expand Down Expand Up @@ -2102,7 +2103,7 @@ struct IrInstructionCall {
IrInstruction **args;
bool is_comptime;
LLVMValueRef tmp_ptr;
bool is_inline;
FnInline fn_inline;
};

struct IrInstructionConst {
Expand Down
8 changes: 5 additions & 3 deletions src/analyze.cpp
Expand Up @@ -3818,12 +3818,14 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_b
index = 6;
} else if (size_in_bits == 16) {
index = 7;
} else if (size_in_bits == 32) {
} else if (size_in_bits == 29) {
index = 8;
} else if (size_in_bits == 64) {
} else if (size_in_bits == 32) {
index = 9;
} else if (size_in_bits == 128) {
} else if (size_in_bits == 64) {
index = 10;
} else if (size_in_bits == 128) {
index = 11;
} else {
return nullptr;
}
Expand Down
22 changes: 17 additions & 5 deletions src/codegen.cpp
Expand Up @@ -839,7 +839,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
assert(g->panic_fn != nullptr);
LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, false, "");
ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}

Expand Down Expand Up @@ -988,7 +988,7 @@ 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, get_llvm_cc(g, CallingConventionUnspecified),
false, "");
ZigLLVM_FnInlineAuto, "");
LLVMBuildUnreachable(g->builder);
}

Expand Down Expand Up @@ -2316,12 +2316,22 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
}
}

bool want_always_inline = (instruction->fn_entry != nullptr &&
instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_inline;
ZigLLVM_FnInline fn_inline;
switch (instruction->fn_inline) {
case FnInlineAuto:
fn_inline = ZigLLVM_FnInlineAuto;
break;
case FnInlineAlways:
fn_inline = (instruction->fn_entry == nullptr) ? ZigLLVM_FnInlineAuto : ZigLLVM_FnInlineAlways;
break;
case FnInlineNever:
fn_inline = ZigLLVM_FnInlineNever;
break;
}

LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc);
LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
gen_param_values, (unsigned)gen_param_index, llvm_cc, want_always_inline, "");
gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, "");

for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
Expand Down Expand Up @@ -4634,6 +4644,7 @@ static const uint8_t int_sizes_in_bits[] = {
7,
8,
16,
29,
32,
64,
128,
Expand Down Expand Up @@ -4971,6 +4982,7 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
Expand Down
28 changes: 15 additions & 13 deletions src/ir.cpp
Expand Up @@ -928,13 +928,13 @@ static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstructio

static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
bool is_comptime, bool is_inline)
bool is_comptime, FnInline fn_inline)
{
IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, scope, source_node);
call_instruction->fn_entry = fn_entry;
call_instruction->fn_ref = fn_ref;
call_instruction->is_comptime = is_comptime;
call_instruction->is_inline = is_inline;
call_instruction->fn_inline = fn_inline;
call_instruction->args = args;
call_instruction->arg_count = arg_count;

Expand All @@ -948,10 +948,10 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc

static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,
FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
bool is_comptime, bool is_inline)
bool is_comptime, FnInline fn_inline)
{
IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope,
old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, is_inline);
old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
Expand Down Expand Up @@ -4672,6 +4672,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdInlineCall:
case BuiltinFnIdNoInlineCall:
{
if (node->data.fn_call_expr.params.length == 0) {
add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
Expand All @@ -4692,8 +4693,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
if (args[i] == irb->codegen->invalid_instruction)
return args[i];
}
FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;

return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, true);
return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline);
}
case BuiltinFnIdTypeId:
{
Expand Down Expand Up @@ -4804,7 +4806,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
return args[i];
}

return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, false);
return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto);
}

static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
Expand Down Expand Up @@ -10617,7 +10619,7 @@ static IrInstruction *ir_get_var_ptr(IrAnalyze *ira, IrInstruction *instruction,

static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
IrInstruction *first_arg_ptr, bool comptime_fn_call, bool inline_fn_call)
IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
{
FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
Expand Down Expand Up @@ -10876,7 +10878,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal

if (type_requires_comptime(return_type)) {
// Throw out our work and call the function as if it were comptime.
return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, false);
return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
}
}

Expand All @@ -10900,7 +10902,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal

size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;
IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
impl_fn, nullptr, impl_param_count, casted_args, false, inline_fn_call);
impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline);

TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
ir_add_alloca(ira, new_call_instruction, return_type);
Expand Down Expand Up @@ -10959,7 +10961,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
return ira->codegen->builtin_types.entry_invalid;

IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
fn_entry, fn_ref, call_param_count, casted_args, false, inline_fn_call);
fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline);

ir_add_alloca(ira, new_call_instruction, return_type);
return ir_finish_anal(ira, return_type);
Expand Down Expand Up @@ -10998,13 +11000,13 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
} else if (fn_ref->value.type->id == TypeTableEntryIdFn) {
FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
fn_ref, nullptr, is_comptime, call_instruction->is_inline);
fn_ref, nullptr, is_comptime, call_instruction->fn_inline);
} else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {
assert(fn_ref->value.special == ConstValSpecialStatic);
FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
nullptr, first_arg_ptr, is_comptime, call_instruction->is_inline);
nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline);
} else {
ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
Expand All @@ -11014,7 +11016,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction

if (fn_ref->value.type->id == TypeTableEntryIdFn) {
return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,
fn_ref, nullptr, false, false);
fn_ref, nullptr, false, FnInlineAuto);
} else {
ir_add_error_node(ira, fn_ref->source_node,
buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
Expand Down
13 changes: 10 additions & 3 deletions src/zig_llvm.cpp
Expand Up @@ -175,12 +175,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM


LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
unsigned NumArgs, unsigned CC, bool always_inline, const char *Name)
unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name)
{
CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);
call_inst->setCallingConv(CC);
if (always_inline) {
call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
switch (fn_inline) {
case ZigLLVM_FnInlineAuto:
break;
case ZigLLVM_FnInlineAlways:
call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
break;
case ZigLLVM_FnInlineNever:
call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline);
break;
}
return wrap(unwrap(B)->Insert(call_inst));
}
Expand Down
7 changes: 6 additions & 1 deletion src/zig_llvm.hpp
Expand Up @@ -45,8 +45,13 @@ enum ZigLLVM_EmitOutputType {
bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);

enum ZigLLVM_FnInline {
ZigLLVM_FnInlineAuto,
ZigLLVM_FnInlineAlways,
ZigLLVM_FnInlineNever,
};
LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
unsigned NumArgs, unsigned CC, bool always_inline, const char *Name);
unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name);

LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
Expand Down
4 changes: 2 additions & 2 deletions std/debug.zig
Expand Up @@ -977,7 +977,7 @@ var some_mem_index: usize = 0;

error OutOfMemory;

fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
fn globalAlloc(self: &mem.Allocator, n: usize, alignment: u29) -> %[]u8 {
const addr = @ptrToInt(&some_mem[some_mem_index]);
const rem = @rem(addr, alignment);
const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
Expand All @@ -991,7 +991,7 @@ fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
return result;
}

fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
if (new_size <= old_mem.len) {
return old_mem[0..new_size];
} else {
Expand Down
8 changes: 4 additions & 4 deletions std/heap.zig
Expand Up @@ -16,15 +16,15 @@ pub var c_allocator = Allocator {
.freeFn = cFree,
};

fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
fn cAlloc(self: &Allocator, n: usize, alignment: u29) -> %[]u8 {
if (c.malloc(usize(n))) |buf| {
@ptrCast(&u8, buf)[0..n]
} else {
error.OutOfMemory
}
}

fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
if (new_size <= old_mem.len) {
old_mem[0..new_size]
} else {
Expand Down Expand Up @@ -106,7 +106,7 @@ pub const IncrementingAllocator = struct {
return self.bytes.len - self.end_index;
}

fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
const addr = @ptrToInt(&self.bytes[self.end_index]);
const rem = @rem(addr, alignment);
Expand All @@ -121,7 +121,7 @@ pub const IncrementingAllocator = struct {
return result;
}

fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
if (new_size <= old_mem.len) {
return old_mem[0..new_size];
} else {
Expand Down

0 comments on commit 62c25af

Please sign in to comment.