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 {...@@ -1270,6 +1270,7 @@ enum BuiltinFnId {
1270 BuiltinFnIdFieldParentPtr,1270 BuiltinFnIdFieldParentPtr,
1271 BuiltinFnIdOffsetOf,1271 BuiltinFnIdOffsetOf,
1272 BuiltinFnIdInlineCall,1272 BuiltinFnIdInlineCall,
1273 BuiltinFnIdNoInlineCall,
1273 BuiltinFnIdTypeId,1274 BuiltinFnIdTypeId,
1274 BuiltinFnIdShlExact,1275 BuiltinFnIdShlExact,
1275 BuiltinFnIdShrExact,1276 BuiltinFnIdShrExact,
...@@ -1439,7 +1440,7 @@ struct CodeGen {...@@ -1439,7 +1440,7 @@ struct CodeGen {
14391440
1440 struct {1441 struct {
1441 TypeTableEntry *entry_bool;1442 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]
1443 TypeTableEntry *entry_c_int[CIntTypeCount];1444 TypeTableEntry *entry_c_int[CIntTypeCount];
1444 TypeTableEntry *entry_c_longdouble;1445 TypeTableEntry *entry_c_longdouble;
1445 TypeTableEntry *entry_c_void;1446 TypeTableEntry *entry_c_void;
...@@ -2102,7 +2103,7 @@ struct IrInstructionCall {...@@ -2102,7 +2103,7 @@ struct IrInstructionCall {
2102 IrInstruction **args;2103 IrInstruction **args;
2103 bool is_comptime;2104 bool is_comptime;
2104 LLVMValueRef tmp_ptr;2105 LLVMValueRef tmp_ptr;
2105 bool is_inline;2106 FnInline fn_inline;
2106};2107};
21072108
2108struct IrInstructionConst {2109struct 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...@@ -3818,12 +3818,14 @@ TypeTableEntry **get_int_type_ptr(CodeGen *g, bool is_signed, uint32_t size_in_b
3818 index = 6;3818 index = 6;
3819 } else if (size_in_bits == 16) {3819 } else if (size_in_bits == 16) {
3820 index = 7;3820 index = 7;
3821 } else if (size_in_bits == 32) {3821 } else if (size_in_bits == 29) {
3822 index = 8;3822 index = 8;
3823 } else if (size_in_bits == 64) {3823 } else if (size_in_bits == 32) {
3824 index = 9;3824 index = 9;
3825 } else if (size_in_bits == 128) {3825 } else if (size_in_bits == 64) {
3826 index = 10;3826 index = 10;
3827 } else if (size_in_bits == 128) {
3828 index = 11;
3827 } else {3829 } else {
3828 return nullptr;3830 return nullptr;
3829 }3831 }
src/codegen.cpp+17-5
...@@ -839,7 +839,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {...@@ -839,7 +839,7 @@ static void gen_panic(CodeGen *g, LLVMValueRef msg_arg) {
839 assert(g->panic_fn != nullptr);839 assert(g->panic_fn != nullptr);
840 LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);840 LLVMValueRef fn_val = fn_llvm_value(g, g->panic_fn);
841 LLVMCallConv llvm_cc = get_llvm_cc(g, g->panic_fn->type_entry->data.fn.fn_type_id.cc);841 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, "");
843 LLVMBuildUnreachable(g->builder);843 LLVMBuildUnreachable(g->builder);
844}844}
845845
...@@ -988,7 +988,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {...@@ -988,7 +988,7 @@ static LLVMValueRef get_safety_crash_err_fn(CodeGen *g) {
988static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {988static void gen_debug_safety_crash_for_err(CodeGen *g, LLVMValueRef err_val) {
989 LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);989 LLVMValueRef safety_crash_err_fn = get_safety_crash_err_fn(g);
990 ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),990 ZigLLVMBuildCall(g->builder, safety_crash_err_fn, &err_val, 1, get_llvm_cc(g, CallingConventionUnspecified),
991 false, "");991 ZigLLVM_FnInlineAuto, "");
992 LLVMBuildUnreachable(g->builder);992 LLVMBuildUnreachable(g->builder);
993}993}
994994
...@@ -2316,12 +2316,22 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr...@@ -2316,12 +2316,22 @@ static LLVMValueRef ir_render_call(CodeGen *g, IrExecutable *executable, IrInstr
2316 }2316 }
2317 }2317 }
23182318
2319 bool want_always_inline = (instruction->fn_entry != nullptr &&2319 ZigLLVM_FnInline fn_inline;
2320 instruction->fn_entry->fn_inline == FnInlineAlways) || instruction->is_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
2322 LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc);2332 LLVMCallConv llvm_cc = get_llvm_cc(g, fn_type->data.fn.fn_type_id.cc);
2323 LLVMValueRef result = ZigLLVMBuildCall(g->builder, fn_val,2333 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
2326 for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {2336 for (size_t param_i = 0; param_i < fn_type_id->param_count; param_i += 1) {
2327 FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];2337 FnGenParamInfo *gen_info = &fn_type->data.fn.gen_param_info[param_i];
...@@ -4634,6 +4644,7 @@ static const uint8_t int_sizes_in_bits[] = {...@@ -4634,6 +4644,7 @@ static const uint8_t int_sizes_in_bits[] = {
4634 7,4644 7,
4635 8,4645 8,
4636 16,4646 16,
4647 29,
4637 32,4648 32,
4638 64,4649 64,
4639 128,4650 128,
...@@ -4971,6 +4982,7 @@ static void define_builtin_fns(CodeGen *g) {...@@ -4971,6 +4982,7 @@ static void define_builtin_fns(CodeGen *g) {
4971 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);4982 create_builtin_fn(g, BuiltinFnIdRem, "rem", 2);
4972 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);4983 create_builtin_fn(g, BuiltinFnIdMod, "mod", 2);
4973 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);4984 create_builtin_fn(g, BuiltinFnIdInlineCall, "inlineCall", SIZE_MAX);
4985 create_builtin_fn(g, BuiltinFnIdNoInlineCall, "noInlineCall", SIZE_MAX);
4974 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);4986 create_builtin_fn(g, BuiltinFnIdTypeId, "typeId", 1);
4975 create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);4987 create_builtin_fn(g, BuiltinFnIdShlExact, "shlExact", 2);
4976 create_builtin_fn(g, BuiltinFnIdShrExact, "shrExact", 2);4988 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...@@ -928,13 +928,13 @@ static IrInstruction *ir_build_union_field_ptr_from(IrBuilder *irb, IrInstructio
928928
929static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,929static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *source_node,
930 FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,930 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)
932{932{
933 IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, scope, source_node);933 IrInstructionCall *call_instruction = ir_build_instruction<IrInstructionCall>(irb, scope, source_node);
934 call_instruction->fn_entry = fn_entry;934 call_instruction->fn_entry = fn_entry;
935 call_instruction->fn_ref = fn_ref;935 call_instruction->fn_ref = fn_ref;
936 call_instruction->is_comptime = is_comptime;936 call_instruction->is_comptime = is_comptime;
937 call_instruction->is_inline = is_inline;937 call_instruction->fn_inline = fn_inline;
938 call_instruction->args = args;938 call_instruction->args = args;
939 call_instruction->arg_count = arg_count;939 call_instruction->arg_count = arg_count;
940940
...@@ -948,10 +948,10 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc...@@ -948,10 +948,10 @@ static IrInstruction *ir_build_call(IrBuilder *irb, Scope *scope, AstNode *sourc
948948
949static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,949static IrInstruction *ir_build_call_from(IrBuilder *irb, IrInstruction *old_instruction,
950 FnTableEntry *fn_entry, IrInstruction *fn_ref, size_t arg_count, IrInstruction **args,950 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)
952{952{
953 IrInstruction *new_instruction = ir_build_call(irb, old_instruction->scope,953 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);
955 ir_link_new_instruction(new_instruction, old_instruction);955 ir_link_new_instruction(new_instruction, old_instruction);
956 return new_instruction;956 return new_instruction;
957}957}
...@@ -4672,6 +4672,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo...@@ -4672,6 +4672,7 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4672 return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);4672 return ir_build_offset_of(irb, scope, node, arg0_value, arg1_value);
4673 }4673 }
4674 case BuiltinFnIdInlineCall:4674 case BuiltinFnIdInlineCall:
4675 case BuiltinFnIdNoInlineCall:
4675 {4676 {
4676 if (node->data.fn_call_expr.params.length == 0) {4677 if (node->data.fn_call_expr.params.length == 0) {
4677 add_node_error(irb->codegen, node, buf_sprintf("expected at least 1 argument, found 0"));4678 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...@@ -4692,8 +4693,9 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
4692 if (args[i] == irb->codegen->invalid_instruction)4693 if (args[i] == irb->codegen->invalid_instruction)
4693 return args[i];4694 return args[i];
4694 }4695 }
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);
4697 }4699 }
4698 case BuiltinFnIdTypeId:4700 case BuiltinFnIdTypeId:
4699 {4701 {
...@@ -4804,7 +4806,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node...@@ -4804,7 +4806,7 @@ static IrInstruction *ir_gen_fn_call(IrBuilder *irb, Scope *scope, AstNode *node
4804 return args[i];4806 return args[i];
4805 }4807 }
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);
4808}4810}
48094811
4810static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {4812static IrInstruction *ir_gen_if_bool_expr(IrBuilder *irb, Scope *scope, AstNode *node) {
...@@ -10617,7 +10619,7 @@ no_mem_slot:...@@ -10617,7 +10619,7 @@ no_mem_slot:
1061710619
10618static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,10620static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *call_instruction,
10619 FnTableEntry *fn_entry, TypeTableEntry *fn_type, IrInstruction *fn_ref,10621 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)
10621{10623{
10622 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;10624 FnTypeId *fn_type_id = &fn_type->data.fn.fn_type_id;
10623 size_t first_arg_1_or_0 = first_arg_ptr ? 1 : 0;10625 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...@@ -10876,7 +10878,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1087610878
10877 if (type_requires_comptime(return_type)) {10879 if (type_requires_comptime(return_type)) {
10878 // Throw out our work and call the function as if it were comptime.10880 // 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);
10880 }10882 }
10881 }10883 }
1088210884
...@@ -10900,7 +10902,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -10900,7 +10902,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
1090010902
10901 size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;10903 size_t impl_param_count = impl_fn->type_entry->data.fn.fn_type_id.param_count;
10902 IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,10904 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
10905 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;10907 TypeTableEntry *return_type = impl_fn->type_entry->data.fn.fn_type_id.return_type;
10906 ir_add_alloca(ira, new_call_instruction, return_type);10908 ir_add_alloca(ira, new_call_instruction, return_type);
...@@ -10959,7 +10961,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal...@@ -10959,7 +10961,7 @@ static TypeTableEntry *ir_analyze_fn_call(IrAnalyze *ira, IrInstructionCall *cal
10959 return ira->codegen->builtin_types.entry_invalid;10961 return ira->codegen->builtin_types.entry_invalid;
1096010962
10961 IrInstruction *new_call_instruction = ir_build_call_from(&ira->new_irb, &call_instruction->base,10963 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
10964 ir_add_alloca(ira, new_call_instruction, return_type);10966 ir_add_alloca(ira, new_call_instruction, return_type);
10965 return ir_finish_anal(ira, return_type);10967 return ir_finish_anal(ira, return_type);
...@@ -10998,13 +11000,13 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction...@@ -10998,13 +11000,13 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
10998 } else if (fn_ref->value.type->id == TypeTableEntryIdFn) {11000 } else if (fn_ref->value.type->id == TypeTableEntryIdFn) {
10999 FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);11001 FnTableEntry *fn_table_entry = ir_resolve_fn(ira, fn_ref);
11000 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,11002 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);
11002 } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {11004 } else if (fn_ref->value.type->id == TypeTableEntryIdBoundFn) {
11003 assert(fn_ref->value.special == ConstValSpecialStatic);11005 assert(fn_ref->value.special == ConstValSpecialStatic);
11004 FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;11006 FnTableEntry *fn_table_entry = fn_ref->value.data.x_bound_fn.fn;
11005 IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;11007 IrInstruction *first_arg_ptr = fn_ref->value.data.x_bound_fn.first_arg;
11006 return ir_analyze_fn_call(ira, call_instruction, fn_table_entry, fn_table_entry->type_entry,11008 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);
11008 } else {11010 } else {
11009 ir_add_error_node(ira, fn_ref->source_node,11011 ir_add_error_node(ira, fn_ref->source_node,
11010 buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));11012 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...@@ -11014,7 +11016,7 @@ static TypeTableEntry *ir_analyze_instruction_call(IrAnalyze *ira, IrInstruction
1101411016
11015 if (fn_ref->value.type->id == TypeTableEntryIdFn) {11017 if (fn_ref->value.type->id == TypeTableEntryIdFn) {
11016 return ir_analyze_fn_call(ira, call_instruction, nullptr, fn_ref->value.type,11018 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);
11018 } else {11020 } else {
11019 ir_add_error_node(ira, fn_ref->source_node,11021 ir_add_error_node(ira, fn_ref->source_node,
11020 buf_sprintf("type '%s' not a function", buf_ptr(&fn_ref->value.type->name)));11022 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...@@ -175,12 +175,19 @@ bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMM
175175
176176
177LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,177LLVMValueRef 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)
179{179{
180 CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);180 CallInst *call_inst = CallInst::Create(unwrap(Fn), makeArrayRef(unwrap(Args), NumArgs), Name);
181 call_inst->setCallingConv(CC);181 call_inst->setCallingConv(CC);
182 if (always_inline) {182 switch (fn_inline) {
183 call_inst->addAttribute(AttributeList::FunctionIndex, Attribute::AlwaysInline);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;
184 }191 }
185 return wrap(unwrap(B)->Insert(call_inst));192 return wrap(unwrap(B)->Insert(call_inst));
186}193}
src/zig_llvm.hpp+6-1
...@@ -45,8 +45,13 @@ enum ZigLLVM_EmitOutputType {...@@ -45,8 +45,13 @@ enum ZigLLVM_EmitOutputType {
45bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,45bool ZigLLVMTargetMachineEmitToFile(LLVMTargetMachineRef targ_machine_ref, LLVMModuleRef module_ref,
46 const char *filename, ZigLLVM_EmitOutputType output_type, char **error_message, bool is_debug);46 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};
48LLVMValueRef ZigLLVMBuildCall(LLVMBuilderRef B, LLVMValueRef Fn, LLVMValueRef *Args,53LLVMValueRef 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
51LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,56LLVMValueRef ZigLLVMBuildCmpXchg(LLVMBuilderRef builder, LLVMValueRef ptr, LLVMValueRef cmp,
52 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,57 LLVMValueRef new_val, LLVMAtomicOrdering success_ordering,
std/debug.zig+2-2
...@@ -977,7 +977,7 @@ var some_mem_index: usize = 0;...@@ -977,7 +977,7 @@ var some_mem_index: usize = 0;
977977
978error OutOfMemory;978error OutOfMemory;
979979
980fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {980fn globalAlloc(self: &mem.Allocator, n: usize, alignment: u29) -> %[]u8 {
981 const addr = @ptrToInt(&some_mem[some_mem_index]);981 const addr = @ptrToInt(&some_mem[some_mem_index]);
982 const rem = @rem(addr, alignment);982 const rem = @rem(addr, alignment);
983 const march_forward_bytes = if (rem == 0) 0 else (alignment - rem);983 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 {...@@ -991,7 +991,7 @@ fn globalAlloc(self: &mem.Allocator, n: usize, alignment: usize) -> %[]u8 {
991 return result;991 return result;
992}992}
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 {
995 if (new_size <= old_mem.len) {995 if (new_size <= old_mem.len) {
996 return old_mem[0..new_size];996 return old_mem[0..new_size];
997 } else {997 } else {
std/heap.zig+4-4
...@@ -16,7 +16,7 @@ pub var c_allocator = Allocator {...@@ -16,7 +16,7 @@ pub var c_allocator = Allocator {
16 .freeFn = cFree,16 .freeFn = cFree,
17};17};
1818
19fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {19fn cAlloc(self: &Allocator, n: usize, alignment: u29) -> %[]u8 {
20 if (c.malloc(usize(n))) |buf| {20 if (c.malloc(usize(n))) |buf| {
21 @ptrCast(&u8, buf)[0..n]21 @ptrCast(&u8, buf)[0..n]
22 } else {22 } else {
...@@ -24,7 +24,7 @@ fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {...@@ -24,7 +24,7 @@ fn cAlloc(self: &Allocator, n: usize, alignment: usize) -> %[]u8 {
24 }24 }
25}25}
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 {
28 if (new_size <= old_mem.len) {28 if (new_size <= old_mem.len) {
29 old_mem[0..new_size]29 old_mem[0..new_size]
30 } else {30 } else {
...@@ -106,7 +106,7 @@ pub const IncrementingAllocator = struct {...@@ -106,7 +106,7 @@ pub const IncrementingAllocator = struct {
106 return self.bytes.len - self.end_index;106 return self.bytes.len - self.end_index;
107 }107 }
108108
109 fn alloc(allocator: &Allocator, n: usize, alignment: usize) -> %[]u8 {109 fn alloc(allocator: &Allocator, n: usize, alignment: u29) -> %[]u8 {
110 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);110 const self = @fieldParentPtr(IncrementingAllocator, "allocator", allocator);
111 const addr = @ptrToInt(&self.bytes[self.end_index]);111 const addr = @ptrToInt(&self.bytes[self.end_index]);
112 const rem = @rem(addr, alignment);112 const rem = @rem(addr, alignment);
...@@ -121,7 +121,7 @@ pub const IncrementingAllocator = struct {...@@ -121,7 +121,7 @@ pub const IncrementingAllocator = struct {
121 return result;121 return result;
122 }122 }
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 {
125 if (new_size <= old_mem.len) {125 if (new_size <= old_mem.len) {
126 return old_mem[0..new_size];126 return old_mem[0..new_size];
127 } else {127 } else {
std/mem.zig+34-20
...@@ -7,22 +7,24 @@ pub const Cmp = math.Cmp;...@@ -7,22 +7,24 @@ pub const Cmp = math.Cmp;
77
8pub const Allocator = struct {8pub const Allocator = struct {
9 /// Allocate byte_count bytes and return them in a slice, with the9 /// Allocate byte_count bytes and return them in a slice, with the
10 /// slicer's pointer aligned at least to alignment bytes.10 /// slice's pointer aligned at least to alignment bytes.
11 allocFn: fn (self: &Allocator, byte_count: usize, alignment: usize) -> %[]u8,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.13 /// If `new_byte_count > old_mem.len`:
14 /// Guaranteed: alignment >= alignment of old_mem.ptr14 /// * `old_mem.len` is the same as what was returned from allocFn or reallocFn.
15 /// * alignment >= alignment of old_mem.ptr
15 ///16 ///
16 /// If `new_byte_count` is less than or equal to `old_mem.len` this function must17 /// If `new_byte_count <= old_mem.len`:
17 /// return successfully.18 /// * this function must return successfully.
18 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: usize) -> %[]u8,19 /// * alignment <= alignment of old_mem.ptr
20 reallocFn: fn (self: &Allocator, old_mem: []u8, new_byte_count: usize, alignment: u29) -> %[]u8,
1921
20 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`22 /// Guaranteed: `old_mem.len` is the same as what was returned from `allocFn` or `reallocFn`
21 freeFn: fn (self: &Allocator, old_mem: []u8),23 freeFn: fn (self: &Allocator, old_mem: []u8),
2224
23 fn create(self: &Allocator, comptime T: type) -> %&T {25 fn create(self: &Allocator, comptime T: type) -> %&T {
24 const slice = %return self.alloc(T, 1);26 const slice = %return self.alloc(T, 1);
25 &slice[0]27 return &slice[0];
26 }28 }
2729
28 fn destroy(self: &Allocator, ptr: var) {30 fn destroy(self: &Allocator, ptr: var) {
...@@ -30,28 +32,43 @@ pub const Allocator = struct {...@@ -30,28 +32,43 @@ pub const Allocator = struct {
30 }32 }
3133
32 fn alloc(self: &Allocator, comptime T: type, n: usize) -> %[]T {34 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 {
33 const byte_count = %return math.mul(usize, @sizeOf(T), n);41 const byte_count = %return math.mul(usize, @sizeOf(T), n);
34 const byte_slice = %return self.allocFn(self, byte_count, @alignOf(T));42 const byte_slice = %return self.allocFn(self, byte_count, alignment);
35 ([]T)(@alignCast(@alignOf(T), byte_slice))43 return ([]align(alignment) T)(@alignCast(alignment, byte_slice));
36 }44 }
3745
38 fn realloc(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> %[]T {46 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 {
39 if (old_mem.len == 0) {53 if (old_mem.len == 0) {
40 return self.alloc(T, n);54 return self.alloc(T, n);
41 }55 }
4256
43 // Assert that old_mem.ptr is properly aligned.
44 const aligned_old_mem = @alignCast(@alignOf(T), old_mem);
45
46 const byte_count = %return math.mul(usize, @sizeOf(T), n);57 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));58 const byte_slice = %return self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment);
48 return ([]T)(@alignCast(@alignOf(T), byte_slice));59 return ([]T)(@alignCast(alignment, byte_slice));
49 }60 }
5061
51 /// Reallocate, but `n` must be less than or equal to `old_mem.len`.62 /// Reallocate, but `n` must be less than or equal to `old_mem.len`.
52 /// Unlike `realloc`, this function cannot fail.63 /// Unlike `realloc`, this function cannot fail.
53 /// Shrinking to 0 is the same as calling `free`.64 /// Shrinking to 0 is the same as calling `free`.
54 fn shrink(self: &Allocator, comptime T: type, old_mem: []T, n: usize) -> []T {65 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 {
55 if (n == 0) {72 if (n == 0) {
56 self.free(old_mem);73 self.free(old_mem);
57 return old_mem[0..0];74 return old_mem[0..0];
...@@ -59,15 +76,12 @@ pub const Allocator = struct {...@@ -59,15 +76,12 @@ pub const Allocator = struct {
5976
60 assert(n <= old_mem.len);77 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
65 // Here we skip the overflow checking on the multiplication because79 // Here we skip the overflow checking on the multiplication because
66 // n <= old_mem.len and the multiplication didn't overflow for that operation.80 // n <= old_mem.len and the multiplication didn't overflow for that operation.
67 const byte_count = @sizeOf(T) * n;81 const byte_count = @sizeOf(T) * n;
6882
69 const byte_slice = %%self.reallocFn(self, ([]u8)(aligned_old_mem), byte_count, @alignOf(T));83 const byte_slice = %%self.reallocFn(self, ([]u8)(old_mem), byte_count, alignment);
70 return ([]T)(@alignCast(@alignOf(T), byte_slice));84 return ([]T)(@alignCast(alignment, byte_slice));
71 }85 }
7286
73 fn free(self: &Allocator, memory: var) {87 fn free(self: &Allocator, memory: var) {
std/os/index.zig+48
...@@ -1422,6 +1422,54 @@ pub fn args() -> ArgIterator {...@@ -1422,6 +1422,54 @@ pub fn args() -> ArgIterator {
1422 return ArgIterator.init();1422 return ArgIterator.init();
1423}1423}
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
1425test "windows arg parsing" {1473test "windows arg parsing" {
1426 testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});1474 testWindowsCmdLine(c"a b\tc d", [][]const u8{"a", "b", "c", "d"});
1427 testWindowsCmdLine(c"\"abc\" d e", [][]const u8{"abc", "d", "e"});1475 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 {...@@ -28,7 +28,9 @@ export nakedcc fn _start() -> noreturn {
28 },28 },
29 else => @compileError("unsupported arch"),29 else => @compileError("unsupported arch"),
30 }30 }
31 posixCallMainAndExit()31 // If LLVM inlines stack variables into _start, they will overwrite
32 // the command line argument data.
33 @noInlineCall(posixCallMainAndExit);
32}34}
3335
34export fn WinMainCRTStartup() -> noreturn {36export fn WinMainCRTStartup() -> noreturn {
test/compare_output.zig+82
...@@ -444,4 +444,86 @@ pub fn addCases(cases: &tests.CompareOutputContext) {...@@ -444,4 +444,86 @@ pub fn addCases(cases: &tests.CompareOutputContext) {
444444
445 tc445 tc
446 });446 });
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 });
447}529}
test/tests.zig+20-4
...@@ -189,6 +189,7 @@ pub const CompareOutputContext = struct {...@@ -189,6 +189,7 @@ pub const CompareOutputContext = struct {
189 expected_output: []const u8,189 expected_output: []const u8,
190 link_libc: bool,190 link_libc: bool,
191 special: Special,191 special: Special,
192 cli_args: []const []const u8,
192193
193 const SourceFile = struct {194 const SourceFile = struct {
194 filename: []const u8,195 filename: []const u8,
...@@ -201,6 +202,10 @@ pub const CompareOutputContext = struct {...@@ -201,6 +202,10 @@ pub const CompareOutputContext = struct {
201 .source = source,202 .source = source,
202 });203 });
203 }204 }
205
206 pub fn setCommandLineArgs(self: &TestCase, args: []const []const u8) {
207 self.cli_args = args;
208 }
204 };209 };
205210
206 const RunCompareOutputStep = struct {211 const RunCompareOutputStep = struct {
...@@ -210,9 +215,11 @@ pub const CompareOutputContext = struct {...@@ -210,9 +215,11 @@ pub const CompareOutputContext = struct {
210 name: []const u8,215 name: []const u8,
211 expected_output: []const u8,216 expected_output: []const u8,
212 test_index: usize,217 test_index: usize,
218 cli_args: []const []const u8,
213219
214 pub fn create(context: &CompareOutputContext, exe_path: []const u8,220 pub fn create(context: &CompareOutputContext, exe_path: []const u8,
215 name: []const u8, expected_output: []const u8) -> &RunCompareOutputStep221 name: []const u8, expected_output: []const u8,
222 cli_args: []const []const u8) -> &RunCompareOutputStep
216 {223 {
217 const allocator = context.b.allocator;224 const allocator = context.b.allocator;
218 const ptr = %%allocator.create(RunCompareOutputStep);225 const ptr = %%allocator.create(RunCompareOutputStep);
...@@ -223,6 +230,7 @@ pub const CompareOutputContext = struct {...@@ -223,6 +230,7 @@ pub const CompareOutputContext = struct {
223 .expected_output = expected_output,230 .expected_output = expected_output,
224 .test_index = context.test_index,231 .test_index = context.test_index,
225 .step = build.Step.init("RunCompareOutput", allocator, make),232 .step = build.Step.init("RunCompareOutput", allocator, make),
233 .cli_args = cli_args,
226 };234 };
227 context.test_index += 1;235 context.test_index += 1;
228 return ptr;236 return ptr;
...@@ -233,10 +241,17 @@ pub const CompareOutputContext = struct {...@@ -233,10 +241,17 @@ pub const CompareOutputContext = struct {
233 const b = self.context.b;241 const b = self.context.b;
234242
235 const full_exe_path = b.pathFromRoot(self.exe_path);243 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
237 warn("Test {}/{} {}...", self.test_index+1, self.context.test_index, self.name);252 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);
240 defer child.deinit();255 defer child.deinit();
241256
242 child.stdin_behavior = StdIo.Ignore;257 child.stdin_behavior = StdIo.Ignore;
...@@ -364,6 +379,7 @@ pub const CompareOutputContext = struct {...@@ -364,6 +379,7 @@ pub const CompareOutputContext = struct {
364 .expected_output = expected_output,379 .expected_output = expected_output,
365 .link_libc = false,380 .link_libc = false,
366 .special = special,381 .special = special,
382 .cli_args = []const []const u8{},
367 };383 };
368 const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";384 const root_src_name = if (special == Special.Asm) "source.s" else "source.zig";
369 tc.addSourceFile(root_src_name, source);385 tc.addSourceFile(root_src_name, source);
...@@ -420,7 +436,7 @@ pub const CompareOutputContext = struct {...@@ -420,7 +436,7 @@ pub const CompareOutputContext = struct {
420 }436 }
421437
422 const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(), annotated_case_name,438 const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(), annotated_case_name,
423 case.expected_output);439 case.expected_output, case.cli_args);
424 run_and_cmp_output.step.dependOn(&exe.step);440 run_and_cmp_output.step.dependOn(&exe.step);
425441
426 self.step.dependOn(&run_and_cmp_output.step);442 self.step.dependOn(&run_and_cmp_output.step);
...@@ -447,7 +463,7 @@ pub const CompareOutputContext = struct {...@@ -447,7 +463,7 @@ pub const CompareOutputContext = struct {
447 }463 }
448464
449 const run_and_cmp_output = RunCompareOutputStep.create(self, exe.getOutputPath(),465 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);
451 run_and_cmp_output.step.dependOn(&exe.step);467 run_and_cmp_output.step.dependOn(&exe.step);
452468
453 self.step.dependOn(&run_and_cmp_output.step);469 self.step.dependOn(&run_and_cmp_output.step);