delete alloca builtin function

See #225

introduce os.EnvMap
master
Andrew Kelley 2017-04-03 18:11:57 -04:00
parent c400cb429a
commit c9ae30d27e
10 changed files with 170 additions and 205 deletions

View File

@ -295,19 +295,6 @@ has a terminating null byte.
Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on
a parameter means that the parameter must be known at compile time.
### @alloca(comptime T: type, count: usize) -> []T
Allocates memory in the stack frame of the caller. This temporary space is
automatically freed when the function that called alloca returns to its caller,
just like other stack variables.
When using this function to allocate memory, you should know the upper bound
of `count`. Consider putting a constant array on the stack with the upper bound
instead of using alloca. If you do use alloca it is to save a few bytes off
the memory size given that you didn't actually hit your upper bound.
The allocated memory contents are undefined.
### @typeOf(expression) -> type
This function returns a compile-time constant, which is the type of the

View File

@ -1190,7 +1190,6 @@ enum BuiltinFnId {
BuiltinFnIdTruncate,
BuiltinFnIdIntType,
BuiltinFnIdSetDebugSafety,
BuiltinFnIdAlloca,
BuiltinFnIdTypeName,
BuiltinFnIdIsInteger,
BuiltinFnIdIsFloat,
@ -1706,7 +1705,6 @@ enum IrInstructionId {
IrInstructionIdTruncate,
IrInstructionIdIntType,
IrInstructionIdBoolNot,
IrInstructionIdAlloca,
IrInstructionIdMemset,
IrInstructionIdMemcpy,
IrInstructionIdSlice,
@ -2234,14 +2232,6 @@ struct IrInstructionBoolNot {
IrInstruction *value;
};
struct IrInstructionAlloca {
IrInstruction base;
IrInstruction *type_value;
IrInstruction *count;
LLVMValueRef tmp_ptr;
};
struct IrInstructionMemset {
IrInstruction base;

View File

@ -2190,26 +2190,6 @@ static LLVMValueRef ir_render_truncate(CodeGen *g, IrExecutable *executable, IrI
}
}
static LLVMValueRef ir_render_alloca(CodeGen *g, IrExecutable *executable, IrInstructionAlloca *instruction) {
TypeTableEntry *slice_type = get_underlying_type(instruction->base.value.type);
TypeTableEntry *ptr_type = slice_type->data.structure.fields[slice_ptr_index].type_entry;
TypeTableEntry *child_type = ptr_type->data.pointer.child_type;
LLVMValueRef size_val = ir_llvm_value(g, instruction->count);
LLVMValueRef ptr_val = LLVMBuildArrayAlloca(g->builder, child_type->type_ref, size_val, "");
// TODO in debug mode, initialize all the bytes to 0xaa
// store the freshly allocated pointer in the slice
LLVMValueRef ptr_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_ptr_index, "");
LLVMBuildStore(g->builder, ptr_val, ptr_field_ptr);
// store the size in the len field
LLVMValueRef len_field_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, slice_len_index, "");
LLVMBuildStore(g->builder, size_val, len_field_ptr);
return instruction->tmp_ptr;
}
static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {
LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
LLVMValueRef char_val = ir_llvm_value(g, instruction->byte);
@ -2548,7 +2528,7 @@ static LLVMValueRef ir_render_maybe_wrap(CodeGen *g, IrExecutable *executable, I
assert(instruction->tmp_ptr);
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, "");
assert(child_type == instruction->value->value.type);
// child_type and instruction->value->value.type may differ by constness
gen_assign_raw(g, val_ptr, payload_val, child_type);
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr);
@ -2796,8 +2776,6 @@ static LLVMValueRef ir_render_instruction(CodeGen *g, IrExecutable *executable,
return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
case IrInstructionIdBoolNot:
return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction);
case IrInstructionIdAlloca:
return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction);
case IrInstructionIdMemset:
return ir_render_memset(g, executable, (IrInstructionMemset *)instruction);
case IrInstructionIdMemcpy:
@ -3648,9 +3626,6 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdCall) {
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
slot = &call_instruction->tmp_ptr;
} else if (instruction->id == IrInstructionIdAlloca) {
IrInstructionAlloca *alloca_instruction = (IrInstructionAlloca *)instruction;
slot = &alloca_instruction->tmp_ptr;
} else if (instruction->id == IrInstructionIdSlice) {
IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
slot = &slice_instruction->tmp_ptr;
@ -4326,7 +4301,6 @@ static void define_builtin_fns(CodeGen *g) {
create_builtin_fn(g, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2);
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 2);
create_builtin_fn(g, BuiltinFnIdAlloca, "alloca", 2);
create_builtin_fn(g, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2);
create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);

View File

@ -404,10 +404,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
return IrInstructionIdBoolNot;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
return IrInstructionIdAlloca;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) {
return IrInstructionIdMemset;
}
@ -1668,27 +1664,6 @@ static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_
return new_instruction;
}
static IrInstruction *ir_build_alloca(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *type_value, IrInstruction *count)
{
IrInstructionAlloca *instruction = ir_build_instruction<IrInstructionAlloca>(irb, scope, source_node);
instruction->type_value = type_value;
instruction->count = count;
ir_ref_instruction(type_value, irb->current_basic_block);
ir_ref_instruction(count, irb->current_basic_block);
return &instruction->base;
}
static IrInstruction *ir_build_alloca_from(IrBuilder *irb, IrInstruction *old_instruction,
IrInstruction *type_value, IrInstruction *count)
{
IrInstruction *new_instruction = ir_build_alloca(irb, old_instruction->scope, old_instruction->source_node, type_value, count);
ir_link_new_instruction(new_instruction, old_instruction);
return new_instruction;
}
static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count)
{
@ -2567,14 +2542,6 @@ static IrInstruction *ir_instruction_boolnot_get_dep(IrInstructionBoolNot *instr
}
}
static IrInstruction *ir_instruction_alloca_get_dep(IrInstructionAlloca *instruction, size_t index) {
switch (index) {
case 0: return instruction->type_value;
case 1: return instruction->count;
default: return nullptr;
}
}
static IrInstruction *ir_instruction_memset_get_dep(IrInstructionMemset *instruction, size_t index) {
switch (index) {
case 0: return instruction->dest_ptr;
@ -2938,8 +2905,6 @@ static IrInstruction *ir_instruction_get_dep(IrInstruction *instruction, size_t
return ir_instruction_inttype_get_dep((IrInstructionIntType *) instruction, index);
case IrInstructionIdBoolNot:
return ir_instruction_boolnot_get_dep((IrInstructionBoolNot *) instruction, index);
case IrInstructionIdAlloca:
return ir_instruction_alloca_get_dep((IrInstructionAlloca *) instruction, index);
case IrInstructionIdMemset:
return ir_instruction_memset_get_dep((IrInstructionMemset *) instruction, index);
case IrInstructionIdMemcpy:
@ -4100,20 +4065,6 @@ static IrInstruction *ir_gen_builtin_fn_call(IrBuilder *irb, Scope *scope, AstNo
return ir_build_int_type(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdAlloca:
{
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;
AstNode *arg1_node = node->data.fn_call_expr.params.at(1);
IrInstruction *arg1_value = ir_gen_node(irb, arg1_node, scope);
if (arg1_value == irb->codegen->invalid_instruction)
return arg1_value;
return ir_build_alloca(irb, scope, node, arg0_value, arg1_value);
}
case BuiltinFnIdMemcpy:
{
AstNode *arg0_node = node->data.fn_call_expr.params.at(0);
@ -11461,31 +11412,6 @@ static TypeTableEntry *ir_analyze_instruction_bool_not(IrAnalyze *ira, IrInstruc
return bool_type;
}
static TypeTableEntry *ir_analyze_instruction_alloca(IrAnalyze *ira, IrInstructionAlloca *instruction) {
IrInstruction *type_value = instruction->type_value->other;
if (type_is_invalid(type_value->value.type))
return ira->codegen->builtin_types.entry_invalid;
IrInstruction *count_value = instruction->count->other;
if (type_is_invalid(count_value->value.type))
return ira->codegen->builtin_types.entry_invalid;
TypeTableEntry *child_type = ir_resolve_type(ira, type_value);
if (type_requires_comptime(child_type)) {
ir_add_error(ira, type_value,
buf_sprintf("invalid alloca type '%s'", buf_ptr(&child_type->name)));
// TODO if this is a typedecl, add error note showing the declaration of the type decl
return ira->codegen->builtin_types.entry_invalid;
} else {
TypeTableEntry *slice_type = get_slice_type(ira->codegen, child_type, false);
IrInstruction *new_instruction = ir_build_alloca_from(&ira->new_irb, &instruction->base, type_value, count_value);
ir_add_alloca(ira, new_instruction, slice_type);
return slice_type;
}
zig_unreachable();
}
static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
IrInstruction *dest_ptr = instruction->dest_ptr->other;
if (type_is_invalid(dest_ptr->value.type))
@ -12550,8 +12476,6 @@ static TypeTableEntry *ir_analyze_instruction_nocast(IrAnalyze *ira, IrInstructi
return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
case IrInstructionIdBoolNot:
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
case IrInstructionIdAlloca:
return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
case IrInstructionIdMemset:
return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction);
case IrInstructionIdMemcpy:
@ -12749,7 +12673,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdTruncate:
case IrInstructionIdIntType:
case IrInstructionIdBoolNot:
case IrInstructionIdAlloca:
case IrInstructionIdSlice:
case IrInstructionIdMemberCount:
case IrInstructionIdAlignOf:

View File

@ -601,14 +601,6 @@ static void ir_print_truncate(IrPrint *irp, IrInstructionTruncate *instruction)
fprintf(irp->f, ")");
}
static void ir_print_alloca(IrPrint *irp, IrInstructionAlloca *instruction) {
fprintf(irp->f, "@alloca(");
ir_print_other_instruction(irp, instruction->type_value);
fprintf(irp->f, ", ");
ir_print_other_instruction(irp, instruction->count);
fprintf(irp->f, ")");
}
static void ir_print_int_type(IrPrint *irp, IrInstructionIntType *instruction) {
fprintf(irp->f, "@intType(");
ir_print_other_instruction(irp, instruction->is_signed);
@ -1049,9 +1041,6 @@ static void ir_print_instruction(IrPrint *irp, IrInstruction *instruction) {
case IrInstructionIdTruncate:
ir_print_truncate(irp, (IrInstructionTruncate *)instruction);
break;
case IrInstructionIdAlloca:
ir_print_alloca(irp, (IrInstructionAlloca *)instruction);
break;
case IrInstructionIdIntType:
ir_print_int_type(irp, (IrInstructionIntType *)instruction);
break;

View File

@ -40,6 +40,8 @@ pub const Builder = struct {
}
pub fn make(self: &Builder, cli_args: []const []const u8) -> %void {
var env_map = %return os.getEnvMap(self.allocator);
var verbose = false;
for (cli_args) |arg| {
if (mem.eql(u8, arg, "--verbose")) {
@ -93,7 +95,7 @@ pub const Builder = struct {
}
printInvocation(self.zig_exe, zig_args);
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), os.environ,
var child = %return os.ChildProcess.spawn(self.zig_exe, zig_args.toSliceConst(), env_map,
StdIo.Ignore, StdIo.Inherit, StdIo.Inherit, self.allocator);
const term = %return child.wait();
switch (term) {

View File

@ -29,7 +29,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
};
pub const Iterator = struct {
hm: &Self,
hm: &const Self,
// how many items have we returned
count: usize,
// iterator through the entry array
@ -99,17 +99,21 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
pub fn get(hm: &Self, key: K) -> ?&Entry {
if (hm.entries.len == 0) {
return null;
}
return hm.internalGet(key);
}
pub fn remove(hm: &Self, key: K) {
pub fn remove(hm: &Self, key: K) -> ?&Entry {
hm.incrementModificationCount();
const start_index = hm.keyToIndex(key);
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) {
const index = (start_index + roll_over) % hm.entries.len;
var entry = &hm.entries[index];
assert(entry.used); // key not found
if (!entry.used)
return null;
if (!eql(entry.key, key)) continue;
@ -119,7 +123,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
if (!next_entry.used or next_entry.distance_from_start_index == 0) {
entry.used = false;
hm.size -= 1;
return;
return entry;
}
*entry = *next_entry;
entry.distance_from_start_index -= 1;
@ -127,10 +131,10 @@ pub fn HashMap(comptime K: type, comptime V: type,
}
unreachable // shifting everything in the table
}}
unreachable // key not found
return null;
}
pub fn entryIterator(hm: &Self) -> Iterator {
pub fn entryIterator(hm: &const Self) -> Iterator {
return Iterator {
.hm = hm,
.count = 0,
@ -231,7 +235,8 @@ test "basicHashMapTest" {
%%map.put(5, 55);
assert((??map.get(2)).value == 22);
map.remove(2);
_ = map.remove(2);
assert(map.remove(2) == null);
assert(if (const entry ?= map.get(2)) false else true);
}

View File

@ -21,6 +21,8 @@ const mem = @import("../mem.zig");
const Allocator = mem.Allocator;
const io = @import("../io.zig");
const HashMap = @import("../hash_map.zig").HashMap;
const cstr = @import("../cstr.zig");
error Unexpected;
error SysResources;
@ -284,12 +286,12 @@ pub const ChildProcess = struct {
Close,
};
pub fn spawn(exe_path: []const u8, args: []const []const u8, env: []const EnvPair,
pub fn spawn(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
switch (@compileVar("os")) {
Os.linux, Os.macosx, Os.ios, Os.darwin => {
return spawnPosix(exe_path, args, env, stdin, stdout, stderr, allocator);
return spawnPosix(exe_path, args, env_map, stdin, stdout, stderr, allocator);
},
else => @compileError("Unsupported OS"),
}
@ -351,7 +353,7 @@ pub const ChildProcess = struct {
};
}
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env: []const EnvPair,
fn spawnPosix(exe_path: []const u8, args: []const []const u8, env_map: &const EnvMap,
stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{
// TODO issue #295
@ -408,7 +410,7 @@ pub const ChildProcess = struct {
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err);
const err = posix.getErrno(%return execve(exe_path, args, env, allocator));
const err = posix.getErrno(%return execve(exe_path, args, env_map, allocator));
assert(err > 0);
forkChildErrReport(err_pipe[1], switch (err) {
errno.EFAULT => unreachable,
@ -473,7 +475,7 @@ pub const ChildProcess = struct {
/// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables.
/// Also make the first arg equal to path.
fn execve(path: []const u8, argv: []const []const u8, envp: []const EnvPair, allocator: &Allocator) -> %usize {
fn execve(path: []const u8, argv: []const []const u8, env_map: &const EnvMap, allocator: &Allocator) -> %usize {
const path_buf = %return allocator.alloc(u8, path.len + 1);
defer allocator.free(path_buf);
@memcpy(&path_buf[0], &path[0], path.len);
@ -505,39 +507,149 @@ fn execve(path: []const u8, argv: []const []const u8, envp: []const EnvPair, all
}
argv_buf[argv.len + 1] = null;
const envp_buf = %return allocator.alloc(?&const u8, envp.len + 1);
const envp_count = env_map.count();
const envp_buf = %return allocator.alloc(?&const u8, envp_count + 1);
mem.set(?&const u8, envp_buf, null);
defer {
for (envp_buf) |env, i| {
const env_buf = if (const ptr ?= env) ptr[0...envp[i].key.len + envp[i].value.len + 2] else break;
const env_buf = if (const ptr ?= env) ptr[0...cstr.len(ptr)] else break;
allocator.free(env_buf);
}
allocator.free(envp_buf);
}
for (envp) |pair, i| {
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
env_buf[pair.key.len] = '=';
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
env_buf[env_buf.len - 1] = 0;
{
var it = env_map.iterator();
var i: usize = 0;
while (true; i += 1) {
const pair = it.next() ?? break;
envp_buf[i] = env_buf.ptr;
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2);
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len);
env_buf[pair.key.len] = '=';
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len);
env_buf[env_buf.len - 1] = 0;
envp_buf[i] = env_buf.ptr;
}
assert(i == envp_count);
}
envp_buf[envp.len] = null;
envp_buf[envp_count] = null;
return posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr);
}
pub const EnvPair = struct {
key: []const u8,
value: []const u8,
pub var environ_raw: []&u8 = undefined;
pub const EnvMap = struct {
hash_map: EnvHashMap,
const EnvHashMap = HashMap([]const u8, []const u8, hash_slice_u8, eql_slice_u8);
pub fn init(allocator: &Allocator) -> EnvMap {
var self = EnvMap {
.hash_map = undefined,
};
self.hash_map.init(allocator);
return self;
}
pub fn deinit(self: &EnvMap) {
var it = self.hash_map.entryIterator();
while (true) {
const entry = it.next() ?? break;
self.free(entry.key);
self.free(entry.value);
}
self.hash_map.deinit();
}
pub fn set(self: &EnvMap, key: []const u8, value: []const u8) -> %void {
if (const entry ?= self.hash_map.get(key)) {
const value_copy = %return self.copy(value);
%defer self.free(value_copy);
%return self.hash_map.put(key, value_copy);
self.free(entry.value);
} else {
const key_copy = %return self.copy(key);
%defer self.free(key_copy);
const value_copy = %return self.copy(value);
%defer self.free(value_copy);
%return self.hash_map.put(key_copy, value_copy);
}
}
pub fn delete(self: &EnvMap, key: []const u8) {
const entry = self.hash_map.remove(key) ?? return;
self.free(entry.key);
self.free(entry.value);
}
pub fn count(self: &const EnvMap) -> usize {
return self.hash_map.size;
}
pub fn iterator(self: &const EnvMap) -> EnvHashMap.Iterator {
return self.hash_map.entryIterator();
}
fn free(self: &EnvMap, value: []const u8) {
// remove the const
const mut_value = @ptrcast(&u8, value.ptr)[0...value.len];
self.hash_map.allocator.free(mut_value);
}
fn copy(self: &EnvMap, value: []const u8) -> %[]const u8 {
const result = %return self.hash_map.allocator.alloc(u8, value.len);
mem.copy(u8, result, value);
return result;
}
};
pub var environ: []const EnvPair = undefined;
pub fn getEnvMap(allocator: &Allocator) -> %EnvMap {
var result = EnvMap.init(allocator);
%defer result.deinit();
for (environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {}
const key = ptr[0...line_i];
var end_i: usize = line_i;
while (ptr[end_i] != 0; end_i += 1) {}
const value = ptr[line_i + 1...end_i];
%return result.set(key, value);
}
return result;
}
pub fn getEnv(key: []const u8) -> ?[]const u8 {
for (environ) |pair| {
if (mem.eql(u8, pair.key, key))
return pair.value;
for (environ_raw) |ptr| {
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {}
const this_key = ptr[0...line_i];
if (!mem.eql(u8, key, this_key))
continue;
var end_i: usize = line_i;
while (ptr[end_i] != 0; end_i += 1) {}
const this_value = ptr[line_i + 1...end_i];
return this_value;
}
return null;
}
fn hash_slice_u8(k: []const u8) -> u32 {
// FNV 32-bit hash
var h: u32 = 2166136261;
for (k) |b| {
h = (h ^ b) *% 16777619;
}
return h;
}
fn eql_slice_u8(a: []const u8, b: []const u8) -> bool {
return mem.eql(u8, a, b);
}

View File

@ -9,8 +9,7 @@ const want_start_symbol = !want_main_symbol;
const exit = std.os.posix.exit;
var argc: usize = undefined;
var argv: &&u8 = undefined;
var argc_ptr: &usize = undefined;
export nakedcc fn _start() -> noreturn {
@setGlobalLinkage(_start, if (want_start_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal);
@ -20,21 +19,28 @@ export nakedcc fn _start() -> noreturn {
switch (@compileVar("arch")) {
Arch.x86_64 => {
argc = asm("mov %[argc], [rsp]": [argc] "=r" (-> usize));
argv = asm("lea %[argv], [rsp + 8h]": [argv] "=r" (-> &&u8));
argc_ptr = asm("lea %[argc], [rsp]": [argc] "=r" (-> &usize));
},
Arch.i386 => {
argc = asm("mov %[argc], [esp]": [argc] "=r" (-> usize));
argv = asm("lea %[argv], [esp + 4h]": [argv] "=r" (-> &&u8));
argc_ptr = asm("lea %[argc], [esp]": [argc] "=r" (-> &usize));
},
else => @compileError("unsupported arch"),
}
callMainAndExit()
}
fn callMain(envp: &?&u8) -> %void {
// TODO issue #225
const args = @alloca([]u8, argc);
fn callMainAndExit() -> noreturn {
const argc = *argc_ptr;
const argv = @ptrcast(&&u8, &argc_ptr[1]);
const envp = @ptrcast(&?&u8, &argv[argc + 1]);
callMain(argc, argv, envp) %% exit(1);
exit(0);
}
var args_data: [32][]u8 = undefined;
fn callMain(argc: usize, argv: &&u8, envp: &?&u8) -> %void {
// TODO create args API to make it work with > 32 args
const args = args_data[0...argc];
for (args) |_, i| {
const ptr = argv[i];
args[i] = ptr[0...std.cstr.len(ptr)];
@ -42,41 +48,17 @@ fn callMain(envp: &?&u8) -> %void {
var env_count: usize = 0;
while (envp[env_count] != null; env_count += 1) {}
// TODO issue #225
const environ = @alloca(std.os.EnvPair, env_count);
for (environ) |_, env_i| {
const ptr = ??envp[env_i];
var line_i: usize = 0;
while (ptr[line_i] != 0 and ptr[line_i] != '='; line_i += 1) {}
var end_i: usize = line_i;
while (ptr[end_i] != 0; end_i += 1) {}
environ[env_i] = std.os.EnvPair {
.key = ptr[0...line_i],
.value = ptr[line_i + 1...end_i],
};
}
std.os.environ = environ;
std.os.environ_raw = @ptrcast(&&u8, envp)[0...env_count];
return root.main(args);
}
fn callMainAndExit() -> noreturn {
const envp = @ptrcast(&?&u8, &argv[argc + 1]);
callMain(envp) %% exit(1);
exit(0);
}
export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
@setGlobalLinkage(main, if (want_main_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal);
if (!want_main_symbol) {
unreachable;
}
argc = usize(c_argc);
argv = c_argv;
callMain(c_envp) %% return 1;
callMain(usize(c_argc), c_argv, c_envp) %% return 1;
return 0;
}

View File

@ -1,4 +1,5 @@
const assert = @import("std").debug.assert;
const debug = @import("std").debug;
const assert = debug.assert;
var argv: &const &const u8 = undefined;
@ -20,7 +21,7 @@ fn foo(args: [][]const u8) {
}
fn bar(argc: usize) {
const args = @alloca([]u8, argc);
const args = %%debug.global_allocator.alloc([]u8, argc);
for (args) |_, i| {
const ptr = argv[i];
args[i] = ptr[0...strlen(ptr)];