delete alloca builtin function

See #225

introduce os.EnvMap
This commit is contained in:
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 Built-in functions are prefixed with `@`. Remember that the `comptime` keyword on
a parameter means that the parameter must be known at compile time. 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 ### @typeOf(expression) -> type
This function returns a compile-time constant, which is the type of the This function returns a compile-time constant, which is the type of the

View File

@ -1190,7 +1190,6 @@ enum BuiltinFnId {
BuiltinFnIdTruncate, BuiltinFnIdTruncate,
BuiltinFnIdIntType, BuiltinFnIdIntType,
BuiltinFnIdSetDebugSafety, BuiltinFnIdSetDebugSafety,
BuiltinFnIdAlloca,
BuiltinFnIdTypeName, BuiltinFnIdTypeName,
BuiltinFnIdIsInteger, BuiltinFnIdIsInteger,
BuiltinFnIdIsFloat, BuiltinFnIdIsFloat,
@ -1706,7 +1705,6 @@ enum IrInstructionId {
IrInstructionIdTruncate, IrInstructionIdTruncate,
IrInstructionIdIntType, IrInstructionIdIntType,
IrInstructionIdBoolNot, IrInstructionIdBoolNot,
IrInstructionIdAlloca,
IrInstructionIdMemset, IrInstructionIdMemset,
IrInstructionIdMemcpy, IrInstructionIdMemcpy,
IrInstructionIdSlice, IrInstructionIdSlice,
@ -2234,14 +2232,6 @@ struct IrInstructionBoolNot {
IrInstruction *value; IrInstruction *value;
}; };
struct IrInstructionAlloca {
IrInstruction base;
IrInstruction *type_value;
IrInstruction *count;
LLVMValueRef tmp_ptr;
};
struct IrInstructionMemset { struct IrInstructionMemset {
IrInstruction base; 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) { static LLVMValueRef ir_render_memset(CodeGen *g, IrExecutable *executable, IrInstructionMemset *instruction) {
LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr); LLVMValueRef dest_ptr = ir_llvm_value(g, instruction->dest_ptr);
LLVMValueRef char_val = ir_llvm_value(g, instruction->byte); 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); assert(instruction->tmp_ptr);
LLVMValueRef val_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_child_index, ""); 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); gen_assign_raw(g, val_ptr, payload_val, child_type);
LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, ""); LLVMValueRef maybe_ptr = LLVMBuildStructGEP(g->builder, instruction->tmp_ptr, maybe_null_index, "");
LLVMBuildStore(g->builder, LLVMConstAllOnes(LLVMInt1Type()), maybe_ptr); 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); return ir_render_truncate(g, executable, (IrInstructionTruncate *)instruction);
case IrInstructionIdBoolNot: case IrInstructionIdBoolNot:
return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction); return ir_render_bool_not(g, executable, (IrInstructionBoolNot *)instruction);
case IrInstructionIdAlloca:
return ir_render_alloca(g, executable, (IrInstructionAlloca *)instruction);
case IrInstructionIdMemset: case IrInstructionIdMemset:
return ir_render_memset(g, executable, (IrInstructionMemset *)instruction); return ir_render_memset(g, executable, (IrInstructionMemset *)instruction);
case IrInstructionIdMemcpy: case IrInstructionIdMemcpy:
@ -3648,9 +3626,6 @@ static void do_code_gen(CodeGen *g) {
} else if (instruction->id == IrInstructionIdCall) { } else if (instruction->id == IrInstructionIdCall) {
IrInstructionCall *call_instruction = (IrInstructionCall *)instruction; IrInstructionCall *call_instruction = (IrInstructionCall *)instruction;
slot = &call_instruction->tmp_ptr; 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) { } else if (instruction->id == IrInstructionIdSlice) {
IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction; IrInstructionSlice *slice_instruction = (IrInstructionSlice *)instruction;
slot = &slice_instruction->tmp_ptr; 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, BuiltinFnIdCompileLog, "compileLog", SIZE_MAX);
create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2); create_builtin_fn(g, BuiltinFnIdIntType, "intType", 2);
create_builtin_fn(g, BuiltinFnIdSetDebugSafety, "setDebugSafety", 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, BuiltinFnIdSetGlobalAlign, "setGlobalAlign", 2);
create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2); create_builtin_fn(g, BuiltinFnIdSetGlobalSection, "setGlobalSection", 2);
create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2); create_builtin_fn(g, BuiltinFnIdSetGlobalLinkage, "setGlobalLinkage", 2);

View File

@ -404,10 +404,6 @@ static constexpr IrInstructionId ir_instruction_id(IrInstructionBoolNot *) {
return IrInstructionIdBoolNot; return IrInstructionIdBoolNot;
} }
static constexpr IrInstructionId ir_instruction_id(IrInstructionAlloca *) {
return IrInstructionIdAlloca;
}
static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) { static constexpr IrInstructionId ir_instruction_id(IrInstructionMemset *) {
return IrInstructionIdMemset; return IrInstructionIdMemset;
} }
@ -1668,27 +1664,6 @@ static IrInstruction *ir_build_bool_not_from(IrBuilder *irb, IrInstruction *old_
return new_instruction; 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, static IrInstruction *ir_build_memset(IrBuilder *irb, Scope *scope, AstNode *source_node,
IrInstruction *dest_ptr, IrInstruction *byte, IrInstruction *count) 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) { static IrInstruction *ir_instruction_memset_get_dep(IrInstructionMemset *instruction, size_t index) {
switch (index) { switch (index) {
case 0: return instruction->dest_ptr; 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); return ir_instruction_inttype_get_dep((IrInstructionIntType *) instruction, index);
case IrInstructionIdBoolNot: case IrInstructionIdBoolNot:
return ir_instruction_boolnot_get_dep((IrInstructionBoolNot *) instruction, index); return ir_instruction_boolnot_get_dep((IrInstructionBoolNot *) instruction, index);
case IrInstructionIdAlloca:
return ir_instruction_alloca_get_dep((IrInstructionAlloca *) instruction, index);
case IrInstructionIdMemset: case IrInstructionIdMemset:
return ir_instruction_memset_get_dep((IrInstructionMemset *) instruction, index); return ir_instruction_memset_get_dep((IrInstructionMemset *) instruction, index);
case IrInstructionIdMemcpy: 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); 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: case BuiltinFnIdMemcpy:
{ {
AstNode *arg0_node = node->data.fn_call_expr.params.at(0); 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; 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) { static TypeTableEntry *ir_analyze_instruction_memset(IrAnalyze *ira, IrInstructionMemset *instruction) {
IrInstruction *dest_ptr = instruction->dest_ptr->other; IrInstruction *dest_ptr = instruction->dest_ptr->other;
if (type_is_invalid(dest_ptr->value.type)) 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); return ir_analyze_instruction_int_type(ira, (IrInstructionIntType *)instruction);
case IrInstructionIdBoolNot: case IrInstructionIdBoolNot:
return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction); return ir_analyze_instruction_bool_not(ira, (IrInstructionBoolNot *)instruction);
case IrInstructionIdAlloca:
return ir_analyze_instruction_alloca(ira, (IrInstructionAlloca *)instruction);
case IrInstructionIdMemset: case IrInstructionIdMemset:
return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction); return ir_analyze_instruction_memset(ira, (IrInstructionMemset *)instruction);
case IrInstructionIdMemcpy: case IrInstructionIdMemcpy:
@ -12749,7 +12673,6 @@ bool ir_has_side_effects(IrInstruction *instruction) {
case IrInstructionIdTruncate: case IrInstructionIdTruncate:
case IrInstructionIdIntType: case IrInstructionIdIntType:
case IrInstructionIdBoolNot: case IrInstructionIdBoolNot:
case IrInstructionIdAlloca:
case IrInstructionIdSlice: case IrInstructionIdSlice:
case IrInstructionIdMemberCount: case IrInstructionIdMemberCount:
case IrInstructionIdAlignOf: case IrInstructionIdAlignOf:

View File

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

View File

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

View File

@ -29,7 +29,7 @@ pub fn HashMap(comptime K: type, comptime V: type,
}; };
pub const Iterator = struct { pub const Iterator = struct {
hm: &Self, hm: &const Self,
// how many items have we returned // how many items have we returned
count: usize, count: usize,
// iterator through the entry array // 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 { pub fn get(hm: &Self, key: K) -> ?&Entry {
if (hm.entries.len == 0) {
return null;
}
return hm.internalGet(key); return hm.internalGet(key);
} }
pub fn remove(hm: &Self, key: K) { pub fn remove(hm: &Self, key: K) -> ?&Entry {
hm.incrementModificationCount(); hm.incrementModificationCount();
const start_index = hm.keyToIndex(key); const start_index = hm.keyToIndex(key);
{var roll_over: usize = 0; while (roll_over <= hm.max_distance_from_start_index; roll_over += 1) { {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; const index = (start_index + roll_over) % hm.entries.len;
var entry = &hm.entries[index]; var entry = &hm.entries[index];
assert(entry.used); // key not found if (!entry.used)
return null;
if (!eql(entry.key, key)) continue; 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) { if (!next_entry.used or next_entry.distance_from_start_index == 0) {
entry.used = false; entry.used = false;
hm.size -= 1; hm.size -= 1;
return; return entry;
} }
*entry = *next_entry; *entry = *next_entry;
entry.distance_from_start_index -= 1; 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 // 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 { return Iterator {
.hm = hm, .hm = hm,
.count = 0, .count = 0,
@ -231,7 +235,8 @@ test "basicHashMapTest" {
%%map.put(5, 55); %%map.put(5, 55);
assert((??map.get(2)).value == 22); 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); 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 Allocator = mem.Allocator;
const io = @import("../io.zig"); const io = @import("../io.zig");
const HashMap = @import("../hash_map.zig").HashMap;
const cstr = @import("../cstr.zig");
error Unexpected; error Unexpected;
error SysResources; error SysResources;
@ -284,12 +286,12 @@ pub const ChildProcess = struct {
Close, 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 stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{ {
switch (@compileVar("os")) { switch (@compileVar("os")) {
Os.linux, Os.macosx, Os.ios, Os.darwin => { 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"), 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 stdin: StdIo, stdout: StdIo, stderr: StdIo, allocator: &Allocator) -> %ChildProcess
{ {
// TODO issue #295 // TODO issue #295
@ -408,7 +410,7 @@ pub const ChildProcess = struct {
setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %% setUpChildIo(stderr, stderr_pipe[1], posix.STDERR_FILENO, dev_null_fd) %%
|err| forkChildErrReport(err_pipe[1], err); |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); assert(err > 0);
forkChildErrReport(err_pipe[1], switch (err) { forkChildErrReport(err_pipe[1], switch (err) {
errno.EFAULT => unreachable, 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 /// It must also convert to KEY=VALUE\0 format for environment variables, and include null
/// pointers after the args and after the environment variables. /// pointers after the args and after the environment variables.
/// Also make the first arg equal to path. /// 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); const path_buf = %return allocator.alloc(u8, path.len + 1);
defer allocator.free(path_buf); defer allocator.free(path_buf);
@memcpy(&path_buf[0], &path[0], path.len); @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; 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); mem.set(?&const u8, envp_buf, null);
defer { defer {
for (envp_buf) |env, i| { 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(env_buf);
} }
allocator.free(envp_buf); allocator.free(envp_buf);
} }
for (envp) |pair, i| { {
const env_buf = %return allocator.alloc(u8, pair.key.len + pair.value.len + 2); var it = env_map.iterator();
@memcpy(&env_buf[0], pair.key.ptr, pair.key.len); var i: usize = 0;
env_buf[pair.key.len] = '='; while (true; i += 1) {
@memcpy(&env_buf[pair.key.len + 1], pair.value.ptr, pair.value.len); const pair = it.next() ?? break;
env_buf[env_buf.len - 1] = 0;
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); return posix.execve(path_buf.ptr, argv_buf.ptr, envp_buf.ptr);
} }
pub const EnvPair = struct { pub var environ_raw: []&u8 = undefined;
key: []const u8,
value: []const u8, 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 { pub fn getEnv(key: []const u8) -> ?[]const u8 {
for (environ) |pair| { for (environ_raw) |ptr| {
if (mem.eql(u8, pair.key, key)) var line_i: usize = 0;
return pair.value; 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; 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; const exit = std.os.posix.exit;
var argc: usize = undefined; var argc_ptr: &usize = undefined;
var argv: &&u8 = undefined;
export nakedcc fn _start() -> noreturn { export nakedcc fn _start() -> noreturn {
@setGlobalLinkage(_start, if (want_start_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal); @setGlobalLinkage(_start, if (want_start_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal);
@ -20,21 +19,28 @@ export nakedcc fn _start() -> noreturn {
switch (@compileVar("arch")) { switch (@compileVar("arch")) {
Arch.x86_64 => { Arch.x86_64 => {
argc = asm("mov %[argc], [rsp]": [argc] "=r" (-> usize)); argc_ptr = asm("lea %[argc], [rsp]": [argc] "=r" (-> &usize));
argv = asm("lea %[argv], [rsp + 8h]": [argv] "=r" (-> &&u8));
}, },
Arch.i386 => { Arch.i386 => {
argc = asm("mov %[argc], [esp]": [argc] "=r" (-> usize)); argc_ptr = asm("lea %[argc], [esp]": [argc] "=r" (-> &usize));
argv = asm("lea %[argv], [esp + 4h]": [argv] "=r" (-> &&u8));
}, },
else => @compileError("unsupported arch"), else => @compileError("unsupported arch"),
} }
callMainAndExit() callMainAndExit()
} }
fn callMain(envp: &?&u8) -> %void { fn callMainAndExit() -> noreturn {
// TODO issue #225 const argc = *argc_ptr;
const args = @alloca([]u8, argc); 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| { for (args) |_, i| {
const ptr = argv[i]; const ptr = argv[i];
args[i] = ptr[0...std.cstr.len(ptr)]; args[i] = ptr[0...std.cstr.len(ptr)];
@ -42,41 +48,17 @@ fn callMain(envp: &?&u8) -> %void {
var env_count: usize = 0; var env_count: usize = 0;
while (envp[env_count] != null; env_count += 1) {} while (envp[env_count] != null; env_count += 1) {}
// TODO issue #225 std.os.environ_raw = @ptrcast(&&u8, envp)[0...env_count];
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;
return root.main(args); 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 { export fn main(c_argc: i32, c_argv: &&u8, c_envp: &?&u8) -> i32 {
@setGlobalLinkage(main, if (want_main_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal); @setGlobalLinkage(main, if (want_main_symbol) GlobalLinkage.Strong else GlobalLinkage.Internal);
if (!want_main_symbol) { if (!want_main_symbol) {
unreachable; unreachable;
} }
argc = usize(c_argc); callMain(usize(c_argc), c_argv, c_envp) %% return 1;
argv = c_argv;
callMain(c_envp) %% return 1;
return 0; 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; var argv: &const &const u8 = undefined;
@ -20,7 +21,7 @@ fn foo(args: [][]const u8) {
} }
fn bar(argc: usize) { fn bar(argc: usize) {
const args = @alloca([]u8, argc); const args = %%debug.global_allocator.alloc([]u8, argc);
for (args) |_, i| { for (args) |_, i| {
const ptr = argv[i]; const ptr = argv[i];
args[i] = ptr[0...strlen(ptr)]; args[i] = ptr[0...strlen(ptr)];