authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-12-06 18:12:05-05:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2017-12-06 18:12:05-05:00
log62c25af8021fc399c9a8c667dd986a458b40a7dd
treebe055f4bc246bb05360dac57c02dc3d43b26563f
parent249cb2aa30bbdd0c30f24ef18097e3b1cd3e0da5

add higher level arg-parsing API + misc. changes

* 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.

13 files changed, 249 insertions(+), 58 deletions(-)

src/all_types.hpp+3-2
......@@ -1270,6 +1270,7 @@ enum BuiltinFnId {
12701270 BuiltinFnIdFieldParentPtr,
12711271 BuiltinFnIdOffsetOf,
12721272 BuiltinFnIdInlineCall,
1273 BuiltinFnIdNoInlineCall,
12731274 BuiltinFnIdTypeId,
12741275 BuiltinFnIdShlExact,
12751276 BuiltinFnIdShrExact,
......@@ -1439,7 +1440,7 @@ struct CodeGen {
14391440
14401441 struct {
14411442 TypeTableEntry *entry_bool;
1442 TypeTableEntry *entry_int[2][11]; // [signed,unsigned][2,3,4,5,6,7,8,16,32,64,128]
1443 TypeTableEntry *entry_int[2][12]; // [signed,unsigned][2,3,4,5,6,7,8,16,29,32,64,128]
14431444 TypeTableEntry *entry_c_int[CIntTypeCount];
14441445 TypeTableEntry *entry_c_longdouble;
14451446 TypeTableEntry *entry_c_void;
......@@ -2102,7 +2103,7 @@ struct IrInstructionCall {
21022103 IrInstruction **args;
21032104 bool is_comptime;
21042105 LLVMValueRef tmp_ptr;
2105 bool is_inline;
2106 FnInline fn_inline;
21062107};
21072108
21082109struct IrInstructionConst {
src/analyze.cpp+5-3
......@@ -3818,12 +3818,14 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_b
38183818 index = 6;
38193819 } else if (size_in_bits == 16) {
38203820 index = 7;
3821 } else if (size_in_bits == 32) {
3821 } else if (size_in_bits == 29) {
38223822 index = 8;
3823 } else if (size_in_bits == 64) {
3823 } else if (size_in_bits == 32) {
38243824 index = 9;
3825 } else if (size_in_bits == 128) {
3825 } else if (size_in_bits == 64) {
38263826 index = 10;
3827 } else if (size_in_bits == 128) {
3828 index = 11;
38273829 } else {
38283830 return nullptr;
38293831 }
src/codegen.cpp+17-5
......@@ -839,7 +839,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
839839 assert(g->panic_fn != nullptr);
840840 LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
841841 LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);
842 ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, false, "");
842 ZigLLVMBuildCall(g->builder, fn_val, &msg_arg, 1, llvm_cc, ZigLLVM_FnInlineAuto, "");
843843 LLVMBuildUnreachable(g->builder);
844844}
845845
......@@ -988,7 +988,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
988988static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
989989 LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
990990 ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
991 false, "");
991 ZigLLVM_FnInlineAuto, "");
992992 LLVMBuildUnreachable(g->builder);
993993}
994994
......@@ -2316,12 +2316,22 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
23162316 }
23172317 }
23182318
2319 bool want_always_inline = (instruction->fn_entry != nullptr &&
2320 instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_inline;
2319 ZigLLVM_FnInline fn_inline;
2320 switch (instruction->fn_inline) {
2321 case FnInlineAuto:
2322 fn_inline = ZigLLVM_FnInlineAuto;
2323 break;
2324 case FnInlineAlways:
2325 fn_inline = (instruction->fn_entry == nullptr) ? ZigLLVM_FnInlineAuto : ZigLLVM_FnInlineAlways;
2326 break;
2327 case FnInlineNever:
2328 fn_inline = ZigLLVM_FnInlineNever;
2329 break;
2330 }
23212331
23222332 LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc);
23232333 LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,
2324 gen_param_values, (unsigned)gen_param_index, llvm_cc, want_always_inline, "");
2334 gen_param_values, (unsigned)gen_param_index, llvm_cc, fn_inline, "");
23252335
23262336 for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
23272337 FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
......@@ -4634,6 +4644,7 @@ static const uint8_t int_sizes_in_bits[] = {
46344644 7,
46354645 8,
46364646 16,
4647 29,
46374648 32,
46384649 64,
46394650 128,
......@@ -4971,6 +4982,7 @@ static void define_builtin_fns(CodeGen *g) {
49714982 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
49724983 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
49734984 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
4985 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
49744986 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
49754987 create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
49764988 create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);
src/ir.cpp+15-13
......@@ -928,13 +928,13 @@ static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstructio
928928
929929static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
930930 FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
931 bool is_comptime, bool is_inline)
931 bool is_comptime, FnInline fn_inline)
932932{
933933 IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, scope, source_node);
934934 call_instruction->fn_entry = fn_entry;
935935 call_instruction->fn_ref = fn_ref;
936936 call_instruction->is_comptime = is_comptime;
937 call_instruction->is_inline = is_inline;
937 call_instruction->fn_inline = fn_inline;
938938 call_instruction->args = args;
939939 call_instruction->arg_count = arg_count;
940940
......@@ -948,10 +948,10 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc
948948
949949static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,
950950 FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,
951 bool is_comptime, bool is_inline)
951 bool is_comptime, FnInline fn_inline)
952952{
953953 IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope,
954 old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, is_inline);
954 old_instruction->source_node, fn_entry, fn_ref, arg_count, args, is_comptime, fn_inline);
955955 ir_link_new_instruction(new_instruction, old_instruction);
956956 return new_instruction;
957957}
......@@ -4672,6 +4672,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
46724672 return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
46734673 }
46744674 case BuiltinFnIdInlineCall:
4675 case BuiltinFnIdNoInlineCall:
46754676 {
46764677 if (node->data.fn_call_expr.params.length == 0) {
46774678 add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));
......@@ -4692,8 +4693,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
46924693 if (args[i] == irb->codegen->invalid_instruction)
46934694 return args[i];
46944695 }
4696 FnInline fn_inline = (builtin_fn->id == BuiltinFnIdInlineCall) ? FnInlineAlways : FnInlineNever;
46954697
4696 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, true);
4698 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, fn_inline);
46974699 }
46984700 case BuiltinFnIdTypeId:
46994701 {
......@@ -4804,7 +4806,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
48044806 return args[i];
48054807 }
48064808
4807 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, false);
4809 return ir_build_call(irb, scope, node, nullptr, fn_ref, arg_count, args, false, FnInlineAuto);
48084810}
48094811
48104812static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
......@@ -10617,7 +10619,7 @@ no_mem_slot:
1061710619
1061810620static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
1061910621 FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,
10620 IrInstruction *first_arg_ptr, bool comptime_fn_call, bool inline_fn_call)
10622 IrInstruction *first_arg_ptr, bool comptime_fn_call, FnInline fn_inline)
1062110623{
1062210624 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
1062310625 size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;
......@@ -10876,7 +10878,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1087610878
1087710879 if (type_requires_comptime(return_type)) {
1087810880 // Throw out our work and call the function as if it were comptime.
10879 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, false);
10881 return ir_analyze_fn_call(ira, call_instruction, fn_entry, fn_type, fn_ref, first_arg_ptr, true, FnInlineAuto);
1088010882 }
1088110883 }
1088210884
......@@ -10900,7 +10902,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1090010902
1090110903 size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;
1090210904 IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
10903 impl_fn, nullptr, impl_param_count, casted_args, false, inline_fn_call);
10905 impl_fn, nullptr, impl_param_count, casted_args, false, fn_inline);
1090410906
1090510907 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
1090610908 ir_add_alloca(ira, new_call_instruction, return_type);
......@@ -10959,7 +10961,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1095910961 return ira->codegen->builtin_types.entry_invalid;
1096010962
1096110963 IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,
10962 fn_entry, fn_ref, call_param_count, casted_args, false, inline_fn_call);
10964 fn_entry, fn_ref, call_param_count, casted_args, false, fn_inline);
1096310965
1096410966 ir_add_alloca(ira, new_call_instruction, return_type);
1096510967 return ir_finish_anal(ira, return_type);
......@@ -10998,13 +11000,13 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
1099811000 } else if (fn_ref->value.type->id == TypeTableEntryIdFn) {
1099911001 FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
1100011002 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
11001 fn_ref, nullptr, is_comptime, call_instruction->is_inline);
11003 fn_ref, nullptr, is_comptime, call_instruction->fn_inline);
1100211004 } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {
1100311005 assert(fn_ref->value.special == ConstValSpecialStatic);
1100411006 FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
1100511007 IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
1100611008 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,
11007 nullptr, first_arg_ptr, is_comptime, call_instruction->is_inline);
11009 nullptr, first_arg_ptr, is_comptime, call_instruction->fn_inline);
1100811010 } else {
1100911011 ir_add_error_node(ira, fn_ref->source_node,
1101011012 buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
......@@ -11014,7 +11016,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
1101411016
1101511017 if (fn_ref->value.type->id == TypeTableEntryIdFn) {
1101611018 return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,
11017 fn_ref, nullptr, false, false);
11019 fn_ref, nullptr, false, FnInlineAuto);
1101811020 } else {
1101911021 ir_add_error_node(ira, fn_ref->source_node,
1102011022 buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));
src/zig_llvm.cpp+10-3
......@@ -175,12 +175,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
175175
176176
177177LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
178 unsigned NumArgs, unsigned CC, bool always_inline, const char *Name)
178 unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name)
179179{
180180 CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);
181181 call_inst->setCallingConv(CC);
182 if (always_inline) {
183 call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
182 switch (fn_inline) {
183 case ZigLLVM_FnInlineAuto:
184 break;
185 case ZigLLVM_FnInlineAlways:
186 call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);
187 break;
188 case ZigLLVM_FnInlineNever:
189 call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::NoInline);
190 break;
184191 }
185192 return wrap(unwrap(B)->Insert(call_inst));
186193}
src/zig_llvm.hpp+6-1
......@@ -45,8 +45,13 @@ enum ZigLLVM_EmitOutputType {
4545bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
4646 const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);
4747
48enum ZigLLVM_FnInline {
49 ZigLLVM_FnInlineAuto,
50 ZigLLVM_FnInlineAlways,
51 ZigLLVM_FnInlineNever,
52};
4853LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,
49 unsigned NumArgs, unsigned CC, bool always_inline, const char *Name);
54 unsigned NumArgs, unsigned CC, ZigLLVM_FnInline fn_inline, const char *Name);
5055
5156LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
5257 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
std/debug.zig+2-2
......@@ -977,7 +977,7 @@ var some_mem_index: usize = 0;
977977
978978error OutOfMemory;
979979
980fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
980fn globalAlloc(self: &mem.Allocator, n: usize, alignment: u29) -> %[]u8 {
981981 const addr = @ptrToInt(&some_mem[some_mem_index]);
982982 const rem = @rem(addr, alignment);
983983 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);
......@@ -991,7 +991,7 @@ fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
991991 return result;
992992}
993993
994fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
994fn globalRealloc(self: &mem.Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
995995 if (new_size <= old_mem.len) {
996996 return old_mem[0..new_size];
997997 } else {
std/heap.zig+4-4
......@@ -16,7 +16,7 @@ pub var c_allocator = Allocator {
1616 .freeFn = cFree,
1717};
1818
19fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
19fn cAlloc(self: &Allocator, n: usize, alignment: u29) -> %[]u8 {
2020 if (c.malloc(usize(n))) |buf| {
2121 @ptrCast(&u8, buf)[0..n]
2222 } else {
......@@ -24,7 +24,7 @@ fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
2424 }
2525}
2626
27fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
27fn cRealloc(self: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
2828 if (new_size <= old_mem.len) {
2929 old_mem[0..new_size]
3030 } else {
......@@ -106,7 +106,7 @@ pub const IncrementingAllocator = struct {
106106 return self.bytes.len - self.end_index;
107107 }
108108
109 fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {
109 fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
110110 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
111111 const addr = @ptrToInt(&self.bytes[self.end_index]);
112112 const rem = @rem(addr, alignment);
......@@ -121,7 +121,7 @@ pub const IncrementingAllocator = struct {
121121 return result;
122122 }
123123
124 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: usize) -> %[]u8 {
124 fn realloc(allocator: &Allocator, old_mem: []u8, new_size: usize, alignment: u29) -> %[]u8 {
125125 if (new_size <= old_mem.len) {
126126 return old_mem[0..new_size];
127127 } else {
std/mem.zig+34-20
......@@ -7,22 +7,24 @@ pub const Cmp = math.Cmp;
77
88pub const Allocator = struct {
99 /// Allocate byte_count bytes and return them in a slice, with the
10 /// slicer's pointer aligned at least to alignment bytes.
11 allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,
10 /// slice's pointer aligned at least to alignment bytes.
11 allocFn: fn (self: &Allocator, byte_count: usize, alignment: u29) -> %[]u8,
1212
13 /// Guaranteed: `old_mem.len` is the same as what was returned from allocFn or reallocFn.
14 /// Guaranteed: alignment >= alignment of old_mem.ptr
13 /// If `new_byte_count > old_mem.len`:
14 /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
15 /// * alignment >= alignment of old_mem.ptr
1516 ///
16 /// If `new_byte_count` is less than or equal to `old_mem.len` this function must
17 /// return successfully.
18 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,
17 /// If `new_byte_count <= old_mem.len`:
18 /// * this function must return successfully.
19 /// * alignment <= alignment of old_mem.ptr
20 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) -> %[]u8,
1921
2022 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
2123 freeFn: fn (self: &Allocator, old_mem: []u8),
2224
2325 fn create(self: &Allocator, comptime T: type) -> %&T {
2426 const slice = %return self.alloc(T, 1);
25 &slice[0]
27 return &slice[0];
2628 }
2729
2830 fn destroy(self: &Allocator, ptr: var) {
......@@ -30,28 +32,43 @@ pub const Allocator = struct {
3032 }
3133
3234 fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {
35 return self.alignedAlloc(T, @alignOf(T), n);
36 }
37
38 fn alignedAlloc(self: &Allocator, comptime T: type, comptime alignment: u29,
39 n: usize) -> %[]align(alignment) T
40 {
3341 const byte_count = %return math.mul(usize, @sizeOf(T), n);
34 const byte_slice = %return self.allocFn(self, byte_count, @alignOf(T));
35 ([]T)(@alignCast(@alignOf(T), byte_slice))
42 const byte_slice = %return self.allocFn(self, byte_count, alignment);
43 return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
3644 }
3745
3846 fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {
47 return self.alignedRealloc(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
48 }
49
50 fn alignedRealloc(self: &Allocator, comptime T: type, comptime alignment: u29,
51 old_mem: []align(alignment) T, n: usize) -> %[]align(alignment) T
52 {
3953 if (old_mem.len == 0) {
4054 return self.alloc(T, n);
4155 }
4256
43 // Assert that old_mem.ptr is properly aligned.
44 const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
45
4657 const byte_count = %return math.mul(usize, @sizeOf(T), n);
47 const byte_slice = %return self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
48 return ([]T)(@alignCast(@alignOf(T), byte_slice));
58 const byte_slice = %return self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment);
59 return ([]T)(@alignCast(alignment, byte_slice));
4960 }
5061
5162 /// Reallocate, but `n` must be less than or equal to `old_mem.len`.
5263 /// Unlike `realloc`, this function cannot fail.
5364 /// Shrinking to 0 is the same as calling `free`.
5465 fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> []T {
66 return self.alignedShrink(T, @alignOf(T), @alignCast(@alignOf(T), old_mem), n);
67 }
68
69 fn alignedShrink(self: &Allocator, comptime T: type, comptime alignment: u29,
70 old_mem: []align(alignment) T, n: usize) -> []align(alignment) T
71 {
5572 if (n == 0) {
5673 self.free(old_mem);
5774 return old_mem[0..0];
......@@ -59,15 +76,12 @@ pub const Allocator = struct {
5976
6077 assert(n <= old_mem.len);
6178
62 // Assert that old_mem.ptr is properly aligned.
63 const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
64
6579 // Here we skip the overflow checking on the multiplication because
6680 // n <= old_mem.len and the multiplication didn't overflow for that operation.
6781 const byte_count = @sizeOf(T) * n;
6882
69 const byte_slice = %%self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));
70 return ([]T)(@alignCast(@alignOf(T), byte_slice));
83 const byte_slice = %%self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment);
84 return ([]T)(@alignCast(alignment, byte_slice));
7185 }
7286
7387 fn free(self: &Allocator, memory: var) {
std/os/index.zig+48
......@@ -1422,6 +1422,54 @@ pub fn args() -> ArgIterator {
14221422 return ArgIterator.init();
14231423}
14241424
1425/// Caller must call freeArgs on result.
1426pub fn argsAlloc(allocator: &mem.Allocator) -> %[]const []u8 {
1427 // TODO refactor to only make 1 allocation.
1428 var it = args();
1429 var contents = %return Buffer.initSize(allocator, 0);
1430 defer contents.deinit();
1431
1432 var slice_list = ArrayList(usize).init(allocator);
1433 defer slice_list.deinit();
1434
1435 while (it.next(allocator)) |arg_or_err| {
1436 const arg = %return arg_or_err;
1437 defer allocator.free(arg);
1438 %return contents.append(arg);
1439 %return slice_list.append(arg.len);
1440 }
1441
1442 const contents_slice = contents.toSliceConst();
1443 const slice_sizes = slice_list.toSliceConst();
1444 const slice_list_bytes = %return math.mul(usize, @sizeOf([]u8), slice_sizes.len);
1445 const total_bytes = %return math.add(usize, slice_list_bytes, contents_slice.len);
1446 const buf = %return allocator.alignedAlloc(u8, @alignOf([]u8), total_bytes);
1447 %defer allocator.free(buf);
1448
1449 const result_slice_list = ([][]u8)(buf[0..slice_list_bytes]);
1450 const result_contents = buf[slice_list_bytes..];
1451 mem.copy(u8, result_contents, contents_slice);
1452
1453 var contents_index: usize = 0;
1454 for (slice_sizes) |len, i| {
1455 const new_index = contents_index + len;
1456 result_slice_list[i] = result_contents[contents_index..new_index];
1457 contents_index = new_index;
1458 }
1459
1460 return result_slice_list;
1461}
1462
1463pub fn argsFree(allocator: &mem.Allocator, args_alloc: []const []u8) {
1464 var total_bytes: usize = 0;
1465 for (args_alloc) |arg| {
1466 total_bytes += @sizeOf([]u8) + arg.len;
1467 }
1468 const unaligned_allocated_buf = @ptrCast(&u8, args_alloc.ptr)[0..total_bytes];
1469 const aligned_allocated_buf = @alignCast(@alignOf([]u8), unaligned_allocated_buf);
1470 return allocator.free(aligned_allocated_buf);
1471}
1472
14251473test "windows arg parsing" {
14261474 testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
14271475 testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});
std/special/bootstrap.zig+3-1
......@@ -28,7 +28,9 @@ export nakedcc fn _start() -> noreturn {
2828 },
2929 else => @compileError("unsupported arch"),
3030 }
31 posixCallMainAndExit()
31 // If LLVM inlines stack variables into _start, they will overwrite
32 // the command line argument data.
33 @noInlineCall(posixCallMainAndExit);
3234}
3335
3436export fn WinMainCRTStartup() -> noreturn {
test/compare_output.zig+82
......@@ -444,4 +444,86 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
444444
445445 tc
446446 });
447
448 cases.addCase({
449 var tc = cases.create("parsing args",
450 \\const std = @import("std");
451 \\const io = std.io;
452 \\const os = std.os;
453 \\const allocator = std.debug.global_allocator;
454 \\
455 \\pub fn main() -> %void {
456 \\ var args_it = os.args();
457 \\ var stdout_file = %return io.getStdOut();
458 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
459 \\ const stdout = &stdout_adapter.stream;
460 \\ var index: usize = 0;
461 \\ _ = args_it.skip();
462 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
463 \\ const arg = %return arg_or_err;
464 \\ %return stdout.print("{}: {}\n", index, arg);
465 \\ }
466 \\}
467 ,
468 \\0: first arg
469 \\1: 'a' 'b' \
470 \\2: bare
471 \\3: ba""re
472 \\4: "
473 \\5: last arg
474 \\
475 );
476
477 tc.setCommandLineArgs([][]const u8 {
478 "first arg",
479 "'a' 'b' \\",
480 "bare",
481 "ba\"\"re",
482 "\"",
483 "last arg",
484 });
485
486 tc
487 });
488
489 cases.addCase({
490 var tc = cases.create("parsing args new API",
491 \\const std = @import("std");
492 \\const io = std.io;
493 \\const os = std.os;
494 \\const allocator = std.debug.global_allocator;
495 \\
496 \\pub fn main() -> %void {
497 \\ var args_it = os.args();
498 \\ var stdout_file = %return io.getStdOut();
499 \\ var stdout_adapter = io.FileOutStream.init(&stdout_file);
500 \\ const stdout = &stdout_adapter.stream;
501 \\ var index: usize = 0;
502 \\ _ = args_it.skip();
503 \\ while (args_it.next(allocator)) |arg_or_err| : (index += 1) {
504 \\ const arg = %return arg_or_err;
505 \\ %return stdout.print("{}: {}\n", index, arg);
506 \\ }
507 \\}
508 ,
509 \\0: first arg
510 \\1: 'a' 'b' \
511 \\2: bare
512 \\3: ba""re
513 \\4: "
514 \\5: last arg
515 \\
516 );
517
518 tc.setCommandLineArgs([][]const u8 {
519 "first arg",
520 "'a' 'b' \\",
521 "bare",
522 "ba\"\"re",
523 "\"",
524 "last arg",
525 });
526
527 tc
528 });
447529}
test/tests.zig+20-4
......@@ -189,6 +189,7 @@ pub const CompareOutputContext = struct {
189189 expected_output: []const u8,
190190 link_libc: bool,
191191 special: Special,
192 cli_args: []const []const u8,
192193
193194 const SourceFile = struct {
194195 filename: []const u8,
......@@ -201,6 +202,10 @@ pub const CompareOutputContext = struct {
201202 .source = source,
202203 });
203204 }
205
206 pub fn setCommandLineArgs(self: &TestCase, args: []const []const u8) {
207 self.cli_args = args;
208 }
204209 };
205210
206211 const RunCompareOutputStep = struct {
......@@ -210,9 +215,11 @@ pub const CompareOutputContext = struct {
210215 name: []const u8,
211216 expected_output: []const u8,
212217 test_index: usize,
218 cli_args: []const []const u8,
213219
214220 pub fn create(context: &CompareOutputContext, exe_path: []const u8,
215 name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep
221 name: []const u8, expected_output: []const u8,
222 cli_args: []const []const u8) -> &RunCompareOutputStep
216223 {
217224 const allocator = context.b.allocator;
218225 const ptr = %%allocator.create(RunCompareOutputStep);
......@@ -223,6 +230,7 @@ pub const CompareOutputContext = struct {
223230 .expected_output = expected_output,
224231 .test_index = context.test_index,
225232 .step = build.Step.init("RunCompareOutput", allocator, make),
233 .cli_args = cli_args,
226234 };
227235 context.test_index += 1;
228236 return ptr;
......@@ -233,10 +241,17 @@ pub const CompareOutputContext = struct {
233241 const b = self.context.b;
234242
235243 const full_exe_path = b.pathFromRoot(self.exe_path);
244 var args = ArrayList([]const u8).init(b.allocator);
245 defer args.deinit();
246
247 %%args.append(full_exe_path);
248 for (self.cli_args) |arg| {
249 %%args.append(arg);
250 }
236251
237252 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);
238253
239 const child = %%os.ChildProcess.init([][]u8{full_exe_path}, b.allocator);
254 const child = %%os.ChildProcess.init(args.toSliceConst(), b.allocator);
240255 defer child.deinit();
241256
242257 child.stdin_behavior = StdIo.Ignore;
......@@ -364,6 +379,7 @@ pub const CompareOutputContext = struct {
364379 .expected_output = expected_output,
365380 .link_libc = false,
366381 .special = special,
382 .cli_args = []const []const u8{},
367383 };
368384 const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";
369385 tc.addSourceFile(root_src_name, source);
......@@ -420,7 +436,7 @@ pub const CompareOutputContext = struct {
420436 }
421437
422438 const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(), annotated_case_name,
423 case.expected_output);
439 case.expected_output, case.cli_args);
424440 run_and_cmp_output.step.dependOn(&exe.step);
425441
426442 self.step.dependOn(&run_and_cmp_output.step);
......@@ -447,7 +463,7 @@ pub const CompareOutputContext = struct {
447463 }
448464
449465 const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(),
450 annotated_case_name, case.expected_output);
466 annotated_case_name, case.expected_output, case.cli_args);
451467 run_and_cmp_output.step.dependOn(&exe.step);
452468
453469 self.step.dependOn(&run_and_cmp_output.step);