Merge pull request #1266 from ziglang/self-hosted-libc-hello-world

Self hosted libc hello world
master
Andrew Kelley 2018-07-24 00:31:33 -04:00 committed by GitHub
commit 10bdf73a02
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
46 changed files with 5619 additions and 1199 deletions

View File

@ -426,6 +426,7 @@ set(ZIG_SOURCES
)
set(ZIG_CPP_SOURCES
"${CMAKE_SOURCE_DIR}/src/zig_llvm.cpp"
"${CMAKE_SOURCE_DIR}/src/windows_sdk.cpp"
)
set(ZIG_STD_FILES
@ -489,6 +490,7 @@ set(ZIG_STD_FILES
"math/atan.zig"
"math/atan2.zig"
"math/atanh.zig"
"math/big/index.zig"
"math/big/int.zig"
"math/cbrt.zig"
"math/ceil.zig"
@ -566,8 +568,14 @@ set(ZIG_STD_FILES
"os/linux/x86_64.zig"
"os/path.zig"
"os/time.zig"
"os/windows/advapi32.zig"
"os/windows/error.zig"
"os/windows/index.zig"
"os/windows/kernel32.zig"
"os/windows/ole32.zig"
"os/windows/shell32.zig"
"os/windows/shlwapi.zig"
"os/windows/user32.zig"
"os/windows/util.zig"
"os/zen.zig"
"rand/index.zig"
@ -616,6 +624,7 @@ set(ZIG_STD_FILES
"zig/ast.zig"
"zig/index.zig"
"zig/parse.zig"
"zig/parse_string_literal.zig"
"zig/render.zig"
"zig/tokenizer.zig"
)

View File

@ -21,19 +21,19 @@ clarity.
* Compatible with C libraries with no wrapper necessary. Directly include
C .h files and get access to the functions and symbols therein.
* Provides standard library which competes with the C standard library and is
always compiled against statically in source form. Compile units do not
always compiled against statically in source form. Zig binaries do not
depend on libc unless explicitly linked.
* Nullable type instead of null pointers.
* Optional type instead of null pointers.
* Safe unions, tagged unions, and C ABI compatible unions.
* Generics so that one can write efficient data structures that work for any
data type.
* No header files required. Top level declarations are entirely
order-independent.
* Compile-time code execution. Compile-time reflection.
* Partial compile-time function evaluation with eliminates the need for
* Partial compile-time function evaluation which eliminates the need for
a preprocessor or macros.
* The binaries produced by Zig have complete debugging information so you can,
for example, use GDB or MSVC to debug your software.
for example, use GDB, MSVC, or LLDB to debug your software.
* Built-in unit tests with `zig test`.
* Friendly toward package maintainers. Reproducible build, bootstrapping
process carefully documented. Issues filed by package maintainers are

View File

@ -4,4 +4,5 @@ pub use @cImport({
@cInclude("inttypes.h");
@cInclude("config.h");
@cInclude("zig_llvm.h");
@cInclude("windows_sdk.h");
});

68
src-self-hosted/c_int.zig Normal file
View File

@ -0,0 +1,68 @@
pub const CInt = struct {
id: Id,
zig_name: []const u8,
c_name: []const u8,
is_signed: bool,
pub const Id = enum {
Short,
UShort,
Int,
UInt,
Long,
ULong,
LongLong,
ULongLong,
};
pub const list = []CInt{
CInt{
.id = Id.Short,
.zig_name = "c_short",
.c_name = "short",
.is_signed = true,
},
CInt{
.id = Id.UShort,
.zig_name = "c_ushort",
.c_name = "unsigned short",
.is_signed = false,
},
CInt{
.id = Id.Int,
.zig_name = "c_int",
.c_name = "int",
.is_signed = true,
},
CInt{
.id = Id.UInt,
.zig_name = "c_uint",
.c_name = "unsigned int",
.is_signed = false,
},
CInt{
.id = Id.Long,
.zig_name = "c_long",
.c_name = "long",
.is_signed = true,
},
CInt{
.id = Id.ULong,
.zig_name = "c_ulong",
.c_name = "unsigned long",
.is_signed = false,
},
CInt{
.id = Id.LongLong,
.zig_name = "c_longlong",
.c_name = "long long",
.is_signed = true,
},
CInt{
.id = Id.ULongLong,
.zig_name = "c_ulonglong",
.c_name = "unsigned long long",
.is_signed = false,
},
};
};

View File

@ -15,7 +15,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
defer fn_val.base.deref(comp);
defer code.destroy(comp.gpa());
var output_path = try await (async comp.createRandomOutputPath(comp.target.oFileExt()) catch unreachable);
var output_path = try await (async comp.createRandomOutputPath(comp.target.objFileExt()) catch unreachable);
errdefer output_path.deinit();
const llvm_handle = try comp.event_loop_local.getAnyLlvmContext();
@ -78,6 +78,7 @@ pub async fn renderToLlvm(comp: *Compilation, fn_val: *Value.Fn, code: *ir.Code)
.dibuilder = dibuilder,
.context = context,
.lock = event.Lock.init(comp.loop),
.arena = &code.arena.allocator,
};
try renderToLlvmModule(&ofile, fn_val, code);
@ -139,6 +140,7 @@ pub const ObjectFile = struct {
dibuilder: *llvm.DIBuilder,
context: llvm.ContextRef,
lock: event.Lock,
arena: *std.mem.Allocator,
fn gpa(self: *ObjectFile) *std.mem.Allocator {
return self.comp.gpa();
@ -147,7 +149,7 @@ pub const ObjectFile = struct {
pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code) !void {
// TODO audit more of codegen.cpp:fn_llvm_value and port more logic
const llvm_fn_type = try fn_val.base.typeof.getLlvmType(ofile);
const llvm_fn_type = try fn_val.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
fn_val.symbol_name.ptr(),
@ -165,7 +167,7 @@ pub fn renderToLlvmModule(ofile: *ObjectFile, fn_val: *Value.Fn, code: *ir.Code)
// try addLLVMFnAttrInt(ofile, llvm_fn, "alignstack", align_stack);
//}
const fn_type = fn_val.base.typeof.cast(Type.Fn).?;
const fn_type = fn_val.base.typ.cast(Type.Fn).?;
try addLLVMFnAttr(ofile, llvm_fn, "nounwind");
//add_uwtable_attr(g, fn_table_entry->llvm_value);

View File

@ -21,13 +21,15 @@ const Scope = @import("scope.zig").Scope;
const Decl = @import("decl.zig").Decl;
const ir = @import("ir.zig");
const Visib = @import("visib.zig").Visib;
const ParsedFile = @import("parsed_file.zig").ParsedFile;
const Value = @import("value.zig").Value;
const Type = Value.Type;
const Span = errmsg.Span;
const Msg = errmsg.Msg;
const codegen = @import("codegen.zig");
const Package = @import("package.zig").Package;
const link = @import("link.zig").link;
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const CInt = @import("c_int.zig").CInt;
/// Data that is local to the event loop.
pub const EventLoopLocal = struct {
@ -37,6 +39,8 @@ pub const EventLoopLocal = struct {
/// TODO pool these so that it doesn't have to lock
prng: event.Locked(std.rand.DefaultPrng),
native_libc: event.Future(LibCInstallation),
var lazy_init_targets = std.lazyInit(void);
fn init(loop: *event.Loop) !EventLoopLocal {
@ -48,13 +52,16 @@ pub const EventLoopLocal = struct {
var seed_bytes: [@sizeOf(u64)]u8 = undefined;
try std.os.getRandomBytes(seed_bytes[0..]);
const seed = std.mem.readInt(seed_bytes, u64, builtin.Endian.Big);
return EventLoopLocal{
.loop = loop,
.llvm_handle_pool = std.atomic.Stack(llvm.ContextRef).init(),
.prng = event.Locked(std.rand.DefaultPrng).init(loop, std.rand.DefaultPrng.init(seed)),
.native_libc = event.Future(LibCInstallation).init(loop),
};
}
/// Must be called only after EventLoop.run completes.
fn deinit(self: *EventLoopLocal) void {
while (self.llvm_handle_pool.pop()) |node| {
c.LLVMContextDispose(node.data);
@ -78,6 +85,13 @@ pub const EventLoopLocal = struct {
return LlvmHandle{ .node = node };
}
pub async fn getNativeLibC(self: *EventLoopLocal) !*LibCInstallation {
if (await (async self.native_libc.start() catch unreachable)) |ptr| return ptr;
try await (async self.native_libc.data.findNative(self.loop) catch unreachable);
self.native_libc.resolve();
return &self.native_libc.data;
}
};
pub const LlvmHandle = struct {
@ -108,13 +122,6 @@ pub const Compilation = struct {
version_patch: u32,
linker_script: ?[]const u8,
cache_dir: []const u8,
libc_lib_dir: ?[]const u8,
libc_static_lib_dir: ?[]const u8,
libc_include_dir: ?[]const u8,
msvc_lib_dir: ?[]const u8,
kernel32_lib_dir: ?[]const u8,
dynamic_linker: ?[]const u8,
out_h_path: ?[]const u8,
is_test: bool,
@ -179,6 +186,8 @@ pub const Compilation = struct {
void_type: *Type.Void,
bool_type: *Type.Bool,
noreturn_type: *Type.NoReturn,
comptime_int_type: *Type.ComptimeInt,
u8_type: *Type.Int,
void_value: *Value.Void,
true_value: *Value.Bool,
@ -188,6 +197,7 @@ pub const Compilation = struct {
target_machine: llvm.TargetMachineRef,
target_data_ref: llvm.TargetDataRef,
target_layout_str: [*]u8,
target_ptr_bits: u32,
/// for allocating things which have the same lifetime as this Compilation
arena_allocator: std.heap.ArenaAllocator,
@ -195,7 +205,30 @@ pub const Compilation = struct {
root_package: *Package,
std_package: *Package,
const CompileErrList = std.ArrayList(*errmsg.Msg);
override_libc: ?*LibCInstallation,
/// need to wait on this group before deinitializing
deinit_group: event.Group(void),
destroy_handle: promise,
have_err_ret_tracing: bool,
/// not locked because it is read-only
primitive_type_table: TypeTable,
int_type_table: event.Locked(IntTypeTable),
array_type_table: event.Locked(ArrayTypeTable),
ptr_type_table: event.Locked(PtrTypeTable),
c_int_types: [CInt.list.len]*Type.Int,
const IntTypeTable = std.HashMap(*const Type.Int.Key, *Type.Int, Type.Int.Key.hash, Type.Int.Key.eql);
const ArrayTypeTable = std.HashMap(*const Type.Array.Key, *Type.Array, Type.Array.Key.hash, Type.Array.Key.eql);
const PtrTypeTable = std.HashMap(*const Type.Pointer.Key, *Type.Pointer, Type.Pointer.Key.hash, Type.Pointer.Key.eql);
const TypeTable = std.HashMap([]const u8, *Type, mem.hash_slice_u8, mem.eql_slice_u8);
const CompileErrList = std.ArrayList(*Msg);
// TODO handle some of these earlier and report them in a way other than error codes
pub const BuildError = error{
@ -240,12 +273,16 @@ pub const Compilation = struct {
EnvironmentVariableNotFound,
AppDataDirUnavailable,
LinkFailed,
LibCRequiredButNotProvidedOrFound,
LibCMissingDynamicLinker,
InvalidDarwinVersionString,
UnsupportedLinkArchitecture,
};
pub const Event = union(enum) {
Ok,
Error: BuildError,
Fail: []*errmsg.Msg,
Fail: []*Msg,
};
pub const DarwinVersionMin = union(enum) {
@ -284,7 +321,6 @@ pub const Compilation = struct {
build_mode: builtin.Mode,
is_static: bool,
zig_lib_dir: []const u8,
cache_dir: []const u8,
) !*Compilation {
const loop = event_loop_local.loop;
const comp = try event_loop_local.loop.allocator.create(Compilation{
@ -299,7 +335,6 @@ pub const Compilation = struct {
.build_mode = build_mode,
.zig_lib_dir = zig_lib_dir,
.zig_std_dir = undefined,
.cache_dir = cache_dir,
.tmp_dir = event.Future(BuildError![]u8).init(loop),
.name = undefined,
@ -318,12 +353,6 @@ pub const Compilation = struct {
.verbose_link = false,
.linker_script = null,
.libc_lib_dir = null,
.libc_static_lib_dir = null,
.libc_include_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
.dynamic_linker = null,
.out_h_path = null,
.is_test = false,
.each_lib_rpath = false,
@ -350,7 +379,12 @@ pub const Compilation = struct {
.link_out_file = null,
.exported_symbol_names = event.Locked(Decl.Table).init(loop, Decl.Table.init(loop.allocator)),
.prelink_group = event.Group(BuildError!void).init(loop),
.deinit_group = event.Group(void).init(loop),
.compile_errors = event.Locked(CompileErrList).init(loop, CompileErrList.init(loop.allocator)),
.int_type_table = event.Locked(IntTypeTable).init(loop, IntTypeTable.init(loop.allocator)),
.array_type_table = event.Locked(ArrayTypeTable).init(loop, ArrayTypeTable.init(loop.allocator)),
.ptr_type_table = event.Locked(PtrTypeTable).init(loop, PtrTypeTable.init(loop.allocator)),
.c_int_types = undefined,
.meta_type = undefined,
.void_type = undefined,
@ -360,15 +394,26 @@ pub const Compilation = struct {
.false_value = undefined,
.noreturn_type = undefined,
.noreturn_value = undefined,
.comptime_int_type = undefined,
.u8_type = undefined,
.target_machine = undefined,
.target_data_ref = undefined,
.target_layout_str = undefined,
.target_ptr_bits = target.getArchPtrBitWidth(),
.root_package = undefined,
.std_package = undefined,
.override_libc = null,
.destroy_handle = undefined,
.have_err_ret_tracing = false,
.primitive_type_table = undefined,
});
errdefer {
comp.int_type_table.private_data.deinit();
comp.array_type_table.private_data.deinit();
comp.ptr_type_table.private_data.deinit();
comp.arena_allocator.deinit();
comp.loop.allocator.destroy(comp);
}
@ -378,6 +423,7 @@ pub const Compilation = struct {
comp.llvm_target = try Target.llvmTargetFromTriple(comp.llvm_triple);
comp.link_libs_list = ArrayList(*LinkLib).init(comp.arena());
comp.zig_std_dir = try std.os.path.join(comp.arena(), zig_lib_dir, "std");
comp.primitive_type_table = TypeTable.init(comp.arena());
const opt_level = switch (build_mode) {
builtin.Mode.Debug => llvm.CodeGenLevelNone,
@ -431,123 +477,221 @@ pub const Compilation = struct {
try comp.initTypes();
comp.destroy_handle = try async<loop.allocator> comp.internalDeinit();
return comp;
}
/// it does ref the result because it could be an arbitrary integer size
pub async fn getPrimitiveType(comp: *Compilation, name: []const u8) !?*Type {
if (name.len >= 2) {
switch (name[0]) {
'i', 'u' => blk: {
for (name[1..]) |byte|
switch (byte) {
'0'...'9' => {},
else => break :blk,
};
const is_signed = name[0] == 'i';
const bit_count = std.fmt.parseUnsigned(u32, name[1..], 10) catch |err| switch (err) {
error.Overflow => return error.Overflow,
error.InvalidCharacter => unreachable, // we just checked the characters above
};
const int_type = try await (async Type.Int.get(comp, Type.Int.Key{
.bit_count = bit_count,
.is_signed = is_signed,
}) catch unreachable);
errdefer int_type.base.base.deref();
return &int_type.base;
},
else => {},
}
}
if (comp.primitive_type_table.get(name)) |entry| {
entry.value.base.ref();
return entry.value;
}
return null;
}
fn initTypes(comp: *Compilation) !void {
comp.meta_type = try comp.gpa().create(Type.MetaType{
comp.meta_type = try comp.arena().create(Type.MetaType{
.base = Type{
.name = "type",
.base = Value{
.id = Value.Id.Type,
.typeof = undefined,
.typ = undefined,
.ref_count = std.atomic.Int(usize).init(3), // 3 because it references itself twice
},
.id = builtin.TypeId.Type,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
.value = undefined,
});
comp.meta_type.value = &comp.meta_type.base;
comp.meta_type.base.base.typeof = &comp.meta_type.base;
errdefer comp.gpa().destroy(comp.meta_type);
comp.meta_type.base.base.typ = &comp.meta_type.base;
assert((try comp.primitive_type_table.put(comp.meta_type.base.name, &comp.meta_type.base)) == null);
comp.void_type = try comp.gpa().create(Type.Void{
comp.void_type = try comp.arena().create(Type.Void{
.base = Type{
.name = "void",
.base = Value{
.id = Value.Id.Type,
.typeof = &Type.MetaType.get(comp).base,
.typ = &Type.MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.Void,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
});
errdefer comp.gpa().destroy(comp.void_type);
assert((try comp.primitive_type_table.put(comp.void_type.base.name, &comp.void_type.base)) == null);
comp.noreturn_type = try comp.gpa().create(Type.NoReturn{
comp.noreturn_type = try comp.arena().create(Type.NoReturn{
.base = Type{
.name = "noreturn",
.base = Value{
.id = Value.Id.Type,
.typeof = &Type.MetaType.get(comp).base,
.typ = &Type.MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.NoReturn,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
});
errdefer comp.gpa().destroy(comp.noreturn_type);
assert((try comp.primitive_type_table.put(comp.noreturn_type.base.name, &comp.noreturn_type.base)) == null);
comp.bool_type = try comp.gpa().create(Type.Bool{
comp.comptime_int_type = try comp.arena().create(Type.ComptimeInt{
.base = Type{
.name = "comptime_int",
.base = Value{
.id = Value.Id.Type,
.typeof = &Type.MetaType.get(comp).base,
.typ = &Type.MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.ComptimeInt,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
});
assert((try comp.primitive_type_table.put(comp.comptime_int_type.base.name, &comp.comptime_int_type.base)) == null);
comp.bool_type = try comp.arena().create(Type.Bool{
.base = Type{
.name = "bool",
.base = Value{
.id = Value.Id.Type,
.typ = &Type.MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.Bool,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
});
errdefer comp.gpa().destroy(comp.bool_type);
assert((try comp.primitive_type_table.put(comp.bool_type.base.name, &comp.bool_type.base)) == null);
comp.void_value = try comp.gpa().create(Value.Void{
comp.void_value = try comp.arena().create(Value.Void{
.base = Value{
.id = Value.Id.Void,
.typeof = &Type.Void.get(comp).base,
.typ = &Type.Void.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
});
errdefer comp.gpa().destroy(comp.void_value);
comp.true_value = try comp.gpa().create(Value.Bool{
comp.true_value = try comp.arena().create(Value.Bool{
.base = Value{
.id = Value.Id.Bool,
.typeof = &Type.Bool.get(comp).base,
.typ = &Type.Bool.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.x = true,
});
errdefer comp.gpa().destroy(comp.true_value);
comp.false_value = try comp.gpa().create(Value.Bool{
comp.false_value = try comp.arena().create(Value.Bool{
.base = Value{
.id = Value.Id.Bool,
.typeof = &Type.Bool.get(comp).base,
.typ = &Type.Bool.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.x = false,
});
errdefer comp.gpa().destroy(comp.false_value);
comp.noreturn_value = try comp.gpa().create(Value.NoReturn{
comp.noreturn_value = try comp.arena().create(Value.NoReturn{
.base = Value{
.id = Value.Id.NoReturn,
.typeof = &Type.NoReturn.get(comp).base,
.typ = &Type.NoReturn.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
});
errdefer comp.gpa().destroy(comp.noreturn_value);
for (CInt.list) |cint, i| {
const c_int_type = try comp.arena().create(Type.Int{
.base = Type{
.name = cint.zig_name,
.base = Value{
.id = Value.Id.Type,
.typ = &Type.MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.Int,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
.key = Type.Int.Key{
.is_signed = cint.is_signed,
.bit_count = comp.target.cIntTypeSizeInBits(cint.id),
},
.garbage_node = undefined,
});
comp.c_int_types[i] = c_int_type;
assert((try comp.primitive_type_table.put(cint.zig_name, &c_int_type.base)) == null);
}
comp.u8_type = try comp.arena().create(Type.Int{
.base = Type{
.name = "u8",
.base = Value{
.id = Value.Id.Type,
.typ = &Type.MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.Int,
.abi_alignment = Type.AbiAlignment.init(comp.loop),
},
.key = Type.Int.Key{
.is_signed = false,
.bit_count = 8,
},
.garbage_node = undefined,
});
assert((try comp.primitive_type_table.put(comp.u8_type.base.name, &comp.u8_type.base)) == null);
}
pub fn destroy(self: *Compilation) void {
/// This function can safely use async/await, because it manages Compilation's lifetime,
/// and EventLoopLocal.deinit will not be called until the event.Loop.run() completes.
async fn internalDeinit(self: *Compilation) void {
suspend;
await (async self.deinit_group.wait() catch unreachable);
if (self.tmp_dir.getOrNull()) |tmp_dir_result| if (tmp_dir_result.*) |tmp_dir| {
// TODO evented I/O?
os.deleteTree(self.arena(), tmp_dir) catch {};
} else |_| {};
self.noreturn_value.base.deref(self);
self.void_value.base.deref(self);
self.false_value.base.deref(self);
self.true_value.base.deref(self);
self.noreturn_type.base.base.deref(self);
self.void_type.base.base.deref(self);
self.meta_type.base.base.deref(self);
self.events.destroy();
llvm.DisposeMessage(self.target_layout_str);
llvm.DisposeTargetData(self.target_data_ref);
llvm.DisposeTargetMachine(self.target_machine);
self.primitive_type_table.deinit();
self.arena_allocator.deinit();
self.gpa().destroy(self);
}
pub fn destroy(self: *Compilation) void {
resume self.destroy_handle;
}
pub fn build(self: *Compilation) !void {
if (self.llvm_argv.len != 0) {
var c_compatible_args = try std.cstr.NullTerminated2DArray.fromSlices(self.arena(), [][]const []const u8{
@ -597,79 +741,103 @@ pub const Compilation = struct {
}
async fn compileAndLink(self: *Compilation) !void {
const root_src_path = self.root_src_path orelse @panic("TODO handle null root src path");
// TODO async/await os.path.real
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
try printError("unable to get real path '{}': {}", root_src_path, err);
return err;
};
errdefer self.gpa().free(root_src_real_path);
if (self.root_src_path) |root_src_path| {
// TODO async/await os.path.real
const root_src_real_path = os.path.real(self.gpa(), root_src_path) catch |err| {
try printError("unable to get real path '{}': {}", root_src_path, err);
return err;
};
const root_scope = blk: {
errdefer self.gpa().free(root_src_real_path);
// TODO async/await readFileAlloc()
const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
errdefer self.gpa().free(source_code);
// TODO async/await readFileAlloc()
const source_code = io.readFileAlloc(self.gpa(), root_src_real_path) catch |err| {
try printError("unable to open '{}': {}", root_src_real_path, err);
return err;
};
errdefer self.gpa().free(source_code);
const parsed_file = try self.gpa().create(ParsedFile{
.tree = undefined,
.realpath = root_src_real_path,
});
errdefer self.gpa().destroy(parsed_file);
const tree = try self.gpa().createOne(ast.Tree);
tree.* = try std.zig.parse(self.gpa(), source_code);
errdefer {
tree.deinit();
self.gpa().destroy(tree);
}
parsed_file.tree = try std.zig.parse(self.gpa(), source_code);
errdefer parsed_file.tree.deinit();
break :blk try Scope.Root.create(self, tree, root_src_real_path);
};
defer root_scope.base.deref(self);
const tree = root_scope.tree;
const tree = &parsed_file.tree;
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try Msg.createFromParseErrorAndScope(self, root_scope, parse_error);
errdefer msg.destroy();
// create empty struct for it
const decls = try Scope.Decls.create(self, null);
defer decls.base.deref(self);
var decl_group = event.Group(BuildError!void).init(self.loop);
errdefer decl_group.cancelAll();
var it = tree.root_node.decls.iterator(0);
while (it.next()) |decl_ptr| {
const decl = decl_ptr.*;
switch (decl.id) {
ast.Node.Id.Comptime => @panic("TODO"),
ast.Node.Id.VarDecl => @panic("TODO"),
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
try self.addCompileError(parsed_file, Span{
.first = fn_proto.fn_token,
.last = fn_proto.fn_token + 1,
}, "missing function name");
continue;
};
const fn_decl = try self.gpa().create(Decl.Fn{
.base = Decl{
.id = Decl.Id.Fn,
.name = name,
.visib = parseVisibToken(tree, fn_proto.visib_token),
.resolution = event.Future(BuildError!void).init(self.loop),
.resolution_in_progress = 0,
.parsed_file = parsed_file,
.parent_scope = &decls.base,
},
.value = Decl.Fn.Val{ .Unresolved = {} },
.fn_proto = fn_proto,
});
errdefer self.gpa().destroy(fn_decl);
try decl_group.call(addTopLevelDecl, self, &fn_decl.base);
},
ast.Node.Id.TestDecl => @panic("TODO"),
else => unreachable,
try await (async self.addCompileErrorAsync(msg) catch unreachable);
}
if (tree.errors.len != 0) {
return;
}
const decls = try Scope.Decls.create(self, &root_scope.base);
defer decls.base.deref(self);
var decl_group = event.Group(BuildError!void).init(self.loop);
var decl_group_consumed = false;
errdefer if (!decl_group_consumed) decl_group.cancelAll();
var it = tree.root_node.decls.iterator(0);
while (it.next()) |decl_ptr| {
const decl = decl_ptr.*;
switch (decl.id) {
ast.Node.Id.Comptime => {
const comptime_node = @fieldParentPtr(ast.Node.Comptime, "base", decl);
try self.prelink_group.call(addCompTimeBlock, self, &decls.base, comptime_node);
},
ast.Node.Id.VarDecl => @panic("TODO"),
ast.Node.Id.FnProto => {
const fn_proto = @fieldParentPtr(ast.Node.FnProto, "base", decl);
const name = if (fn_proto.name_token) |name_token| tree.tokenSlice(name_token) else {
try self.addCompileError(root_scope, Span{
.first = fn_proto.fn_token,
.last = fn_proto.fn_token + 1,
}, "missing function name");
continue;
};
const fn_decl = try self.gpa().create(Decl.Fn{
.base = Decl{
.id = Decl.Id.Fn,
.name = name,
.visib = parseVisibToken(tree, fn_proto.visib_token),
.resolution = event.Future(BuildError!void).init(self.loop),
.parent_scope = &decls.base,
},
.value = Decl.Fn.Val{ .Unresolved = {} },
.fn_proto = fn_proto,
});
errdefer self.gpa().destroy(fn_decl);
try decl_group.call(addTopLevelDecl, self, decls, &fn_decl.base);
},
ast.Node.Id.TestDecl => @panic("TODO"),
else => unreachable,
}
}
decl_group_consumed = true;
try await (async decl_group.wait() catch unreachable);
// Now other code can rely on the decls scope having a complete list of names.
decls.name_future.resolve();
}
try await (async decl_group.wait() catch unreachable);
try await (async self.prelink_group.wait() catch unreachable);
(await (async self.prelink_group.wait() catch unreachable)) catch |err| switch (err) {
error.SemanticAnalysisFailed => {},
else => return err,
};
const any_prelink_errors = blk: {
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
@ -679,39 +847,108 @@ pub const Compilation = struct {
};
if (!any_prelink_errors) {
try link(self);
try await (async link(self) catch unreachable);
}
}
async fn addTopLevelDecl(self: *Compilation, decl: *Decl) !void {
const is_export = decl.isExported(&decl.parsed_file.tree);
/// caller takes ownership of resulting Code
async fn genAndAnalyzeCode(
comp: *Compilation,
scope: *Scope,
node: *ast.Node,
expected_type: ?*Type,
) !*ir.Code {
const unanalyzed_code = try await (async ir.gen(
comp,
node,
scope,
) catch unreachable);
defer unanalyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {
std.debug.warn("unanalyzed:\n");
unanalyzed_code.dump();
}
const analyzed_code = try await (async ir.analyze(
comp,
unanalyzed_code,
expected_type,
) catch unreachable);
errdefer analyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {
std.debug.warn("analyzed:\n");
analyzed_code.dump();
}
return analyzed_code;
}
async fn addCompTimeBlock(
comp: *Compilation,
scope: *Scope,
comptime_node: *ast.Node.Comptime,
) !void {
const void_type = Type.Void.get(comp);
defer void_type.base.base.deref(comp);
const analyzed_code = (await (async genAndAnalyzeCode(
comp,
scope,
comptime_node.expr,
&void_type.base,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that comp.compile_errors is populated.
error.SemanticAnalysisFailed => return {},
else => return err,
};
analyzed_code.destroy(comp.gpa());
}
async fn addTopLevelDecl(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
const tree = decl.findRootScope().tree;
const is_export = decl.isExported(tree);
var add_to_table_resolved = false;
const add_to_table = async self.addDeclToTable(decls, decl) catch unreachable;
errdefer if (!add_to_table_resolved) cancel add_to_table; // TODO https://github.com/ziglang/zig/issues/1261
if (is_export) {
try self.prelink_group.call(verifyUniqueSymbol, self, decl);
try self.prelink_group.call(resolveDecl, self, decl);
}
add_to_table_resolved = true;
try await add_to_table;
}
fn addCompileError(self: *Compilation, parsed_file: *ParsedFile, span: Span, comptime fmt: []const u8, args: ...) !void {
const text = try std.fmt.allocPrint(self.loop.allocator, fmt, args);
errdefer self.loop.allocator.free(text);
async fn addDeclToTable(self: *Compilation, decls: *Scope.Decls, decl: *Decl) !void {
const held = await (async decls.table.acquire() catch unreachable);
defer held.release();
try self.prelink_group.call(addCompileErrorAsync, self, parsed_file, span, text);
if (try held.value.put(decl.name, decl)) |other_decl| {
try self.addCompileError(decls.base.findRoot(), decl.getSpan(), "redefinition of '{}'", decl.name);
// TODO note: other definition here
}
}
fn addCompileError(self: *Compilation, root: *Scope.Root, span: Span, comptime fmt: []const u8, args: ...) !void {
const text = try std.fmt.allocPrint(self.gpa(), fmt, args);
errdefer self.gpa().free(text);
const msg = try Msg.createFromScope(self, root, span, text);
errdefer msg.destroy();
try self.prelink_group.call(addCompileErrorAsync, self, msg);
}
async fn addCompileErrorAsync(
self: *Compilation,
parsed_file: *ParsedFile,
span: Span,
text: []u8,
msg: *Msg,
) !void {
const msg = try self.loop.allocator.create(errmsg.Msg{
.path = parsed_file.realpath,
.text = text,
.span = span,
.tree = &parsed_file.tree,
});
errdefer self.loop.allocator.destroy(msg);
errdefer msg.destroy();
const compile_errors = await (async self.compile_errors.acquire() catch unreachable);
defer compile_errors.release();
@ -725,7 +962,7 @@ pub const Compilation = struct {
if (try exported_symbol_names.value.put(decl.name, decl)) |other_decl| {
try self.addCompileError(
decl.parsed_file,
decl.findRootScope(),
decl.getSpan(),
"exported symbol collision: '{}'",
decl.name,
@ -762,10 +999,22 @@ pub const Compilation = struct {
try self.link_libs_list.append(link_lib);
if (is_libc) {
self.libc_link_lib = link_lib;
// get a head start on looking for the native libc
if (self.target == Target.Native and self.override_libc == null) {
try self.deinit_group.call(startFindingNativeLibC, self);
}
}
return link_lib;
}
/// cancels itself so no need to await or cancel the promise.
async fn startFindingNativeLibC(self: *Compilation) void {
await (async self.loop.yield() catch unreachable);
// we don't care if it fails, we're just trying to kick off the future resolution
_ = (await (async self.event_loop_local.getNativeLibC() catch unreachable)) catch return;
}
/// General Purpose Allocator. Must free when done.
fn gpa(self: Compilation) *mem.Allocator {
return self.loop.allocator;
@ -831,6 +1080,37 @@ pub const Compilation = struct {
b64_fs_encoder.encode(result[0..], rand_bytes);
return result;
}
fn registerGarbage(comp: *Compilation, comptime T: type, node: *std.atomic.Stack(*T).Node) void {
// TODO put the garbage somewhere
}
/// Returns a value which has been ref()'d once
async fn analyzeConstValue(comp: *Compilation, scope: *Scope, node: *ast.Node, expected_type: *Type) !*Value {
const analyzed_code = try await (async comp.genAndAnalyzeCode(scope, node, expected_type) catch unreachable);
defer analyzed_code.destroy(comp.gpa());
return analyzed_code.getCompTimeResult(comp);
}
async fn analyzeTypeExpr(comp: *Compilation, scope: *Scope, node: *ast.Node) !*Type {
const meta_type = &Type.MetaType.get(comp).base;
defer meta_type.base.deref(comp);
const result_val = try await (async comp.analyzeConstValue(scope, node, meta_type) catch unreachable);
errdefer result_val.base.deref(comp);
return result_val.cast(Type).?;
}
/// This declaration has been blessed as going into the final code generation.
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
decl.resolution.data = try await (async generateDecl(comp, decl) catch unreachable);
decl.resolution.resolve();
return decl.resolution.data;
}
};
fn printError(comptime format: []const u8, args: ...) !void {
@ -850,15 +1130,6 @@ fn parseVisibToken(tree: *ast.Tree, optional_token_index: ?ast.TokenIndex) Visib
}
}
/// This declaration has been blessed as going into the final code generation.
pub async fn resolveDecl(comp: *Compilation, decl: *Decl) !void {
if (await (async decl.resolution.start() catch unreachable)) |ptr| return ptr.*;
decl.resolution.data = await (async generateDecl(comp, decl) catch unreachable);
decl.resolution.resolve();
return decl.resolution.data;
}
/// The function that actually does the generation.
async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
switch (decl.id) {
@ -872,66 +1143,30 @@ async fn generateDecl(comp: *Compilation, decl: *Decl) !void {
}
async fn generateDeclFn(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const body_node = fn_decl.fn_proto.body_node orelse @panic("TODO extern fn proto decl");
const body_node = fn_decl.fn_proto.body_node orelse return await (async generateDeclFnProto(comp, fn_decl) catch unreachable);
const fndef_scope = try Scope.FnDef.create(comp, fn_decl.base.parent_scope);
defer fndef_scope.base.deref(comp);
// TODO actually look at the return type of the AST
const return_type = &Type.Void.get(comp).base;
defer return_type.base.deref(comp);
const is_var_args = false;
const params = ([*]Type.Fn.Param)(undefined)[0..0];
const fn_type = try Type.Fn.create(comp, return_type, params, is_var_args);
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
defer fn_type.base.base.deref(comp);
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
errdefer symbol_name.deinit();
var symbol_name_consumed = false;
errdefer if (!symbol_name_consumed) symbol_name.deinit();
// The Decl.Fn owns the initial 1 reference count
const fn_val = try Value.Fn.create(comp, fn_type, fndef_scope, symbol_name);
fn_decl.value = Decl.Fn.Val{ .Ok = fn_val };
fn_decl.value = Decl.Fn.Val{ .Fn = fn_val };
symbol_name_consumed = true;
const unanalyzed_code = (await (async ir.gen(
comp,
body_node,
const analyzed_code = try await (async comp.genAndAnalyzeCode(
&fndef_scope.base,
Span.token(body_node.lastToken()),
fn_decl.base.parsed_file,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that self.compile_errors is populated.
// TODO https://github.com/ziglang/zig/issues/769
error.SemanticAnalysisFailed => return {},
else => return err,
};
defer unanalyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {
std.debug.warn("unanalyzed:\n");
unanalyzed_code.dump();
}
const analyzed_code = (await (async ir.analyze(
comp,
fn_decl.base.parsed_file,
unanalyzed_code,
null,
) catch unreachable)) catch |err| switch (err) {
// This poison value should not cause the errdefers to run. It simply means
// that self.compile_errors is populated.
// TODO https://github.com/ziglang/zig/issues/769
error.SemanticAnalysisFailed => return {},
else => return err,
};
body_node,
fn_type.return_type,
) catch unreachable);
errdefer analyzed_code.destroy(comp.gpa());
if (comp.verbose_ir) {
std.debug.warn("analyzed:\n");
analyzed_code.dump();
}
// Kick off rendering to LLVM module, but it doesn't block the fn decl
// analysis from being complete.
try comp.prelink_group.call(codegen.renderToLlvm, comp, fn_val, analyzed_code);
@ -953,3 +1188,54 @@ async fn addFnToLinkSet(comp: *Compilation, fn_val: *Value.Fn) void {
fn getZigDir(allocator: *mem.Allocator) ![]u8 {
return os.getAppDataDir(allocator, "zig");
}
async fn analyzeFnType(comp: *Compilation, scope: *Scope, fn_proto: *ast.Node.FnProto) !*Type.Fn {
const return_type_node = switch (fn_proto.return_type) {
ast.Node.FnProto.ReturnType.Explicit => |n| n,
ast.Node.FnProto.ReturnType.InferErrorSet => |n| n,
};
const return_type = try await (async comp.analyzeTypeExpr(scope, return_type_node) catch unreachable);
return_type.base.deref(comp);
var params = ArrayList(Type.Fn.Param).init(comp.gpa());
var params_consumed = false;
defer if (params_consumed) {
for (params.toSliceConst()) |param| {
param.typ.base.deref(comp);
}
params.deinit();
};
const is_var_args = false;
{
var it = fn_proto.params.iterator(0);
while (it.next()) |param_node_ptr| {
const param_node = param_node_ptr.*.cast(ast.Node.ParamDecl).?;
const param_type = try await (async comp.analyzeTypeExpr(scope, param_node.type_node) catch unreachable);
errdefer param_type.base.deref(comp);
try params.append(Type.Fn.Param{
.typ = param_type,
.is_noalias = param_node.noalias_token != null,
});
}
}
const fn_type = try Type.Fn.create(comp, return_type, params.toOwnedSlice(), is_var_args);
params_consumed = true;
errdefer fn_type.base.base.deref(comp);
return fn_type;
}
async fn generateDeclFnProto(comp: *Compilation, fn_decl: *Decl.Fn) !void {
const fn_type = try await (async analyzeFnType(comp, fn_decl.base.parent_scope, fn_decl.fn_proto) catch unreachable);
defer fn_type.base.base.deref(comp);
var symbol_name = try std.Buffer.init(comp.gpa(), fn_decl.base.name);
var symbol_name_consumed = false;
defer if (!symbol_name_consumed) symbol_name.deinit();
// The Decl.Fn owns the initial 1 reference count
const fn_proto_val = try Value.FnProto.create(comp, fn_type, symbol_name);
fn_decl.value = Decl.Fn.Val{ .FnProto = fn_proto_val };
symbol_name_consumed = true;
}

View File

@ -3,7 +3,6 @@ const Allocator = mem.Allocator;
const mem = std.mem;
const ast = std.zig.ast;
const Visib = @import("visib.zig").Visib;
const ParsedFile = @import("parsed_file.zig").ParsedFile;
const event = std.event;
const Value = @import("value.zig").Value;
const Token = std.zig.Token;
@ -16,8 +15,6 @@ pub const Decl = struct {
name: []const u8,
visib: Visib,
resolution: event.Future(Compilation.BuildError!void),
resolution_in_progress: u8,
parsed_file: *ParsedFile,
parent_scope: *Scope,
pub const Table = std.HashMap([]const u8, *Decl, mem.hash_slice_u8, mem.eql_slice_u8);
@ -48,6 +45,10 @@ pub const Decl = struct {
}
}
pub fn findRootScope(base: *const Decl) *Scope.Root {
return base.parent_scope.findRoot();
}
pub const Id = enum {
Var,
Fn,
@ -61,12 +62,13 @@ pub const Decl = struct {
pub const Fn = struct {
base: Decl,
value: Val,
fn_proto: *const ast.Node.FnProto,
fn_proto: *ast.Node.FnProto,
// TODO https://github.com/ziglang/zig/issues/683 and then make this anonymous
pub const Val = union {
pub const Val = union(enum) {
Unresolved: void,
Ok: *Value.Fn,
Fn: *Value.Fn,
FnProto: *Value.FnProto,
};
pub fn externLibName(self: Fn, tree: *ast.Tree) ?[]const u8 {

View File

@ -4,6 +4,8 @@ const os = std.os;
const Token = std.zig.Token;
const ast = std.zig.ast;
const TokenIndex = std.zig.ast.TokenIndex;
const Compilation = @import("compilation.zig").Compilation;
const Scope = @import("scope.zig").Scope;
pub const Color = enum {
Auto,
@ -16,85 +18,220 @@ pub const Span = struct {
last: ast.TokenIndex,
pub fn token(i: TokenIndex) Span {
return Span {
return Span{
.first = i,
.last = i,
};
}
pub fn node(n: *ast.Node) Span {
return Span{
.first = n.firstToken(),
.last = n.lastToken(),
};
}
};
pub const Msg = struct {
path: []const u8,
text: []u8,
span: Span,
tree: *ast.Tree,
};
text: []u8,
data: Data,
/// `path` must outlive the returned Msg
/// `tree` must outlive the returned Msg
/// Caller owns returned Msg and must free with `allocator`
pub fn createFromParseError(
allocator: *mem.Allocator,
parse_error: *const ast.Error,
tree: *ast.Tree,
path: []const u8,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(allocator, 0);
defer text_buf.deinit();
const Data = union(enum) {
PathAndTree: PathAndTree,
ScopeAndComp: ScopeAndComp,
};
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&tree.tokens, out_stream);
const PathAndTree = struct {
realpath: []const u8,
tree: *ast.Tree,
allocator: *mem.Allocator,
};
const msg = try allocator.create(Msg{
.tree = tree,
.path = path,
.text = text_buf.toOwnedSlice(),
.span = Span{
.first = loc_token,
.last = loc_token,
},
});
errdefer allocator.destroy(msg);
const ScopeAndComp = struct {
root_scope: *Scope.Root,
compilation: *Compilation,
};
return msg;
}
pub fn destroy(self: *Msg) void {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
path_and_tree.allocator.free(self.text);
path_and_tree.allocator.destroy(self);
},
Data.ScopeAndComp => |scope_and_comp| {
scope_and_comp.root_scope.base.deref(scope_and_comp.compilation);
scope_and_comp.compilation.gpa().free(self.text);
scope_and_comp.compilation.gpa().destroy(self);
},
}
}
fn getAllocator(self: *const Msg) *mem.Allocator {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.allocator;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.compilation.gpa();
},
}
}
pub fn getRealPath(self: *const Msg) []const u8 {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.realpath;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.root_scope.realpath;
},
}
}
pub fn getTree(self: *const Msg) *ast.Tree {
switch (self.data) {
Data.PathAndTree => |path_and_tree| {
return path_and_tree.tree;
},
Data.ScopeAndComp => |scope_and_comp| {
return scope_and_comp.root_scope.tree;
},
}
}
/// Takes ownership of text
/// References root_scope, and derefs when the msg is freed
pub fn createFromScope(comp: *Compilation, root_scope: *Scope.Root, span: Span, text: []u8) !*Msg {
const msg = try comp.gpa().create(Msg{
.text = text,
.span = span,
.data = Data{
.ScopeAndComp = ScopeAndComp{
.root_scope = root_scope,
.compilation = comp,
},
},
});
root_scope.base.ref();
return msg;
}
pub fn createFromParseErrorAndScope(
comp: *Compilation,
root_scope: *Scope.Root,
parse_error: *const ast.Error,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(comp.gpa(), 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&root_scope.tree.tokens, out_stream);
const msg = try comp.gpa().create(Msg{
.text = undefined,
.span = Span{
.first = loc_token,
.last = loc_token,
},
.data = Data{
.ScopeAndComp = ScopeAndComp{
.root_scope = root_scope,
.compilation = comp,
},
},
});
root_scope.base.ref();
msg.text = text_buf.toOwnedSlice();
return msg;
}
/// `realpath` must outlive the returned Msg
/// `tree` must outlive the returned Msg
/// Caller owns returned Msg and must free with `allocator`
/// allocator will additionally be used for printing messages later.
pub fn createFromParseError(
allocator: *mem.Allocator,
parse_error: *const ast.Error,
tree: *ast.Tree,
realpath: []const u8,
) !*Msg {
const loc_token = parse_error.loc();
var text_buf = try std.Buffer.initSize(allocator, 0);
defer text_buf.deinit();
var out_stream = &std.io.BufferOutStream.init(&text_buf).stream;
try parse_error.render(&tree.tokens, out_stream);
const msg = try allocator.create(Msg{
.text = undefined,
.data = Data{
.PathAndTree = PathAndTree{
.allocator = allocator,
.realpath = realpath,
.tree = tree,
},
},
.span = Span{
.first = loc_token,
.last = loc_token,
},
});
msg.text = text_buf.toOwnedSlice();
errdefer allocator.destroy(msg);
return msg;
}
pub fn printToStream(msg: *const Msg, stream: var, color_on: bool) !void {
const allocator = msg.getAllocator();
const realpath = msg.getRealPath();
const tree = msg.getTree();
const cwd = try os.getCwd(allocator);
defer allocator.free(cwd);
const relpath = try os.path.relative(allocator, cwd, realpath);
defer allocator.free(relpath);
const path = if (relpath.len < realpath.len) relpath else realpath;
const first_token = tree.tokens.at(msg.span.first);
const last_token = tree.tokens.at(msg.span.last);
const start_loc = tree.tokenLocationPtr(0, first_token);
const end_loc = tree.tokenLocationPtr(first_token.end, last_token);
if (!color_on) {
try stream.print(
"{}:{}:{}: error: {}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
msg.text,
);
return;
}
pub fn printToStream(stream: var, msg: *const Msg, color_on: bool) !void {
const first_token = msg.tree.tokens.at(msg.span.first);
const last_token = msg.tree.tokens.at(msg.span.last);
const start_loc = msg.tree.tokenLocationPtr(0, first_token);
const end_loc = msg.tree.tokenLocationPtr(first_token.end, last_token);
if (!color_on) {
try stream.print(
"{}:{}:{}: error: {}\n",
msg.path,
"{}:{}:{}: error: {}\n{}\n",
path,
start_loc.line + 1,
start_loc.column + 1,
msg.text,
tree.source[start_loc.line_start..start_loc.line_end],
);
return;
try stream.writeByteNTimes(' ', start_loc.column);
try stream.writeByteNTimes('~', last_token.end - first_token.start);
try stream.write("\n");
}
try stream.print(
"{}:{}:{}: error: {}\n{}\n",
msg.path,
start_loc.line + 1,
start_loc.column + 1,
msg.text,
msg.tree.source[start_loc.line_start..start_loc.line_end],
);
try stream.writeByteNTimes(' ', start_loc.column);
try stream.writeByteNTimes('~', last_token.end - first_token.start);
try stream.write("\n");
}
pub fn printToFile(file: *os.File, msg: *const Msg, color: Color) !void {
const color_on = switch (color) {
Color.Auto => file.isTty(),
Color.On => true,
Color.Off => false,
};
var stream = &std.io.FileOutStream.init(file).stream;
return printToStream(stream, msg, color_on);
}
pub fn printToFile(msg: *const Msg, file: *os.File, color: Color) !void {
const color_on = switch (color) {
Color.Auto => file.isTty(),
Color.On => true,
Color.Off => false,
};
var stream = &std.io.FileOutStream.init(file).stream;
return msg.printToStream(stream, color_on);
}
};

File diff suppressed because it is too large Load Diff

View File

@ -0,0 +1,462 @@
const std = @import("std");
const builtin = @import("builtin");
const event = std.event;
const Target = @import("target.zig").Target;
const c = @import("c.zig");
/// See the render function implementation for documentation of the fields.
pub const LibCInstallation = struct {
include_dir: []const u8,
lib_dir: ?[]const u8,
static_lib_dir: ?[]const u8,
msvc_lib_dir: ?[]const u8,
kernel32_lib_dir: ?[]const u8,
dynamic_linker_path: ?[]const u8,
pub const FindError = error{
OutOfMemory,
FileSystem,
UnableToSpawnCCompiler,
CCompilerExitCode,
CCompilerCrashed,
CCompilerCannotFindHeaders,
LibCRuntimeNotFound,
LibCStdLibHeaderNotFound,
LibCKernel32LibNotFound,
UnsupportedArchitecture,
};
pub fn parse(
self: *LibCInstallation,
allocator: *std.mem.Allocator,
libc_file: []const u8,
stderr: *std.io.OutStream(std.io.FileOutStream.Error),
) !void {
self.initEmpty();
const keys = []const []const u8{
"include_dir",
"lib_dir",
"static_lib_dir",
"msvc_lib_dir",
"kernel32_lib_dir",
"dynamic_linker_path",
};
const FoundKey = struct {
found: bool,
allocated: ?[]u8,
};
var found_keys = [1]FoundKey{FoundKey{ .found = false, .allocated = null }} ** keys.len;
errdefer {
self.initEmpty();
for (found_keys) |found_key| {
if (found_key.allocated) |s| allocator.free(s);
}
}
const contents = try std.io.readFileAlloc(allocator, libc_file);
defer allocator.free(contents);
var it = std.mem.split(contents, "\n");
while (it.next()) |line| {
if (line.len == 0 or line[0] == '#') continue;
var line_it = std.mem.split(line, "=");
const name = line_it.next() orelse {
try stderr.print("missing equal sign after field name\n");
return error.ParseError;
};
const value = line_it.rest();
inline for (keys) |key, i| {
if (std.mem.eql(u8, name, key)) {
found_keys[i].found = true;
switch (@typeInfo(@typeOf(@field(self, key)))) {
builtin.TypeId.Optional => {
if (value.len == 0) {
@field(self, key) = null;
} else {
found_keys[i].allocated = try std.mem.dupe(allocator, u8, value);
@field(self, key) = found_keys[i].allocated;
}
},
else => {
if (value.len == 0) {
try stderr.print("field cannot be empty: {}\n", key);
return error.ParseError;
}
const dupe = try std.mem.dupe(allocator, u8, value);
found_keys[i].allocated = dupe;
@field(self, key) = dupe;
},
}
break;
}
}
}
for (found_keys) |found_key, i| {
if (!found_key.found) {
try stderr.print("missing field: {}\n", keys[i]);
return error.ParseError;
}
}
}
pub fn render(self: *const LibCInstallation, out: *std.io.OutStream(std.io.FileOutStream.Error)) !void {
@setEvalBranchQuota(4000);
try out.print(
\\# The directory that contains `stdlib.h`.
\\# On Linux, can be found with: `cc -E -Wp,-v -xc /dev/null`
\\include_dir={}
\\
\\# The directory that contains `crt1.o`.
\\# On Linux, can be found with `cc -print-file-name=crt1.o`.
\\# Not needed when targeting MacOS.
\\lib_dir={}
\\
\\# The directory that contains `crtbegin.o`.
\\# On Linux, can be found with `cc -print-file-name=crtbegin.o`.
\\# Not needed when targeting MacOS or Windows.
\\static_lib_dir={}
\\
\\# The directory that contains `vcruntime.lib`.
\\# Only needed when targeting Windows.
\\msvc_lib_dir={}
\\
\\# The directory that contains `kernel32.lib`.
\\# Only needed when targeting Windows.
\\kernel32_lib_dir={}
\\
\\# The full path to the dynamic linker, on the target system.
\\# Only needed when targeting Linux.
\\dynamic_linker_path={}
\\
,
self.include_dir,
self.lib_dir orelse "",
self.static_lib_dir orelse "",
self.msvc_lib_dir orelse "",
self.kernel32_lib_dir orelse "",
self.dynamic_linker_path orelse Target(Target.Native).getDynamicLinkerPath(),
);
}
/// Finds the default, native libc.
pub async fn findNative(self: *LibCInstallation, loop: *event.Loop) !void {
self.initEmpty();
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
var windows_sdk: ?*c.ZigWindowsSDK = null;
errdefer if (windows_sdk) |sdk| c.zig_free_windows_sdk(@ptrCast(?[*]c.ZigWindowsSDK, sdk));
switch (builtin.os) {
builtin.Os.windows => {
var sdk: *c.ZigWindowsSDK = undefined;
switch (c.zig_find_windows_sdk(@ptrCast(?[*]?[*]c.ZigWindowsSDK, &sdk))) {
c.ZigFindWindowsSdkError.None => {
windows_sdk = sdk;
if (sdk.msvc_lib_dir_ptr) |ptr| {
self.msvc_lib_dir = try std.mem.dupe(loop.allocator, u8, ptr[0..sdk.msvc_lib_dir_len]);
}
try group.call(findNativeKernel32LibDir, self, loop, sdk);
try group.call(findNativeIncludeDirWindows, self, loop, sdk);
try group.call(findNativeLibDirWindows, self, loop, sdk);
},
c.ZigFindWindowsSdkError.OutOfMemory => return error.OutOfMemory,
c.ZigFindWindowsSdkError.NotFound => return error.NotFound,
c.ZigFindWindowsSdkError.PathTooLong => return error.NotFound,
}
},
builtin.Os.linux => {
try group.call(findNativeIncludeDirLinux, self, loop);
try group.call(findNativeLibDirLinux, self, loop);
try group.call(findNativeStaticLibDir, self, loop);
try group.call(findNativeDynamicLinker, self, loop);
},
builtin.Os.macosx => {
self.include_dir = try std.mem.dupe(loop.allocator, u8, "/usr/include");
},
else => @compileError("unimplemented: find libc for this OS"),
}
return await (async group.wait() catch unreachable);
}
async fn findNativeIncludeDirLinux(self: *LibCInstallation, loop: *event.Loop) !void {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const argv = []const []const u8{
cc_exe,
"-E",
"-Wp,-v",
"-xc",
"/dev/null",
};
// TODO make this use event loop
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
} else blk: {
break :blk errorable_result catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.UnableToSpawnCCompiler,
};
};
defer {
loop.allocator.free(exec_result.stdout);
loop.allocator.free(exec_result.stderr);
}
switch (exec_result.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) return error.CCompilerExitCode;
},
else => {
return error.CCompilerCrashed;
},
}
var it = std.mem.split(exec_result.stderr, "\n\r");
var search_paths = std.ArrayList([]const u8).init(loop.allocator);
defer search_paths.deinit();
while (it.next()) |line| {
if (line.len != 0 and line[0] == ' ') {
try search_paths.append(line);
}
}
if (search_paths.len == 0) {
return error.CCompilerCannotFindHeaders;
}
// search in reverse order
var path_i: usize = 0;
while (path_i < search_paths.len) : (path_i += 1) {
const search_path_untrimmed = search_paths.at(search_paths.len - path_i - 1);
const search_path = std.mem.trimLeft(u8, search_path_untrimmed, " ");
const stdlib_path = try std.os.path.join(loop.allocator, search_path, "stdlib.h");
defer loop.allocator.free(stdlib_path);
if (try fileExists(loop.allocator, stdlib_path)) {
self.include_dir = try std.mem.dupe(loop.allocator, u8, search_path);
return;
}
}
return error.LibCStdLibHeaderNotFound;
}
async fn findNativeIncludeDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) !void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Include\\{}\\ucrt", search.path, search.version);
const stdlib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "stdlib.h");
defer loop.allocator.free(stdlib_path);
if (try fileExists(loop.allocator, stdlib_path)) {
self.include_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCStdLibHeaderNotFound;
}
async fn findNativeLibDirWindows(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Lib\\{}\\ucrt\\", search.path, search.version);
switch (builtin.arch) {
builtin.Arch.i386 => try stream.write("x86"),
builtin.Arch.x86_64 => try stream.write("x64"),
builtin.Arch.aarch64 => try stream.write("arm"),
else => return error.UnsupportedArchitecture,
}
const ucrt_lib_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "ucrt.lib");
defer loop.allocator.free(ucrt_lib_path);
if (try fileExists(loop.allocator, ucrt_lib_path)) {
self.lib_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCRuntimeNotFound;
}
async fn findNativeLibDirLinux(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.lib_dir = try await (async ccPrintFileName(loop, "crt1.o", true) catch unreachable);
}
async fn findNativeStaticLibDir(self: *LibCInstallation, loop: *event.Loop) FindError!void {
self.static_lib_dir = try await (async ccPrintFileName(loop, "crtbegin.o", true) catch unreachable);
}
async fn findNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop) FindError!void {
var dyn_tests = []DynTest{
DynTest{
.name = "ld-linux-x86-64.so.2",
.result = null,
},
DynTest{
.name = "ld-musl-x86_64.so.1",
.result = null,
},
};
var group = event.Group(FindError!void).init(loop);
errdefer group.cancelAll();
for (dyn_tests) |*dyn_test| {
try group.call(testNativeDynamicLinker, self, loop, dyn_test);
}
try await (async group.wait() catch unreachable);
for (dyn_tests) |*dyn_test| {
if (dyn_test.result) |result| {
self.dynamic_linker_path = result;
return;
}
}
}
const DynTest = struct {
name: []const u8,
result: ?[]const u8,
};
async fn testNativeDynamicLinker(self: *LibCInstallation, loop: *event.Loop, dyn_test: *DynTest) FindError!void {
if (await (async ccPrintFileName(loop, dyn_test.name, false) catch unreachable)) |result| {
dyn_test.result = result;
return;
} else |err| switch (err) {
error.LibCRuntimeNotFound => return,
else => return err,
}
}
async fn findNativeKernel32LibDir(self: *LibCInstallation, loop: *event.Loop, sdk: *c.ZigWindowsSDK) FindError!void {
var search_buf: [2]Search = undefined;
const searches = fillSearch(&search_buf, sdk);
var result_buf = try std.Buffer.initSize(loop.allocator, 0);
defer result_buf.deinit();
for (searches) |search| {
result_buf.shrink(0);
const stream = &std.io.BufferOutStream.init(&result_buf).stream;
try stream.print("{}\\Lib\\{}\\um\\", search.path, search.version);
switch (builtin.arch) {
builtin.Arch.i386 => try stream.write("x86\\"),
builtin.Arch.x86_64 => try stream.write("x64\\"),
builtin.Arch.aarch64 => try stream.write("arm\\"),
else => return error.UnsupportedArchitecture,
}
const kernel32_path = try std.os.path.join(loop.allocator, result_buf.toSliceConst(), "kernel32.lib");
defer loop.allocator.free(kernel32_path);
if (try fileExists(loop.allocator, kernel32_path)) {
self.kernel32_lib_dir = result_buf.toOwnedSlice();
return;
}
}
return error.LibCKernel32LibNotFound;
}
fn initEmpty(self: *LibCInstallation) void {
self.* = LibCInstallation{
.include_dir = ([*]const u8)(undefined)[0..0],
.lib_dir = null,
.static_lib_dir = null,
.msvc_lib_dir = null,
.kernel32_lib_dir = null,
.dynamic_linker_path = null,
};
}
};
/// caller owns returned memory
async fn ccPrintFileName(loop: *event.Loop, o_file: []const u8, want_dirname: bool) ![]u8 {
const cc_exe = std.os.getEnvPosix("CC") orelse "cc";
const arg1 = try std.fmt.allocPrint(loop.allocator, "-print-file-name={}", o_file);
defer loop.allocator.free(arg1);
const argv = []const []const u8{ cc_exe, arg1 };
// TODO This simulates evented I/O for the child process exec
await (async loop.yield() catch unreachable);
const errorable_result = std.os.ChildProcess.exec(loop.allocator, argv, null, null, 1024 * 1024);
const exec_result = if (std.debug.runtime_safety) blk: {
break :blk errorable_result catch unreachable;
} else blk: {
break :blk errorable_result catch |err| switch (err) {
error.OutOfMemory => return error.OutOfMemory,
else => return error.UnableToSpawnCCompiler,
};
};
defer {
loop.allocator.free(exec_result.stdout);
loop.allocator.free(exec_result.stderr);
}
switch (exec_result.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) return error.CCompilerExitCode;
},
else => {
return error.CCompilerCrashed;
},
}
var it = std.mem.split(exec_result.stdout, "\n\r");
const line = it.next() orelse return error.LibCRuntimeNotFound;
const dirname = std.os.path.dirname(line) orelse return error.LibCRuntimeNotFound;
if (want_dirname) {
return std.mem.dupe(loop.allocator, u8, dirname);
} else {
return std.mem.dupe(loop.allocator, u8, line);
}
}
const Search = struct {
path: []const u8,
version: []const u8,
};
fn fillSearch(search_buf: *[2]Search, sdk: *c.ZigWindowsSDK) []Search {
var search_end: usize = 0;
if (sdk.path10_ptr) |path10_ptr| {
if (sdk.version10_ptr) |ver10_ptr| {
search_buf[search_end] = Search{
.path = path10_ptr[0..sdk.path10_len],
.version = ver10_ptr[0..sdk.version10_len],
};
search_end += 1;
}
}
if (sdk.path81_ptr) |path81_ptr| {
if (sdk.version81_ptr) |ver81_ptr| {
search_buf[search_end] = Search{
.path = path81_ptr[0..sdk.path81_len],
.version = ver81_ptr[0..sdk.version81_len],
};
search_end += 1;
}
}
return search_buf[0..search_end];
}
fn fileExists(allocator: *std.mem.Allocator, path: []const u8) !bool {
if (std.os.File.access(allocator, path)) |_| {
return true;
} else |err| switch (err) {
error.NotFound, error.PermissionDenied => return false,
error.OutOfMemory => return error.OutOfMemory,
else => return error.FileSystem,
}
}

View File

@ -1,8 +1,12 @@
const std = @import("std");
const mem = std.mem;
const c = @import("c.zig");
const builtin = @import("builtin");
const ObjectFormat = builtin.ObjectFormat;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
const assert = std.debug.assert;
const Context = struct {
comp: *Compilation,
@ -12,9 +16,12 @@ const Context = struct {
link_err: error{OutOfMemory}!void,
link_msg: std.Buffer,
libc: *LibCInstallation,
out_file_path: std.Buffer,
};
pub fn link(comp: *Compilation) !void {
pub async fn link(comp: *Compilation) !void {
var ctx = Context{
.comp = comp,
.arena = std.heap.ArenaAllocator.init(comp.gpa()),
@ -22,15 +29,45 @@ pub fn link(comp: *Compilation) !void {
.link_in_crt = comp.haveLibC() and comp.kind == Compilation.Kind.Exe,
.link_err = {},
.link_msg = undefined,
.libc = undefined,
.out_file_path = undefined,
};
defer ctx.arena.deinit();
ctx.args = std.ArrayList([*]const u8).init(&ctx.arena.allocator);
ctx.link_msg = std.Buffer.initNull(&ctx.arena.allocator);
if (comp.link_out_file) |out_file| {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, out_file);
} else {
ctx.out_file_path = try std.Buffer.init(&ctx.arena.allocator, comp.name.toSliceConst());
switch (comp.kind) {
Compilation.Kind.Exe => {
try ctx.out_file_path.append(comp.target.exeFileExt());
},
Compilation.Kind.Lib => {
try ctx.out_file_path.append(comp.target.libFileExt(comp.is_static));
},
Compilation.Kind.Obj => {
try ctx.out_file_path.append(comp.target.objFileExt());
},
}
}
// even though we're calling LLD as a library it thinks the first
// argument is its own exe name
try ctx.args.append(c"lld");
if (comp.haveLibC()) {
ctx.libc = ctx.comp.override_libc orelse blk: {
switch (comp.target) {
Target.Native => {
break :blk (await (async comp.event_loop_local.getNativeLibC() catch unreachable)) catch return error.LibCRequiredButNotProvidedOrFound;
},
else => return error.LibCRequiredButNotProvidedOrFound,
}
};
}
try constructLinkerArgs(&ctx);
if (comp.verbose_link) {
@ -43,6 +80,7 @@ pub fn link(comp: *Compilation) !void {
const extern_ofmt = toExternObjectFormatType(comp.target.getObjectFormat());
const args_slice = ctx.args.toSlice();
// Not evented I/O. LLD does its own multithreading internally.
if (!ZigLLDLink(extern_ofmt, args_slice.ptr, args_slice.len, linkDiagCallback, @ptrCast(*c_void, &ctx))) {
if (!ctx.link_msg.isNull()) {
// TODO capture these messages and pass them through the system, reporting them through the
@ -95,10 +133,7 @@ fn constructLinkerArgs(ctx: *Context) !void {
}
fn constructLinkerArgsElf(ctx: *Context) !void {
//if (g->libc_link_lib != nullptr) {
// find_libc_lib_path(g);
//}
// TODO commented out code in this function
//if (g->linker_script) {
// lj->args.append("-T");
// lj->args.append(g->linker_script);
@ -107,7 +142,7 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
//if (g->no_rosegment_workaround) {
// lj->args.append("--no-rosegment");
//}
//lj->args.append("--gc-sections");
try ctx.args.append(c"--gc-sections");
//lj->args.append("-m");
//lj->args.append(getLDMOption(&g->zig_target));
@ -115,14 +150,13 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
//bool is_lib = g->out_type == OutTypeLib;
//bool shared = !g->is_static && is_lib;
//Buf *soname = nullptr;
//if (g->is_static) {
// if (g->zig_target.arch.arch == ZigLLVM_arm || g->zig_target.arch.arch == ZigLLVM_armeb ||
// g->zig_target.arch.arch == ZigLLVM_thumb || g->zig_target.arch.arch == ZigLLVM_thumbeb)
// {
// lj->args.append("-Bstatic");
// } else {
// lj->args.append("-static");
// }
if (ctx.comp.is_static) {
if (ctx.comp.target.isArmOrThumb()) {
try ctx.args.append(c"-Bstatic");
} else {
try ctx.args.append(c"-static");
}
}
//} else if (shared) {
// lj->args.append("-shared");
@ -133,23 +167,16 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// soname = buf_sprintf("lib%s.so.%" ZIG_PRI_usize "", buf_ptr(g->root_out_name), g->version_major);
//}
//lj->args.append("-o");
//lj->args.append(buf_ptr(&lj->out_file));
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
//if (lj->link_in_crt) {
// const char *crt1o;
// const char *crtbegino;
// if (g->is_static) {
// crt1o = "crt1.o";
// crtbegino = "crtbeginT.o";
// } else {
// crt1o = "Scrt1.o";
// crtbegino = "crtbegin.o";
// }
// lj->args.append(get_libc_file(g, crt1o));
// lj->args.append(get_libc_file(g, "crti.o"));
// lj->args.append(get_libc_static_file(g, crtbegino));
//}
if (ctx.link_in_crt) {
const crt1o = if (ctx.comp.is_static) "crt1.o" else "Scrt1.o";
const crtbegino = if (ctx.comp.is_static) "crtbeginT.o" else "crtbegin.o";
try addPathJoin(ctx, ctx.libc.lib_dir.?, crt1o);
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crti.o");
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, crtbegino);
}
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
@ -182,25 +209,23 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// lj->args.append(lib_dir);
//}
//if (g->libc_link_lib != nullptr) {
// lj->args.append("-L");
// lj->args.append(buf_ptr(g->libc_lib_dir));
if (ctx.comp.haveLibC()) {
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.lib_dir.?)).ptr);
// lj->args.append("-L");
// lj->args.append(buf_ptr(g->libc_static_lib_dir));
//}
try ctx.args.append(c"-L");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, ctx.libc.static_lib_dir.?)).ptr);
//if (!g->is_static) {
// if (g->dynamic_linker != nullptr) {
// assert(buf_len(g->dynamic_linker) != 0);
// lj->args.append("-dynamic-linker");
// lj->args.append(buf_ptr(g->dynamic_linker));
// } else {
// Buf *resolved_dynamic_linker = get_dynamic_linker_path(g);
// lj->args.append("-dynamic-linker");
// lj->args.append(buf_ptr(resolved_dynamic_linker));
// }
//}
if (!ctx.comp.is_static) {
const dl = blk: {
if (ctx.libc.dynamic_linker_path) |dl| break :blk dl;
if (ctx.comp.target.getDynamicLinkerPath()) |dl| break :blk dl;
return error.LibCMissingDynamicLinker;
};
try ctx.args.append(c"-dynamic-linker");
try ctx.args.append((try std.cstr.addNullByte(&ctx.arena.allocator, dl)).ptr);
}
}
//if (shared) {
// lj->args.append("-soname");
@ -241,55 +266,358 @@ fn constructLinkerArgsElf(ctx: *Context) !void {
// lj->args.append(buf_ptr(arg));
//}
//// libc dep
//if (g->libc_link_lib != nullptr) {
// if (g->is_static) {
// lj->args.append("--start-group");
// lj->args.append("-lgcc");
// lj->args.append("-lgcc_eh");
// lj->args.append("-lc");
// lj->args.append("-lm");
// lj->args.append("--end-group");
// libc dep
if (ctx.comp.haveLibC()) {
if (ctx.comp.is_static) {
try ctx.args.append(c"--start-group");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"-lgcc_eh");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"--end-group");
} else {
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
try ctx.args.append(c"-lc");
try ctx.args.append(c"-lm");
try ctx.args.append(c"-lgcc");
try ctx.args.append(c"--as-needed");
try ctx.args.append(c"-lgcc_s");
try ctx.args.append(c"--no-as-needed");
}
}
// crt end
if (ctx.link_in_crt) {
try addPathJoin(ctx, ctx.libc.static_lib_dir.?, "crtend.o");
try addPathJoin(ctx, ctx.libc.lib_dir.?, "crtn.o");
}
if (ctx.comp.target != Target.Native) {
try ctx.args.append(c"--allow-shlib-undefined");
}
if (ctx.comp.target.getOs() == builtin.Os.zen) {
try ctx.args.append(c"-e");
try ctx.args.append(c"_start");
try ctx.args.append(c"--image-base=0x10000000");
}
}
fn addPathJoin(ctx: *Context, dirname: []const u8, basename: []const u8) !void {
const full_path = try std.os.path.join(&ctx.arena.allocator, dirname, basename);
const full_path_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, full_path);
try ctx.args.append(full_path_with_null.ptr);
}
fn constructLinkerArgsCoff(ctx: *Context) !void {
try ctx.args.append(c"-NOLOGO");
if (!ctx.comp.strip) {
try ctx.args.append(c"-DEBUG");
}
switch (ctx.comp.target.getArch()) {
builtin.Arch.i386 => try ctx.args.append(c"-MACHINE:X86"),
builtin.Arch.x86_64 => try ctx.args.append(c"-MACHINE:X64"),
builtin.Arch.aarch64 => try ctx.args.append(c"-MACHINE:ARM"),
else => return error.UnsupportedLinkArchitecture,
}
if (ctx.comp.windows_subsystem_windows) {
try ctx.args.append(c"/SUBSYSTEM:windows");
} else if (ctx.comp.windows_subsystem_console) {
try ctx.args.append(c"/SUBSYSTEM:console");
}
const is_library = ctx.comp.kind == Compilation.Kind.Lib;
const out_arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-OUT:{}\x00", ctx.out_file_path.toSliceConst());
try ctx.args.append(out_arg.ptr);
if (ctx.comp.haveLibC()) {
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.msvc_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.kernel32_lib_dir.?)).ptr);
try ctx.args.append((try std.fmt.allocPrint(&ctx.arena.allocator, "-LIBPATH:{}\x00", ctx.libc.lib_dir.?)).ptr);
}
if (ctx.link_in_crt) {
const lib_str = if (ctx.comp.is_static) "lib" else "";
const d_str = if (ctx.comp.build_mode == builtin.Mode.Debug) "d" else "";
if (ctx.comp.is_static) {
const cmt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "libcmt{}.lib\x00", d_str);
try ctx.args.append(cmt_lib_name.ptr);
} else {
const msvcrt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "msvcrt{}.lib\x00", d_str);
try ctx.args.append(msvcrt_lib_name.ptr);
}
const vcruntime_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}vcruntime{}.lib\x00", lib_str, d_str);
try ctx.args.append(vcruntime_lib_name.ptr);
const crt_lib_name = try std.fmt.allocPrint(&ctx.arena.allocator, "{}ucrt{}.lib\x00", lib_str, d_str);
try ctx.args.append(crt_lib_name.ptr);
// Visual C++ 2015 Conformance Changes
// https://msdn.microsoft.com/en-us/library/bb531344.aspx
try ctx.args.append(c"legacy_stdio_definitions.lib");
// msvcrt depends on kernel32
try ctx.args.append(c"kernel32.lib");
} else {
try ctx.args.append(c"-NODEFAULTLIB");
if (!is_library) {
try ctx.args.append(c"-ENTRY:WinMainCRTStartup");
// TODO
//if (g->have_winmain) {
// lj->args.append("-ENTRY:WinMain");
//} else {
// lj->args.append("-ENTRY:WinMainCRTStartup");
//}
}
}
if (is_library and !ctx.comp.is_static) {
try ctx.args.append(c"-DLL");
}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append(buf_ptr(buf_sprintf("-LIBPATH:%s", lib_dir)));
//}
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
switch (ctx.comp.kind) {
Compilation.Kind.Exe, Compilation.Kind.Lib => {
if (!ctx.comp.haveLibC()) {
@panic("TODO");
//Buf *builtin_o_path = build_o(g, "builtin");
//lj->args.append(buf_ptr(builtin_o_path));
}
// msvc compiler_rt is missing some stuff, so we still build it and rely on weak linkage
// TODO
//Buf *compiler_rt_o_path = build_compiler_rt(g);
//lj->args.append(buf_ptr(compiler_rt_o_path));
},
Compilation.Kind.Obj => {},
}
//Buf *def_contents = buf_alloc();
//ZigList<const char *> gen_lib_args = {0};
//for (size_t lib_i = 0; lib_i < g->link_libs_list.length; lib_i += 1) {
// LinkLib *link_lib = g->link_libs_list.at(lib_i);
// if (buf_eql_str(link_lib->name, "c")) {
// continue;
// }
// if (link_lib->provided_explicitly) {
// if (lj->codegen->zig_target.env_type == ZigLLVM_GNU) {
// Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
// lj->args.append(buf_ptr(arg));
// }
// else {
// lj->args.append(buf_ptr(link_lib->name));
// }
// } else {
// lj->args.append("-lgcc");
// lj->args.append("--as-needed");
// lj->args.append("-lgcc_s");
// lj->args.append("--no-as-needed");
// lj->args.append("-lc");
// lj->args.append("-lm");
// lj->args.append("-lgcc");
// lj->args.append("--as-needed");
// lj->args.append("-lgcc_s");
// lj->args.append("--no-as-needed");
// buf_resize(def_contents, 0);
// buf_appendf(def_contents, "LIBRARY %s\nEXPORTS\n", buf_ptr(link_lib->name));
// for (size_t exp_i = 0; exp_i < link_lib->symbols.length; exp_i += 1) {
// Buf *symbol_name = link_lib->symbols.at(exp_i);
// buf_appendf(def_contents, "%s\n", buf_ptr(symbol_name));
// }
// buf_appendf(def_contents, "\n");
// Buf *def_path = buf_alloc();
// os_path_join(g->cache_dir, buf_sprintf("%s.def", buf_ptr(link_lib->name)), def_path);
// os_write_file(def_path, def_contents);
// Buf *generated_lib_path = buf_alloc();
// os_path_join(g->cache_dir, buf_sprintf("%s.lib", buf_ptr(link_lib->name)), generated_lib_path);
// gen_lib_args.resize(0);
// gen_lib_args.append("link");
// coff_append_machine_arg(g, &gen_lib_args);
// gen_lib_args.append(buf_ptr(buf_sprintf("-DEF:%s", buf_ptr(def_path))));
// gen_lib_args.append(buf_ptr(buf_sprintf("-OUT:%s", buf_ptr(generated_lib_path))));
// Buf diag = BUF_INIT;
// if (!zig_lld_link(g->zig_target.oformat, gen_lib_args.items, gen_lib_args.length, &diag)) {
// fprintf(stderr, "%s\n", buf_ptr(&diag));
// exit(1);
// }
// lj->args.append(buf_ptr(generated_lib_path));
// }
//}
}
fn constructLinkerArgsMachO(ctx: *Context) !void {
try ctx.args.append(c"-demangle");
if (ctx.comp.linker_rdynamic) {
try ctx.args.append(c"-export_dynamic");
}
const is_lib = ctx.comp.kind == Compilation.Kind.Lib;
const shared = !ctx.comp.is_static and is_lib;
if (ctx.comp.is_static) {
try ctx.args.append(c"-static");
} else {
try ctx.args.append(c"-dynamic");
}
//if (is_lib) {
// if (!g->is_static) {
// lj->args.append("-dylib");
// Buf *compat_vers = buf_sprintf("%" ZIG_PRI_usize ".0.0", g->version_major);
// lj->args.append("-compatibility_version");
// lj->args.append(buf_ptr(compat_vers));
// Buf *cur_vers = buf_sprintf("%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize,
// g->version_major, g->version_minor, g->version_patch);
// lj->args.append("-current_version");
// lj->args.append(buf_ptr(cur_vers));
// // TODO getting an error when running an executable when doing this rpath thing
// //Buf *dylib_install_name = buf_sprintf("@rpath/lib%s.%" ZIG_PRI_usize ".dylib",
// // buf_ptr(g->root_out_name), g->version_major);
// //lj->args.append("-install_name");
// //lj->args.append(buf_ptr(dylib_install_name));
// if (buf_len(&lj->out_file) == 0) {
// buf_appendf(&lj->out_file, "lib%s.%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".%" ZIG_PRI_usize ".dylib",
// buf_ptr(g->root_out_name), g->version_major, g->version_minor, g->version_patch);
// }
// }
//}
//// crt end
//if (lj->link_in_crt) {
// lj->args.append(get_libc_static_file(g, "crtend.o"));
// lj->args.append(get_libc_file(g, "crtn.o"));
try ctx.args.append(c"-arch");
const darwin_arch_str = try std.cstr.addNullByte(
&ctx.arena.allocator,
ctx.comp.target.getDarwinArchString(),
);
try ctx.args.append(darwin_arch_str.ptr);
const platform = try DarwinPlatform.get(ctx.comp);
switch (platform.kind) {
DarwinPlatform.Kind.MacOS => try ctx.args.append(c"-macosx_version_min"),
DarwinPlatform.Kind.IPhoneOS => try ctx.args.append(c"-iphoneos_version_min"),
DarwinPlatform.Kind.IPhoneOSSimulator => try ctx.args.append(c"-ios_simulator_version_min"),
}
const ver_str = try std.fmt.allocPrint(&ctx.arena.allocator, "{}.{}.{}\x00", platform.major, platform.minor, platform.micro);
try ctx.args.append(ver_str.ptr);
if (ctx.comp.kind == Compilation.Kind.Exe) {
if (ctx.comp.is_static) {
try ctx.args.append(c"-no_pie");
} else {
try ctx.args.append(c"-pie");
}
}
try ctx.args.append(c"-o");
try ctx.args.append(ctx.out_file_path.ptr());
//for (size_t i = 0; i < g->rpath_list.length; i += 1) {
// Buf *rpath = g->rpath_list.at(i);
// add_rpath(lj, rpath);
//}
//add_rpath(lj, &lj->out_file);
if (shared) {
try ctx.args.append(c"-headerpad_max_install_names");
} else if (ctx.comp.is_static) {
try ctx.args.append(c"-lcrt0.o");
} else {
switch (platform.kind) {
DarwinPlatform.Kind.MacOS => {
if (platform.versionLessThan(10, 5)) {
try ctx.args.append(c"-lcrt1.o");
} else if (platform.versionLessThan(10, 6)) {
try ctx.args.append(c"-lcrt1.10.5.o");
} else if (platform.versionLessThan(10, 8)) {
try ctx.args.append(c"-lcrt1.10.6.o");
}
},
DarwinPlatform.Kind.IPhoneOS => {
if (ctx.comp.target.getArch() == builtin.Arch.aarch64) {
// iOS does not need any crt1 files for arm64
} else if (platform.versionLessThan(3, 1)) {
try ctx.args.append(c"-lcrt1.o");
} else if (platform.versionLessThan(6, 0)) {
try ctx.args.append(c"-lcrt1.3.1.o");
}
},
DarwinPlatform.Kind.IPhoneOSSimulator => {}, // no crt1.o needed
}
}
//for (size_t i = 0; i < g->lib_dirs.length; i += 1) {
// const char *lib_dir = g->lib_dirs.at(i);
// lj->args.append("-L");
// lj->args.append(lib_dir);
//}
//if (!g->is_native_target) {
// lj->args.append("--allow-shlib-undefined");
for (ctx.comp.link_objects) |link_object| {
const link_obj_with_null = try std.cstr.addNullByte(&ctx.arena.allocator, link_object);
try ctx.args.append(link_obj_with_null.ptr);
}
try addFnObjects(ctx);
//// compiler_rt on darwin is missing some stuff, so we still build it and rely on LinkOnce
//if (g->out_type == OutTypeExe || g->out_type == OutTypeLib) {
// Buf *compiler_rt_o_path = build_compiler_rt(g);
// lj->args.append(buf_ptr(compiler_rt_o_path));
//}
//if (g->zig_target.os == OsZen) {
// lj->args.append("-e");
// lj->args.append("_start");
if (ctx.comp.target == Target.Native) {
for (ctx.comp.link_libs_list.toSliceConst()) |lib| {
if (mem.eql(u8, lib.name, "c")) {
// on Darwin, libSystem has libc in it, but also you have to use it
// to make syscalls because the syscall numbers are not documented
// and change between versions.
// so we always link against libSystem
try ctx.args.append(c"-lSystem");
} else {
if (mem.indexOfScalar(u8, lib.name, '/') == null) {
const arg = try std.fmt.allocPrint(&ctx.arena.allocator, "-l{}\x00", lib.name);
try ctx.args.append(arg.ptr);
} else {
const arg = try std.cstr.addNullByte(&ctx.arena.allocator, lib.name);
try ctx.args.append(arg.ptr);
}
}
}
} else {
try ctx.args.append(c"-undefined");
try ctx.args.append(c"dynamic_lookup");
}
// lj->args.append("--image-base=0x10000000");
if (platform.kind == DarwinPlatform.Kind.MacOS) {
if (platform.versionLessThan(10, 5)) {
try ctx.args.append(c"-lgcc_s.10.4");
} else if (platform.versionLessThan(10, 6)) {
try ctx.args.append(c"-lgcc_s.10.5");
}
} else {
@panic("TODO");
}
//for (size_t i = 0; i < g->darwin_frameworks.length; i += 1) {
// lj->args.append("-framework");
// lj->args.append(buf_ptr(g->darwin_frameworks.at(i)));
//}
}
fn constructLinkerArgsCoff(ctx: *Context) void {
@panic("TODO");
}
fn constructLinkerArgsMachO(ctx: *Context) void {
@panic("TODO");
}
fn constructLinkerArgsWasm(ctx: *Context) void {
@panic("TODO");
}
@ -312,3 +640,85 @@ fn addFnObjects(ctx: *Context) !void {
it = node.next;
}
}
const DarwinPlatform = struct {
kind: Kind,
major: u32,
minor: u32,
micro: u32,
const Kind = enum {
MacOS,
IPhoneOS,
IPhoneOSSimulator,
};
fn get(comp: *Compilation) !DarwinPlatform {
var result: DarwinPlatform = undefined;
const ver_str = switch (comp.darwin_version_min) {
Compilation.DarwinVersionMin.MacOS => |ver| blk: {
result.kind = Kind.MacOS;
break :blk ver;
},
Compilation.DarwinVersionMin.Ios => |ver| blk: {
result.kind = Kind.IPhoneOS;
break :blk ver;
},
Compilation.DarwinVersionMin.None => blk: {
assert(comp.target.getOs() == builtin.Os.macosx);
result.kind = Kind.MacOS;
break :blk "10.10";
},
};
var had_extra: bool = undefined;
try darwinGetReleaseVersion(ver_str, &result.major, &result.minor, &result.micro, &had_extra,);
if (had_extra or result.major != 10 or result.minor >= 100 or result.micro >= 100) {
return error.InvalidDarwinVersionString;
}
if (result.kind == Kind.IPhoneOS) {
switch (comp.target.getArch()) {
builtin.Arch.i386,
builtin.Arch.x86_64,
=> result.kind = Kind.IPhoneOSSimulator,
else => {},
}
}
return result;
}
fn versionLessThan(self: DarwinPlatform, major: u32, minor: u32) bool {
if (self.major < major)
return true;
if (self.major > major)
return false;
if (self.minor < minor)
return true;
return false;
}
};
/// Parse (([0-9]+)(.([0-9]+)(.([0-9]+)?))?)? and return the
/// grouped values as integers. Numbers which are not provided are set to 0.
/// return true if the entire string was parsed (9.2), or all groups were
/// parsed (10.3.5extrastuff).
fn darwinGetReleaseVersion(str: []const u8, major: *u32, minor: *u32, micro: *u32, had_extra: *bool) !void {
major.* = 0;
minor.* = 0;
micro.* = 0;
had_extra.* = false;
if (str.len == 0)
return error.InvalidDarwinVersionString;
var start_pos: usize = 0;
for ([]*u32{major, minor, micro}) |v| {
const dot_pos = mem.indexOfScalarPos(u8, str, start_pos, '.');
const end_pos = dot_pos orelse str.len;
v.* = std.fmt.parseUnsigned(u32, str[start_pos..end_pos], 10) catch return error.InvalidDarwinVersionString;
start_pos = (dot_pos orelse return) + 1;
if (start_pos == str.len) return;
}
had_extra.* = true;
}

View File

@ -23,13 +23,20 @@ pub const TargetMachineRef = removeNullability(c.LLVMTargetMachineRef);
pub const TargetDataRef = removeNullability(c.LLVMTargetDataRef);
pub const DIBuilder = c.ZigLLVMDIBuilder;
pub const ABIAlignmentOfType = c.LLVMABIAlignmentOfType;
pub const AddAttributeAtIndex = c.LLVMAddAttributeAtIndex;
pub const AddFunction = c.LLVMAddFunction;
pub const AddGlobal = c.LLVMAddGlobal;
pub const AddModuleCodeViewFlag = c.ZigLLVMAddModuleCodeViewFlag;
pub const AddModuleDebugInfoFlag = c.ZigLLVMAddModuleDebugInfoFlag;
pub const ArrayType = c.LLVMArrayType;
pub const ClearCurrentDebugLocation = c.ZigLLVMClearCurrentDebugLocation;
pub const ConstAllOnes = c.LLVMConstAllOnes;
pub const ConstArray = c.LLVMConstArray;
pub const ConstBitCast = c.LLVMConstBitCast;
pub const ConstInt = c.LLVMConstInt;
pub const ConstIntOfArbitraryPrecision = c.LLVMConstIntOfArbitraryPrecision;
pub const ConstNeg = c.LLVMConstNeg;
pub const ConstNull = c.LLVMConstNull;
pub const ConstStringInContext = c.LLVMConstStringInContext;
pub const ConstStructInContext = c.LLVMConstStructInContext;
@ -57,6 +64,7 @@ pub const GetEnumAttributeKindForName = c.LLVMGetEnumAttributeKindForName;
pub const GetHostCPUName = c.ZigLLVMGetHostCPUName;
pub const GetMDKindIDInContext = c.LLVMGetMDKindIDInContext;
pub const GetNativeFeatures = c.ZigLLVMGetNativeFeatures;
pub const GetUndef = c.LLVMGetUndef;
pub const HalfTypeInContext = c.LLVMHalfTypeInContext;
pub const InitializeAllAsmParsers = c.LLVMInitializeAllAsmParsers;
pub const InitializeAllAsmPrinters = c.LLVMInitializeAllAsmPrinters;
@ -79,14 +87,24 @@ pub const MDStringInContext = c.LLVMMDStringInContext;
pub const MetadataTypeInContext = c.LLVMMetadataTypeInContext;
pub const ModuleCreateWithNameInContext = c.LLVMModuleCreateWithNameInContext;
pub const PPCFP128TypeInContext = c.LLVMPPCFP128TypeInContext;
pub const PointerType = c.LLVMPointerType;
pub const SetAlignment = c.LLVMSetAlignment;
pub const SetDataLayout = c.LLVMSetDataLayout;
pub const SetGlobalConstant = c.LLVMSetGlobalConstant;
pub const SetInitializer = c.LLVMSetInitializer;
pub const SetLinkage = c.LLVMSetLinkage;
pub const SetTarget = c.LLVMSetTarget;
pub const SetUnnamedAddr = c.LLVMSetUnnamedAddr;
pub const StructTypeInContext = c.LLVMStructTypeInContext;
pub const TokenTypeInContext = c.LLVMTokenTypeInContext;
pub const TypeOf = c.LLVMTypeOf;
pub const VoidTypeInContext = c.LLVMVoidTypeInContext;
pub const X86FP80TypeInContext = c.LLVMX86FP80TypeInContext;
pub const X86MMXTypeInContext = c.LLVMX86MMXTypeInContext;
pub const ConstInBoundsGEP = LLVMConstInBoundsGEP;
pub extern fn LLVMConstInBoundsGEP(ConstantVal: ValueRef, ConstantIndices: [*]ValueRef, NumIndices: c_uint) ?ValueRef;
pub const GetTargetFromTriple = LLVMGetTargetFromTriple;
extern fn LLVMGetTargetFromTriple(Triple: [*]const u8, T: *TargetRef, ErrorMessage: ?*[*]u8) Bool;
@ -143,13 +161,28 @@ pub const EmitBinary = EmitOutputType.ZigLLVM_EmitBinary;
pub const EmitLLVMIr = EmitOutputType.ZigLLVM_EmitLLVMIr;
pub const EmitOutputType = c.ZigLLVM_EmitOutputType;
pub const CCallConv = c.LLVMCCallConv;
pub const FastCallConv = c.LLVMFastCallConv;
pub const ColdCallConv = c.LLVMColdCallConv;
pub const WebKitJSCallConv = c.LLVMWebKitJSCallConv;
pub const AnyRegCallConv = c.LLVMAnyRegCallConv;
pub const X86StdcallCallConv = c.LLVMX86StdcallCallConv;
pub const X86FastcallCallConv = c.LLVMX86FastcallCallConv;
pub const CallConv = c.LLVMCallConv;
pub const FnInline = extern enum {
Auto,
Always,
Never,
};
fn removeNullability(comptime T: type) type {
comptime assert(@typeId(T) == builtin.TypeId.Optional);
return T.Child;
}
pub const BuildRet = LLVMBuildRet;
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ValueRef;
extern fn LLVMBuildRet(arg0: BuilderRef, V: ?ValueRef) ?ValueRef;
pub const TargetMachineEmitToFile = ZigLLVMTargetMachineEmitToFile;
extern fn ZigLLVMTargetMachineEmitToFile(
@ -161,3 +194,8 @@ extern fn ZigLLVMTargetMachineEmitToFile(
is_debug: bool,
is_small: bool,
) bool;
pub const BuildCall = ZigLLVMBuildCall;
extern fn ZigLLVMBuildCall(B: BuilderRef, Fn: ValueRef, Args: [*]ValueRef, NumArgs: c_uint, CC: c_uint, fn_inline: FnInline, Name: [*]const u8) ?ValueRef;
pub const PrivateLinkage = c.LLVMLinkage.LLVMPrivateLinkage;

View File

@ -18,6 +18,7 @@ const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
const Compilation = @import("compilation.zig").Compilation;
const Target = @import("target.zig").Target;
const errmsg = @import("errmsg.zig");
const LibCInstallation = @import("libc_installation.zig").LibCInstallation;
var stderr_file: os.File = undefined;
var stderr: *io.OutStream(io.FileOutStream.Error) = undefined;
@ -28,13 +29,14 @@ const usage =
\\
\\Commands:
\\
\\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly
\\ fmt [source] Parse file and render in canonical zig format
\\ targets List available compilation targets
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\ build-exe [source] Create executable from source or object files
\\ build-lib [source] Create library from source or object files
\\ build-obj [source] Create object from source or assembly
\\ fmt [source] Parse file and render in canonical zig format
\\ libc [paths_file] Display native libc paths file or validate one
\\ targets List available compilation targets
\\ version Print version number and exit
\\ zen Print zen of zig and exit
\\
\\
;
@ -85,6 +87,10 @@ pub fn main() !void {
.name = "fmt",
.exec = cmdFmt,
},
Command{
.name = "libc",
.exec = cmdLibC,
},
Command{
.name = "targets",
.exec = cmdTargets,
@ -130,11 +136,10 @@ const usage_build_generic =
\\ --color [auto|off|on] Enable or disable colored error messages
\\
\\Compile Options:
\\ --libc [file] Provide a file which specifies libc paths
\\ --assembly [source] Add assembly file to build
\\ --cache-dir [path] Override the cache directory
\\ --emit [filetype] Emit a specific file format as compilation output
\\ --enable-timing-info Print timing diagnostics
\\ --libc-include-dir [path] Directory where libc stdlib.h resides
\\ --name [name] Override output name
\\ --output [file] Override destination path
\\ --output-h [file] Override generated header file path
@ -163,12 +168,7 @@ const usage_build_generic =
\\
\\Link Options:
\\ --ar-path [path] Set the path to ar
\\ --dynamic-linker [path] Set the path to ld.so
\\ --each-lib-rpath Add rpath for each used dynamic library
\\ --libc-lib-dir [path] Directory where libc crt1.o resides
\\ --libc-static-lib-dir [path] Directory where libc crtbegin.o resides
\\ --msvc-lib-dir [path] (windows) directory where vcruntime.lib resides
\\ --kernel32-lib-dir [path] (windows) directory where kernel32.lib resides
\\ --library [lib] Link against lib
\\ --forbid-library [lib] Make it an error to link against lib
\\ --library-path [dir] Add a directory to the library search path
@ -203,14 +203,13 @@ const args_build_generic = []Flag{
}),
Flag.ArgMergeN("--assembly", 1),
Flag.Arg1("--cache-dir"),
Flag.Option("--emit", []const []const u8{
"asm",
"bin",
"llvm-ir",
}),
Flag.Bool("--enable-timing-info"),
Flag.Arg1("--libc-include-dir"),
Flag.Arg1("--libc"),
Flag.Arg1("--name"),
Flag.Arg1("--output"),
Flag.Arg1("--output-h"),
@ -234,12 +233,7 @@ const args_build_generic = []Flag{
Flag.Arg1("-mllvm"),
Flag.Arg1("--ar-path"),
Flag.Arg1("--dynamic-linker"),
Flag.Bool("--each-lib-rpath"),
Flag.Arg1("--libc-lib-dir"),
Flag.Arg1("--libc-static-lib-dir"),
Flag.Arg1("--msvc-lib-dir"),
Flag.Arg1("--kernel32-lib-dir"),
Flag.ArgMergeN("--library", 1),
Flag.ArgMergeN("--forbid-library", 1),
Flag.ArgMergeN("--library-path", 1),
@ -377,16 +371,11 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
os.exit(1);
}
const rel_cache_dir = flags.single("cache-dir") orelse "zig-cache"[0..];
const full_cache_dir = os.path.resolve(allocator, ".", rel_cache_dir) catch {
try stderr.print("invalid cache dir: {}\n", rel_cache_dir);
os.exit(1);
};
defer allocator.free(full_cache_dir);
const zig_lib_dir = introspect.resolveZigLibDir(allocator) catch os.exit(1);
defer allocator.free(zig_lib_dir);
var override_libc: LibCInstallation = undefined;
var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
@ -403,10 +392,18 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
build_mode,
is_static,
zig_lib_dir,
full_cache_dir,
);
defer comp.destroy();
if (flags.single("libc")) |libc_path| {
parseLibcPaths(loop.allocator, &override_libc, libc_path);
comp.override_libc = &override_libc;
}
for (flags.many("library")) |lib| {
_ = try comp.addLinkLib(lib, true);
}
comp.version_major = try std.fmt.parseUnsigned(u32, flags.single("ver-major") orelse "0", 10);
comp.version_minor = try std.fmt.parseUnsigned(u32, flags.single("ver-minor") orelse "0", 10);
comp.version_patch = try std.fmt.parseUnsigned(u32, flags.single("ver-patch") orelse "0", 10);
@ -430,25 +427,6 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.strip = flags.present("strip");
if (flags.single("libc-lib-dir")) |libc_lib_dir| {
comp.libc_lib_dir = libc_lib_dir;
}
if (flags.single("libc-static-lib-dir")) |libc_static_lib_dir| {
comp.libc_static_lib_dir = libc_static_lib_dir;
}
if (flags.single("libc-include-dir")) |libc_include_dir| {
comp.libc_include_dir = libc_include_dir;
}
if (flags.single("msvc-lib-dir")) |msvc_lib_dir| {
comp.msvc_lib_dir = msvc_lib_dir;
}
if (flags.single("kernel32-lib-dir")) |kernel32_lib_dir| {
comp.kernel32_lib_dir = kernel32_lib_dir;
}
if (flags.single("dynamic-linker")) |dynamic_linker| {
comp.dynamic_linker = dynamic_linker;
}
comp.verbose_tokenize = flags.present("verbose-tokenize");
comp.verbose_ast_tree = flags.present("verbose-ast-tree");
comp.verbose_ast_fmt = flags.present("verbose-ast-fmt");
@ -484,7 +462,7 @@ fn buildOutputType(allocator: *Allocator, args: []const []const u8, out_type: Co
comp.emit_file_type = emit_type;
comp.assembly_files = assembly_files;
comp.link_out_file = flags.single("out-file");
comp.link_out_file = flags.single("output");
comp.link_objects = link_objects;
try comp.build();
@ -499,7 +477,6 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
switch (build_event) {
Compilation.Event.Ok => {
std.debug.warn("Build succeeded\n");
return;
},
Compilation.Event.Error => |err| {
@ -508,7 +485,8 @@ async fn processBuildEvents(comp: *Compilation, color: errmsg.Color) void {
},
Compilation.Event.Fail => |msgs| {
for (msgs) |msg| {
errmsg.printToFile(&stderr_file, msg, color) catch os.exit(1);
defer msg.destroy();
msg.printToFile(&stderr_file, color) catch os.exit(1);
}
},
}
@ -579,6 +557,53 @@ const Fmt = struct {
}
};
fn parseLibcPaths(allocator: *Allocator, libc: *LibCInstallation, libc_paths_file: []const u8) void {
libc.parse(allocator, libc_paths_file, stderr) catch |err| {
stderr.print(
"Unable to parse libc path file '{}': {}.\n" ++
"Try running `zig libc` to see an example for the native target.\n",
libc_paths_file,
@errorName(err),
) catch os.exit(1);
os.exit(1);
};
}
fn cmdLibC(allocator: *Allocator, args: []const []const u8) !void {
switch (args.len) {
0 => {},
1 => {
var libc_installation: LibCInstallation = undefined;
parseLibcPaths(allocator, &libc_installation, args[0]);
return;
},
else => {
try stderr.print("unexpected extra parameter: {}\n", args[1]);
os.exit(1);
},
}
var loop: event.Loop = undefined;
try loop.initMultiThreaded(allocator);
defer loop.deinit();
var event_loop_local = try EventLoopLocal.init(&loop);
defer event_loop_local.deinit();
const handle = try async<loop.allocator> findLibCAsync(&event_loop_local);
defer cancel handle;
loop.run();
}
async fn findLibCAsync(event_loop_local: *EventLoopLocal) void {
const libc = (await (async event_loop_local.getNativeLibC() catch unreachable)) catch |err| {
stderr.print("unable to find libc: {}\n", @errorName(err)) catch os.exit(1);
os.exit(1);
};
libc.render(stdout) catch os.exit(1);
}
fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var flags = try Args.parse(allocator, args_fmt_spec, args);
defer flags.deinit();
@ -622,10 +647,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
defer allocator.destroy(msg);
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, "<stdin>");
defer msg.destroy();
try errmsg.printToFile(&stderr_file, msg, color);
try msg.printToFile(&stderr_file, color);
}
if (tree.errors.len != 0) {
os.exit(1);
@ -678,10 +703,10 @@ fn cmdFmt(allocator: *Allocator, args: []const []const u8) !void {
var error_it = tree.errors.iterator(0);
while (error_it.next()) |parse_error| {
const msg = try errmsg.createFromParseError(allocator, parse_error, &tree, file_path);
defer allocator.destroy(msg);
const msg = try errmsg.Msg.createFromParseError(allocator, parse_error, &tree, file_path);
defer msg.destroy();
try errmsg.printToFile(&stderr_file, msg, color);
try msg.printToFile(&stderr_file, color);
}
if (tree.errors.len != 0) {
fmt.any_error = true;

View File

@ -1,6 +0,0 @@
const ast = @import("std").zig.ast;
pub const ParsedFile = struct {
tree: ast.Tree,
realpath: []const u8,
};

View File

@ -8,6 +8,8 @@ const ast = std.zig.ast;
const Value = @import("value.zig").Value;
const ir = @import("ir.zig");
const Span = @import("errmsg.zig").Span;
const assert = std.debug.assert;
const event = std.event;
pub const Scope = struct {
id: Id,
@ -23,7 +25,8 @@ pub const Scope = struct {
if (base.ref_count == 0) {
if (base.parent) |parent| parent.deref(comp);
switch (base.id) {
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(),
Id.Root => @fieldParentPtr(Root, "base", base).destroy(comp),
Id.Decls => @fieldParentPtr(Decls, "base", base).destroy(comp),
Id.Block => @fieldParentPtr(Block, "base", base).destroy(comp),
Id.FnDef => @fieldParentPtr(FnDef, "base", base).destroy(comp),
Id.CompTime => @fieldParentPtr(CompTime, "base", base).destroy(comp),
@ -33,6 +36,15 @@ pub const Scope = struct {
}
}
pub fn findRoot(base: *Scope) *Root {
var scope = base;
while (scope.parent) |parent| {
scope = parent;
}
assert(scope.id == Id.Root);
return @fieldParentPtr(Root, "base", scope);
}
pub fn findFnDef(base: *Scope) ?*FnDef {
var scope = base;
while (true) {
@ -44,12 +56,33 @@ pub const Scope = struct {
Id.Defer,
Id.DeferExpr,
Id.CompTime,
Id.Root,
=> scope = scope.parent orelse return null,
}
}
}
pub fn findDeferExpr(base: *Scope) ?*DeferExpr {
var scope = base;
while (true) {
switch (scope.id) {
Id.DeferExpr => return @fieldParentPtr(DeferExpr, "base", base),
Id.FnDef,
Id.Decls,
=> return null,
Id.Block,
Id.Defer,
Id.CompTime,
Id.Root,
=> scope = scope.parent orelse return null,
}
}
}
pub const Id = enum {
Root,
Decls,
Block,
FnDef,
@ -58,42 +91,82 @@ pub const Scope = struct {
DeferExpr,
};
pub const Root = struct {
base: Scope,
tree: *ast.Tree,
realpath: []const u8,
/// Creates a Root scope with 1 reference
/// Takes ownership of realpath
/// Takes ownership of tree, will deinit and destroy when done.
pub fn create(comp: *Compilation, tree: *ast.Tree, realpath: []u8) !*Root {
const self = try comp.gpa().create(Root{
.base = Scope{
.id = Id.Root,
.parent = null,
.ref_count = 1,
},
.tree = tree,
.realpath = realpath,
});
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Root, comp: *Compilation) void {
comp.gpa().free(self.tree.source);
self.tree.deinit();
comp.gpa().destroy(self.tree);
comp.gpa().free(self.realpath);
comp.gpa().destroy(self);
}
};
pub const Decls = struct {
base: Scope,
table: Decl.Table,
/// The lock must be respected for writing. However once name_future resolves,
/// readers can freely access it.
table: event.Locked(Decl.Table),
/// Once this future is resolved, the table is complete and available for unlocked
/// read-only access. It does not mean all the decls are resolved; it means only that
/// the table has all the names. Each decl in the table has its own resolution state.
name_future: event.Future(void),
/// Creates a Decls scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope) !*Decls {
pub fn create(comp: *Compilation, parent: *Scope) !*Decls {
const self = try comp.gpa().create(Decls{
.base = Scope{
.id = Id.Decls,
.parent = parent,
.ref_count = 1,
},
.table = undefined,
.table = event.Locked(Decl.Table).init(comp.loop, Decl.Table.init(comp.gpa())),
.name_future = event.Future(void).init(comp.loop),
});
errdefer comp.gpa().destroy(self);
self.table = Decl.Table.init(comp.gpa());
errdefer self.table.deinit();
if (parent) |p| p.ref();
parent.ref();
return self;
}
pub fn destroy(self: *Decls) void {
pub fn destroy(self: *Decls, comp: *Compilation) void {
self.table.deinit();
self.table.allocator.destroy(self);
comp.gpa().destroy(self);
}
pub async fn getTableReadOnly(self: *Decls) *Decl.Table {
_ = await (async self.name_future.get() catch unreachable);
return &self.table.private_data;
}
};
pub const Block = struct {
base: Scope,
incoming_values: std.ArrayList(*ir.Instruction),
incoming_values: std.ArrayList(*ir.Inst),
incoming_blocks: std.ArrayList(*ir.BasicBlock),
end_block: *ir.BasicBlock,
is_comptime: *ir.Instruction,
is_comptime: *ir.Inst,
safety: Safety,
@ -125,7 +198,7 @@ pub const Scope = struct {
};
/// Creates a Block scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope) !*Block {
pub fn create(comp: *Compilation, parent: *Scope) !*Block {
const self = try comp.gpa().create(Block{
.base = Scope{
.id = Id.Block,
@ -140,7 +213,7 @@ pub const Scope = struct {
});
errdefer comp.gpa().destroy(self);
if (parent) |p| p.ref();
parent.ref();
return self;
}
@ -157,7 +230,7 @@ pub const Scope = struct {
/// Creates a FnDef scope with 1 reference
/// Must set the fn_val later
pub fn create(comp: *Compilation, parent: ?*Scope) !*FnDef {
pub fn create(comp: *Compilation, parent: *Scope) !*FnDef {
const self = try comp.gpa().create(FnDef{
.base = Scope{
.id = Id.FnDef,
@ -167,7 +240,7 @@ pub const Scope = struct {
.fn_val = undefined,
});
if (parent) |p| p.ref();
parent.ref();
return self;
}
@ -181,7 +254,7 @@ pub const Scope = struct {
base: Scope,
/// Creates a CompTime scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope) !*CompTime {
pub fn create(comp: *Compilation, parent: *Scope) !*CompTime {
const self = try comp.gpa().create(CompTime{
.base = Scope{
.id = Id.CompTime,
@ -190,7 +263,7 @@ pub const Scope = struct {
},
});
if (parent) |p| p.ref();
parent.ref();
return self;
}
@ -212,7 +285,7 @@ pub const Scope = struct {
/// Creates a Defer scope with 1 reference
pub fn create(
comp: *Compilation,
parent: ?*Scope,
parent: *Scope,
kind: Kind,
defer_expr_scope: *DeferExpr,
) !*Defer {
@ -229,7 +302,7 @@ pub const Scope = struct {
defer_expr_scope.base.ref();
if (parent) |p| p.ref();
parent.ref();
return self;
}
@ -242,9 +315,10 @@ pub const Scope = struct {
pub const DeferExpr = struct {
base: Scope,
expr_node: *ast.Node,
reported_err: bool,
/// Creates a DeferExpr scope with 1 reference
pub fn create(comp: *Compilation, parent: ?*Scope, expr_node: *ast.Node) !*DeferExpr {
pub fn create(comp: *Compilation, parent: *Scope, expr_node: *ast.Node) !*DeferExpr {
const self = try comp.gpa().create(DeferExpr{
.base = Scope{
.id = Id.DeferExpr,
@ -252,10 +326,11 @@ pub const Scope = struct {
.ref_count = 1,
},
.expr_node = expr_node,
.reported_err = false,
});
errdefer comp.gpa().destroy(self);
if (parent) |p| p.ref();
parent.ref();
return self;
}

View File

@ -1,6 +1,13 @@
const std = @import("std");
const builtin = @import("builtin");
const llvm = @import("llvm.zig");
const CInt = @import("c_int.zig").CInt;
pub const FloatAbi = enum {
Hard,
Soft,
SoftFp,
};
pub const Target = union(enum) {
Native,
@ -13,7 +20,7 @@ pub const Target = union(enum) {
object_format: builtin.ObjectFormat,
};
pub fn oFileExt(self: Target) []const u8 {
pub fn objFileExt(self: Target) []const u8 {
return switch (self.getObjectFormat()) {
builtin.ObjectFormat.coff => ".obj",
else => ".o",
@ -27,6 +34,13 @@ pub const Target = union(enum) {
};
}
pub fn libFileExt(self: Target, is_static: bool) []const u8 {
return switch (self.getOs()) {
builtin.Os.windows => if (is_static) ".lib" else ".dll",
else => if (is_static) ".a" else ".so",
};
}
pub fn getOs(self: Target) builtin.Os {
return switch (self) {
Target.Native => builtin.os,
@ -76,6 +90,56 @@ pub const Target = union(enum) {
};
}
/// TODO expose the arch and subarch separately
pub fn isArmOrThumb(self: Target) bool {
return switch (self.getArch()) {
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
=> true,
else => false,
};
}
pub fn initializeAll() void {
llvm.InitializeAllTargets();
llvm.InitializeAllTargetInfos();
@ -106,6 +170,257 @@ pub const Target = union(enum) {
return result;
}
pub fn is64bit(self: Target) bool {
return self.getArchPtrBitWidth() == 64;
}
pub fn getArchPtrBitWidth(self: Target) u32 {
switch (self.getArch()) {
builtin.Arch.avr,
builtin.Arch.msp430,
=> return 16,
builtin.Arch.arc,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.hexagon,
builtin.Arch.le32,
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.nios2,
builtin.Arch.powerpc,
builtin.Arch.r600,
builtin.Arch.riscv32,
builtin.Arch.sparc,
builtin.Arch.sparcel,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.thumb,
builtin.Arch.thumbeb,
builtin.Arch.i386,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.amdil,
builtin.Arch.hsail,
builtin.Arch.spir,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.renderscript32,
=> return 32,
builtin.Arch.aarch64,
builtin.Arch.aarch64_be,
builtin.Arch.mips64,
builtin.Arch.mips64el,
builtin.Arch.powerpc64,
builtin.Arch.powerpc64le,
builtin.Arch.riscv64,
builtin.Arch.x86_64,
builtin.Arch.nvptx64,
builtin.Arch.le64,
builtin.Arch.amdil64,
builtin.Arch.hsail64,
builtin.Arch.spir64,
builtin.Arch.wasm64,
builtin.Arch.renderscript64,
builtin.Arch.amdgcn,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.sparcv9,
builtin.Arch.s390x,
=> return 64,
}
}
pub fn getFloatAbi(self: Target) FloatAbi {
return switch (self.getEnviron()) {
builtin.Environ.gnueabihf,
builtin.Environ.eabihf,
builtin.Environ.musleabihf,
=> FloatAbi.Hard,
else => FloatAbi.Soft,
};
}
pub fn getDynamicLinkerPath(self: Target) ?[]const u8 {
const env = self.getEnviron();
const arch = self.getArch();
switch (env) {
builtin.Environ.android => {
if (self.is64bit()) {
return "/system/bin/linker64";
} else {
return "/system/bin/linker";
}
},
builtin.Environ.gnux32 => {
if (arch == builtin.Arch.x86_64) {
return "/libx32/ld-linux-x32.so.2";
}
},
builtin.Environ.musl,
builtin.Environ.musleabi,
builtin.Environ.musleabihf,
=> {
if (arch == builtin.Arch.x86_64) {
return "/lib/ld-musl-x86_64.so.1";
}
},
else => {},
}
switch (arch) {
builtin.Arch.i386,
builtin.Arch.sparc,
builtin.Arch.sparcel,
=> return "/lib/ld-linux.so.2",
builtin.Arch.aarch64 => return "/lib/ld-linux-aarch64.so.1",
builtin.Arch.aarch64_be => return "/lib/ld-linux-aarch64_be.so.1",
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
builtin.Arch.thumb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.armebv8_3a,
builtin.Arch.armebv8_2a,
builtin.Arch.armebv8_1a,
builtin.Arch.armebv8,
builtin.Arch.armebv8r,
builtin.Arch.armebv8m_baseline,
builtin.Arch.armebv8m_mainline,
builtin.Arch.armebv7,
builtin.Arch.armebv7em,
builtin.Arch.armebv7m,
builtin.Arch.armebv7s,
builtin.Arch.armebv7k,
builtin.Arch.armebv7ve,
builtin.Arch.armebv6,
builtin.Arch.armebv6m,
builtin.Arch.armebv6k,
builtin.Arch.armebv6t2,
builtin.Arch.armebv5,
builtin.Arch.armebv5te,
builtin.Arch.armebv4t,
builtin.Arch.thumbeb,
=> return switch (self.getFloatAbi()) {
FloatAbi.Hard => return "/lib/ld-linux-armhf.so.3",
else => return "/lib/ld-linux.so.3",
},
builtin.Arch.mips,
builtin.Arch.mipsel,
builtin.Arch.mips64,
builtin.Arch.mips64el,
=> return null,
builtin.Arch.powerpc => return "/lib/ld.so.1",
builtin.Arch.powerpc64 => return "/lib64/ld64.so.2",
builtin.Arch.powerpc64le => return "/lib64/ld64.so.2",
builtin.Arch.s390x => return "/lib64/ld64.so.1",
builtin.Arch.sparcv9 => return "/lib64/ld-linux.so.2",
builtin.Arch.x86_64 => return "/lib64/ld-linux-x86-64.so.2",
builtin.Arch.arc,
builtin.Arch.avr,
builtin.Arch.bpfel,
builtin.Arch.bpfeb,
builtin.Arch.hexagon,
builtin.Arch.msp430,
builtin.Arch.nios2,
builtin.Arch.r600,
builtin.Arch.amdgcn,
builtin.Arch.riscv32,
builtin.Arch.riscv64,
builtin.Arch.tce,
builtin.Arch.tcele,
builtin.Arch.xcore,
builtin.Arch.nvptx,
builtin.Arch.nvptx64,
builtin.Arch.le32,
builtin.Arch.le64,
builtin.Arch.amdil,
builtin.Arch.amdil64,
builtin.Arch.hsail,
builtin.Arch.hsail64,
builtin.Arch.spir,
builtin.Arch.spir64,
builtin.Arch.kalimbav3,
builtin.Arch.kalimbav4,
builtin.Arch.kalimbav5,
builtin.Arch.shave,
builtin.Arch.lanai,
builtin.Arch.wasm32,
builtin.Arch.wasm64,
builtin.Arch.renderscript32,
builtin.Arch.renderscript64,
=> return null,
}
}
pub fn llvmTargetFromTriple(triple: std.Buffer) !llvm.TargetRef {
var result: llvm.TargetRef = undefined;
var err_msg: [*]u8 = undefined;
@ -115,4 +430,133 @@ pub const Target = union(enum) {
}
return result;
}
pub fn cIntTypeSizeInBits(self: Target, id: CInt.Id) u32 {
const arch = self.getArch();
switch (self.getOs()) {
builtin.Os.freestanding => switch (self.getArch()) {
builtin.Arch.msp430 => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
CInt.Id.Int,
CInt.Id.UInt,
=> return 16,
CInt.Id.Long,
CInt.Id.ULong,
=> return 32,
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
else => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
=> return self.getArchPtrBitWidth(),
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
},
builtin.Os.linux,
builtin.Os.macosx,
builtin.Os.openbsd,
builtin.Os.zen,
=> switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
=> return self.getArchPtrBitWidth(),
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
builtin.Os.windows => switch (id) {
CInt.Id.Short,
CInt.Id.UShort,
=> return 16,
CInt.Id.Int,
CInt.Id.UInt,
=> return 32,
CInt.Id.Long,
CInt.Id.ULong,
CInt.Id.LongLong,
CInt.Id.ULongLong,
=> return 64,
},
builtin.Os.ananas,
builtin.Os.cloudabi,
builtin.Os.dragonfly,
builtin.Os.freebsd,
builtin.Os.fuchsia,
builtin.Os.ios,
builtin.Os.kfreebsd,
builtin.Os.lv2,
builtin.Os.netbsd,
builtin.Os.solaris,
builtin.Os.haiku,
builtin.Os.minix,
builtin.Os.rtems,
builtin.Os.nacl,
builtin.Os.cnk,
builtin.Os.aix,
builtin.Os.cuda,
builtin.Os.nvcl,
builtin.Os.amdhsa,
builtin.Os.ps4,
builtin.Os.elfiamcu,
builtin.Os.tvos,
builtin.Os.watchos,
builtin.Os.mesa3d,
builtin.Os.contiki,
builtin.Os.amdpal,
=> @panic("TODO specify the C integer type sizes for this OS"),
}
}
pub fn getDarwinArchString(self: Target) []const u8 {
const arch = self.getArch();
switch (arch) {
builtin.Arch.aarch64 => return "arm64",
builtin.Arch.thumb,
builtin.Arch.armv8_3a,
builtin.Arch.armv8_2a,
builtin.Arch.armv8_1a,
builtin.Arch.armv8,
builtin.Arch.armv8r,
builtin.Arch.armv8m_baseline,
builtin.Arch.armv8m_mainline,
builtin.Arch.armv7,
builtin.Arch.armv7em,
builtin.Arch.armv7m,
builtin.Arch.armv7s,
builtin.Arch.armv7k,
builtin.Arch.armv7ve,
builtin.Arch.armv6,
builtin.Arch.armv6m,
builtin.Arch.armv6k,
builtin.Arch.armv6t2,
builtin.Arch.armv5,
builtin.Arch.armv5te,
builtin.Arch.armv4t,
=> return "arm",
builtin.Arch.powerpc => return "ppc",
builtin.Arch.powerpc64 => return "ppc64",
builtin.Arch.powerpc64le => return "ppc64le",
else => return @tagName(arch),
}
}
};

View File

@ -8,12 +8,14 @@ const assertOrPanic = std.debug.assertOrPanic;
const errmsg = @import("errmsg.zig");
const EventLoopLocal = @import("compilation.zig").EventLoopLocal;
test "compile errors" {
var ctx: TestContext = undefined;
var ctx: TestContext = undefined;
test "stage2" {
try ctx.init();
defer ctx.deinit();
try @import("../test/stage2/compile_errors.zig").addCases(&ctx);
try @import("../test/stage2/compare_output.zig").addCases(&ctx);
try ctx.run();
}
@ -25,7 +27,6 @@ pub const TestContext = struct {
loop: std.event.Loop,
event_loop_local: EventLoopLocal,
zig_lib_dir: []u8,
zig_cache_dir: []u8,
file_index: std.atomic.Int(usize),
group: std.event.Group(error!void),
any_err: error!void,
@ -38,7 +39,6 @@ pub const TestContext = struct {
.loop = undefined,
.event_loop_local = undefined,
.zig_lib_dir = undefined,
.zig_cache_dir = undefined,
.group = undefined,
.file_index = std.atomic.Int(usize).init(0),
};
@ -55,16 +55,12 @@ pub const TestContext = struct {
self.zig_lib_dir = try introspect.resolveZigLibDir(allocator);
errdefer allocator.free(self.zig_lib_dir);
self.zig_cache_dir = try introspect.resolveZigCacheDir(allocator);
errdefer allocator.free(self.zig_cache_dir);
try std.os.makePath(allocator, tmp_dir_name);
errdefer std.os.deleteTree(allocator, tmp_dir_name) catch {};
}
fn deinit(self: *TestContext) void {
std.os.deleteTree(allocator, tmp_dir_name) catch {};
allocator.free(self.zig_cache_dir);
allocator.free(self.zig_lib_dir);
self.event_loop_local.deinit();
self.loop.deinit();
@ -109,7 +105,6 @@ pub const TestContext = struct {
builtin.Mode.Debug,
true, // is_static
self.zig_lib_dir,
self.zig_cache_dir,
);
errdefer comp.destroy();
@ -118,6 +113,84 @@ pub const TestContext = struct {
try self.group.call(getModuleEvent, comp, source, path, line, column, msg);
}
fn testCompareOutputLibC(
self: *TestContext,
source: []const u8,
expected_output: []const u8,
) !void {
var file_index_buf: [20]u8 = undefined;
const file_index = try std.fmt.bufPrint(file_index_buf[0..], "{}", self.file_index.incr());
const file1_path = try std.os.path.join(allocator, tmp_dir_name, file_index, file1);
const output_file = try std.fmt.allocPrint(allocator, "{}-out{}", file1_path, Target(Target.Native).exeFileExt());
if (std.os.path.dirname(file1_path)) |dirname| {
try std.os.makePath(allocator, dirname);
}
// TODO async I/O
try std.io.writeFile(allocator, file1_path, source);
var comp = try Compilation.create(
&self.event_loop_local,
"test",
file1_path,
Target.Native,
Compilation.Kind.Exe,
builtin.Mode.Debug,
false,
self.zig_lib_dir,
);
errdefer comp.destroy();
_ = try comp.addLinkLib("c", true);
comp.link_out_file = output_file;
try comp.build();
try self.group.call(getModuleEventSuccess, comp, output_file, expected_output);
}
async fn getModuleEventSuccess(
comp: *Compilation,
exe_file: []const u8,
expected_output: []const u8,
) !void {
// TODO this should not be necessary
const exe_file_2 = try std.mem.dupe(allocator, u8, exe_file);
defer comp.destroy();
const build_event = await (async comp.events.get() catch unreachable);
switch (build_event) {
Compilation.Event.Ok => {
const argv = []const []const u8{exe_file_2};
// TODO use event loop
const child = try std.os.ChildProcess.exec(allocator, argv, null, null, 1024 * 1024);
switch (child.term) {
std.os.ChildProcess.Term.Exited => |code| {
if (code != 0) {
return error.BadReturnCode;
}
},
else => {
return error.Crashed;
},
}
if (!mem.eql(u8, child.stdout, expected_output)) {
return error.OutputMismatch;
}
},
Compilation.Event.Error => |err| return err,
Compilation.Event.Fail => |msgs| {
var stderr = try std.io.getStdErr();
try stderr.write("build incorrectly failed:\n");
for (msgs) |msg| {
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
}
},
}
}
async fn getModuleEvent(
comp: *Compilation,
source: []const u8,
@ -139,10 +212,10 @@ pub const TestContext = struct {
Compilation.Event.Fail => |msgs| {
assertOrPanic(msgs.len != 0);
for (msgs) |msg| {
if (mem.endsWith(u8, msg.path, path) and mem.eql(u8, msg.text, text)) {
const first_token = msg.tree.tokens.at(msg.span.first);
const last_token = msg.tree.tokens.at(msg.span.first);
const start_loc = msg.tree.tokenLocationPtr(0, first_token);
if (mem.endsWith(u8, msg.getRealPath(), path) and mem.eql(u8, msg.text, text)) {
const first_token = msg.getTree().tokens.at(msg.span.first);
const last_token = msg.getTree().tokens.at(msg.span.first);
const start_loc = msg.getTree().tokenLocationPtr(0, first_token);
if (start_loc.line + 1 == line and start_loc.column + 1 == column) {
return;
}
@ -159,7 +232,8 @@ pub const TestContext = struct {
std.debug.warn("\n====found:========\n");
var stderr = try std.io.getStdErr();
for (msgs) |msg| {
try errmsg.printToFile(&stderr, msg, errmsg.Color.Auto);
defer msg.destroy();
try msg.printToFile(&stderr, errmsg.Color.Auto);
}
std.debug.warn("============\n");
return error.TestFailed;

View File

@ -4,11 +4,17 @@ const Scope = @import("scope.zig").Scope;
const Compilation = @import("compilation.zig").Compilation;
const Value = @import("value.zig").Value;
const llvm = @import("llvm.zig");
const ObjectFile = @import("codegen.zig").ObjectFile;
const event = std.event;
const Allocator = std.mem.Allocator;
const assert = std.debug.assert;
pub const Type = struct {
base: Value,
id: Id,
name: []const u8,
abi_alignment: AbiAlignment,
pub const AbiAlignment = event.Future(error{OutOfMemory}!u32);
pub const Id = builtin.TypeId;
@ -42,33 +48,37 @@ pub const Type = struct {
}
}
pub fn getLlvmType(base: *Type, ofile: *ObjectFile) (error{OutOfMemory}!llvm.TypeRef) {
pub fn getLlvmType(
base: *Type,
allocator: *Allocator,
llvm_context: llvm.ContextRef,
) (error{OutOfMemory}!llvm.TypeRef) {
switch (base.id) {
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(ofile),
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(ofile),
Id.Struct => return @fieldParentPtr(Struct, "base", base).getLlvmType(allocator, llvm_context),
Id.Fn => return @fieldParentPtr(Fn, "base", base).getLlvmType(allocator, llvm_context),
Id.Type => unreachable,
Id.Void => unreachable,
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(ofile),
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmType(allocator, llvm_context),
Id.NoReturn => unreachable,
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(ofile),
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(ofile),
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(ofile),
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(ofile),
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmType(allocator, llvm_context),
Id.Float => return @fieldParentPtr(Float, "base", base).getLlvmType(allocator, llvm_context),
Id.Pointer => return @fieldParentPtr(Pointer, "base", base).getLlvmType(allocator, llvm_context),
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmType(allocator, llvm_context),
Id.ComptimeFloat => unreachable,
Id.ComptimeInt => unreachable,
Id.Undefined => unreachable,
Id.Null => unreachable,
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(ofile),
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(ofile),
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(ofile),
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(ofile),
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(ofile),
Id.Optional => return @fieldParentPtr(Optional, "base", base).getLlvmType(allocator, llvm_context),
Id.ErrorUnion => return @fieldParentPtr(ErrorUnion, "base", base).getLlvmType(allocator, llvm_context),
Id.ErrorSet => return @fieldParentPtr(ErrorSet, "base", base).getLlvmType(allocator, llvm_context),
Id.Enum => return @fieldParentPtr(Enum, "base", base).getLlvmType(allocator, llvm_context),
Id.Union => return @fieldParentPtr(Union, "base", base).getLlvmType(allocator, llvm_context),
Id.Namespace => unreachable,
Id.Block => unreachable,
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(ofile),
Id.BoundFn => return @fieldParentPtr(BoundFn, "base", base).getLlvmType(allocator, llvm_context),
Id.ArgTuple => unreachable,
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(ofile),
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(ofile),
Id.Opaque => return @fieldParentPtr(Opaque, "base", base).getLlvmType(allocator, llvm_context),
Id.Promise => return @fieldParentPtr(Promise, "base", base).getLlvmType(allocator, llvm_context),
}
}
@ -151,8 +161,49 @@ pub const Type = struct {
std.debug.warn("{}", @tagName(base.id));
}
pub fn getAbiAlignment(base: *Type, comp: *Compilation) u32 {
@panic("TODO getAbiAlignment");
fn init(base: *Type, comp: *Compilation, id: Id, name: []const u8) void {
base.* = Type{
.base = Value{
.id = Value.Id.Type,
.typ = &MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = id,
.name = name,
.abi_alignment = AbiAlignment.init(comp.loop),
};
}
/// If you happen to have an llvm context handy, use getAbiAlignmentInContext instead.
/// Otherwise, this one will grab one from the pool and then release it.
pub async fn getAbiAlignment(base: *Type, comp: *Compilation) !u32 {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
{
const held = try comp.event_loop_local.getAnyLlvmContext();
defer held.release(comp.event_loop_local);
const llvm_context = held.node.data;
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
}
base.abi_alignment.resolve();
return base.abi_alignment.data;
}
/// If you have an llvm conext handy, you can use it here.
pub async fn getAbiAlignmentInContext(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
if (await (async base.abi_alignment.start() catch unreachable)) |ptr| return ptr.*;
base.abi_alignment.data = await (async base.resolveAbiAlignment(comp, llvm_context) catch unreachable);
base.abi_alignment.resolve();
return base.abi_alignment.data;
}
/// Lower level function that does the work. See getAbiAlignment.
async fn resolveAbiAlignment(base: *Type, comp: *Compilation, llvm_context: llvm.ContextRef) !u32 {
const llvm_type = try base.getLlvmType(comp.gpa(), llvm_context);
return @intCast(u32, llvm.ABIAlignmentOfType(comp.target_data_ref, llvm_type));
}
pub const Struct = struct {
@ -163,7 +214,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Struct, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Struct, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -176,28 +227,23 @@ pub const Type = struct {
pub const Param = struct {
is_noalias: bool,
typeof: *Type,
typ: *Type,
};
pub fn create(comp: *Compilation, return_type: *Type, params: []Param, is_var_args: bool) !*Fn {
const result = try comp.gpa().create(Fn{
.base = Type{
.base = Value{
.id = Value.Id.Type,
.typeof = &MetaType.get(comp).base,
.ref_count = std.atomic.Int(usize).init(1),
},
.id = builtin.TypeId.Fn,
},
.base = undefined,
.return_type = return_type,
.params = params,
.is_var_args = is_var_args,
});
errdefer comp.gpa().destroy(result);
result.base.init(comp, Id.Fn, "TODO fn type name");
result.return_type.base.ref();
for (result.params) |param| {
param.typeof.base.ref();
param.typ.base.ref();
}
return result;
}
@ -205,20 +251,20 @@ pub const Type = struct {
pub fn destroy(self: *Fn, comp: *Compilation) void {
self.return_type.base.deref(comp);
for (self.params) |param| {
param.typeof.base.deref(comp);
param.typ.base.deref(comp);
}
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Fn, ofile: *ObjectFile) !llvm.TypeRef {
pub fn getLlvmType(self: *Fn, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
const llvm_return_type = switch (self.return_type.id) {
Type.Id.Void => llvm.VoidTypeInContext(ofile.context) orelse return error.OutOfMemory,
else => try self.return_type.getLlvmType(ofile),
Type.Id.Void => llvm.VoidTypeInContext(llvm_context) orelse return error.OutOfMemory,
else => try self.return_type.getLlvmType(allocator, llvm_context),
};
const llvm_param_types = try ofile.gpa().alloc(llvm.TypeRef, self.params.len);
defer ofile.gpa().free(llvm_param_types);
const llvm_param_types = try allocator.alloc(llvm.TypeRef, self.params.len);
defer allocator.free(llvm_param_types);
for (llvm_param_types) |*llvm_param_type, i| {
llvm_param_type.* = try self.params[i].typeof.getLlvmType(ofile);
llvm_param_type.* = try self.params[i].typ.getLlvmType(allocator, llvm_context);
}
return llvm.FunctionType(
@ -272,7 +318,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Bool, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Bool, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -293,13 +339,83 @@ pub const Type = struct {
pub const Int = struct {
base: Type,
key: Key,
garbage_node: std.atomic.Stack(*Int).Node,
pub const Key = struct {
bit_count: u32,
is_signed: bool,
pub fn hash(self: *const Key) u32 {
const rands = [2]u32{ 0xa4ba6498, 0x75fc5af7 };
return rands[@boolToInt(self.is_signed)] *% self.bit_count;
}
pub fn eql(self: *const Key, other: *const Key) bool {
return self.bit_count == other.bit_count and self.is_signed == other.is_signed;
}
};
pub fn get_u8(comp: *Compilation) *Int {
comp.u8_type.base.base.ref();
return comp.u8_type;
}
pub async fn get(comp: *Compilation, key: Key) !*Int {
{
const held = await (async comp.int_type_table.acquire() catch unreachable);
defer held.release();
if (held.value.get(&key)) |entry| {
entry.value.base.base.ref();
return entry.value;
}
}
const self = try comp.gpa().create(Int{
.base = undefined,
.key = key,
.garbage_node = undefined,
});
errdefer comp.gpa().destroy(self);
const u_or_i = "ui"[@boolToInt(key.is_signed)];
const name = try std.fmt.allocPrint(comp.gpa(), "{c}{}", u_or_i, key.bit_count);
errdefer comp.gpa().free(name);
self.base.init(comp, Id.Int, name);
{
const held = await (async comp.int_type_table.acquire() catch unreachable);
defer held.release();
_ = try held.value.put(&self.key, self);
}
return self;
}
pub fn destroy(self: *Int, comp: *Compilation) void {
self.garbage_node = std.atomic.Stack(*Int).Node{
.data = self,
.next = undefined,
};
comp.registerGarbage(Int, &self.garbage_node);
}
pub async fn gcDestroy(self: *Int, comp: *Compilation) void {
{
const held = await (async comp.int_type_table.acquire() catch unreachable);
defer held.release();
_ = held.value.remove(&self.key).?;
}
// we allocated the name
comp.gpa().free(self.base.name);
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Int, ofile: *ObjectFile) llvm.TypeRef {
@panic("TODO");
pub fn getLlvmType(self: *Int, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
return llvm.IntTypeInContext(llvm_context, self.key.bit_count) orelse return error.OutOfMemory;
}
};
@ -310,56 +426,236 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Float, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Float, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
pub const Pointer = struct {
base: Type,
mut: Mut,
vol: Vol,
size: Size,
alignment: u32,
key: Key,
garbage_node: std.atomic.Stack(*Pointer).Node,
pub const Key = struct {
child_type: *Type,
mut: Mut,
vol: Vol,
size: Size,
alignment: Align,
pub fn hash(self: *const Key) u32 {
const align_hash = switch (self.alignment) {
Align.Abi => 0xf201c090,
Align.Override => |x| x,
};
return hash_usize(@ptrToInt(self.child_type)) *%
hash_enum(self.mut) *%
hash_enum(self.vol) *%
hash_enum(self.size) *%
align_hash;
}
pub fn eql(self: *const Key, other: *const Key) bool {
if (self.child_type != other.child_type or
self.mut != other.mut or
self.vol != other.vol or
self.size != other.size or
@TagType(Align)(self.alignment) != @TagType(Align)(other.alignment))
{
return false;
}
switch (self.alignment) {
Align.Abi => return true,
Align.Override => |x| return x == other.alignment.Override,
}
}
};
pub const Mut = enum {
Mut,
Const,
};
pub const Vol = enum {
Non,
Volatile,
};
pub const Align = union(enum) {
Abi,
Override: u32,
};
pub const Size = builtin.TypeInfo.Pointer.Size;
pub fn destroy(self: *Pointer, comp: *Compilation) void {
self.garbage_node = std.atomic.Stack(*Pointer).Node{
.data = self,
.next = undefined,
};
comp.registerGarbage(Pointer, &self.garbage_node);
}
pub async fn gcDestroy(self: *Pointer, comp: *Compilation) void {
{
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
defer held.release();
_ = held.value.remove(&self.key).?;
}
self.key.child_type.base.deref(comp);
comp.gpa().destroy(self);
}
pub fn get(
comp: *Compilation,
elem_type: *Type,
mut: Mut,
vol: Vol,
size: Size,
alignment: u32,
) *Pointer {
@panic("TODO get pointer");
pub async fn getAlignAsInt(self: *Pointer, comp: *Compilation) u32 {
switch (self.key.alignment) {
Align.Abi => return await (async self.key.child_type.getAbiAlignment(comp) catch unreachable),
Align.Override => |alignment| return alignment,
}
}
pub fn getLlvmType(self: *Pointer, ofile: *ObjectFile) llvm.TypeRef {
@panic("TODO");
pub async fn get(
comp: *Compilation,
key: Key,
) !*Pointer {
var normal_key = key;
switch (key.alignment) {
Align.Abi => {},
Align.Override => |alignment| {
const abi_align = try await (async key.child_type.getAbiAlignment(comp) catch unreachable);
if (abi_align == alignment) {
normal_key.alignment = Align.Abi;
}
},
}
{
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
defer held.release();
if (held.value.get(&normal_key)) |entry| {
entry.value.base.base.ref();
return entry.value;
}
}
const self = try comp.gpa().create(Pointer{
.base = undefined,
.key = normal_key,
.garbage_node = undefined,
});
errdefer comp.gpa().destroy(self);
const size_str = switch (self.key.size) {
Size.One => "*",
Size.Many => "[*]",
Size.Slice => "[]",
};
const mut_str = switch (self.key.mut) {
Mut.Const => "const ",
Mut.Mut => "",
};
const vol_str = switch (self.key.vol) {
Vol.Volatile => "volatile ",
Vol.Non => "",
};
const name = switch (self.key.alignment) {
Align.Abi => try std.fmt.allocPrint(
comp.gpa(),
"{}{}{}{}",
size_str,
mut_str,
vol_str,
self.key.child_type.name,
),
Align.Override => |alignment| try std.fmt.allocPrint(
comp.gpa(),
"{}align<{}> {}{}{}",
size_str,
alignment,
mut_str,
vol_str,
self.key.child_type.name,
),
};
errdefer comp.gpa().free(name);
self.base.init(comp, Id.Pointer, name);
{
const held = await (async comp.ptr_type_table.acquire() catch unreachable);
defer held.release();
_ = try held.value.put(&self.key, self);
}
return self;
}
pub fn getLlvmType(self: *Pointer, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
const elem_llvm_type = try self.key.child_type.getLlvmType(allocator, llvm_context);
return llvm.PointerType(elem_llvm_type, 0) orelse return error.OutOfMemory;
}
};
pub const Array = struct {
base: Type,
key: Key,
garbage_node: std.atomic.Stack(*Array).Node,
pub const Key = struct {
elem_type: *Type,
len: usize,
pub fn hash(self: *const Key) u32 {
return hash_usize(@ptrToInt(self.elem_type)) *% hash_usize(self.len);
}
pub fn eql(self: *const Key, other: *const Key) bool {
return self.elem_type == other.elem_type and self.len == other.len;
}
};
pub fn destroy(self: *Array, comp: *Compilation) void {
self.key.elem_type.base.deref(comp);
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Array, ofile: *ObjectFile) llvm.TypeRef {
@panic("TODO");
pub async fn get(comp: *Compilation, key: Key) !*Array {
key.elem_type.base.ref();
errdefer key.elem_type.base.deref(comp);
{
const held = await (async comp.array_type_table.acquire() catch unreachable);
defer held.release();
if (held.value.get(&key)) |entry| {
entry.value.base.base.ref();
return entry.value;
}
}
const self = try comp.gpa().create(Array{
.base = undefined,
.key = key,
.garbage_node = undefined,
});
errdefer comp.gpa().destroy(self);
const name = try std.fmt.allocPrint(comp.gpa(), "[{}]{}", key.len, key.elem_type.name);
errdefer comp.gpa().free(name);
self.base.init(comp, Id.Array, name);
{
const held = await (async comp.array_type_table.acquire() catch unreachable);
defer held.release();
_ = try held.value.put(&self.key, self);
}
return self;
}
pub fn getLlvmType(self: *Array, allocator: *Allocator, llvm_context: llvm.ContextRef) !llvm.TypeRef {
const elem_llvm_type = try self.key.elem_type.getLlvmType(allocator, llvm_context);
return llvm.ArrayType(elem_llvm_type, @intCast(c_uint, self.key.len)) orelse return error.OutOfMemory;
}
};
@ -374,6 +670,12 @@ pub const Type = struct {
pub const ComptimeInt = struct {
base: Type,
/// Adds 1 reference to the resulting type
pub fn get(comp: *Compilation) *ComptimeInt {
comp.comptime_int_type.base.base.ref();
return comp.comptime_int_type;
}
pub fn destroy(self: *ComptimeInt, comp: *Compilation) void {
comp.gpa().destroy(self);
}
@ -402,7 +704,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Optional, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Optional, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -414,7 +716,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *ErrorUnion, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *ErrorUnion, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -426,7 +728,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *ErrorSet, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *ErrorSet, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -438,7 +740,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Enum, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Enum, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -450,7 +752,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Union, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Union, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -478,7 +780,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *BoundFn, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *BoundFn, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -498,7 +800,7 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Opaque, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Opaque, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
@ -510,8 +812,33 @@ pub const Type = struct {
comp.gpa().destroy(self);
}
pub fn getLlvmType(self: *Promise, ofile: *ObjectFile) llvm.TypeRef {
pub fn getLlvmType(self: *Promise, allocator: *Allocator, llvm_context: llvm.ContextRef) llvm.TypeRef {
@panic("TODO");
}
};
};
fn hash_usize(x: usize) u32 {
return switch (@sizeOf(usize)) {
4 => x,
8 => @truncate(u32, x *% 0xad44ee2d8e3fc13d),
else => @compileError("implement this hash function"),
};
}
fn hash_enum(x: var) u32 {
const rands = []u32{
0x85ebf64f,
0x3fcb3211,
0x240a4e8e,
0x40bb0e3c,
0x78be45af,
0x1ca98e37,
0xec56053a,
0x906adc48,
0xd4fe9763,
0x54c80dac,
};
comptime assert(@memberCount(@typeOf(x)) < rands.len);
return rands[@enumToInt(x)];
}

View File

@ -5,12 +5,13 @@ const Compilation = @import("compilation.zig").Compilation;
const ObjectFile = @import("codegen.zig").ObjectFile;
const llvm = @import("llvm.zig");
const Buffer = std.Buffer;
const assert = std.debug.assert;
/// Values are ref-counted, heap-allocated, and copy-on-write
/// If there is only 1 ref then write need not copy
pub const Value = struct {
id: Id,
typeof: *Type,
typ: *Type,
ref_count: std.atomic.Int(usize),
/// Thread-safe
@ -21,23 +22,37 @@ pub const Value = struct {
/// Thread-safe
pub fn deref(base: *Value, comp: *Compilation) void {
if (base.ref_count.decr() == 1) {
base.typeof.base.deref(comp);
base.typ.base.deref(comp);
switch (base.id) {
Id.Type => @fieldParentPtr(Type, "base", base).destroy(comp),
Id.Fn => @fieldParentPtr(Fn, "base", base).destroy(comp),
Id.FnProto => @fieldParentPtr(FnProto, "base", base).destroy(comp),
Id.Void => @fieldParentPtr(Void, "base", base).destroy(comp),
Id.Bool => @fieldParentPtr(Bool, "base", base).destroy(comp),
Id.NoReturn => @fieldParentPtr(NoReturn, "base", base).destroy(comp),
Id.Ptr => @fieldParentPtr(Ptr, "base", base).destroy(comp),
Id.Int => @fieldParentPtr(Int, "base", base).destroy(comp),
Id.Array => @fieldParentPtr(Array, "base", base).destroy(comp),
}
}
}
pub fn setType(base: *Value, new_type: *Type, comp: *Compilation) void {
base.typ.base.deref(comp);
new_type.base.ref();
base.typ = new_type;
}
pub fn getRef(base: *Value) *Value {
base.ref();
return base;
}
pub fn cast(base: *Value, comptime T: type) ?*T {
if (base.id != @field(Id, @typeName(T))) return null;
return @fieldParentPtr(T, "base", base);
}
pub fn dump(base: *const Value) void {
std.debug.warn("{}", @tagName(base.id));
}
@ -46,24 +61,111 @@ pub const Value = struct {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => @panic("TODO"),
Id.FnProto => return @fieldParentPtr(FnProto, "base", base).getLlvmConst(ofile),
Id.Void => return null,
Id.Bool => return @fieldParentPtr(Bool, "base", base).getLlvmConst(ofile),
Id.NoReturn => unreachable,
Id.Ptr => @panic("TODO"),
Id.Ptr => return @fieldParentPtr(Ptr, "base", base).getLlvmConst(ofile),
Id.Int => return @fieldParentPtr(Int, "base", base).getLlvmConst(ofile),
Id.Array => return @fieldParentPtr(Array, "base", base).getLlvmConst(ofile),
}
}
pub fn derefAndCopy(self: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
if (self.ref_count.get() == 1) {
// ( ͡° ͜ʖ ͡°)
return self;
}
assert(self.ref_count.decr() != 1);
return self.copy(comp);
}
pub fn copy(base: *Value, comp: *Compilation) (error{OutOfMemory}!*Value) {
switch (base.id) {
Id.Type => unreachable,
Id.Fn => unreachable,
Id.FnProto => unreachable,
Id.Void => unreachable,
Id.Bool => unreachable,
Id.NoReturn => unreachable,
Id.Ptr => unreachable,
Id.Array => unreachable,
Id.Int => return &(try @fieldParentPtr(Int, "base", base).copy(comp)).base,
}
}
pub const Parent = union(enum) {
None,
BaseStruct: BaseStruct,
BaseArray: BaseArray,
BaseUnion: *Value,
BaseScalar: *Value,
pub const BaseStruct = struct {
val: *Value,
field_index: usize,
};
pub const BaseArray = struct {
val: *Value,
elem_index: usize,
};
};
pub const Id = enum {
Type,
Fn,
Void,
Bool,
NoReturn,
Array,
Ptr,
Int,
FnProto,
};
pub const Type = @import("type.zig").Type;
pub const FnProto = struct {
base: Value,
/// The main external name that is used in the .o file.
/// TODO https://github.com/ziglang/zig/issues/265
symbol_name: Buffer,
pub fn create(comp: *Compilation, fn_type: *Type.Fn, symbol_name: Buffer) !*FnProto {
const self = try comp.gpa().create(FnProto{
.base = Value{
.id = Value.Id.FnProto,
.typ = &fn_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.symbol_name = symbol_name,
});
fn_type.base.base.ref();
return self;
}
pub fn destroy(self: *FnProto, comp: *Compilation) void {
self.symbol_name.deinit();
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *FnProto, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_fn_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
const llvm_fn = llvm.AddFunction(
ofile.module,
self.symbol_name.ptr(),
llvm_fn_type,
) orelse return error.OutOfMemory;
// TODO port more logic from codegen.cpp:fn_llvm_value
return llvm_fn;
}
};
pub const Fn = struct {
base: Value,
@ -98,7 +200,7 @@ pub const Value = struct {
const self = try comp.gpa().create(Fn{
.base = Value{
.id = Value.Id.Fn,
.typeof = &fn_type.base,
.typ = &fn_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.fndef_scope = fndef_scope,
@ -187,6 +289,8 @@ pub const Value = struct {
pub const Ptr = struct {
base: Value,
special: Special,
mut: Mut,
pub const Mut = enum {
CompTimeConst,
@ -194,8 +298,268 @@ pub const Value = struct {
RunTime,
};
pub const Special = union(enum) {
Scalar: *Value,
BaseArray: BaseArray,
BaseStruct: BaseStruct,
HardCodedAddr: u64,
Discard,
};
pub const BaseArray = struct {
val: *Value,
elem_index: usize,
};
pub const BaseStruct = struct {
val: *Value,
field_index: usize,
};
pub async fn createArrayElemPtr(
comp: *Compilation,
array_val: *Array,
mut: Type.Pointer.Mut,
size: Type.Pointer.Size,
elem_index: usize,
) !*Ptr {
array_val.base.ref();
errdefer array_val.base.deref(comp);
const elem_type = array_val.base.typ.cast(Type.Array).?.key.elem_type;
const ptr_type = try await (async Type.Pointer.get(comp, Type.Pointer.Key{
.child_type = elem_type,
.mut = mut,
.vol = Type.Pointer.Vol.Non,
.size = size,
.alignment = Type.Pointer.Align.Abi,
}) catch unreachable);
var ptr_type_consumed = false;
errdefer if (!ptr_type_consumed) ptr_type.base.base.deref(comp);
const self = try comp.gpa().create(Value.Ptr{
.base = Value{
.id = Value.Id.Ptr,
.typ = &ptr_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.special = Special{
.BaseArray = BaseArray{
.val = &array_val.base,
.elem_index = 0,
},
},
.mut = Mut.CompTimeConst,
});
ptr_type_consumed = true;
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Ptr, comp: *Compilation) void {
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Ptr, ofile: *ObjectFile) !?llvm.ValueRef {
const llvm_type = self.base.typ.getLlvmType(ofile.arena, ofile.context);
// TODO carefully port the logic from codegen.cpp:gen_const_val_ptr
switch (self.special) {
Special.Scalar => |scalar| @panic("TODO"),
Special.BaseArray => |base_array| {
// TODO put this in one .o file only, and after that, generate extern references to it
const array_llvm_value = (try base_array.val.getLlvmConst(ofile)).?;
const ptr_bit_count = ofile.comp.target_ptr_bits;
const usize_llvm_type = llvm.IntTypeInContext(ofile.context, ptr_bit_count) orelse return error.OutOfMemory;
const indices = []llvm.ValueRef{
llvm.ConstNull(usize_llvm_type) orelse return error.OutOfMemory,
llvm.ConstInt(usize_llvm_type, base_array.elem_index, 0) orelse return error.OutOfMemory,
};
return llvm.ConstInBoundsGEP(
array_llvm_value,
&indices,
@intCast(c_uint, indices.len),
) orelse return error.OutOfMemory;
},
Special.BaseStruct => |base_struct| @panic("TODO"),
Special.HardCodedAddr => |addr| @panic("TODO"),
Special.Discard => unreachable,
}
}
};
pub const Array = struct {
base: Value,
special: Special,
pub const Special = union(enum) {
Undefined,
OwnedBuffer: []u8,
Explicit: Data,
};
pub const Data = struct {
parent: Parent,
elements: []*Value,
};
/// Takes ownership of buffer
pub async fn createOwnedBuffer(comp: *Compilation, buffer: []u8) !*Array {
const u8_type = Type.Int.get_u8(comp);
defer u8_type.base.base.deref(comp);
const array_type = try await (async Type.Array.get(comp, Type.Array.Key{
.elem_type = &u8_type.base,
.len = buffer.len,
}) catch unreachable);
errdefer array_type.base.base.deref(comp);
const self = try comp.gpa().create(Value.Array{
.base = Value{
.id = Value.Id.Array,
.typ = &array_type.base,
.ref_count = std.atomic.Int(usize).init(1),
},
.special = Special{ .OwnedBuffer = buffer },
});
errdefer comp.gpa().destroy(self);
return self;
}
pub fn destroy(self: *Array, comp: *Compilation) void {
switch (self.special) {
Special.Undefined => {},
Special.OwnedBuffer => |buf| {
comp.gpa().free(buf);
},
Special.Explicit => {},
}
comp.gpa().destroy(self);
}
pub fn getLlvmConst(self: *Array, ofile: *ObjectFile) !?llvm.ValueRef {
switch (self.special) {
Special.Undefined => {
const llvm_type = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
return llvm.GetUndef(llvm_type);
},
Special.OwnedBuffer => |buf| {
const dont_null_terminate = 1;
const llvm_str_init = llvm.ConstStringInContext(
ofile.context,
buf.ptr,
@intCast(c_uint, buf.len),
dont_null_terminate,
) orelse return error.OutOfMemory;
const str_init_type = llvm.TypeOf(llvm_str_init);
const global = llvm.AddGlobal(ofile.module, str_init_type, c"") orelse return error.OutOfMemory;
llvm.SetInitializer(global, llvm_str_init);
llvm.SetLinkage(global, llvm.PrivateLinkage);
llvm.SetGlobalConstant(global, 1);
llvm.SetUnnamedAddr(global, 1);
llvm.SetAlignment(global, llvm.ABIAlignmentOfType(ofile.comp.target_data_ref, str_init_type));
return global;
},
Special.Explicit => @panic("TODO"),
}
//{
// uint64_t len = type_entry->data.array.len;
// if (const_val->data.x_array.special == ConstArraySpecialUndef) {
// return LLVMGetUndef(type_entry->type_ref);
// }
// LLVMValueRef *values = allocate<LLVMValueRef>(len);
// LLVMTypeRef element_type_ref = type_entry->data.array.child_type->type_ref;
// bool make_unnamed_struct = false;
// for (uint64_t i = 0; i < len; i += 1) {
// ConstExprValue *elem_value = &const_val->data.x_array.s_none.elements[i];
// LLVMValueRef val = gen_const_val(g, elem_value, "");
// values[i] = val;
// make_unnamed_struct = make_unnamed_struct || is_llvm_value_unnamed_type(elem_value->type, val);
// }
// if (make_unnamed_struct) {
// return LLVMConstStruct(values, len, true);
// } else {
// return LLVMConstArray(element_type_ref, values, (unsigned)len);
// }
//}
}
};
pub const Int = struct {
base: Value,
big_int: std.math.big.Int,
pub fn createFromString(comp: *Compilation, typ: *Type, base: u8, value: []const u8) !*Int {
const self = try comp.gpa().create(Value.Int{
.base = Value{
.id = Value.Id.Int,
.typ = typ,
.ref_count = std.atomic.Int(usize).init(1),
},
.big_int = undefined,
});
typ.base.ref();
errdefer comp.gpa().destroy(self);
self.big_int = try std.math.big.Int.init(comp.gpa());
errdefer self.big_int.deinit();
try self.big_int.setString(base, value);
return self;
}
pub fn getLlvmConst(self: *Int, ofile: *ObjectFile) !?llvm.ValueRef {
switch (self.base.typ.id) {
Type.Id.Int => {
const type_ref = try self.base.typ.getLlvmType(ofile.arena, ofile.context);
if (self.big_int.len == 0) {
return llvm.ConstNull(type_ref);
}
const unsigned_val = if (self.big_int.len == 1) blk: {
break :blk llvm.ConstInt(type_ref, self.big_int.limbs[0], @boolToInt(false));
} else if (@sizeOf(std.math.big.Limb) == @sizeOf(u64)) blk: {
break :blk llvm.ConstIntOfArbitraryPrecision(
type_ref,
@intCast(c_uint, self.big_int.len),
@ptrCast([*]u64, self.big_int.limbs.ptr),
);
} else {
@compileError("std.math.Big.Int.Limb size does not match LLVM");
};
return if (self.big_int.positive) unsigned_val else llvm.ConstNeg(unsigned_val);
},
Type.Id.ComptimeInt => unreachable,
else => unreachable,
}
}
pub fn copy(old: *Int, comp: *Compilation) !*Int {
old.base.typ.base.ref();
errdefer old.base.typ.base.deref(comp);
const new = try comp.gpa().create(Value.Int{
.base = Value{
.id = Value.Id.Int,
.typ = old.base.typ,
.ref_count = std.atomic.Int(usize).init(1),
},
.big_int = undefined,
});
errdefer comp.gpa().destroy(new);
new.big_int = try old.big_int.clone();
errdefer new.big_int.deinit();
return new;
}
pub fn destroy(self: *Int, comp: *Compilation) void {
self.big_int.deinit();
comp.gpa().destroy(self);
}
};
};

View File

@ -4379,7 +4379,7 @@ bool handle_is_ptr(TypeTableEntry *type_entry) {
static ZigWindowsSDK *get_windows_sdk(CodeGen *g) {
if (g->win_sdk == nullptr) {
if (os_find_windows_sdk(&g->win_sdk)) {
if (zig_find_windows_sdk(&g->win_sdk)) {
fprintf(stderr, "unable to determine windows sdk path\n");
exit(1);
}
@ -4499,12 +4499,11 @@ void find_libc_lib_path(CodeGen *g) {
ZigWindowsSDK *sdk = get_windows_sdk(g);
if (g->msvc_lib_dir == nullptr) {
Buf* vc_lib_dir = buf_alloc();
if (os_get_win32_vcruntime_path(vc_lib_dir, g->zig_target.arch.arch)) {
if (sdk->msvc_lib_dir_ptr == nullptr) {
fprintf(stderr, "Unable to determine vcruntime path. --msvc-lib-dir");
exit(1);
}
g->msvc_lib_dir = vc_lib_dir;
g->msvc_lib_dir = buf_create_from_mem(sdk->msvc_lib_dir_ptr, sdk->msvc_lib_dir_len);
}
if (g->libc_lib_dir == nullptr) {

View File

@ -901,7 +901,7 @@ static void construct_linker_job_macho(LinkJob *lj) {
if (strchr(buf_ptr(link_lib->name), '/') == nullptr) {
Buf *arg = buf_sprintf("-l%s", buf_ptr(link_lib->name));
lj->args.append(buf_ptr(arg));
} else {
} else {
lj->args.append(buf_ptr(link_lib->name));
}
}

View File

@ -26,7 +26,6 @@
#include <windows.h>
#include <io.h>
#include <fcntl.h>
#include "windows_com.hpp"
typedef SSIZE_T ssize_t;
#else
@ -1115,249 +1114,10 @@ void os_stderr_set_color(TermColor color) {
#endif
}
int os_find_windows_sdk(ZigWindowsSDK **out_sdk) {
#if defined(ZIG_OS_WINDOWS)
ZigWindowsSDK *result_sdk = allocate<ZigWindowsSDK>(1);
buf_resize(&result_sdk->path10, 0);
buf_resize(&result_sdk->path81, 0);
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
if (rc != ERROR_SUCCESS) {
return ErrorFileNotFound;
}
{
DWORD tmp_buf_len = MAX_PATH;
buf_resize(&result_sdk->path10, tmp_buf_len);
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path10), &tmp_buf_len);
if (rc == ERROR_FILE_NOT_FOUND) {
buf_resize(&result_sdk->path10, 0);
} else {
buf_resize(&result_sdk->path10, tmp_buf_len);
}
}
{
DWORD tmp_buf_len = MAX_PATH;
buf_resize(&result_sdk->path81, tmp_buf_len);
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)buf_ptr(&result_sdk->path81), &tmp_buf_len);
if (rc == ERROR_FILE_NOT_FOUND) {
buf_resize(&result_sdk->path81, 0);
} else {
buf_resize(&result_sdk->path81, tmp_buf_len);
}
}
if (buf_len(&result_sdk->path10) != 0) {
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\*", buf_ptr(&result_sdk->path10));
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ErrorFileNotFound;
}
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
bool found_version_dir = false;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
// Microsoft released 26624 as 10240 accidentally.
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
c2 = 26624;
}
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
buf_init_from_str(&result_sdk->version10, ffd.cFileName);
found_version_dir = true;
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
if (!found_version_dir) {
buf_resize(&result_sdk->path10, 0);
}
}
if (buf_len(&result_sdk->path81) != 0) {
Buf *sdk_lib_dir = buf_sprintf("%s\\Lib\\winv*", buf_ptr(&result_sdk->path81));
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(buf_ptr(sdk_lib_dir), &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ErrorFileNotFound;
}
int v0 = 0, v1 = 0;
bool found_version_dir = false;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0;
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
if ((c0 > v0) || (c1 > v1)) {
v0 = c0, v1 = c1;
buf_init_from_str(&result_sdk->version81, ffd.cFileName);
found_version_dir = true;
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
if (!found_version_dir) {
buf_resize(&result_sdk->path81, 0);
}
}
*out_sdk = result_sdk;
return 0;
#else
return ErrorFileNotFound;
#endif
}
int os_get_win32_vcruntime_path(Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
//COM Smart Pointerse requires explicit scope
{
HRESULT rc;
rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (rc != S_OK) {
goto com_done;
}
//This COM class is installed when a VS2017
ISetupConfigurationPtr setup_config;
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
if (rc != S_OK) {
goto com_done;
}
IEnumSetupInstancesPtr all_instances;
rc = setup_config->EnumInstances(&all_instances);
if (rc != S_OK) {
goto com_done;
}
ISetupInstance* curr_instance;
ULONG found_inst;
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
BSTR bstr_inst_path;
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
if (rc != S_OK) {
goto com_done;
}
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
ULONG tmp_path_len = bstr_path_len / 2 + 1;
char* conv_path = (char*)bstr_inst_path;
char *tmp_path = (char*)alloca(tmp_path_len);
memset(tmp_path, 0, tmp_path_len);
uint32_t c = 0;
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
tmp_path[c] = conv_path[i];
++c;
assert(c != tmp_path_len);
}
buf_append_str(output_buf, tmp_path);
buf_append_char(output_buf, '\\');
Buf* tmp_buf = buf_alloc();
buf_append_buf(tmp_buf, output_buf);
buf_append_str(tmp_buf, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
FILE* tools_file = fopen(buf_ptr(tmp_buf), "r");
if (!tools_file) {
goto com_done;
}
memset(tmp_path, 0, tmp_path_len);
fgets(tmp_path, tmp_path_len, tools_file);
strtok(tmp_path, " \r\n");
fclose(tools_file);
buf_appendf(output_buf, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
break;
case ZigLLVM_x86_64:
buf_append_str(output_buf, "x64\\");
break;
case ZigLLVM_arm:
buf_append_str(output_buf, "arm\\");
break;
default:
zig_panic("Attemped to use vcruntime for non-supported platform.");
}
buf_resize(tmp_buf, 0);
buf_append_buf(tmp_buf, output_buf);
buf_append_str(tmp_buf, "vcruntime.lib");
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
}
}
}
com_done:;
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0, KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
if (rc != ERROR_SUCCESS) {
return ErrorFileNotFound;
}
DWORD dw_type = 0;
DWORD cb_data = 0;
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
return ErrorFileNotFound;
}
Buf* tmp_buf = buf_alloc_fixed(cb_data);
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)buf_ptr(tmp_buf), &cb_data);
//RegQueryValueExA returns the length of the string INCLUDING the null terminator
buf_resize(tmp_buf, cb_data-1);
buf_append_str(tmp_buf, "VC\\Lib\\");
switch (platform_type) {
case ZigLLVM_x86:
//x86 is in the root of the Lib folder
break;
case ZigLLVM_x86_64:
buf_append_str(tmp_buf, "amd64\\");
break;
case ZigLLVM_arm:
buf_append_str(tmp_buf, "arm\\");
break;
default:
zig_panic("Attemped to use vcruntime for non-supported platform.");
}
buf_append_buf(output_buf, tmp_buf);
buf_append_str(tmp_buf, "vcruntime.lib");
if (GetFileAttributesA(buf_ptr(tmp_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
} else {
buf_resize(output_buf, 0);
return ErrorFileNotFound;
}
#else
return ErrorFileNotFound;
#endif
}
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchType platform_type) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
buf_appendf(output_buf, "%s\\Lib\\%s\\ucrt\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
@ -1389,7 +1149,7 @@ int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_Arch
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf* output_buf) {
#if defined(ZIG_OS_WINDOWS)
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
buf_appendf(output_buf, "%s\\Include\\%s\\ucrt", sdk->path10_ptr, sdk->version10_ptr);
if (GetFileAttributesA(buf_ptr(output_buf)) != INVALID_FILE_ATTRIBUTES) {
return 0;
}
@ -1406,7 +1166,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
#if defined(ZIG_OS_WINDOWS)
{
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path10), buf_ptr(&sdk->version10));
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path10_ptr, sdk->version10_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");
@ -1429,7 +1189,7 @@ int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf* output_buf, ZigLLVM_ArchTy
}
{
buf_resize(output_buf, 0);
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", buf_ptr(&sdk->path81), buf_ptr(&sdk->version81));
buf_appendf(output_buf, "%s\\Lib\\%s\\um\\", sdk->path81_ptr, sdk->version81_ptr);
switch (platform_type) {
case ZigLLVM_x86:
buf_append_str(output_buf, "x86\\");

View File

@ -12,6 +12,7 @@
#include "buffer.hpp"
#include "error.hpp"
#include "zig_llvm.h"
#include "windows_sdk.h"
#include <stdio.h>
#include <inttypes.h>
@ -79,15 +80,6 @@ bool os_is_sep(uint8_t c);
int os_self_exe_path(Buf *out_path);
struct ZigWindowsSDK {
Buf path10;
Buf version10;
Buf path81;
Buf version81;
};
int os_find_windows_sdk(ZigWindowsSDK **out_sdk);
int os_get_win32_vcruntime_path(Buf *output_buf, ZigLLVM_ArchType platform_type);
int os_get_win32_ucrt_include_path(ZigWindowsSDK *sdk, Buf *output_buf);
int os_get_win32_ucrt_lib_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);
int os_get_win32_kern32_path(ZigWindowsSDK *sdk, Buf *output_buf, ZigLLVM_ArchType platform_type);

352
src/windows_sdk.cpp Normal file
View File

@ -0,0 +1,352 @@
/*
* Copyright (c) 2018 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#include "windows_sdk.h"
#if defined(_WIN32)
#include "windows_com.hpp"
#include <inttypes.h>
#include <assert.h>
struct ZigWindowsSDKPrivate {
ZigWindowsSDK base;
};
enum NativeArch {
NativeArchArm,
NativeArchi386,
NativeArchx86_64,
};
#if defined(_M_ARM) || defined(__arm_)
static const NativeArch native_arch = NativeArchArm;
#endif
#if defined(_M_IX86) || defined(__i386__)
static const NativeArch native_arch = NativeArchi386;
#endif
#if defined(_M_X64) || defined(__x86_64__)
static const NativeArch native_arch = NativeArchx86_64;
#endif
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {
if (sdk == nullptr) {
return;
}
free((void*)sdk->path10_ptr);
free((void*)sdk->version10_ptr);
free((void*)sdk->path81_ptr);
free((void*)sdk->version81_ptr);
free((void*)sdk->msvc_lib_dir_ptr);
}
static ZigFindWindowsSdkError find_msvc_lib_dir(ZigWindowsSDKPrivate *priv) {
//COM Smart Pointers requires explicit scope
{
HRESULT rc = CoInitializeEx(NULL, COINIT_MULTITHREADED);
if (rc != S_OK && rc != S_FALSE) {
goto com_done;
}
//This COM class is installed when a VS2017
ISetupConfigurationPtr setup_config;
rc = setup_config.CreateInstance(__uuidof(SetupConfiguration));
if (rc != S_OK) {
goto com_done;
}
IEnumSetupInstancesPtr all_instances;
rc = setup_config->EnumInstances(&all_instances);
if (rc != S_OK) {
goto com_done;
}
ISetupInstance* curr_instance;
ULONG found_inst;
while ((rc = all_instances->Next(1, &curr_instance, &found_inst) == S_OK)) {
BSTR bstr_inst_path;
rc = curr_instance->GetInstallationPath(&bstr_inst_path);
if (rc != S_OK) {
goto com_done;
}
//BSTRs are UTF-16 encoded, so we need to convert the string & adjust the length
//TODO call an actual function to do this
UINT bstr_path_len = *((UINT*)bstr_inst_path - 1);
ULONG tmp_path_len = bstr_path_len / 2 + 1;
char* conv_path = (char*)bstr_inst_path;
// TODO don't use alloca
char *tmp_path = (char*)alloca(tmp_path_len);
memset(tmp_path, 0, tmp_path_len);
uint32_t c = 0;
for (uint32_t i = 0; i < bstr_path_len; i += 2) {
tmp_path[c] = conv_path[i];
++c;
assert(c != tmp_path_len);
}
char output_path[4096];
output_path[0] = 0;
char *out_append_ptr = output_path;
out_append_ptr += sprintf(out_append_ptr, "%s\\", tmp_path);
char tmp_buf[4096];
sprintf(tmp_buf, "%s%s", output_path, "VC\\Auxiliary\\Build\\Microsoft.VCToolsVersion.default.txt");
FILE* tools_file = fopen(tmp_buf, "rb");
if (!tools_file) {
goto com_done;
}
memset(tmp_path, 0, tmp_path_len);
fgets(tmp_path, tmp_path_len, tools_file);
strtok(tmp_path, " \r\n");
fclose(tools_file);
out_append_ptr += sprintf(out_append_ptr, "VC\\Tools\\MSVC\\%s\\lib\\", tmp_path);
switch (native_arch) {
case NativeArchi386:
out_append_ptr += sprintf(out_append_ptr, "x86\\");
break;
case NativeArchx86_64:
out_append_ptr += sprintf(out_append_ptr, "x64\\");
break;
case NativeArchArm:
out_append_ptr += sprintf(out_append_ptr, "arm\\");
break;
}
sprintf(tmp_buf, "%s%s", output_path, "vcruntime.lib");
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
priv->base.msvc_lib_dir_ptr = strdup(output_path);
if (priv->base.msvc_lib_dir_ptr == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
priv->base.msvc_lib_dir_len = strlen(priv->base.msvc_lib_dir_ptr);
return ZigFindWindowsSdkErrorNone;
}
}
}
com_done:;
HKEY key;
HRESULT rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\VisualStudio\\SxS\\VS7", 0,
KEY_QUERY_VALUE | KEY_WOW64_32KEY, &key);
if (rc != ERROR_SUCCESS) {
return ZigFindWindowsSdkErrorNotFound;
}
DWORD dw_type = 0;
DWORD cb_data = 0;
rc = RegQueryValueEx(key, "14.0", NULL, &dw_type, NULL, &cb_data);
if ((rc == ERROR_FILE_NOT_FOUND) || (REG_SZ != dw_type)) {
return ZigFindWindowsSdkErrorNotFound;
}
char tmp_buf[4096];
RegQueryValueExA(key, "14.0", NULL, NULL, (LPBYTE)tmp_buf, &cb_data);
// RegQueryValueExA returns the length of the string INCLUDING the null terminator
char *tmp_buf_append_ptr = tmp_buf + (cb_data - 1);
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "VC\\Lib\\");
switch (native_arch) {
case NativeArchi386:
//x86 is in the root of the Lib folder
break;
case NativeArchx86_64:
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "amd64\\");
break;
case NativeArchArm:
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "arm\\");
break;
}
char *output_path = strdup(tmp_buf);
if (output_path == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
tmp_buf_append_ptr += sprintf(tmp_buf_append_ptr, "vcruntime.lib");
if (GetFileAttributesA(tmp_buf) != INVALID_FILE_ATTRIBUTES) {
priv->base.msvc_lib_dir_ptr = output_path;
priv->base.msvc_lib_dir_len = strlen(output_path);
return ZigFindWindowsSdkErrorNone;
} else {
free(output_path);
return ZigFindWindowsSdkErrorNotFound;
}
}
static ZigFindWindowsSdkError find_10_version(ZigWindowsSDKPrivate *priv) {
if (priv->base.path10_ptr == nullptr)
return ZigFindWindowsSdkErrorNone;
char sdk_lib_dir[4096];
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\*", priv->base.path10_ptr);
if (n < 0 || n >= 4096) {
return ZigFindWindowsSdkErrorPathTooLong;
}
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ZigFindWindowsSdkErrorNotFound;
}
int v0 = 0, v1 = 0, v2 = 0, v3 = 0;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0, c2 = 0, c3 = 0;
sscanf(ffd.cFileName, "%d.%d.%d.%d", &c0, &c1, &c2, &c3);
if (c0 == 10 && c1 == 0 && c2 == 10240 && c3 == 0) {
// Microsoft released 26624 as 10240 accidentally.
// https://developer.microsoft.com/en-us/windows/downloads/sdk-archive
c2 = 26624;
}
if ((c0 > v0) || (c1 > v1) || (c2 > v2) || (c3 > v3)) {
v0 = c0, v1 = c1, v2 = c2, v3 = c3;
free((void*)priv->base.version10_ptr);
priv->base.version10_ptr = strdup(ffd.cFileName);
if (priv->base.version10_ptr == nullptr) {
FindClose(hFind);
return ZigFindWindowsSdkErrorOutOfMemory;
}
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
priv->base.version10_len = strlen(priv->base.version10_ptr);
return ZigFindWindowsSdkErrorNone;
}
static ZigFindWindowsSdkError find_81_version(ZigWindowsSDKPrivate *priv) {
if (priv->base.path81_ptr == nullptr)
return ZigFindWindowsSdkErrorNone;
char sdk_lib_dir[4096];
int n = snprintf(sdk_lib_dir, 4096, "%s\\Lib\\winv*", priv->base.path81_ptr);
if (n < 0 || n >= 4096) {
return ZigFindWindowsSdkErrorPathTooLong;
}
// enumerate files in sdk path looking for latest version
WIN32_FIND_DATA ffd;
HANDLE hFind = FindFirstFileA(sdk_lib_dir, &ffd);
if (hFind == INVALID_HANDLE_VALUE) {
return ZigFindWindowsSdkErrorNotFound;
}
int v0 = 0, v1 = 0;
for (;;) {
if (ffd.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) {
int c0 = 0, c1 = 0;
sscanf(ffd.cFileName, "winv%d.%d", &c0, &c1);
if ((c0 > v0) || (c1 > v1)) {
v0 = c0, v1 = c1;
free((void*)priv->base.version81_ptr);
priv->base.version81_ptr = strdup(ffd.cFileName);
if (priv->base.version81_ptr == nullptr) {
FindClose(hFind);
return ZigFindWindowsSdkErrorOutOfMemory;
}
}
}
if (FindNextFile(hFind, &ffd) == 0) {
FindClose(hFind);
break;
}
}
priv->base.version81_len = strlen(priv->base.version81_ptr);
return ZigFindWindowsSdkErrorNone;
}
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
ZigWindowsSDKPrivate *priv = (ZigWindowsSDKPrivate*)calloc(1, sizeof(ZigWindowsSDKPrivate));
if (priv == nullptr) {
return ZigFindWindowsSdkErrorOutOfMemory;
}
HKEY key;
HRESULT rc;
rc = RegOpenKeyEx(HKEY_LOCAL_MACHINE, "SOFTWARE\\Microsoft\\Windows Kits\\Installed Roots", 0,
KEY_QUERY_VALUE | KEY_WOW64_32KEY | KEY_ENUMERATE_SUB_KEYS, &key);
if (rc != ERROR_SUCCESS) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorNotFound;
}
{
DWORD tmp_buf_len = MAX_PATH;
priv->base.path10_ptr = (const char *)calloc(tmp_buf_len, 1);
if (priv->base.path10_ptr == nullptr) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorOutOfMemory;
}
rc = RegQueryValueEx(key, "KitsRoot10", NULL, NULL, (LPBYTE)priv->base.path10_ptr, &tmp_buf_len);
if (rc == ERROR_SUCCESS) {
priv->base.path10_len = tmp_buf_len - 1;
if (priv->base.path10_ptr[priv->base.path10_len - 1] == '\\') {
priv->base.path10_len -= 1;
}
} else {
free((void*)priv->base.path10_ptr);
priv->base.path10_ptr = nullptr;
}
}
{
DWORD tmp_buf_len = MAX_PATH;
priv->base.path81_ptr = (const char *)calloc(tmp_buf_len, 1);
if (priv->base.path81_ptr == nullptr) {
zig_free_windows_sdk(&priv->base);
return ZigFindWindowsSdkErrorOutOfMemory;
}
rc = RegQueryValueEx(key, "KitsRoot81", NULL, NULL, (LPBYTE)priv->base.path81_ptr, &tmp_buf_len);
if (rc == ERROR_SUCCESS) {
priv->base.path81_len = tmp_buf_len - 1;
if (priv->base.path81_ptr[priv->base.path81_len - 1] == '\\') {
priv->base.path81_len -= 1;
}
} else {
free((void*)priv->base.path81_ptr);
priv->base.path81_ptr = nullptr;
}
}
{
ZigFindWindowsSdkError err = find_10_version(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
{
ZigFindWindowsSdkError err = find_81_version(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
{
ZigFindWindowsSdkError err = find_msvc_lib_dir(priv);
if (err == ZigFindWindowsSdkErrorOutOfMemory) {
zig_free_windows_sdk(&priv->base);
return err;
}
}
*out_sdk = &priv->base;
return ZigFindWindowsSdkErrorNone;
}
#else
void zig_free_windows_sdk(struct ZigWindowsSDK *sdk) {}
ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk) {
return ZigFindWindowsSdkErrorNotFound;
}
#endif

47
src/windows_sdk.h Normal file
View File

@ -0,0 +1,47 @@
/*
* Copyright (c) 2018 Andrew Kelley
*
* This file is part of zig, which is MIT licensed.
* See http://opensource.org/licenses/MIT
*/
#ifndef ZIG_WINDOWS_SDK_H
#define ZIG_WINDOWS_SDK_H
#ifdef __cplusplus
#define ZIG_EXTERN_C extern "C"
#else
#define ZIG_EXTERN_C
#endif
#include <stddef.h>
struct ZigWindowsSDK {
const char *path10_ptr;
size_t path10_len;
const char *version10_ptr;
size_t version10_len;
const char *path81_ptr;
size_t path81_len;
const char *version81_ptr;
size_t version81_len;
const char *msvc_lib_dir_ptr;
size_t msvc_lib_dir_len;
};
enum ZigFindWindowsSdkError {
ZigFindWindowsSdkErrorNone,
ZigFindWindowsSdkErrorOutOfMemory,
ZigFindWindowsSdkErrorNotFound,
ZigFindWindowsSdkErrorPathTooLong,
};
ZIG_EXTERN_C enum ZigFindWindowsSdkError zig_find_windows_sdk(struct ZigWindowsSDK **out_sdk);
ZIG_EXTERN_C void zig_free_windows_sdk(struct ZigWindowsSDK *sdk);
#endif

View File

@ -6,7 +6,7 @@ const AtomicRmwOp = builtin.AtomicRmwOp;
const AtomicOrder = builtin.AtomicOrder;
const assert = std.debug.assert;
/// ReturnType should be `void` or `E!void`
/// ReturnType must be `void` or `E!void`
pub fn Group(comptime ReturnType: type) type {
return struct {
coro_stack: Stack,
@ -38,8 +38,17 @@ pub fn Group(comptime ReturnType: type) type {
self.alloc_stack.push(node);
}
/// Add a node to the group. Thread-safe. Cannot fail.
/// `node.data` should be the promise handle to add to the group.
/// The node's memory should be in the coroutine frame of
/// the handle that is in the node, or somewhere guaranteed to live
/// at least as long.
pub fn addNode(self: *Self, node: *Stack.Node) void {
self.coro_stack.push(node);
}
/// This is equivalent to an async call, but the async function is added to the group, instead
/// of returning a promise. func must be async and have return type void.
/// of returning a promise. func must be async and have return type ReturnType.
/// Thread-safe.
pub fn call(self: *Self, comptime func: var, args: ...) (error{OutOfMemory}!void) {
const S = struct {
@ -67,6 +76,7 @@ pub fn Group(comptime ReturnType: type) type {
/// Wait for all the calls and promises of the group to complete.
/// Thread-safe.
/// Safe to call any number of times.
pub async fn wait(self: *Self) ReturnType {
// TODO catch unreachable because the allocation can be grouped with
// the coro frame allocation
@ -98,6 +108,8 @@ pub fn Group(comptime ReturnType: type) type {
}
/// Cancel all the outstanding promises. May only be called if wait was never called.
/// TODO These should be `cancelasync` not `cancel`.
/// See https://github.com/ziglang/zig/issues/1261
pub fn cancelAll(self: *Self) void {
while (self.coro_stack.pop()) |node| {
cancel node.data;

View File

@ -444,7 +444,7 @@ pub const Loop = struct {
.next = undefined,
.data = p,
};
loop.onNextTick(&my_tick_node);
self.onNextTick(&my_tick_node);
}
}

View File

@ -785,11 +785,15 @@ pub fn bufPrint(buf: []u8, comptime fmt: []const u8, args: ...) ![]u8 {
return buf[0 .. buf.len - context.remaining.len];
}
pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) ![]u8 {
pub const AllocPrintError = error{OutOfMemory};
pub fn allocPrint(allocator: *mem.Allocator, comptime fmt: []const u8, args: ...) AllocPrintError![]u8 {
var size: usize = 0;
format(&size, error{}, countSize, fmt, args) catch |err| switch (err) {};
const buf = try allocator.alloc(u8, size);
return bufPrint(buf, fmt, args);
return bufPrint(buf, fmt, args) catch |err| switch (err) {
error.BufferTooSmall => unreachable, // we just counted the size above
};
}
fn countSize(size: *usize, bytes: []const u8) (error{}!void) {

View File

@ -141,7 +141,7 @@ pub fn loadSymbols(allocator: *mem.Allocator, in: *io.FileInStream) !SymbolTable
}
// Effectively a no-op, lld emits symbols in ascending order.
std.sort.insertionSort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
std.sort.sort(Symbol, symbols[0..nsyms], Symbol.addressLessThan);
// Insert the sentinel. Since we don't know where the last function ends,
// we arbitrarily limit it to the start address + 4 KB.

View File

@ -60,8 +60,9 @@ pub const Int = struct {
self.limbs = try self.allocator.realloc(Limb, self.limbs, capacity);
}
pub fn deinit(self: Int) void {
pub fn deinit(self: *Int) void {
self.allocator.free(self.limbs);
self.* = undefined;
}
pub fn clone(other: Int) !Int {
@ -332,6 +333,7 @@ pub const Int = struct {
self.positive = positive;
}
/// TODO make this call format instead of the other way around
pub fn toString(self: Int, allocator: *Allocator, base: u8) ![]const u8 {
if (base < 2 or base > 16) {
return error.InvalidBase;
@ -414,6 +416,21 @@ pub const Int = struct {
return s;
}
/// for the std lib format function
/// TODO make this non-allocating
pub fn format(
self: Int,
comptime fmt: []const u8,
context: var,
comptime FmtError: type,
output: fn (@typeOf(context), []const u8) FmtError!void,
) FmtError!void {
// TODO look at fmt and support other bases
const str = self.toString(self.allocator, 10) catch @panic("TODO make this non allocating");
defer self.allocator.free(str);
return output(context, str);
}
// returns -1, 0, 1 if |a| < |b|, |a| == |b| or |a| > |b| respectively.
pub fn cmpAbs(a: Int, b: Int) i8 {
if (a.len < b.len) {

View File

@ -35,6 +35,7 @@ pub const Allocator = struct {
freeFn: fn (self: *Allocator, old_mem: []u8) void,
/// Call `destroy` with the result
/// TODO this is deprecated. use createOne instead
pub fn create(self: *Allocator, init: var) Error!*@typeOf(init) {
const T = @typeOf(init);
if (@sizeOf(T) == 0) return &(T{});
@ -44,6 +45,14 @@ pub const Allocator = struct {
return ptr;
}
/// Call `destroy` with the result.
/// Returns undefined memory.
pub fn createOne(self: *Allocator, comptime T: type) Error!*T {
if (@sizeOf(T) == 0) return &(T{});
const slice = try self.alloc(T, 1);
return &slice[0];
}
/// `ptr` should be the return value of `create`
pub fn destroy(self: *Allocator, ptr: var) void {
const non_const_ptr = @intToPtr([*]u8, @ptrToInt(ptr));
@ -149,13 +158,12 @@ pub fn copyBackwards(comptime T: type, dest: []T, source: []const T) void {
@setRuntimeSafety(false);
assert(dest.len >= source.len);
var i = source.len;
while(i > 0){
while (i > 0) {
i -= 1;
dest[i] = source[i];
}
}
pub fn set(comptime T: type, dest: []T, value: T) void {
for (dest) |*d|
d.* = value;

View File

@ -109,43 +109,42 @@ pub const File = struct {
Unexpected,
};
pub fn access(allocator: *mem.Allocator, path: []const u8, file_mode: os.FileMode) AccessError!bool {
pub fn access(allocator: *mem.Allocator, path: []const u8) AccessError!void {
const path_with_null = try std.cstr.addNullByte(allocator, path);
defer allocator.free(path_with_null);
if (is_posix) {
// mode is ignored and is always F_OK for now
const result = posix.access(path_with_null.ptr, posix.F_OK);
const err = posix.getErrno(result);
if (err > 0) {
return switch (err) {
posix.EACCES => error.PermissionDenied,
posix.EROFS => error.PermissionDenied,
posix.ELOOP => error.PermissionDenied,
posix.ETXTBSY => error.PermissionDenied,
posix.ENOTDIR => error.NotFound,
posix.ENOENT => error.NotFound,
switch (err) {
0 => return,
posix.EACCES => return error.PermissionDenied,
posix.EROFS => return error.PermissionDenied,
posix.ELOOP => return error.PermissionDenied,
posix.ETXTBSY => return error.PermissionDenied,
posix.ENOTDIR => return error.NotFound,
posix.ENOENT => return error.NotFound,
posix.ENAMETOOLONG => error.NameTooLong,
posix.EINVAL => error.BadMode,
posix.EFAULT => error.BadPathName,
posix.EIO => error.Io,
posix.ENOMEM => error.SystemResources,
else => os.unexpectedErrorPosix(err),
};
posix.ENAMETOOLONG => return error.NameTooLong,
posix.EINVAL => unreachable,
posix.EFAULT => return error.BadPathName,
posix.EIO => return error.Io,
posix.ENOMEM => return error.SystemResources,
else => return os.unexpectedErrorPosix(err),
}
return true;
} else if (is_windows) {
if (os.windows.GetFileAttributesA(path_with_null.ptr) != os.windows.INVALID_FILE_ATTRIBUTES) {
return true;
return;
}
const err = windows.GetLastError();
return switch (err) {
windows.ERROR.FILE_NOT_FOUND => error.NotFound,
windows.ERROR.ACCESS_DENIED => error.PermissionDenied,
else => os.unexpectedErrorWindows(err),
};
switch (err) {
windows.ERROR.FILE_NOT_FOUND,
windows.ERROR.PATH_NOT_FOUND,
=> return error.NotFound,
windows.ERROR.ACCESS_DENIED => return error.PermissionDenied,
else => return os.unexpectedErrorWindows(err),
}
} else {
@compileError("TODO implement access for this OS");
}

View File

@ -23,14 +23,14 @@ test "makePath, put some files in it, deleteTree" {
test "access file" {
try os.makePath(a, "os_test_tmp");
if (os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) |ok| {
unreachable;
if (os.File.access(a, "os_test_tmp/file.txt")) |ok| {
@panic("expected error");
} else |err| {
assert(err == error.NotFound);
}
try io.writeFile(a, "os_test_tmp/file.txt", "");
assert((try os.File.access(a, "os_test_tmp/file.txt", os.default_file_mode)) == true);
try os.File.access(a, "os_test_tmp/file.txt");
try os.deleteTree(a, "os_test_tmp");
}

View File

@ -0,0 +1,30 @@
use @import("index.zig");
pub const PROV_RSA_FULL = 1;
pub const REGSAM = ACCESS_MASK;
pub const ACCESS_MASK = DWORD;
pub const PHKEY = &HKEY;
pub const HKEY = &HKEY__;
pub const HKEY__ = extern struct {
unused: c_int,
};
pub const LSTATUS = LONG;
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
phProv: *HCRYPTPROV,
pszContainer: ?LPCSTR,
pszProvider: ?LPCSTR,
dwProvType: DWORD,
dwFlags: DWORD,
) BOOL;
pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
pub extern "advapi32" stdcallcc fn RegOpenKeyExW(hKey: HKEY, lpSubKey: LPCWSTR, ulOptions: DWORD, samDesired: REGSAM,
phkResult: &HKEY,) LSTATUS;
pub extern "advapi32" stdcallcc fn RegQueryValueExW(hKey: HKEY, lpValueName: LPCWSTR, lpReserved: LPDWORD,
lpType: LPDWORD, lpData: LPBYTE, lpcbData: LPDWORD,) LSTATUS;

View File

@ -1,190 +1,19 @@
const std = @import("../../index.zig");
const assert = std.debug.assert;
pub use @import("advapi32.zig");
pub use @import("kernel32.zig");
pub use @import("ole32.zig");
pub use @import("shell32.zig");
pub use @import("shlwapi.zig");
pub use @import("user32.zig");
test "import" {
_ = @import("util.zig");
}
pub const ERROR = @import("error.zig");
pub extern "advapi32" stdcallcc fn CryptAcquireContextA(
phProv: *HCRYPTPROV,
pszContainer: ?LPCSTR,
pszProvider: ?LPCSTR,
dwProvType: DWORD,
dwFlags: DWORD,
) BOOL;
pub extern "advapi32" stdcallcc fn CryptReleaseContext(hProv: HCRYPTPROV, dwFlags: DWORD) BOOL;
pub extern "advapi32" stdcallcc fn CryptGenRandom(hProv: HCRYPTPROV, dwLen: DWORD, pbBuffer: [*]BYTE) BOOL;
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
lpPathName: LPCSTR,
lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateFileA(
lpFileName: LPCSTR,
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: ?HANDLE,
) HANDLE;
pub extern "kernel32" stdcallcc fn CreatePipe(
hReadPipe: *HANDLE,
hWritePipe: *HANDLE,
lpPipeAttributes: *const SECURITY_ATTRIBUTES,
nSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateProcessA(
lpApplicationName: ?LPCSTR,
lpCommandLine: LPSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: ?*c_void,
lpCurrentDirectory: ?LPCSTR,
lpStartupInfo: *STARTUPINFOA,
lpProcessInformation: *PROCESS_INFORMATION,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
lpSymlinkFileName: LPCSTR,
lpTargetFileName: LPCSTR,
dwFlags: DWORD,
) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_hFile: HANDLE,
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
out_lpFileInformation: *c_void,
in_dwBufferSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
hFile: HANDLE,
lpszFilePath: LPSTR,
cchFilePath: DWORD,
dwFlags: DWORD,
) DWORD;
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: LPCSTR,
lpNewFileName: LPCSTR,
dwFlags: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile(
in_hFile: HANDLE,
out_lpBuffer: *c_void,
in_nNumberOfBytesToRead: DWORD,
out_lpNumberOfBytesRead: *DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
in_fFile: HANDLE,
in_liDistanceToMove: LARGE_INTEGER,
out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
in_dwMoveMethod: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(
in_hFile: HANDLE,
in_lpBuffer: *const c_void,
in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
//TODO: call unicode versions instead of relying on ANSI code page
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;
pub const PROV_RSA_FULL = 1;
pub const BOOL = c_int;
pub const BOOLEAN = BYTE;
pub const BYTE = u8;
@ -206,6 +35,7 @@ pub const LPSTR = [*]CHAR;
pub const LPTSTR = if (UNICODE) LPWSTR else LPSTR;
pub const LPVOID = *c_void;
pub const LPWSTR = [*]WCHAR;
pub const LPCWSTR = [*]const WCHAR;
pub const PVOID = *c_void;
pub const PWSTR = [*]WCHAR;
pub const SIZE_T = usize;
@ -442,10 +272,6 @@ pub const SYSTEM_INFO = extern struct {
wProcessorRevision: WORD,
};
pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;
pub const HRESULT = c_long;
pub const KNOWNFOLDERID = GUID;

162
std/os/windows/kernel32.zig Normal file
View File

@ -0,0 +1,162 @@
use @import("index.zig");
pub extern "kernel32" stdcallcc fn CloseHandle(hObject: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn CreateDirectoryA(
lpPathName: LPCSTR,
lpSecurityAttributes: ?*SECURITY_ATTRIBUTES,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateFileA(
lpFileName: LPCSTR,
dwDesiredAccess: DWORD,
dwShareMode: DWORD,
lpSecurityAttributes: ?LPSECURITY_ATTRIBUTES,
dwCreationDisposition: DWORD,
dwFlagsAndAttributes: DWORD,
hTemplateFile: ?HANDLE,
) HANDLE;
pub extern "kernel32" stdcallcc fn CreatePipe(
hReadPipe: *HANDLE,
hWritePipe: *HANDLE,
lpPipeAttributes: *const SECURITY_ATTRIBUTES,
nSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateProcessA(
lpApplicationName: ?LPCSTR,
lpCommandLine: LPSTR,
lpProcessAttributes: ?*SECURITY_ATTRIBUTES,
lpThreadAttributes: ?*SECURITY_ATTRIBUTES,
bInheritHandles: BOOL,
dwCreationFlags: DWORD,
lpEnvironment: ?*c_void,
lpCurrentDirectory: ?LPCSTR,
lpStartupInfo: *STARTUPINFOA,
lpProcessInformation: *PROCESS_INFORMATION,
) BOOL;
pub extern "kernel32" stdcallcc fn CreateSymbolicLinkA(
lpSymlinkFileName: LPCSTR,
lpTargetFileName: LPCSTR,
dwFlags: DWORD,
) BOOLEAN;
pub extern "kernel32" stdcallcc fn CreateIoCompletionPort(FileHandle: HANDLE, ExistingCompletionPort: ?HANDLE, CompletionKey: ULONG_PTR, NumberOfConcurrentThreads: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn CreateThread(lpThreadAttributes: ?LPSECURITY_ATTRIBUTES, dwStackSize: SIZE_T, lpStartAddress: LPTHREAD_START_ROUTINE, lpParameter: ?LPVOID, dwCreationFlags: DWORD, lpThreadId: ?LPDWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn DeleteFileA(lpFileName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn ExitProcess(exit_code: UINT) noreturn;
pub extern "kernel32" stdcallcc fn FindFirstFileA(lpFileName: LPCSTR, lpFindFileData: *WIN32_FIND_DATAA) HANDLE;
pub extern "kernel32" stdcallcc fn FindClose(hFindFile: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn FindNextFileA(hFindFile: HANDLE, lpFindFileData: *WIN32_FIND_DATAA) BOOL;
pub extern "kernel32" stdcallcc fn FreeEnvironmentStringsA(penv: [*]u8) BOOL;
pub extern "kernel32" stdcallcc fn GetCommandLineA() LPSTR;
pub extern "kernel32" stdcallcc fn GetConsoleMode(in_hConsoleHandle: HANDLE, out_lpMode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetCurrentDirectoryA(nBufferLength: WORD, lpBuffer: ?LPSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetEnvironmentStringsA() ?[*]u8;
pub extern "kernel32" stdcallcc fn GetEnvironmentVariableA(lpName: LPCSTR, lpBuffer: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetExitCodeProcess(hProcess: HANDLE, lpExitCode: *DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetFileSizeEx(hFile: HANDLE, lpFileSize: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn GetFileAttributesA(lpFileName: LPCSTR) DWORD;
pub extern "kernel32" stdcallcc fn GetModuleFileNameA(hModule: ?HMODULE, lpFilename: LPSTR, nSize: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn GetLastError() DWORD;
pub extern "kernel32" stdcallcc fn GetFileInformationByHandleEx(
in_hFile: HANDLE,
in_FileInformationClass: FILE_INFO_BY_HANDLE_CLASS,
out_lpFileInformation: *c_void,
in_dwBufferSize: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn GetFinalPathNameByHandleA(
hFile: HANDLE,
lpszFilePath: LPSTR,
cchFilePath: DWORD,
dwFlags: DWORD,
) DWORD;
pub extern "kernel32" stdcallcc fn GetProcessHeap() ?HANDLE;
pub extern "kernel32" stdcallcc fn GetQueuedCompletionStatus(CompletionPort: HANDLE, lpNumberOfBytesTransferred: LPDWORD, lpCompletionKey: *ULONG_PTR, lpOverlapped: *?*OVERLAPPED, dwMilliseconds: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn GetSystemInfo(lpSystemInfo: *SYSTEM_INFO) void;
pub extern "kernel32" stdcallcc fn GetSystemTimeAsFileTime(*FILETIME) void;
pub extern "kernel32" stdcallcc fn HeapCreate(flOptions: DWORD, dwInitialSize: SIZE_T, dwMaximumSize: SIZE_T) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapDestroy(hHeap: HANDLE) BOOL;
pub extern "kernel32" stdcallcc fn HeapReAlloc(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapSize(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapValidate(hHeap: HANDLE, dwFlags: DWORD, lpMem: *const c_void) BOOL;
pub extern "kernel32" stdcallcc fn HeapCompact(hHeap: HANDLE, dwFlags: DWORD) SIZE_T;
pub extern "kernel32" stdcallcc fn HeapSummary(hHeap: HANDLE, dwFlags: DWORD, lpSummary: LPHEAP_SUMMARY) BOOL;
pub extern "kernel32" stdcallcc fn GetStdHandle(in_nStdHandle: DWORD) ?HANDLE;
pub extern "kernel32" stdcallcc fn HeapAlloc(hHeap: HANDLE, dwFlags: DWORD, dwBytes: SIZE_T) ?*c_void;
pub extern "kernel32" stdcallcc fn HeapFree(hHeap: HANDLE, dwFlags: DWORD, lpMem: *c_void) BOOL;
pub extern "kernel32" stdcallcc fn MoveFileExA(
lpExistingFileName: LPCSTR,
lpNewFileName: LPCSTR,
dwFlags: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn PostQueuedCompletionStatus(CompletionPort: HANDLE, dwNumberOfBytesTransferred: DWORD, dwCompletionKey: ULONG_PTR, lpOverlapped: ?*OVERLAPPED) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceCounter(lpPerformanceCount: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn QueryPerformanceFrequency(lpFrequency: *LARGE_INTEGER) BOOL;
pub extern "kernel32" stdcallcc fn ReadFile(
in_hFile: HANDLE,
out_lpBuffer: *c_void,
in_nNumberOfBytesToRead: DWORD,
out_lpNumberOfBytesRead: *DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
pub extern "kernel32" stdcallcc fn RemoveDirectoryA(lpPathName: LPCSTR) BOOL;
pub extern "kernel32" stdcallcc fn SetFilePointerEx(
in_fFile: HANDLE,
in_liDistanceToMove: LARGE_INTEGER,
out_opt_ldNewFilePointer: ?*LARGE_INTEGER,
in_dwMoveMethod: DWORD,
) BOOL;
pub extern "kernel32" stdcallcc fn SetHandleInformation(hObject: HANDLE, dwMask: DWORD, dwFlags: DWORD) BOOL;
pub extern "kernel32" stdcallcc fn Sleep(dwMilliseconds: DWORD) void;
pub extern "kernel32" stdcallcc fn TerminateProcess(hProcess: HANDLE, uExitCode: UINT) BOOL;
pub extern "kernel32" stdcallcc fn WaitForSingleObject(hHandle: HANDLE, dwMilliseconds: DWORD) DWORD;
pub extern "kernel32" stdcallcc fn WriteFile(
in_hFile: HANDLE,
in_lpBuffer: *const c_void,
in_nNumberOfBytesToWrite: DWORD,
out_lpNumberOfBytesWritten: ?*DWORD,
in_out_lpOverlapped: ?*OVERLAPPED,
) BOOL;
//TODO: call unicode versions instead of relying on ANSI code page
pub extern "kernel32" stdcallcc fn LoadLibraryA(lpLibFileName: LPCSTR) ?HMODULE;
pub extern "kernel32" stdcallcc fn FreeLibrary(hModule: HMODULE) BOOL;

18
std/os/windows/ole32.zig Normal file
View File

@ -0,0 +1,18 @@
use @import("index.zig");
pub extern "ole32.dll" stdcallcc fn CoTaskMemFree(pv: LPVOID) void;
pub extern "ole32.dll" stdcallcc fn CoUninitialize() void;
pub extern "ole32.dll" stdcallcc fn CoGetCurrentProcess() DWORD;
pub extern "ole32.dll" stdcallcc fn CoInitializeEx(pvReserved: LPVOID, dwCoInit: DWORD) HRESULT;
pub const COINIT_APARTMENTTHREADED = COINIT.COINIT_APARTMENTTHREADED;
pub const COINIT_MULTITHREADED = COINIT.COINIT_MULTITHREADED;
pub const COINIT_DISABLE_OLE1DDE = COINIT.COINIT_DISABLE_OLE1DDE;
pub const COINIT_SPEED_OVER_MEMORY = COINIT.COINIT_SPEED_OVER_MEMORY;
pub const COINIT = extern enum {
COINIT_APARTMENTTHREADED = 2,
COINIT_MULTITHREADED = 0,
COINIT_DISABLE_OLE1DDE = 4,
COINIT_SPEED_OVER_MEMORY = 8,
};

View File

@ -0,0 +1,4 @@
use @import("index.zig");
pub extern "shell32.dll" stdcallcc fn SHGetKnownFolderPath(rfid: *const KNOWNFOLDERID, dwFlags: DWORD, hToken: ?HANDLE, ppszPath: *[*]WCHAR) HRESULT;

View File

@ -0,0 +1,4 @@
use @import("index.zig");
pub extern "shlwapi" stdcallcc fn PathFileExistsA(pszPath: ?LPCTSTR) BOOL;

View File

@ -0,0 +1,4 @@
use @import("index.zig");
pub extern "user32" stdcallcc fn MessageBoxA(hWnd: ?HANDLE, lpText: ?LPCTSTR, lpCaption: ?LPCTSTR, uType: UINT) c_int;

View File

@ -2,6 +2,7 @@ const tokenizer = @import("tokenizer.zig");
pub const Token = tokenizer.Token;
pub const Tokenizer = tokenizer.Tokenizer;
pub const parse = @import("parse.zig").parse;
pub const parseStringLiteral = @import("parse_string_literal.zig").parseStringLiteral;
pub const render = @import("render.zig").render;
pub const ast = @import("ast.zig");
@ -10,4 +11,6 @@ test "std.zig tests" {
_ = @import("parse.zig");
_ = @import("render.zig");
_ = @import("tokenizer.zig");
_ = @import("parse_string_literal.zig");
}

View File

@ -2356,7 +2356,7 @@ pub fn parse(allocator: *mem.Allocator, source: []const u8) !ast.Tree {
const token = nextToken(&tok_it, &tree);
switch (token.ptr.id) {
Token.Id.IntegerLiteral => {
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.StringLiteral, token.index);
_ = try createToCtxLiteral(arena, opt_ctx, ast.Node.IntegerLiteral, token.index);
continue;
},
Token.Id.FloatLiteral => {

View File

@ -0,0 +1,76 @@
const std = @import("../index.zig");
const assert = std.debug.assert;
const State = enum {
Start,
Backslash,
};
pub const ParseStringLiteralError = error{
OutOfMemory,
/// When this is returned, index will be the position of the character.
InvalidCharacter,
};
/// caller owns returned memory
pub fn parseStringLiteral(
allocator: *std.mem.Allocator,
bytes: []const u8,
bad_index: *usize, // populated if error.InvalidCharacter is returned
) ParseStringLiteralError![]u8 {
const first_index = if (bytes[0] == 'c') usize(2) else usize(1);
assert(bytes[bytes.len - 1] == '"');
var list = std.ArrayList(u8).init(allocator);
errdefer list.deinit();
const slice = bytes[first_index..];
try list.ensureCapacity(slice.len - 1);
var state = State.Start;
for (slice) |b, index| {
switch (state) {
State.Start => switch (b) {
'\\' => state = State.Backslash,
'\n' => {
bad_index.* = index;
return error.InvalidCharacter;
},
'"' => return list.toOwnedSlice(),
else => try list.append(b),
},
State.Backslash => switch (b) {
'x' => @panic("TODO"),
'u' => @panic("TODO"),
'U' => @panic("TODO"),
'n' => {
try list.append('\n');
state = State.Start;
},
'r' => {
try list.append('\r');
state = State.Start;
},
'\\' => {
try list.append('\\');
state = State.Start;
},
't' => {
try list.append('\t');
state = State.Start;
},
'"' => {
try list.append('"');
state = State.Start;
},
else => {
bad_index.* = index;
return error.InvalidCharacter;
},
},
else => unreachable,
}
}
unreachable;
}

View File

@ -73,6 +73,7 @@ pub const Token = struct {
return null;
}
/// TODO remove this enum
const StrLitKind = enum {
Normal,
C,

View File

@ -0,0 +1,12 @@
const std = @import("std");
const TestContext = @import("../../src-self-hosted/test.zig").TestContext;
pub fn addCases(ctx: *TestContext) !void {
try ctx.testCompareOutputLibC(
\\extern fn puts([*]const u8) void;
\\export fn main() c_int {
\\ puts(c"Hello, world!");
\\ return 0;
\\}
, "Hello, world!" ++ std.cstr.line_sep);
}

View File

@ -9,4 +9,22 @@ pub fn addCases(ctx: *TestContext) !void {
try ctx.testCompileError(
\\fn() void {}
, "1.zig", 1, 1, "missing function name");
try ctx.testCompileError(
\\comptime {
\\ return;
\\}
, "1.zig", 2, 5, "return expression outside function definition");
try ctx.testCompileError(
\\export fn entry() void {
\\ defer return;
\\}
, "1.zig", 2, 11, "cannot return from defer expression");
try ctx.testCompileError(
\\export fn entry() c_int {
\\ return 36893488147419103232;
\\}
, "1.zig", 2, 12, "integer value '36893488147419103232' cannot be stored in type 'c_int'");
}