Merge remote-tracking branch 'offical/master'

This commit is contained in:
ed
2024-06-11 19:23:35 -04:00
30 changed files with 466 additions and 189 deletions
+29
View File
@@ -39,6 +39,35 @@ jobs:
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false ./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false ./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
(cd tests/issues; ./run.sh) (cd tests/issues; ./run.sh)
build_freebsd:
name: FreeBSD Build, Check, and Test
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v4
- name: Build, Check, and Test
timeout-minutes: 15
uses: vmactions/freebsd-vm@v1
with:
usesh: true
copyback: false
prepare: |
pkg install -y gmake git bash python3 libxml2 llvm17
run: |
# `set -e` is needed for test failures to register. https://github.com/vmactions/freebsd-vm/issues/72
set -e -x
git config --global --add safe.directory $(pwd)
gmake release
./odin version
./odin report
gmake -C vendor/stb/src
gmake -C vendor/cgltf/src
gmake -C vendor/miniaudio/src
./odin check examples/all -vet -strict-style -target:freebsd_amd64
./odin test tests/core/normal.odin -file -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/core/speed.odin -file -all-packages -o:speed -define:ODIN_TEST_FANCY=false
./odin test tests/vendor -all-packages -define:ODIN_TEST_FANCY=false
./odin test tests/benchmark -all-packages -define:ODIN_TEST_FANCY=false
(cd tests/issues; ./run.sh)
ci: ci:
strategy: strategy:
fail-fast: false fail-fast: false
+4
View File
@@ -295,6 +295,10 @@ simd_rotate_right :: proc(a: #simd[N]T, $offset: int) -> #simd[N]T ---
// if all listed features are supported. // if all listed features are supported.
has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) --- has_target_feature :: proc($test: $T) -> bool where type_is_string(T) || type_is_proc(T) ---
// Returns the value of the procedure where `x` must be a call expression
procedure_of :: proc(x: $T) -> T where type_is_proc(T) ---
// WASM targets only // WASM targets only
wasm_memory_grow :: proc(index, delta: uintptr) -> int --- wasm_memory_grow :: proc(index, delta: uintptr) -> int ---
wasm_memory_size :: proc(index: uintptr) -> int --- wasm_memory_size :: proc(index: uintptr) -> int ---
+13
View File
@@ -560,6 +560,19 @@ Odin_Platform_Subtarget_Type :: type_of(ODIN_PLATFORM_SUBTARGET)
*/ */
Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS) Odin_Sanitizer_Flags :: type_of(ODIN_SANITIZER_FLAGS)
/*
// Defined internally by the compiler
Odin_Optimization_Mode :: enum int {
None = -1,
Minimal = 0,
Size = 1,
Speed = 2,
Aggressive = 3,
}
ODIN_OPTIMIZATION_MODE // is a constant
*/
Odin_Optimization_Mode :: type_of(ODIN_OPTIMIZATION_MODE)
///////////////////////////// /////////////////////////////
// Init Startup Procedures // // Init Startup Procedures //
+5 -5
View File
@@ -383,7 +383,7 @@ clear_map :: proc "contextless" (m: ^$T/map[$K]$V) {
// //
// Note: Prefer the procedure group `reserve` // Note: Prefer the procedure group `reserve`
@builtin @builtin
reserve_map :: proc(m: ^$T/map[$K]$V, capacity: int, loc := #caller_location) -> Allocator_Error { reserve_map :: proc(m: ^$T/map[$K]$V, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil return __dynamic_map_reserve((^Raw_Map)(m), map_info(T), uint(capacity), loc) if m != nil else nil
} }
@@ -721,12 +721,12 @@ _reserve_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, capacity: i
} }
@builtin @builtin
reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error { reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array(array, capacity, true, loc) return _reserve_dynamic_array(array, capacity, true, loc)
} }
@builtin @builtin
non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, capacity: int, loc := #caller_location) -> Allocator_Error { non_zero_reserve_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int capacity: int, loc := #caller_location) -> Allocator_Error {
return _reserve_dynamic_array(array, capacity, false, loc) return _reserve_dynamic_array(array, capacity, false, loc)
} }
@@ -773,12 +773,12 @@ _resize_dynamic_array :: #force_inline proc(array: ^$T/[dynamic]$E, length: int,
} }
@builtin @builtin
resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error { resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array(array, length, true, loc=loc) return _resize_dynamic_array(array, length, true, loc=loc)
} }
@builtin @builtin
non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, length: int, loc := #caller_location) -> Allocator_Error { non_zero_resize_dynamic_array :: proc(array: ^$T/[dynamic]$E, #any_int length: int, loc := #caller_location) -> Allocator_Error {
return _resize_dynamic_array(array, length, false, loc=loc) return _resize_dynamic_array(array, length, false, loc=loc)
} }
+2 -2
View File
@@ -11,7 +11,7 @@ HAS_RAND_BYTES :: true
_rand_bytes :: proc(dst: []byte) { _rand_bytes :: proc(dst: []byte) {
err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst)) err := Sec.RandomCopyBytes(count=len(dst), bytes=raw_data(dst))
if err != .Success { if err != .Success {
msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err)) msg := CF.StringCopyToOdinString(Sec.CopyErrorMessageString(err))
panic(fmt.tprintf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)) fmt.panicf("crypto/rand_bytes: SecRandomCopyBytes returned non-zero result: %v %s", err, msg)
} }
} }
+1 -1
View File
@@ -32,7 +32,7 @@ _rand_bytes :: proc (dst: []byte) {
// All other failures are things that should NEVER happen // All other failures are things that should NEVER happen
// unless the kernel interface changes (ie: the Linux // unless the kernel interface changes (ie: the Linux
// developers break userland). // developers break userland).
panic(fmt.tprintf("crypto: getrandom failed: %v", errno)) fmt.panicf("crypto: getrandom failed: %v", errno)
} }
l -= n_read l -= n_read
dst = dst[n_read:] dst = dst[n_read:]
+10 -10
View File
@@ -11,16 +11,16 @@ _rand_bytes :: proc(dst: []byte) {
ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG)) ret := (os.Errno)(win32.BCryptGenRandom(nil, raw_data(dst), u32(len(dst)), win32.BCRYPT_USE_SYSTEM_PREFERRED_RNG))
if ret != os.ERROR_NONE { if ret != os.ERROR_NONE {
switch ret { switch ret {
case os.ERROR_INVALID_HANDLE: case os.ERROR_INVALID_HANDLE:
// The handle to the first parameter is invalid. // The handle to the first parameter is invalid.
// This should not happen here, since we explicitly pass nil to it // This should not happen here, since we explicitly pass nil to it
panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm") panic("crypto: BCryptGenRandom Invalid handle for hAlgorithm")
case os.ERROR_INVALID_PARAMETER: case os.ERROR_INVALID_PARAMETER:
// One of the parameters was invalid // One of the parameters was invalid
panic("crypto: BCryptGenRandom Invalid parameter") panic("crypto: BCryptGenRandom Invalid parameter")
case: case:
// Unknown error // Unknown error
panic(fmt.tprintf("crypto: BCryptGenRandom failed: %d\n", ret)) fmt.panicf("crypto: BCryptGenRandom failed: %d\n", ret)
} }
} }
} }
+3 -3
View File
@@ -527,7 +527,7 @@ angle_from_quaternion :: proc{
@(require_results) @(require_results)
axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> Vector3f16 { axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> Vector3f16 {
t1 := 1 - q.w*q.w t1 := 1 - q.w*q.w
if t1 < 0 { if t1 <= 0 {
return {0, 0, 1} return {0, 0, 1}
} }
t2 := 1.0 / math.sqrt(t1) t2 := 1.0 / math.sqrt(t1)
@@ -536,7 +536,7 @@ axis_from_quaternion_f16 :: proc "contextless" (q: Quaternionf16) -> Vector3f16
@(require_results) @(require_results)
axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> Vector3f32 { axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> Vector3f32 {
t1 := 1 - q.w*q.w t1 := 1 - q.w*q.w
if t1 < 0 { if t1 <= 0 {
return {0, 0, 1} return {0, 0, 1}
} }
t2 := 1.0 / math.sqrt(t1) t2 := 1.0 / math.sqrt(t1)
@@ -545,7 +545,7 @@ axis_from_quaternion_f32 :: proc "contextless" (q: Quaternionf32) -> Vector3f32
@(require_results) @(require_results)
axis_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> Vector3f64 { axis_from_quaternion_f64 :: proc "contextless" (q: Quaternionf64) -> Vector3f64 {
t1 := 1 - q.w*q.w t1 := 1 - q.w*q.w
if t1 < 0 { if t1 <= 0 {
return {0, 0, 1} return {0, 0, 1}
} }
t2 := 1.0 / math.sqrt(t1) t2 := 1.0 / math.sqrt(t1)
+36 -1
View File
@@ -1455,7 +1455,7 @@ parse_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
case "unroll": case "unroll":
return parse_unrolled_for_loop(p, tag) return parse_unrolled_for_loop(p, tag)
case "reverse": case "reverse":
stmt := parse_for_stmt(p) stmt := parse_stmt(p)
if range, is_range := stmt.derived.(^ast.Range_Stmt); is_range { if range, is_range := stmt.derived.(^ast.Range_Stmt); is_range {
if range.reverse { if range.reverse {
@@ -3515,6 +3515,25 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
case op.kind == .Colon: case op.kind == .Colon:
expect_token_after(p, .Colon, "identifier list") expect_token_after(p, .Colon, "identifier list")
if .Label in flags && len(lhs) == 1 { if .Label in flags && len(lhs) == 1 {
is_partial := false
is_reverse := false
partial_token: tokenizer.Token
if p.curr_tok.kind == .Hash {
name := peek_token(p)
if name.kind == .Ident && name.text == "partial" &&
peek_token(p, 1).kind == .Switch {
partial_token = expect_token(p, .Hash)
expect_token(p, .Ident)
is_partial = true
} else if name.kind == .Ident && name.text == "reverse" &&
peek_token(p, 1).kind == .For {
partial_token = expect_token(p, .Hash)
expect_token(p, .Ident)
is_reverse = true
}
}
#partial switch p.curr_tok.kind { #partial switch p.curr_tok.kind {
case .Open_Brace, .If, .For, .Switch: case .Open_Brace, .If, .For, .Switch:
label := lhs[0] label := lhs[0]
@@ -3529,6 +3548,22 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
case ^ast.Type_Switch_Stmt: n.label = label case ^ast.Type_Switch_Stmt: n.label = label
case ^ast.Range_Stmt: n.label = label case ^ast.Range_Stmt: n.label = label
} }
if is_partial {
#partial switch n in stmt.derived_stmt {
case ^ast.Switch_Stmt: n.partial = true
case ^ast.Type_Switch_Stmt: n.partial = true
case:
error(p, partial_token.pos, "incorrect use of directive, use '%s: #partial switch'", partial_token.text)
}
}
if is_reverse {
#partial switch n in stmt.derived_stmt {
case ^ast.Range_Stmt: n.reverse = true
case:
error(p, partial_token.pos, "incorrect use of directive, use '%s: #reverse for'", partial_token.text)
}
}
} }
return stmt return stmt
+49 -12
View File
@@ -112,15 +112,15 @@ EOWNERDEAD: Errno : 96
O_RDONLY :: 0x00000 O_RDONLY :: 0x00000
O_WRONLY :: 0x00001 O_WRONLY :: 0x00001
O_RDWR :: 0x00002 O_RDWR :: 0x00002
O_CREATE :: 0x00040 O_NONBLOCK :: 0x00004
O_EXCL :: 0x00080 O_APPEND :: 0x00008
O_NOCTTY :: 0x00100 O_ASYNC :: 0x00040
O_TRUNC :: 0x00200 O_SYNC :: 0x00080
O_NONBLOCK :: 0x00800 O_CREATE :: 0x00200
O_APPEND :: 0x00400 O_TRUNC :: 0x00400
O_SYNC :: 0x01000 O_EXCL :: 0x00800
O_ASYNC :: 0x02000 O_NOCTTY :: 0x08000
O_CLOEXEC :: 0x80000 O_CLOEXEC :: 0100000
SEEK_DATA :: 3 SEEK_DATA :: 3
@@ -140,6 +140,8 @@ RTLD_NOLOAD :: 0x02000
MAX_PATH :: 1024 MAX_PATH :: 1024
KINFO_FILE_SIZE :: 1392
args := _alloc_command_line_arguments() args := _alloc_command_line_arguments()
Unix_File_Time :: struct { Unix_File_Time :: struct {
@@ -191,6 +193,21 @@ OS_Stat :: struct {
lspare: [10]u64, lspare: [10]u64,
} }
KInfo_File :: struct {
structsize: c.int,
type: c.int,
fd: c.int,
ref_count: c.int,
flags: c.int,
pad0: c.int,
offset: i64,
// NOTE(Feoramund): This field represents a complicated union that I am
// avoiding implementing for now. I only need the path data below.
_union: [336]byte,
path: [MAX_PATH]c.char,
}
// since FreeBSD v12 // since FreeBSD v12
Dirent :: struct { Dirent :: struct {
@@ -254,6 +271,8 @@ X_OK :: 1 // Test for execute permission
W_OK :: 2 // Test for write permission W_OK :: 2 // Test for write permission
R_OK :: 4 // Test for read permission R_OK :: 4 // Test for read permission
F_KINFO :: 22
foreign libc { foreign libc {
@(link_name="__error") __errno_location :: proc() -> ^c.int --- @(link_name="__error") __errno_location :: proc() -> ^c.int ---
@@ -274,6 +293,7 @@ foreign libc {
@(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int --- @(link_name="unlink") _unix_unlink :: proc(path: cstring) -> c.int ---
@(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int --- @(link_name="rmdir") _unix_rmdir :: proc(path: cstring) -> c.int ---
@(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int --- @(link_name="mkdir") _unix_mkdir :: proc(path: cstring, mode: mode_t) -> c.int ---
@(link_name="fcntl") _unix_fcntl :: proc(fd: Handle, cmd: c.int, arg: uintptr) -> c.int ---
@(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir --- @(link_name="fdopendir") _unix_fdopendir :: proc(fd: Handle) -> Dir ---
@(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int --- @(link_name="closedir") _unix_closedir :: proc(dirp: Dir) -> c.int ---
@@ -365,7 +385,7 @@ seek :: proc(fd: Handle, offset: i64, whence: int) -> (i64, Errno) {
} }
file_size :: proc(fd: Handle) -> (i64, Errno) { file_size :: proc(fd: Handle) -> (i64, Errno) {
s, err := fstat(fd) s, err := _fstat(fd)
if err != ERROR_NONE { if err != ERROR_NONE {
return -1, err return -1, err
} }
@@ -591,9 +611,26 @@ _readlink :: proc(path: string) -> (string, Errno) {
return "", Errno{} return "", Errno{}
} }
// XXX FreeBSD
absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) { absolute_path_from_handle :: proc(fd: Handle) -> (string, Errno) {
return "", Errno(ENOSYS) // NOTE(Feoramund): The situation isn't ideal, but this was the best way I
// could find to implement this. There are a couple outstanding bug reports
// regarding the desire to retrieve an absolute path from a handle, but to
// my knowledge, there hasn't been any work done on it.
//
// https://bugs.freebsd.org/bugzilla/show_bug.cgi?id=198570
//
// This may be unreliable, according to a comment from 2023.
kinfo: KInfo_File
kinfo.structsize = KINFO_FILE_SIZE
res := _unix_fcntl(fd, F_KINFO, cast(uintptr)&kinfo)
if res == -1 {
return "", Errno(get_last_error())
}
path := strings.clone_from_cstring_bounded(cast(cstring)&kinfo.path[0], len(kinfo.path))
return path, ERROR_NONE
} }
absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) { absolute_path_from_relative :: proc(rel: string) -> (path: string, err: Errno) {
+1 -1
View File
@@ -56,7 +56,7 @@ foreign libc {
@(link_name="free") _unix_free :: proc(ptr: rawptr) --- @(link_name="free") _unix_free :: proc(ptr: rawptr) ---
} }
when ODIN_OS == .Darwin { when ODIN_OS == .Darwin || ODIN_OS == .FreeBSD {
@(private) @(private)
foreign libc { foreign libc {
@(link_name="__error") __error :: proc() -> ^i32 --- @(link_name="__error") __error :: proc() -> ^i32 ---
+8 -1
View File
@@ -6,6 +6,7 @@ import "base:intrinsics"
import "core:c/libc" import "core:c/libc"
import "core:encoding/ansi" import "core:encoding/ansi"
import "core:sync" import "core:sync"
import "core:os"
@require import "core:sys/unix" @require import "core:sys/unix"
@(private="file") stop_runner_flag: libc.sig_atomic_t @(private="file") stop_runner_flag: libc.sig_atomic_t
@@ -20,7 +21,13 @@ local_test_index: libc.sig_atomic_t
@(private="file") @(private="file")
stop_runner_callback :: proc "c" (sig: libc.int) { stop_runner_callback :: proc "c" (sig: libc.int) {
intrinsics.atomic_store(&stop_runner_flag, 1) prev := intrinsics.atomic_add(&stop_runner_flag, 1)
// If the flag was already set (if this is the second signal sent for example),
// consider this a forced (not graceful) exit.
if prev > 0 {
os.exit(int(sig))
}
} }
@(private="file") @(private="file")
+2 -2
View File
@@ -127,13 +127,13 @@ days_remaining :: proc "contextless" (date: Date) -> (days_remaining: i64, err:
return delta.days, .None return delta.days, .None
} }
last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i64, err: Error) { last_day_of_month :: proc "contextless" (#any_int year: i64, #any_int month: i8) -> (day: i8, err: Error) {
// Not using formula 2.27 from the book. This is far simpler and gives the same answer. // Not using formula 2.27 from the book. This is far simpler and gives the same answer.
validate(Date{year, month, 1}) or_return validate(Date{year, month, 1}) or_return
month_days := MONTH_DAYS month_days := MONTH_DAYS
day = i64(month_days[month]) day = month_days[month]
if month == 2 && is_leap_year(year) { if month == 2 && is_leap_year(year) {
day += 1 day += 1
} }
+46
View File
@@ -1857,6 +1857,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case BuiltinProc_objc_register_class: case BuiltinProc_objc_register_class:
case BuiltinProc_atomic_type_is_lock_free: case BuiltinProc_atomic_type_is_lock_free:
case BuiltinProc_has_target_feature: case BuiltinProc_has_target_feature:
case BuiltinProc_procedure_of:
// NOTE(bill): The first arg may be a Type, this will be checked case by case // NOTE(bill): The first arg may be a Type, this will be checked case by case
break; break;
@@ -6171,6 +6172,51 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
break; break;
} }
case BuiltinProc_procedure_of:
{
Ast *call_expr = unparen_expr(ce->args[0]);
Operand op = {};
check_expr_base(c, &op, ce->args[0], nullptr);
if (op.mode != Addressing_Value && !(call_expr && call_expr->kind == Ast_CallExpr)) {
error(ce->args[0], "Expected a call expression for '%.*s'", LIT(builtin_name));
return false;
}
Ast *proc = call_expr->CallExpr.proc;
Entity *e = entity_of_node(proc);
if (e == nullptr) {
error(ce->args[0], "Invalid procedure value, expected a regular/specialized procedure");
return false;
}
TypeAndValue tav = proc->tav;
operand->type = e->type;
operand->mode = Addressing_Value;
operand->value = tav.value;
operand->builtin_id = BuiltinProc_Invalid;
operand->proc_group = nullptr;
if (tav.mode == Addressing_Builtin) {
operand->mode = tav.mode;
operand->builtin_id = cast(BuiltinProcId)e->Builtin.id;
break;
}
if (!is_type_proc(e->type)) {
gbString s = type_to_string(e->type);
error(ce->args[0], "Expected a procedure value, got '%s'", s);
gb_string_free(s);
return false;
}
ce->entity_procedure_of = e;
break;
}
case BuiltinProc_constant_utf16_cstring: case BuiltinProc_constant_utf16_cstring:
{ {
String value = {}; String value = {};
+23 -6
View File
@@ -88,11 +88,17 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
e->type = t_invalid; e->type = t_invalid;
return nullptr; return nullptr;
} else if (is_type_polymorphic(t)) { } else if (is_type_polymorphic(t)) {
gbString str = type_to_string(t); Entity *e = entity_of_node(operand->expr);
defer (gb_string_free(str)); if (e == nullptr) {
error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name)); return nullptr;
e->type = t_invalid; }
return nullptr; if (e->state.load() != EntityState_Resolved) {
gbString str = type_to_string(t);
defer (gb_string_free(str));
error(e->token, "Invalid use of a polymorphic type '%s' in %.*s", str, LIT(context_name));
e->type = t_invalid;
return nullptr;
}
} else if (is_type_empty_union(t)) { } else if (is_type_empty_union(t)) {
gbString str = type_to_string(t); gbString str = type_to_string(t);
defer (gb_string_free(str)); defer (gb_string_free(str));
@@ -479,6 +485,9 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
entity = check_selector(ctx, &operand, init, e->type); entity = check_selector(ctx, &operand, init, e->type);
} else { } else {
check_expr_or_type(ctx, &operand, init, e->type); check_expr_or_type(ctx, &operand, init, e->type);
if (init->kind == Ast_CallExpr) {
entity = init->CallExpr.entity_procedure_of;
}
} }
switch (operand.mode) { switch (operand.mode) {
@@ -526,6 +535,7 @@ gb_internal void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr
return; return;
} }
if (entity != nullptr) { if (entity != nullptr) {
if (e->type != nullptr) { if (e->type != nullptr) {
Operand x = {}; Operand x = {};
@@ -1135,7 +1145,14 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
} }
if (ac.link_name.len > 0) { if (ac.link_name.len > 0) {
e->Procedure.link_name = ac.link_name; String ln = ac.link_name;
e->Procedure.link_name = ln;
if (ln == "memcpy" ||
ln == "memmove" ||
ln == "mem_copy" ||
ln == "mem_copy_non_overlapping") {
e->Procedure.is_memcpy_like = true;
}
} }
if (ac.deferred_procedure.entity != nullptr) { if (ac.deferred_procedure.entity != nullptr) {
+2 -1
View File
@@ -578,6 +578,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
d->defer_use_checked = false; d->defer_use_checked = false;
Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags); Entity *entity = alloc_entity_procedure(nullptr, token, final_proc_type, tags);
entity->state.store(EntityState_Resolved);
entity->identifier = ident; entity->identifier = ident;
add_entity_and_decl_info(&nctx, ident, entity, d); add_entity_and_decl_info(&nctx, ident, entity, d);
@@ -7674,7 +7675,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
if (decl->proc_lit) { if (decl->proc_lit) {
ast_node(pl, ProcLit, decl->proc_lit); ast_node(pl, ProcLit, decl->proc_lit);
if (pl->inlining == ProcInlining_no_inline) { if (pl->inlining == ProcInlining_no_inline) {
error(call, "'#force_inline' cannot be applied to a procedure that has be marked as '#force_no_inline'"); error(call, "'#force_inline' cannot be applied to a procedure that has been marked as '#force_no_inline'");
} }
} }
} }
+9 -1
View File
@@ -2224,8 +2224,16 @@ gb_internal void check_expr_stmt(CheckerContext *ctx, Ast *node) {
} }
if (do_require) { if (do_require) {
gbString expr_str = expr_to_string(ce->proc); gbString expr_str = expr_to_string(ce->proc);
defer (gb_string_free(expr_str));
if (builtin_id) {
String real_name = builtin_procs[builtin_id].name;
if (real_name != make_string(cast(u8 const *)expr_str, gb_string_length(expr_str))) {
error(node, "'%s' ('%.*s.%.*s') requires that its results must be handled", expr_str,
LIT(builtin_proc_pkg_name[builtin_procs[builtin_id].pkg]), LIT(real_name));
return;
}
}
error(node, "'%s' requires that its results must be handled", expr_str); error(node, "'%s' requires that its results must be handled", expr_str);
gb_string_free(expr_str);
} }
return; return;
} else if (expr && expr->kind == Ast_SelectorCallExpr) { } else if (expr && expr->kind == Ast_SelectorCallExpr) {
+31
View File
@@ -3,6 +3,8 @@
#include "entity.cpp" #include "entity.cpp"
#include "types.cpp" #include "types.cpp"
String get_final_microarchitecture();
gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression); gb_internal void check_expr(CheckerContext *c, Operand *operand, Ast *expression);
gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr); gb_internal void check_expr_or_type(CheckerContext *c, Operand *operand, Ast *expression, Type *type_hint=nullptr);
gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t); gb_internal void add_comparison_procedures_for_fields(CheckerContext *c, Type *t);
@@ -1040,6 +1042,8 @@ gb_internal void init_universal(void) {
add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch); add_global_enum_constant(fields, "ODIN_ARCH", bc->metrics.arch);
add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]); add_global_string_constant("ODIN_ARCH_STRING", target_arch_names[bc->metrics.arch]);
} }
add_global_string_constant("ODIN_MICROARCH_STRING", get_final_microarchitecture());
{ {
GlobalEnumValue values[BuildMode_COUNT] = { GlobalEnumValue values[BuildMode_COUNT] = {
@@ -1130,6 +1134,17 @@ gb_internal void init_universal(void) {
add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp())); add_global_constant("ODIN_COMPILE_TIMESTAMP", t_untyped_integer, exact_value_i64(odin_compile_timestamp()));
{
String version = {};
#ifdef GIT_SHA
version.text = cast(u8 *)GIT_SHA;
version.len = gb_strlen(GIT_SHA);
#endif
add_global_string_constant("ODIN_VERSION_HASH", version);
}
{ {
bool f16_supported = lb_use_new_pass_system(); bool f16_supported = lb_use_new_pass_system();
if (is_arch_wasm()) { if (is_arch_wasm()) {
@@ -1167,6 +1182,18 @@ gb_internal void init_universal(void) {
add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags)); add_global_constant("ODIN_SANITIZER_FLAGS", named_type, exact_value_u64(bc->sanitizer_flags));
} }
{
GlobalEnumValue values[5] = {
{"None", -1},
{"Minimal", 0},
{"Size", 1},
{"Speed", 2},
{"Aggressive", 3},
};
auto fields = add_global_enum_type(str_lit("Odin_Optimization_Mode"), values, gb_count_of(values));
add_global_enum_constant(fields, "ODIN_OPTIMIZATION_MODE", bc->optimization_level);
}
// Builtin Procedures // Builtin Procedures
@@ -1452,6 +1479,10 @@ gb_internal Entity *entity_of_node(Ast *expr) {
case_ast_node(cc, CaseClause, expr); case_ast_node(cc, CaseClause, expr);
return cc->implicit_entity; return cc->implicit_entity;
case_end; case_end;
case_ast_node(ce, CallExpr, expr);
return ce->entity_procedure_of;
case_end;
} }
return nullptr; return nullptr;
} }
+6
View File
@@ -51,6 +51,12 @@ enum StmtFlag {
enum BuiltinProcPkg { enum BuiltinProcPkg {
BuiltinProcPkg_builtin, BuiltinProcPkg_builtin,
BuiltinProcPkg_intrinsics, BuiltinProcPkg_intrinsics,
BuiltinProcPkg_COUNT
};
String builtin_proc_pkg_name[BuiltinProcPkg_COUNT] = {
str_lit("builtin"),
str_lit("intrinsics"),
}; };
struct BuiltinProc { struct BuiltinProc {
+4
View File
@@ -299,6 +299,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc__type_end, BuiltinProc__type_end,
BuiltinProc_procedure_of,
BuiltinProc___entry_point, BuiltinProc___entry_point,
BuiltinProc_objc_send, BuiltinProc_objc_send,
@@ -614,6 +616,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT(""), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("procedure_of"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("__entry_point"), 0, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true}, {STR_LIT("objc_send"), 3, true, Expr_Expr, BuiltinProcPkg_intrinsics, false, true},
+1
View File
@@ -256,6 +256,7 @@ struct Entity {
bool generated_from_polymorphic : 1; bool generated_from_polymorphic : 1;
bool entry_point_only : 1; bool entry_point_only : 1;
bool has_instrumentation : 1; bool has_instrumentation : 1;
bool is_memcpy_like : 1;
} Procedure; } Procedure;
struct { struct {
Array<Entity *> entities; Array<Entity *> entities;
+2 -2
View File
@@ -516,7 +516,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end,
if (pos.line == 0) { if (pos.line == 0) {
error_out_empty(); error_out_empty();
error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
error_out_va(fmt, va); error_out_va(fmt, va);
error_out("\n"); error_out("\n");
} else { } else {
@@ -527,7 +527,7 @@ gb_internal void syntax_error_with_verbose_va(TokenPos const &pos, TokenPos end,
error_out_pos(pos); error_out_pos(pos);
} }
if (has_ansi_terminal_colours()) { if (has_ansi_terminal_colours()) {
error_out_coloured("Syntax_Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_coloured("Syntax Error: ", TerminalStyle_Normal, TerminalColour_Red);
} }
error_out_va(fmt, va); error_out_va(fmt, va);
error_out("\n"); error_out("\n");
+136 -108
View File
@@ -9,6 +9,11 @@
#endif #endif
#ifndef LLVM_IGNORE_VERIFICATION
#define LLVM_IGNORE_VERIFICATION 0
#endif
#include "llvm_backend.hpp" #include "llvm_backend.hpp"
#include "llvm_abi.cpp" #include "llvm_abi.cpp"
#include "llvm_backend_opt.cpp" #include "llvm_backend_opt.cpp"
@@ -1125,6 +1130,53 @@ gb_internal void lb_finalize_objc_names(lbProcedure *p) {
lb_end_procedure_body(p); lb_end_procedure_body(p);
} }
gb_internal void lb_verify_function(lbModule *m, lbProcedure *p, bool dump_ll=false) {
if (LLVM_IGNORE_VERIFICATION) {
return;
}
if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) {
char *llvm_error = nullptr;
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
LLVMDumpValue(p->value);
gb_printf_err("\n");
if (dump_ll) {
gb_printf_err("\n\n\n");
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
}
}
LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
exit_with_errors();
}
}
gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
char *llvm_error = nullptr;
defer (LLVMDisposeMessage(llvm_error));
lbModule *m = cast(lbModule *)data;
if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
gb_printf_err("LLVM Error:\n%s\n", llvm_error);
if (build_context.keep_temp_files) {
TIME_SECTION("LLVM Print Module to File");
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
exit_with_errors();
return false;
}
}
exit_with_errors();
return 1;
}
return 0;
}
gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *objc_names, Array<lbGlobalVariable> &global_variables) { // Startup Runtime
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin); Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
@@ -1227,13 +1279,7 @@ gb_internal lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProc
lb_end_procedure_body(p); lb_end_procedure_body(p);
if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { lb_verify_function(main_module, p);
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
LLVMDumpValue(p->value);
gb_printf_err("\n\n\n\n");
LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
}
return p; return p;
} }
@@ -1256,31 +1302,21 @@ gb_internal lbProcedure *lb_create_cleanup_runtime(lbModule *main_module) { // C
lb_end_procedure_body(p); lb_end_procedure_body(p);
if (!main_module->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { lb_verify_function(main_module, p);
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
LLVMDumpValue(p->value);
gb_printf_err("\n\n\n\n");
LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
}
return p; return p;
} }
gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) { gb_internal WORKER_TASK_PROC(lb_generate_procedures_and_types_per_module) {
lbModule *m = cast(lbModule *)data; lbModule *m = cast(lbModule *)data;
for (Entity *e : m->global_procedures_and_types_to_create) { for (Entity *e : m->global_types_to_create) {
if (e->kind == Entity_TypeName) { (void)lb_get_entity_name(m, e);
(void)lb_get_entity_name(m, e); (void)lb_type(m, e->type);
lb_type(m, e->type);
}
} }
for (Entity *e : m->global_procedures_and_types_to_create) { for (Entity *e : m->global_procedures_to_create) {
if (e->kind == Entity_Procedure) { (void)lb_get_entity_name(m, e);
(void)lb_get_entity_name(m, e); array_add(&m->procedures_to_generate, lb_create_procedure(m, e));
array_add(&m->procedures_to_generate, lb_create_procedure(m, e));
}
} }
return 0; return 0;
} }
@@ -1334,16 +1370,24 @@ gb_internal void lb_create_global_procedures_and_types(lbGenerator *gen, Checker
m = lb_module_of_entity(gen, e); m = lb_module_of_entity(gen, e);
} }
array_add(&m->global_procedures_and_types_to_create, e); if (e->kind == Entity_Procedure) {
array_add(&m->global_procedures_to_create, e);
} else if (e->kind == Entity_TypeName) {
array_add(&m->global_types_to_create, e);
}
} }
for (auto const &entry : gen->modules) { if (do_threading) {
lbModule *m = entry.value; for (auto const &entry : gen->modules) {
if (do_threading) { lbModule *m = entry.value;
thread_pool_add_task(lb_generate_procedures_and_types_per_module, m); thread_pool_add_task(lb_generate_procedures_and_types_per_module, m);
} else { }
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
lb_generate_procedures_and_types_per_module(m); lb_generate_procedures_and_types_per_module(m);
} }
} }
thread_pool_wait(); thread_pool_wait();
@@ -2374,16 +2418,19 @@ gb_internal WORKER_TASK_PROC(lb_generate_procedures_worker_proc) {
} }
gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) { gb_internal void lb_generate_procedures(lbGenerator *gen, bool do_threading) {
for (auto const &entry : gen->modules) { if (do_threading) {
lbModule *m = entry.value; for (auto const &entry : gen->modules) {
if (do_threading) { lbModule *m = entry.value;
thread_pool_add_task(lb_generate_procedures_worker_proc, m); thread_pool_add_task(lb_generate_procedures_worker_proc, m);
} else { }
thread_pool_wait();
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
lb_generate_procedures_worker_proc(m); lb_generate_procedures_worker_proc(m);
} }
} }
thread_pool_wait();
} }
gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) { gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc) {
@@ -2397,17 +2444,20 @@ gb_internal WORKER_TASK_PROC(lb_generate_missing_procedures_to_check_worker_proc
} }
gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) { gb_internal void lb_generate_missing_procedures(lbGenerator *gen, bool do_threading) {
for (auto const &entry : gen->modules) { if (do_threading) {
lbModule *m = entry.value; for (auto const &entry : gen->modules) {
// NOTE(bill): procedures may be added during generation lbModule *m = entry.value;
if (do_threading) { // NOTE(bill): procedures may be added during generation
thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m); thread_pool_add_task(lb_generate_missing_procedures_to_check_worker_proc, m);
} else { }
thread_pool_wait();
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
// NOTE(bill): procedures may be added during generation
lb_generate_missing_procedures_to_check_worker_proc(m); lb_generate_missing_procedures_to_check_worker_proc(m);
} }
} }
thread_pool_wait();
} }
gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) { gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
@@ -2420,32 +2470,45 @@ gb_internal void lb_debug_info_complete_types_and_finalize(lbGenerator *gen) {
} }
gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) { gb_internal void lb_llvm_function_passes(lbGenerator *gen, bool do_threading) {
for (auto const &entry : gen->modules) { if (do_threading) {
lbModule *m = entry.value; for (auto const &entry : gen->modules) {
if (do_threading) { lbModule *m = entry.value;
thread_pool_add_task(lb_llvm_function_pass_per_module, m); thread_pool_add_task(lb_llvm_function_pass_per_module, m);
} else { }
thread_pool_wait();
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
lb_llvm_function_pass_per_module(m); lb_llvm_function_pass_per_module(m);
} }
} }
thread_pool_wait();
} }
gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) { gb_internal void lb_llvm_module_passes(lbGenerator *gen, bool do_threading) {
for (auto const &entry : gen->modules) { if (do_threading) {
lbModule *m = entry.value; for (auto const &entry : gen->modules) {
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData); lbModule *m = entry.value;
wd->m = m; auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
wd->target_machine = m->target_machine; wd->m = m;
wd->target_machine = m->target_machine;
if (do_threading) { if (do_threading) {
thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd); thread_pool_add_task(lb_llvm_module_pass_worker_proc, wd);
} else { } else {
lb_llvm_module_pass_worker_proc(wd);
}
}
thread_pool_wait();
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
auto wd = gb_alloc_item(permanent_allocator(), lbLLVMModulePassWorkerData);
wd->m = m;
wd->target_machine = m->target_machine;
lb_llvm_module_pass_worker_proc(wd); lb_llvm_module_pass_worker_proc(wd);
} }
} }
thread_pool_wait();
} }
gb_internal String lb_filepath_ll_for_module(lbModule *m) { gb_internal String lb_filepath_ll_for_module(lbModule *m) {
@@ -2523,40 +2586,27 @@ gb_internal String lb_filepath_obj_for_module(lbModule *m) {
return concatenate_strings(permanent_allocator(), path, ext); return concatenate_strings(permanent_allocator(), path, ext);
} }
gb_internal WORKER_TASK_PROC(lb_llvm_module_verification_worker_proc) {
char *llvm_error = nullptr;
defer (LLVMDisposeMessage(llvm_error));
lbModule *m = cast(lbModule *)data;
if (LLVMVerifyModule(m->mod, LLVMReturnStatusAction, &llvm_error)) {
gb_printf_err("LLVM Error:\n%s\n", llvm_error);
if (build_context.keep_temp_files) {
TIME_SECTION("LLVM Print Module to File");
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
exit_with_errors();
return false;
}
}
exit_with_errors();
return 1;
}
return 0;
}
gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) { gb_internal bool lb_llvm_module_verification(lbGenerator *gen, bool do_threading) {
for (auto const &entry : gen->modules) { if (LLVM_IGNORE_VERIFICATION) {
lbModule *m = entry.value; return true;
if (do_threading) { }
if (do_threading) {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
thread_pool_add_task(lb_llvm_module_verification_worker_proc, m); thread_pool_add_task(lb_llvm_module_verification_worker_proc, m);
} else { }
thread_pool_wait();
} else {
for (auto const &entry : gen->modules) {
lbModule *m = entry.value;
if (lb_llvm_module_verification_worker_proc(m)) { if (lb_llvm_module_verification_worker_proc(m)) {
return false; return false;
} }
} }
} }
thread_pool_wait();
return true; return true;
} }
@@ -2777,12 +2827,7 @@ gb_internal lbProcedure *lb_create_main_procedure(lbModule *m, lbProcedure *star
} }
if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { lb_verify_function(m, p);
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %s\n", "main");
LLVMDumpValue(p->value);
gb_printf_err("\n\n\n\n");
LLVMVerifyFunction(p->value, LLVMAbortProcessAction);
}
lb_run_function_pass_manager(default_function_pass_manager, p, lbFunctionPassManager_default); lb_run_function_pass_manager(default_function_pass_manager, p, lbFunctionPassManager_default);
return p; return p;
@@ -2803,28 +2848,11 @@ gb_internal void lb_generate_procedure(lbModule *m, lbProcedure *p) {
lb_end_procedure(p); lb_end_procedure(p);
// Add Flags // Add Flags
if (p->body != nullptr) { if (p->entity && p->entity->kind == Entity_Procedure && p->entity->Procedure.is_memcpy_like) {
if (p->name == "memcpy" || p->name == "memmove" || p->flags |= lbProcedureFlag_WithoutMemcpyPass;
p->name == "runtime.mem_copy" || p->name == "mem_copy_non_overlapping" ||
string_starts_with(p->name, str_lit("llvm.memcpy")) ||
string_starts_with(p->name, str_lit("llvm.memmove"))) {
p->flags |= lbProcedureFlag_WithoutMemcpyPass;
}
} }
if (!m->debug_builder && LLVMVerifyFunction(p->value, LLVMReturnStatusAction)) { lb_verify_function(m, p, true);
char *llvm_error = nullptr;
gb_printf_err("LLVM CODE GEN FAILED FOR PROCEDURE: %.*s\n", LIT(p->name));
LLVMDumpValue(p->value);
gb_printf_err("\n\n\n\n");
String filepath_ll = lb_filepath_ll_for_module(m);
if (LLVMPrintModuleToFile(m->mod, cast(char const *)filepath_ll.text, &llvm_error)) {
gb_printf_err("LLVM Error: %s\n", llvm_error);
}
LLVMVerifyFunction(p->value, LLVMPrintMessageAction);
exit_with_errors();
}
} }
+2 -1
View File
@@ -181,7 +181,8 @@ struct lbModule {
std::atomic<u32> nested_type_name_guid; std::atomic<u32> nested_type_name_guid;
Array<lbProcedure *> procedures_to_generate; Array<lbProcedure *> procedures_to_generate;
Array<Entity *> global_procedures_and_types_to_create; Array<Entity *> global_procedures_to_create;
Array<Entity *> global_types_to_create;
lbProcedure *curr_procedure; lbProcedure *curr_procedure;
+18 -18
View File
@@ -626,50 +626,50 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
case Basic_complex32: case Basic_complex32:
{ {
LLVMMetadataRef elements[2] = {}; LLVMMetadataRef elements[2] = {};
elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0); elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f16, 0*16);
elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 4); elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 1*16);
return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements)); return lb_debug_basic_struct(m, str_lit("complex32"), 64, 32, elements, gb_count_of(elements));
} }
case Basic_complex64: case Basic_complex64:
{ {
LLVMMetadataRef elements[2] = {}; LLVMMetadataRef elements[2] = {};
elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0); elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f32, 0*32);
elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 4); elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 2*32);
return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements)); return lb_debug_basic_struct(m, str_lit("complex64"), 64, 32, elements, gb_count_of(elements));
} }
case Basic_complex128: case Basic_complex128:
{ {
LLVMMetadataRef elements[2] = {}; LLVMMetadataRef elements[2] = {};
elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0); elements[0] = lb_debug_struct_field(m, str_lit("real"), t_f64, 0*64);
elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 8); elements[1] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 1*64);
return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements)); return lb_debug_basic_struct(m, str_lit("complex128"), 128, 64, elements, gb_count_of(elements));
} }
case Basic_quaternion64: case Basic_quaternion64:
{ {
LLVMMetadataRef elements[4] = {}; LLVMMetadataRef elements[4] = {};
elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0); elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f16, 0*16);
elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 4); elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f16, 1*16);
elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 8); elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f16, 2*16);
elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 12); elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f16, 3*16);
return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements)); return lb_debug_basic_struct(m, str_lit("quaternion64"), 128, 32, elements, gb_count_of(elements));
} }
case Basic_quaternion128: case Basic_quaternion128:
{ {
LLVMMetadataRef elements[4] = {}; LLVMMetadataRef elements[4] = {};
elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0); elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f32, 0*32);
elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 4); elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f32, 1*32);
elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 8); elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f32, 2*32);
elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 12); elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f32, 3*32);
return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements)); return lb_debug_basic_struct(m, str_lit("quaternion128"), 128, 32, elements, gb_count_of(elements));
} }
case Basic_quaternion256: case Basic_quaternion256:
{ {
LLVMMetadataRef elements[4] = {}; LLVMMetadataRef elements[4] = {};
elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0); elements[0] = lb_debug_struct_field(m, str_lit("imag"), t_f64, 0*64);
elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 8); elements[1] = lb_debug_struct_field(m, str_lit("jmag"), t_f64, 1*64);
elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 16); elements[2] = lb_debug_struct_field(m, str_lit("kmag"), t_f64, 2*64);
elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 24); elements[3] = lb_debug_struct_field(m, str_lit("real"), t_f64, 3*64);
return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements)); return lb_debug_basic_struct(m, str_lit("quaternion256"), 256, 32, elements, gb_count_of(elements));
} }
+2 -1
View File
@@ -78,7 +78,8 @@ gb_internal void lb_init_module(lbModule *m, Checker *c) {
array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count); array_init(&m->procedures_to_generate, a, 0, c->info.all_procedures.count);
map_init(&m->procedure_values, c->info.all_procedures.count*2); map_init(&m->procedure_values, c->info.all_procedures.count*2);
} }
array_init(&m->global_procedures_and_types_to_create, a, 0, 1024); array_init(&m->global_procedures_to_create, a, 0, 1024);
array_init(&m->global_types_to_create, a, 0, 1024);
array_init(&m->missing_procedures_to_check, a, 0, 16); array_init(&m->missing_procedures_to_check, a, 0, 16);
map_init(&m->debug_values); map_init(&m->debug_values);
+11 -6
View File
@@ -555,7 +555,7 @@ gb_internal Ast *ast_unary_expr(AstFile *f, Token op, Ast *expr) {
syntax_error_with_verbose(expr, "'or_return' within an unary expression not wrapped in parentheses (...)"); syntax_error_with_verbose(expr, "'or_return' within an unary expression not wrapped in parentheses (...)");
break; break;
case Ast_OrBranchExpr: case Ast_OrBranchExpr:
syntax_error_with_verbose(expr, "'or_%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string)); syntax_error_with_verbose(expr, "'%.*s' within an unary expression not wrapped in parentheses (...)", LIT(expr->OrBranchExpr.token.string));
break; break;
} }
@@ -583,7 +583,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) {
syntax_error_with_verbose(left, "'or_return' within a binary expression not wrapped in parentheses (...)"); syntax_error_with_verbose(left, "'or_return' within a binary expression not wrapped in parentheses (...)");
break; break;
case Ast_OrBranchExpr: case Ast_OrBranchExpr:
syntax_error_with_verbose(left, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string)); syntax_error_with_verbose(left, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(left->OrBranchExpr.token.string));
break; break;
} }
if (right) switch (right->kind) { if (right) switch (right->kind) {
@@ -591,7 +591,7 @@ gb_internal Ast *ast_binary_expr(AstFile *f, Token op, Ast *left, Ast *right) {
syntax_error_with_verbose(right, "'or_return' within a binary expression not wrapped in parentheses (...)"); syntax_error_with_verbose(right, "'or_return' within a binary expression not wrapped in parentheses (...)");
break; break;
case Ast_OrBranchExpr: case Ast_OrBranchExpr:
syntax_error_with_verbose(right, "'or_%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string)); syntax_error_with_verbose(right, "'%.*s' within a binary expression not wrapped in parentheses (...)", LIT(right->OrBranchExpr.token.string));
break; break;
} }
@@ -2091,6 +2091,9 @@ gb_internal bool ast_on_same_line(Token const &x, Ast *yp) {
gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) { gb_internal Ast *parse_force_inlining_operand(AstFile *f, Token token) {
Ast *expr = parse_unary_expr(f, false); Ast *expr = parse_unary_expr(f, false);
Ast *e = strip_or_return_expr(expr); Ast *e = strip_or_return_expr(expr);
if (e == nullptr) {
return expr;
}
if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) { if (e->kind != Ast_ProcLit && e->kind != Ast_CallExpr) {
syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind])); syntax_error(expr, "%.*s must be followed by a procedure literal or call, got %.*s", LIT(token.string), LIT(ast_strings[expr->kind]));
return ast_bad_expr(f, token, f->curr_token); return ast_bad_expr(f, token, f->curr_token);
@@ -3102,7 +3105,7 @@ gb_internal void parse_check_or_return(Ast *operand, char const *msg) {
syntax_error_with_verbose(operand, "'or_return' use within %s is not wrapped in parentheses (...)", msg); syntax_error_with_verbose(operand, "'or_return' use within %s is not wrapped in parentheses (...)", msg);
break; break;
case Ast_OrBranchExpr: case Ast_OrBranchExpr:
syntax_error_with_verbose(operand, "'or_%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string)); syntax_error_with_verbose(operand, "'%.*s' use within %s is not wrapped in parentheses (...)", msg, LIT(operand->OrBranchExpr.token.string));
break; break;
} }
} }
@@ -3747,8 +3750,10 @@ gb_internal Ast *parse_simple_stmt(AstFile *f, u32 flags) {
case Ast_TypeSwitchStmt: case Ast_TypeSwitchStmt:
stmt->TypeSwitchStmt.partial = true; stmt->TypeSwitchStmt.partial = true;
break; break;
default:
syntax_error(partial_token, "Incorrect use of directive, use '%.*s: #partial switch'", LIT(ast_token(name).string));
break;
} }
syntax_error(partial_token, "Incorrect use of directive, use '#partial %.*s: switch'", LIT(ast_token(name).string));
} else if (is_reverse) { } else if (is_reverse) {
switch (stmt->kind) { switch (stmt->kind) {
case Ast_RangeStmt: case Ast_RangeStmt:
@@ -5176,7 +5181,7 @@ gb_internal Ast *parse_stmt(AstFile *f) {
} else if (tag == "unroll") { } else if (tag == "unroll") {
return parse_unrolled_for_loop(f, name); return parse_unrolled_for_loop(f, name);
} else if (tag == "reverse") { } else if (tag == "reverse") {
Ast *for_stmt = parse_for_stmt(f); Ast *for_stmt = parse_stmt(f);
if (for_stmt->kind == Ast_RangeStmt) { if (for_stmt->kind == Ast_RangeStmt) {
if (for_stmt->RangeStmt.reverse) { if (for_stmt->RangeStmt.reverse) {
syntax_error(token, "#reverse already applied to a 'for in' statement"); syntax_error(token, "#reverse already applied to a 'for in' statement");
+1
View File
@@ -458,6 +458,7 @@ AST_KIND(_ExprBegin, "", bool) \
bool optional_ok_one; \ bool optional_ok_one; \
bool was_selector; \ bool was_selector; \
AstSplitArgs *split_args; \ AstSplitArgs *split_args; \
Entity *entity_procedure_of; \
}) \ }) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(EnumFieldValue, "enum field value", struct { \ AST_KIND(EnumFieldValue, "enum field value", struct { \
+1 -1
View File
@@ -8,7 +8,7 @@
A test suite for `core:net` A test suite for `core:net`
*/ */
//+build !netbsd //+build !netbsd !freebsd !openbsd
package test_core_net package test_core_net
import "core:testing" import "core:testing"
+8 -6
View File
@@ -984,12 +984,14 @@ foreign lib {
LoadShader :: proc(vsFileName, fsFileName: cstring) -> Shader --- // Load shader from files and bind default locations LoadShader :: proc(vsFileName, fsFileName: cstring) -> Shader --- // Load shader from files and bind default locations
LoadShaderFromMemory :: proc(vsCode, fsCode: cstring) -> Shader --- // Load shader from code strings and bind default locations LoadShaderFromMemory :: proc(vsCode, fsCode: cstring) -> Shader --- // Load shader from code strings and bind default locations
IsShaderReady :: proc(shader: Shader) -> bool --- // Check if a shader is ready IsShaderReady :: proc(shader: Shader) -> bool --- // Check if a shader is ready
GetShaderLocation :: proc(shader: Shader, uniformName: cstring) -> ShaderLocationIndex --- // Get shader uniform location GetShaderLocation :: proc(shader: Shader, uniformName: cstring) -> c.int --- // Get shader uniform location
GetShaderLocationAttrib :: proc(shader: Shader, attribName: cstring) -> ShaderLocationIndex --- // Get shader attribute location GetShaderLocationAttrib :: proc(shader: Shader, attribName: cstring) -> c.int --- // Get shader attribute location
SetShaderValue :: proc(shader: Shader, locIndex: ShaderLocationIndex, value: rawptr, uniformType: ShaderUniformDataType) --- // Set shader uniform value
SetShaderValueV :: proc(shader: Shader, locIndex: ShaderLocationIndex, value: rawptr, uniformType: ShaderUniformDataType, count: c.int) --- // Set shader uniform value vector // We use #any_int here so we can pass ShaderLocationIndex
SetShaderValueMatrix :: proc(shader: Shader, locIndex: ShaderLocationIndex, mat: Matrix) --- // Set shader uniform value (matrix 4x4) SetShaderValue :: proc(shader: Shader, #any_int locIndex: c.int, value: rawptr, uniformType: ShaderUniformDataType) --- // Set shader uniform value
SetShaderValueTexture :: proc(shader: Shader, locIndex: ShaderLocationIndex, texture: Texture2D) --- // Set shader uniform value for texture (sampler2d) SetShaderValueV :: proc(shader: Shader, #any_int locIndex: c.int, value: rawptr, uniformType: ShaderUniformDataType, count: c.int) --- // Set shader uniform value vector
SetShaderValueMatrix :: proc(shader: Shader, #any_int locIndex: c.int, mat: Matrix) --- // Set shader uniform value (matrix 4x4)
SetShaderValueTexture :: proc(shader: Shader, #any_int locIndex: c.int, texture: Texture2D) --- // Set shader uniform value for texture (sampler2d)
UnloadShader :: proc(shader: Shader) --- // Unload shader from GPU memory (VRAM) UnloadShader :: proc(shader: Shader) --- // Unload shader from GPU memory (VRAM)
// Screen-space-related functions // Screen-space-related functions