Merge branch 'master' into fix-sync-badopt

This commit is contained in:
Tetralux
2020-04-22 06:12:41 +01:00
committed by GitHub
22 changed files with 1313 additions and 378 deletions
+21 -12
View File
@@ -1,4 +1,5 @@
name: Nightly
on:
schedule:
- cron: 0 20 * * *
@@ -91,36 +92,44 @@ jobs:
needs: [build_windows, build_macos, build_ubuntu]
steps:
- uses: actions/checkout@v1
- name: Install B2 CLI
shell: bash
run: sudo pip install --upgrade b2
- name: Download Windows artifacts
uses: actions/download-artifact@v1
with:
name: windows_artifacts
- name: Download Ubuntu artifacts
uses: actions/download-artifact@v1
with:
name: ubuntu_artifacts
- name: Download macOS artifacts
uses: actions/download-artifact@v1
with:
name: macos_artifacts
- name: Create archieves
run: |
now=$(date +'%Y-%m-%d')
7z a output/odin-windows-amd64-nightly+$now.zip -r windows_artifacts/
7z a output/odin-ubuntu-amd64-nightly+$now.zip -r ubuntu_artifacts/
7z a output/odin-macos-amd64-nightly+$now.zip -r macos_artifact/
- name: Upload artifacts to b2
- name: Create archives and upload
shell: bash
env:
APPID: ${{ secrets.B2_APPID }}
APPKEY: ${{ secrets.B2_APPKEY }}
BUCKET: ${{ secrets.B2_BUCKET }}
DAYS_TO_KEEP: ${{ secrets.B2_DAYS_TO_KEEP }}
run: |
b2 authorize-account "$APPID" "$APPKEY"
b2 sync --keepDays 7 output b2://odin-binaries/nightly
python3 ci/create_nightly_json.py > nightly.json
b2 upload-file odin-binaries nightly.json nightly.json
b2 clear-account
chmod +x ./ci/upload_create_nightly.sh
./ci/upload_create_nightly.sh "$BUCKET" windows-amd64 windows_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" ubuntu-amd64 ubuntu_artifacts/
./ci/upload_create_nightly.sh "$BUCKET" macos-amd64 macos_artifacts/
python3 ci/delete_old_binaries.py "$BUCKET" "$DAYS_TO_KEEP"
python3 ci/create_nightly_json.py "$BUCKET" > nightly.json
b2 upload-file "$BUCKET" nightly.json nightly.json
b2 clear-account
+12 -5
View File
@@ -1,8 +1,11 @@
@echo off
:: Make sure this is a decent name and not generic
set exe_name=odin.exe
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -GS- -EHsc- -GR- -O2 -MT -Z7 -DNO_ARRAY_BOUNDS_CHECK
set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF -O2 -MT -Z7
set compiler_defines= -DLLVM_BACKEND_SUPPORT -DNO_ARRAY_BOUNDS_CHECK
set compiler_warnings= ^
-W4 -WX ^
-wd4100 -wd4101 -wd4127 -wd4189 ^
@@ -12,13 +15,17 @@ set compiler_warnings= ^
set compiler_includes=
set libs= ^
kernel32.lib
kernel32.lib ^
bin\llvm\windows\LLVM-C.lib
set linker_flags= -incremental:no -opt:ref -subsystem:console -debug
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings%
set compiler_settings=%compiler_includes% %compiler_flags% %compiler_warnings% %compiler_defines%
set linker_settings=%libs% %linker_flags%
cl %compiler_settings% "src\main.cpp" ^
/link %linker_settings% -OUT:%exe_name% ^
del *.pdb > NUL 2> NUL
del *.ilk > NUL 2> NUL
cl %compiler_settings% "src\main.cpp" /link %linker_settings% -OUT:%exe_name%
:end_of_build
+4 -2
View File
@@ -3,18 +3,20 @@ import sys
import json
import datetime
import urllib.parse
import sys
def main():
files_by_date = {}
bucket = sys.argv[1]
files_lines = execute_cli("b2 ls --long odin-binaries nightly").split("\n")
files_lines = execute_cli(f"b2 ls --long {bucket} nightly").split("\n")
for x in files_lines:
parts = x.split(" ", 1)
if parts[0]:
json_str = execute_cli(f"b2 get-file-info {parts[0]}")
data = json.loads(json_str)
name = remove_prefix(data['fileName'], "nightly/")
url = f"https://f001.backblazeb2.com/file/odin-binaries/nightly/{urllib.parse.quote_plus(name)}"
url = f"https://f001.backblazeb2.com/file/{bucket}/nightly/{urllib.parse.quote_plus(name)}"
sha1 = data['contentSha1']
ts = int(data['fileInfo']['src_last_modified_millis'])
date = datetime.datetime.fromtimestamp(ts/1000).strftime('%Y-%m-%d')
+34
View File
@@ -0,0 +1,34 @@
import subprocess
import sys
import json
import datetime
import urllib.parse
import sys
def main():
files_by_date = {}
bucket = sys.argv[1]
days_to_keep = int(sys.argv[2])
print(f"Looking for binaries to delete older than {days_to_keep} days")
files_lines = execute_cli(f"b2 ls --long --versions {bucket} nightly").split("\n")
for x in files_lines:
parts = [y for y in x.split(' ') if y]
if parts and parts[0]:
date = datetime.datetime.strptime(parts[2], '%Y-%m-%d').replace(hour=0, minute=0, second=0, microsecond=0)
now = datetime.datetime.utcnow().replace(hour=0, minute=0, second=0, microsecond=0)
delta = now - date
if delta.days > days_to_keep:
print(f'Deleting {parts[5]}')
execute_cli(f'b2 delete-file-version {parts[0]}')
def execute_cli(command):
sb = subprocess.Popen(command, shell=True, stdout=subprocess.PIPE)
return sb.stdout.read().decode("utf-8");
if __name__ == '__main__':
sys.exit(main())
+13
View File
@@ -0,0 +1,13 @@
#!/bin/bash
bucket=$1
platform=$2
artifact=$3
now=$(date +'%Y-%m-%d')
filename="odin-$platform-nightly+$now.zip"
echo "Creating archive $filename from $artifact and uploading to $bucket"
7z a -bd "output/$filename" -r "$artifact"
b2 upload-file --noProgress "$bucket" "output/$filename" "nightly/$filename"
+73 -23
View File
@@ -1,6 +1,7 @@
package container
import "core:mem"
import "core:runtime"
Array :: struct(T: typeid) {
data: ^T,
@@ -9,6 +10,38 @@ Array :: struct(T: typeid) {
allocator: mem.Allocator,
}
/*
array_init :: proc {
array_init_none,
array_init_len,
array_init_len_cap,
}
array_init
array_delete
array_len
array_cap
array_space
array_slice
array_get
array_get_ptr
array_set
array_reserve
array_resize
array_push = array_append :: proc{
array_push_back,
array_push_back_elems,
}
array_push_front
array_pop_back
array_pop_font
array_consume
array_trim
array_clear
array_clone
array_set_capacity
array_grow
*/
array_init_none :: proc(a: ^$A/Array, allocator := context.allocator) {
array_init_len(a, 0, allocator);
}
@@ -16,10 +49,10 @@ array_init_len :: proc(a: ^$A/Array, len: int, allocator := context.allocator) {
array_init_len_cap(a, 0, 16, allocator);
}
array_init_len_cap :: proc(a: ^$A/Array($T), len: int, cap: int, allocator := context.allocator) {
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), allocator));
a.allocator = allocator;
a.data = (^T)(mem.alloc(size_of(T)*cap, align_of(T), a.allocator));
a.len = len;
a.cap = cap;
a.allocator = allocator;
}
array_init :: proc{array_init_none, array_init_len, array_init_len_cap};
@@ -46,17 +79,23 @@ array_slice :: proc(a: $A/Array($T)) -> []T {
}
array_get :: proc(a: $A/Array($T), index: int) -> T {
array_get :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> T {
runtime.bounds_check_error_loc(loc, index, array_len(a));
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index))^;
}
array_get_ptr :: proc(a: $A/Array($T), index: int, loc := #caller_location) -> ^T {
runtime.bounds_check_error_loc(loc, index, array_len(a));
return (^T)(uintptr(a.data) + size_of(T)*uintptr(index));
}
array_set :: proc(a: ^$A/Array($T), index: int, item: T) {
array_set :: proc(a: ^$A/Array($T), index: int, item: T, loc := #caller_location) {
runtime.bounds_check_error_loc(loc, index, array_len(a^));
(^T)(uintptr(a.data) + size_of(T)*uintptr(index))^ = item;
}
array_reserve :: proc(a: ^$A/Array, capacity: int) {
if capacity > a.size {
if capacity > a.len {
array_set_capacity(a, capacity);
}
}
@@ -75,8 +114,8 @@ array_push_back :: proc(a: ^$A/Array($T), item: T) {
array_grow(a);
}
a.size += 1;
array_set(a, a.size, item);
a.len += 1;
array_set(a, a.len-1, item);
}
array_push_front :: proc(a: ^$A/Array($T), item: T) {
@@ -90,15 +129,15 @@ array_push_front :: proc(a: ^$A/Array($T), item: T) {
data[0] = item;
}
array_pop_back :: proc(a: ^$A/Array($T)) -> T {
assert(a.len > 0);
array_pop_back :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := array_get(a^, a.len-1);
a.len -= 1;
return item;
}
array_pop_font :: proc(a: ^$A/Array($T)) -> T {
assert(a.len > 0);
array_pop_font :: proc(a: ^$A/Array($T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := array_get(a^, 0);
s := array_slice(a^);
copy(s[:], s[1:]);
@@ -107,9 +146,9 @@ array_pop_font :: proc(a: ^$A/Array($T)) -> T {
}
array_consume :: proc(a: ^$A/Array($T), count: int) {
assert(a.size >= count);
a.size -= count;
array_consume :: proc(a: ^$A/Array($T), count: int, loc := #caller_location) {
assert(condition=a.len >= count, loc=loc);
a.len -= count;
}
@@ -117,22 +156,30 @@ array_trim :: proc(a: ^$A/Array($T)) {
array_set_capacity(a, a.len);
}
array_clear :: proc(q: ^$Q/Queue($T)) {
array_resize(q, 0);
array_clear :: proc(a: ^$A/Array($T)) {
array_resize(a, 0);
}
array_clone :: proc(a: $A/Array($T), allocator := context.allocator) -> A {
res: A;
array_init(&res, array_len(a), array_len(a), allocator);
copy(array_slice(res), array_slice(a));
return res;
}
array_push :: proc(a: ^$A/Array($T), items: ..T) {
array_push_back_elems :: proc(a: ^$A/Array($T), items: ..T) {
if array_space(a^) < len(items) {
array_grow(a, a.size + len(items));
array_grow(a, a.len + len(items));
}
offset := a.len;
a.len += len(items);
data := array_slice(a^);
n := copy(data[offset:], items);
a.len = offset + n;
n := copy(data[a.len:], items);
a.len += n;
}
array_push :: proc{array_push_back, array_push_back_elems};
array_append :: proc{array_push_back, array_push_back_elems};
array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
if new_capacity == a.cap {
@@ -145,16 +192,19 @@ array_set_capacity :: proc(a: ^$A/Array($T), new_capacity: int) {
new_data: ^T;
if new_capacity > 0 {
if a.allocator.procedure == nil {
a.allocator = context.allocator;
}
new_data = (^T)(mem.alloc(size_of(T)*new_capacity, align_of(T), a.allocator));
if new_data != nil {
mem.copy(new_data, a.data, size_of(T)*a.len);
}
}
mem.free(a.data);
mem.free(a.data, a.allocator);
a.data = new_data;
a.cap = new_capacity;
}
array_grow :: proc(a: ^$A/Array, min_capacity: int = 0) {
new_capacity := max(len(a.data)*2 + 8, min_capacity);
new_capacity := max(array_len(a^)*2 + 8, min_capacity);
array_set_capacity(a, new_capacity);
}
+363
View File
@@ -0,0 +1,363 @@
package container
Map :: struct(Value: typeid) {
hash: Array(int),
entries: Array(Map_Entry(Value)),
}
Map_Entry :: struct(Value: typeid) {
key: u64,
next: int,
value: Value,
}
/*
map_init :: proc{
map_init_none,
map_init_cap,
}
map_delete
map_has
map_get
map_get_default
map_get_ptr
map_set
map_remove
map_reserve
map_clear
// Multi Map
multi_map_find_first
multi_map_find_next
multi_map_count
multi_map_get :: proc{
multi_map_get_array,
multi_map_get_slice,
};
multi_map_get_as_slice
multi_map_insert
multi_map_remove
multi_map_remove_all
*/
map_init :: proc{map_init_none, map_init_cap};
map_init_none :: proc(m: ^$M/Map($Value), allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
}
map_init_cap :: proc(m: ^$M/Map($Value), cap: int, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
map_reserve(m, cap);
}
map_delete :: proc(m: $M/Map($Value)) {
array_delete(m.hash);
array_delete(m.entries);
}
map_has :: proc(m: $M/Map($Value), key: u64) -> bool {
return _map_find_or_fail(m, key) >= 0;
}
map_get :: proc(m: $M/Map($Value), key: u64) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key);
if i < 0 {
return {}, false;
}
return array_get(m.entries, i).value, true;
}
map_get_default :: proc(m: $M/Map($Value), key: u64, default: Value) -> (res: Value, ok: bool) #optional_ok {
i := _map_find_or_fail(m, key);
if i < 0 {
return default, false;
}
return array_get(m.entries, i).value, true;
}
map_get_ptr :: proc(m: $M/Map($Value), key: u64) -> ^Value {
i := _map_find_or_fail(m, key);
if i < 0 {
return nil;
}
return array_get_ptr(m.entries, i).value;
}
map_set :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m);
}
i := _map_find_or_make(m, key);
array_get_ptr(m.entries, i).value = value;
if _map_full(m^) {
_map_grow(m);
}
}
map_remove :: proc(m: ^$M/Map($Value), key: u64) {
fr := _map_find_key(m^, key);
if fr.entry_index >= 0 {
_map_erase(m, fr);
}
}
map_reserve :: proc(m: ^$M/Map($Value), new_size: int) {
nm: M;
map_init(&nm, m.hash.allocator);
array_resize(&nm.hash, new_size);
array_reserve(&nm.entries, array_len(m.entries));
for i in 0..<new_size {
array_set(&nm.hash, i, -1);
}
for i in 0..<array_len(m.entries) {
e := array_get(m.entries, i);
multi_map_insert(&nm, e.key, e.value);
}
map_delete(m^);
m^ = nm;
}
map_clear :: proc(m: ^$M/Map($Value)) {
array_clear(&m.hash);
array_clear(&m.entries);
}
multi_map_find_first :: proc(m: $M/Map($Value), key: u64) -> ^Map_Entry(Value) {
i := _map_find_or_fail(m, key);
if i < 0 {
return nil;
}
return array_get_ptr(m.entries, i);
}
multi_map_find_next :: proc(m: $M/Map($Value), e: ^Map_Entry(Value)) -> ^Map_Entry(Value) {
i := e.next;
for i >= 0 {
it := array_get_ptr(m.entries, i);
if it.key == e.key {
return it;
}
i = it.next;
}
return nil;
}
multi_map_count :: proc(m: $M/Map($Value), key: u64) -> int {
n := 0;
e := multi_map_find_first(m, key);
for e != nil {
n += 1;
e = multi_map_find_next(m, e);
}
return n;
}
multi_map_get :: proc{multi_map_get_array, multi_map_get_slice};
multi_map_get_array :: proc(m: $M/Map($Value), key: u64, items: ^Array(Value)) {
if items == nil do return;
e := multi_map_find_first(m, key);
for e != nil {
array_append(items, e.value);
e = multi_map_find_next(m, e);
}
}
multi_map_get_slice :: proc(m: $M/Map($Value), key: u64, items: []Value) {
e := multi_map_find_first(m, key);
i := 0;
for e != nil && i < len(items) {
items[i] = e.value;
i += 1;
e = multi_map_find_next(m, e);
}
}
multi_map_get_as_slice :: proc(m: $M/Map($Value), key: u64) -> []Value {
items: Array(Value);
array_init(&items, 0);
e := multi_map_find_first(m, key);
for e != nil {
array_append(&items, e.value);
e = multi_map_find_next(m, e);
}
return array_slice(items);
}
multi_map_insert :: proc(m: ^$M/Map($Value), key: u64, value: Value) {
if array_len(m.hash) == 0 {
_map_grow(m);
}
i := _map_make(m, key);
array_get_ptr(m.entries, i).value = value;
if _map_full(m^) {
_map_grow(m);
}
}
multi_map_remove :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) {
fr := _map_find_entry(m, e);
if fr.entry_index >= 0 {
_map_erase(m, fr);
}
}
multi_map_remove_all :: proc(m: ^$M/Map($Value), key: u64) {
for map_exist(m^, key) {
map_remove(m, key);
}
}
/// Internal
Map_Find_Result :: struct {
hash_index: int,
entry_prev: int,
entry_index: int,
}
_map_add_entry :: proc(m: ^$M/Map($Value), key: u64) -> int {
e: Map_Entry(Value);
e.key = key;
e.next = -1;
idx := array_len(m.entries);
array_push(&m.entries, e);
return idx;
}
_map_erase :: proc(m: ^$M/Map, fr: Map_Find_Result) {
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
} else {
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
}
if fr.entry_index == array_len(m.entries)-1 {
array_pop_back(&m.entries);
return;
}
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
last := _map_find_key(m^, array_get(m.entries, fr.entry_index).key);
if last.entry_prev < 0 {
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
} else {
array_set(&m.hash, last.hash_index, fr.entry_index);
}
}
_map_find_key :: proc(m: $M/Map($Value), key: u64) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
if array_len(m.hash) == 0 {
return fr;
}
fr.hash_index = int(key % u64(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
if it.key == key {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
}
return fr;
}
_map_find_entry :: proc(m: ^$M/Map($Value), e: ^Map_Entry(Value)) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
if array_len(m.hash) == 0 {
return fr;
}
fr.hash_index = int(e.key % u64(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
if it == e {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
}
return fr;
}
_map_find_or_fail :: proc(m: $M/Map($Value), key: u64) -> int {
return _map_find_key(m, key).entry_index;
}
_map_find_or_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
fr := _map_find_key(m^, key);
if fr.entry_index >= 0 {
return fr.entry_index;
}
i := _map_add_entry(m, key);
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
}
return i;
}
_map_make :: proc(m: ^$M/Map($Value), key: u64) -> int {
fr := _map_find_key(m^, key);
i := _map_add_entry(m, key);
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
}
array_get_ptr(m.entries, i).next = fr.entry_index;
return i;
}
_map_full :: proc(m: $M/Map($Value)) -> bool {
// TODO(bill): Determine good max load factor
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
}
_map_grow :: proc(m: ^$M/Map($Value)) {
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
map_reserve(m, new_size);
}
+25 -1
View File
@@ -6,6 +6,31 @@ Queue :: struct(T: typeid) {
offset: int,
}
/*
queue_init :: proc{
queue_init_none,
queue_init_len,
queue_init_len_cap,
}
queue_delete
queue_clear
queue_len
queue_cap
queue_space
queue_get
queue_set
queue_reserve
queue_resize
queue_push :: proc{
queue_push_back,
queue_push_elems,
};
queue_push_front
queue_pop_front
queue_pop_back
queue_consume
*/
queue_init_none :: proc(q: ^$Q/Queue($T), allocator := context.allocator) {
queue_init_len(q, 0, allocator);
}
@@ -40,7 +65,6 @@ queue_space :: proc(q: $Q/Queue($T)) -> int {
return array_len(q.data) - q.len;
}
queue_get :: proc(q: $Q/Queue($T), index: int) -> T {
i := (index + q.offset) % array_len(q.data);
data := array_slice(q.data);
+240
View File
@@ -0,0 +1,240 @@
package container
Set :: struct {
hash: Array(int),
entries: Array(Set_Entry),
}
Set_Entry :: struct {
key: u64,
next: int,
}
/*
set_init :: proc{
set_init_none,
set_init_cap,
}
set_delete
set_in
set_not_in
set_add
set_remove
set_reserve
set_clear
*/
set_init :: proc{set_init_none, set_init_cap};
set_init_none :: proc(m: ^Set, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
}
set_init_cap :: proc(m: ^Set, cap: int, allocator := context.allocator) {
m.hash.allocator = allocator;
m.entries.allocator = allocator;
set_reserve(m, cap);
}
set_delete :: proc(m: Set) {
array_delete(m.hash);
array_delete(m.entries);
}
set_in :: proc(m: Set, key: u64) -> bool {
return _set_find_or_fail(m, key) >= 0;
}
set_not_in :: proc(m: Set, key: u64) -> bool {
return _set_find_or_fail(m, key) < 0;
}
set_add :: proc(m: ^Set, key: u64) {
if array_len(m.hash) == 0 {
_set_grow(m);
}
i := _set_find_or_make(m, key);
if _set_full(m^) {
_set_grow(m);
}
}
set_remove :: proc(m: ^Set, key: u64) {
fr := _set_find_key(m^, key);
if fr.entry_index >= 0 {
_set_erase(m, fr);
}
}
set_reserve :: proc(m: ^Set, new_size: int) {
nm: Set;
set_init(&nm, m.hash.allocator);
array_resize(&nm.hash, new_size);
array_reserve(&nm.entries, array_len(m.entries));
for i in 0..<new_size {
array_set(&nm.hash, i, -1);
}
for i in 0..<array_len(m.entries) {
e := array_get(m.entries, i);
set_add(&nm, e.key);
}
set_delete(m^);
m^ = nm;
}
set_clear :: proc(m: ^Set) {
array_clear(&m.hash);
array_clear(&m.entries);
}
set_equal :: proc(a, b: Set) -> bool {
a_entries := array_slice(a.entries);
b_entries := array_slice(b.entries);
if len(a_entries) != len(b_entries) {
return false;
}
for e in a_entries {
if set_not_in(b, e.key) {
return false;
}
}
return true;
}
/// Internal
_set_add_entry :: proc(m: ^Set, key: u64) -> int {
e: Set_Entry;
e.key = key;
e.next = -1;
idx := array_len(m.entries);
array_push(&m.entries, e);
return idx;
}
_set_erase :: proc(m: ^Set, fr: Map_Find_Result) {
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, array_get(m.entries, fr.entry_index).next);
} else {
array_get_ptr(m.entries, fr.entry_prev).next = array_get(m.entries, fr.entry_index).next;
}
if fr.entry_index == array_len(m.entries)-1 {
array_pop_back(&m.entries);
return;
}
array_set(&m.entries, fr.entry_index, array_get(m.entries, array_len(m.entries)-1));
last := _set_find_key(m^, array_get(m.entries, fr.entry_index).key);
if last.entry_prev < 0 {
array_get_ptr(m.entries, last.entry_prev).next = fr.entry_index;
} else {
array_set(&m.hash, last.hash_index, fr.entry_index);
}
}
_set_find_key :: proc(m: Set, key: u64) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
if array_len(m.hash) == 0 {
return fr;
}
fr.hash_index = int(key % u64(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
if it.key == key {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
}
return fr;
}
_set_find_entry :: proc(m: ^Set, e: ^Set_Entry) -> Map_Find_Result {
fr: Map_Find_Result;
fr.hash_index = -1;
fr.entry_prev = -1;
fr.entry_index = -1;
if array_len(m.hash) == 0 {
return fr;
}
fr.hash_index = int(e.key % u64(array_len(m.hash)));
fr.entry_index = array_get(m.hash, fr.hash_index);
for fr.entry_index >= 0 {
it := array_get_ptr(m.entries, fr.entry_index);
if it == e {
return fr;
}
fr.entry_prev = fr.entry_index;
fr.entry_index = it.next;
}
return fr;
}
_set_find_or_fail :: proc(m: Set, key: u64) -> int {
return _set_find_key(m, key).entry_index;
}
_set_find_or_make :: proc(m: ^Set, key: u64) -> int {
fr := _set_find_key(m^, key);
if fr.entry_index >= 0 {
return fr.entry_index;
}
i := _set_add_entry(m, key);
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
}
return i;
}
_set_make :: proc(m: ^Set, key: u64) -> int {
fr := _set_find_key(m^, key);
i := _set_add_entry(m, key);
if fr.entry_prev < 0 {
array_set(&m.hash, fr.hash_index, i);
} else {
array_get_ptr(m.entries, fr.entry_prev).next = i;
}
array_get_ptr(m.entries, i).next = fr.entry_index;
return i;
}
_set_full :: proc(m: Set) -> bool {
// TODO(bill): Determine good max load factor
return array_len(m.entries) >= (array_len(m.hash) / 4)*3;
}
_set_grow :: proc(m: ^Set) {
new_size := array_len(m.entries) * 4 + 7; // TODO(bill): Determine good grow rate
set_reserve(m, new_size);
}
+97
View File
@@ -0,0 +1,97 @@
package container
import "core:mem"
Small_Array :: struct(N: int, T: typeid) where N >= 0 {
data: [N]T,
len: int,
}
small_array_len :: proc(a: $A/Small_Array) -> int {
return a.len;
}
small_array_cap :: proc(a: $A/Small_Array) -> int {
return len(a.data);
}
small_array_space :: proc(a: $A/Small_Array) -> int {
return len(a.data) - a.len;
}
small_array_slice :: proc(a: ^$A/Small_Array($N, $T)) -> []T {
return a.data[:a.len];
}
small_array_get :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> T {
return a.data[index];
}
small_array_get_ptr :: proc(a: $A/Small_Array($N, $T), index: int, loc := #caller_location) -> ^T {
return &a.data[index];
}
small_array_set :: proc(a: ^$A/Small_Array($N, $T), index: int, item: T, loc := #caller_location) {
a.data[index] = item;
}
small_array_resize :: proc(a: ^$A/Small_Array, length: int) {
a.len = min(length, len(a.data));
}
small_array_push_back :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < len(a.data) {
a.len += 1;
a.data[a.len-1] = item;
return true;
}
return false;
}
small_array_push_front :: proc(a: ^$A/Small_Array($N, $T), item: T) -> bool {
if a.len < len(a.data) {
a.len += 1;
data := small_array_slice(a);
copy(data[1:], data[:]);
data[0] = item;
return true;
}
return false;
}
small_array_pop_back :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := a.data[a.len-1];
a.len -= 1;
return item;
}
small_array_pop_font :: proc(a: ^$A/Small_Array($N, $T), loc := #caller_location) -> T {
assert(condition=a.len > 0, loc=loc);
item := a.data[0];
s := small_array_slice(a);
copy(s[:], s[1:]);
a.len -= 1;
return item;
}
small_array_consume :: proc(a: ^$A/Small_Array($N, $T), count: int, loc := #caller_location) {
assert(condition=a.len >= count, loc=loc);
a.len -= count;
}
small_array_clear :: proc(a: ^$A/Small_Array($N, $T)) {
small_array_resize(a, 0);
}
small_array_push_back_elems :: proc(a: ^$A/Small_Array($N, $T), items: ..T) {
n := copy(a.data[a.len:], items[:]);
a.len += n;
}
small_array_push :: proc{small_array_push_back, small_array_push_back_elems};
small_array_append :: proc{small_array_push_back, small_array_push_back_elems};
+11 -4
View File
@@ -107,8 +107,12 @@ ptr_sub :: inline proc "contextless" (a, b: $P/^$T) -> int {
slice_ptr :: inline proc "contextless" (ptr: ^$T, len: int) -> []T {
assert(len >= 0);
slice := Raw_Slice{data = ptr, len = len};
return transmute([]T)slice;
return transmute([]T)Raw_Slice{data = ptr, len = len};
}
slice_ptr_to_bytes :: proc "contextless" (ptr: rawptr, len: int) -> []byte {
assert(len >= 0);
return transmute([]byte)Raw_Slice{data = ptr, len = len};
}
slice_to_bytes :: inline proc "contextless" (slice: $E/[]$T) -> []byte {
@@ -127,16 +131,19 @@ slice_data_cast :: inline proc "contextless" ($T: typeid/[]$A, slice: $S/[]$B) -
}
}
slice_to_components :: proc "contextless" (slice: $E/[]$T) -> (data: ^T, len: int) {
s := transmute(Raw_Slice)slice;
return s.data, s.len;
}
buffer_from_slice :: inline proc(backing: $T/[]$E) -> [dynamic]E {
s := transmute(Raw_Slice)backing;
d := Raw_Dynamic_Array{
return transmute([dynamic]E)Raw_Dynamic_Array{
data = s.data,
len = 0,
cap = s.len,
allocator = nil_allocator(),
};
return transmute([dynamic]E)d;
}
ptr_to_bytes :: inline proc "contextless" (ptr: ^$T, len := 1) -> []byte {
+6 -2
View File
@@ -235,8 +235,12 @@ print_caller_location :: proc(fd: os.Handle, using loc: Source_Code_Location) {
os.write_byte(fd, ')');
}
print_typeid :: proc(fd: os.Handle, id: typeid) {
ti := type_info_of(id);
print_type(fd, ti);
if id == nil {
os.write_string(fd, "nil");
} else {
ti := type_info_of(id);
print_type(fd, ti);
}
}
print_type :: proc(fd: os.Handle, ti: ^Type_Info) {
if ti == nil {
+5 -4
View File
@@ -1,8 +1,9 @@
package sync
foreign {
@(link_name="llvm.x86.sse2.pause")
yield_processor :: proc() ---
import "core:intrinsics"
cpu_relax :: inline proc() {
intrinsics.cpu_relax();
}
Ticket_Mutex :: struct {
@@ -18,7 +19,7 @@ ticket_mutex_init :: proc(m: ^Ticket_Mutex) {
ticket_mutex_lock :: inline proc(m: ^Ticket_Mutex) {
ticket := atomic_add(&m.ticket, 1, .Relaxed);
for ticket != atomic_load(&m.serving, .Acquire) {
yield_processor();
intrinsics.cpu_relax();
}
}
+2 -10
View File
@@ -2,6 +2,7 @@
package thread;
import "core:runtime"
import "core:intrinsics"
import "core:sync"
import "core:sys/unix"
@@ -40,15 +41,6 @@ Thread_Priority :: enum {
// Creates a thread which will run the given procedure.
// It then waits for `start` to be called.
//
// You may provide a slice of bytes to use as the stack for the new thread,
// but if you do, you are expected to set up the guard pages yourself.
//
// The stack must also be aligned appropriately for the platform.
// We require it's at least 16 bytes aligned to help robustness; other
// platforms may require page-size alignment.
// Note also that pthreads requires the stack is at least 6 OS pages in size:
// 4 are required by pthreads, and two extra for guards pages that will be applied.
//
create :: proc(procedure: Thread_Proc, priority := Thread_Priority.Normal) -> ^Thread {
__linux_thread_entry_proc :: proc "c" (t: rawptr) -> rawptr {
t := (^Thread)(t);
@@ -134,7 +126,7 @@ join :: proc(t: ^Thread) {
if sync.atomic_swap(&t.already_joined, true, .Sequentially_Consistent) {
for {
if sync.atomic_load(&t.done, .Sequentially_Consistent) do return;
sync.yield_processor();
intrinsics.cpu_relax();
}
}
+61 -13
View File
@@ -5750,19 +5750,45 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
optional_ok = true;
tuple_index += 2;
} else if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type)) {
Type *tuple = o.type;
GB_ASSERT(tuple->Tuple.variables.count == 2);
Ast *expr = unparen_expr(o.expr);
if (expr->kind == Ast_CallExpr) {
expr->CallExpr.optional_ok_one = true;
}
Operand val = o;
val.type = tuple->Tuple.variables[0]->type;
val.mode = Addressing_Value;
array_add(operands, val);
tuple_index += tuple->Tuple.variables.count;
} else {
array_add(operands, o);
tuple_index += 1;
}
} else {
TypeTuple *tuple = &o.type->Tuple;
for_array(j, tuple->variables) {
o.type = tuple->variables[j]->type;
array_add(operands, o);
}
if (o.mode == Addressing_OptionalOk && is_type_tuple(o.type) && lhs.count == 1) {
GB_ASSERT(tuple->variables.count == 2);
Ast *expr = unparen_expr(o.expr);
if (expr->kind == Ast_CallExpr) {
expr->CallExpr.optional_ok_one = true;
}
Operand val = o;
val.type = tuple->variables[0]->type;
val.mode = Addressing_Value;
array_add(operands, val);
tuple_index += tuple->variables.count;
isize count = tuple->variables.count;
tuple_index += 2;
add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
} else {
for_array(j, tuple->variables) {
o.type = tuple->variables[j]->type;
array_add(operands, o);
}
tuple_index += tuple->variables.count;
}
}
}
@@ -5839,13 +5865,30 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
}
} else {
TypeTuple *tuple = &o.type->Tuple;
for_array(j, tuple->variables) {
o.type = tuple->variables[j]->type;
array_add(operands, o);
}
if (o.mode == Addressing_OptionalOk && lhs_count == 1) {
GB_ASSERT(tuple->variables.count == 2);
Ast *expr = unparen_expr(o.expr);
if (expr->kind == Ast_CallExpr) {
expr->CallExpr.optional_ok_one = true;
}
Operand val = o;
val.type = tuple->variables[0]->type;
val.mode = Addressing_Value;
array_add(operands, val);
isize count = tuple->variables.count;
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
isize count = tuple->variables.count;
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
add_type_and_value(c->info, val.expr, val.mode, val.type, val.value);
} else {
for_array(j, tuple->variables) {
o.type = tuple->variables[j]->type;
array_add(operands, o);
}
isize count = tuple->variables.count;
tuple_index += add_dependencies_from_unpacking(c, lhs, lhs_count, tuple_index, count);
}
}
}
@@ -7332,7 +7375,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
if (pl->inlining == ProcInlining_no_inline) {
error(call, "'inline' cannot be applied to a procedure that has be marked as 'no_inline'");
}
}
}
}
break;
}
@@ -7342,6 +7385,11 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Type *t
}
operand->expr = call;
if (pt->kind == Type_Proc && pt->Proc.optional_ok) {
operand->mode = Addressing_OptionalOk;
}
return Expr_Expr;
}
+17
View File
@@ -2530,6 +2530,22 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
}
GB_ASSERT(cc > 0);
bool optional_ok = (pt->tags & ProcTag_optional_ok) != 0;
if (optional_ok) {
if (result_count != 2) {
error(proc_type_node, "A procedure type with the #optional_ok tag requires 2 return values, got %td", result_count);
} else {
Entity *second = results->Tuple.variables[1];
if (is_type_polymorphic(second->type)) {
// ignore
} else if (is_type_boolean(second->type)) {
// GOOD
} else {
error(second->token, "Second return value of an #optional_ok procedure must be a boolean, got %s", type_to_string(second->type));
}
}
}
type->Proc.node = proc_type_node;
type->Proc.scope = c->scope;
type->Proc.params = params;
@@ -2542,6 +2558,7 @@ bool check_procedure_type(CheckerContext *ctx, Type *type, Ast *proc_type_node,
type->Proc.is_polymorphic = pt->generic;
type->Proc.specialization_count = specialization_count;
type->Proc.diverging = pt->diverging;
type->Proc.optional_ok = optional_ok;
type->Proc.tags = pt->tags;
if (param_count > 0) {
+1 -1
View File
@@ -468,8 +468,8 @@ GB_ALLOCATOR_PROC(arena_allocator_proc) {
struct StringIntern {
isize len;
StringIntern *next;
isize len;
char str[1];
};
+311 -297
View File
@@ -7081,6 +7081,311 @@ irValue *ir_build_expr(irProcedure *proc, Ast *expr) {
return v;
}
irValue *ir_build_call_expr(irProcedure *proc, Ast *expr) {
ast_node(ce, CallExpr, expr);
TypeAndValue tv = type_and_value_of_expr(expr);
TypeAndValue proc_tv = type_and_value_of_expr(ce->proc);
AddressingMode proc_mode = proc_tv.mode;
if (proc_mode == Addressing_Type) {
GB_ASSERT(ce->args.count == 1);
irValue *x = ir_build_expr(proc, ce->args[0]);
irValue *y = ir_emit_conv(proc, x, tv.type);
return y;
}
Ast *p = unparen_expr(ce->proc);
if (proc_mode == Addressing_Builtin) {
Entity *e = entity_of_node(p);
BuiltinProcId id = BuiltinProc_Invalid;
if (e != nullptr) {
id = cast(BuiltinProcId)e->Builtin.id;
} else {
id = BuiltinProc_DIRECTIVE;
}
return ir_build_builtin_proc(proc, expr, tv, id);
}
// NOTE(bill): Regular call
irValue *value = nullptr;
Ast *proc_expr = unparen_expr(ce->proc);
if (proc_expr->tav.mode == Addressing_Constant) {
ExactValue v = proc_expr->tav.value;
switch (v.kind) {
case ExactValue_Integer:
{
u64 u = big_int_to_u64(&v.value_integer);
irValue *x = ir_const_uintptr(u);
x = ir_emit_conv(proc, x, t_rawptr);
value = ir_emit_conv(proc, x, proc_expr->tav.type);
break;
}
case ExactValue_Pointer:
{
u64 u = cast(u64)v.value_pointer;
irValue *x = ir_const_uintptr(u);
x = ir_emit_conv(proc, x, t_rawptr);
value = ir_emit_conv(proc, x, proc_expr->tav.type);
break;
}
}
}
if (value == nullptr) {
value = ir_build_expr(proc, proc_expr);
}
GB_ASSERT(value != nullptr);
Type *proc_type_ = base_type(ir_type(value));
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
set_procedure_abi_types(heap_allocator(), proc_type_);
if (is_call_expr_field_value(ce)) {
auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
for_array(arg_index, ce->args) {
Ast *arg = ce->args[arg_index];
ast_node(fv, FieldValue, arg);
GB_ASSERT(fv->field->kind == Ast_Ident);
String name = fv->field->Ident.token.string;
isize index = lookup_procedure_parameter(pt, name);
GB_ASSERT(index >= 0);
TypeAndValue tav = type_and_value_of_expr(fv->value);
if (tav.mode == Addressing_Type) {
args[index] = ir_value_nil(tav.type);
} else {
args[index] = ir_build_expr(proc, fv->value);
}
}
TypeTuple *params = &pt->params->Tuple;
for (isize i = 0; i < args.count; i++) {
Entity *e = params->variables[i];
if (e->kind == Entity_TypeName) {
args[i] = ir_value_nil(e->type);
} else if (e->kind == Entity_Constant) {
continue;
} else {
GB_ASSERT(e->kind == Entity_Variable);
if (args[i] == nullptr) {
switch (e->Variable.param_value.kind) {
case ParameterValue_Constant:
args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
break;
case ParameterValue_Nil:
args[i] = ir_value_nil(e->type);
break;
case ParameterValue_Location:
args[i] = ir_emit_source_code_location(proc, proc->entity->token.string, ast_token(expr).pos);
break;
case ParameterValue_Value:
args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
break;
}
} else {
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
}
return ir_emit_call(proc, value, args, ce->inlining, proc->return_ptr_hint_ast == expr);
}
isize arg_index = 0;
isize arg_count = 0;
for_array(i, ce->args) {
Ast *arg = ce->args[i];
TypeAndValue tav = type_and_value_of_expr(arg);
GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr));
GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
Type *at = tav.type;
if (at->kind == Type_Tuple) {
arg_count += at->Tuple.variables.count;
} else {
arg_count++;
}
}
isize param_count = 0;
if (pt->params) {
GB_ASSERT(pt->params->kind == Type_Tuple);
param_count = pt->params->Tuple.variables.count;
}
auto args = array_make<irValue *>(ir_allocator(), cast(isize)gb_max(param_count, arg_count));
isize variadic_index = pt->variadic_index;
bool variadic = pt->variadic && variadic_index >= 0;
bool vari_expand = ce->ellipsis.pos.line != 0;
bool is_c_vararg = pt->c_vararg;
String proc_name = {};
if (proc->entity != nullptr) {
proc_name = proc->entity->token.string;
}
TokenPos pos = ast_token(ce->proc).pos;
TypeTuple *param_tuple = nullptr;
if (pt->params) {
GB_ASSERT(pt->params->kind == Type_Tuple);
param_tuple = &pt->params->Tuple;
}
for_array(i, ce->args) {
Ast *arg = ce->args[i];
TypeAndValue arg_tv = type_and_value_of_expr(arg);
if (arg_tv.mode == Addressing_Type) {
args[arg_index++] = ir_value_nil(arg_tv.type);
} else {
irValue *a = ir_build_expr(proc, arg);
Type *at = ir_type(a);
if (at->kind == Type_Tuple) {
for_array(i, at->Tuple.variables) {
Entity *e = at->Tuple.variables[i];
irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i);
args[arg_index++] = v;
}
} else {
args[arg_index++] = a;
}
}
}
if (param_count > 0) {
GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count);
GB_ASSERT(param_count < 1000000);
if (arg_count < param_count) {
isize end = cast(isize)param_count;
if (variadic) {
end = variadic_index;
}
while (arg_index < end) {
Entity *e = param_tuple->variables[arg_index];
GB_ASSERT(e->kind == Entity_Variable);
switch (e->Variable.param_value.kind) {
case ParameterValue_Constant:
args[arg_index++] = ir_value_constant(e->type, e->Variable.param_value.value);
break;
case ParameterValue_Nil:
args[arg_index++] = ir_value_nil(e->type);
break;
case ParameterValue_Location:
args[arg_index++] = ir_emit_source_code_location(proc, proc_name, pos);
break;
case ParameterValue_Value:
args[arg_index++] = ir_build_expr(proc, e->Variable.param_value.ast_value);
break;
}
}
}
if (is_c_vararg) {
GB_ASSERT(variadic);
GB_ASSERT(!vari_expand);
isize i = 0;
for (; i < variadic_index; i++) {
Entity *e = param_tuple->variables[i];
if (e->kind == Entity_Variable) {
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
Type *variadic_type = param_tuple->variables[i]->type;
GB_ASSERT(is_type_slice(variadic_type));
variadic_type = base_type(variadic_type)->Slice.elem;
if (!is_type_any(variadic_type)) {
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], variadic_type);
}
} else {
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i])));
}
}
} else if (variadic) {
isize i = 0;
for (; i < variadic_index; i++) {
Entity *e = param_tuple->variables[i];
if (e->kind == Entity_Variable) {
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
if (!vari_expand) {
Type *variadic_type = param_tuple->variables[i]->type;
GB_ASSERT(is_type_slice(variadic_type));
variadic_type = base_type(variadic_type)->Slice.elem;
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], variadic_type);
}
}
} else {
for (isize i = 0; i < param_count; i++) {
Entity *e = param_tuple->variables[i];
if (e->kind == Entity_Variable) {
GB_ASSERT(args[i] != nullptr);
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
}
if (variadic && !vari_expand && !is_c_vararg) {
ir_emit_comment(proc, str_lit("variadic call argument generation"));
gbAllocator allocator = ir_allocator();
Type *slice_type = param_tuple->variables[variadic_index]->type;
Type *elem_type = base_type(slice_type)->Slice.elem;
irValue *slice = ir_add_local_generated(proc, slice_type, true);
isize slice_len = arg_count+1 - (variadic_index+1);
if (slice_len > 0) {
irValue *base_array = ir_add_local_generated(proc, alloc_type_array(elem_type, slice_len), true);
for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) {
irValue *addr = ir_emit_array_epi(proc, base_array, cast(i32)j);
ir_emit_store(proc, addr, args[i]);
}
irValue *base_elem = ir_emit_array_epi(proc, base_array, 0);
irValue *len = ir_const_int(slice_len);
ir_fill_slice(proc, slice, base_elem, len);
}
arg_count = param_count;
args[variadic_index] = ir_emit_load(proc, slice);
}
}
if (variadic && variadic_index+1 < param_count) {
for (isize i = variadic_index+1; i < param_count; i++) {
Entity *e = param_tuple->variables[i];
switch (e->Variable.param_value.kind) {
case ParameterValue_Constant:
args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
break;
case ParameterValue_Nil:
args[i] = ir_value_nil(e->type);
break;
case ParameterValue_Location:
args[i] = ir_emit_source_code_location(proc, proc_name, pos);
break;
case ParameterValue_Value:
args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
break;
}
}
}
isize final_count = param_count;
if (is_c_vararg) {
final_count = arg_count;
}
auto call_args = array_slice(args, 0, final_count);
return ir_emit_call(proc, value, call_args, ce->inlining, proc->return_ptr_hint_ast == expr);
}
irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
Ast *original_expr = expr;
expr = unparen_expr(expr);
@@ -7551,304 +7856,13 @@ irValue *ir_build_expr_internal(irProcedure *proc, Ast *expr) {
case_ast_node(ce, CallExpr, expr);
TypeAndValue proc_tv = type_and_value_of_expr(ce->proc);
AddressingMode proc_mode = proc_tv.mode;
if (proc_mode == Addressing_Type) {
GB_ASSERT(ce->args.count == 1);
irValue *x = ir_build_expr(proc, ce->args[0]);
irValue *y = ir_emit_conv(proc, x, tv.type);
return y;
irValue *res = ir_build_call_expr(proc, expr);
if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
GB_ASSERT(is_type_tuple(ir_type(res)));
GB_ASSERT(ir_type(res)->Tuple.variables.count == 2);
return ir_emit_struct_ev(proc, res, 0);
}
Ast *p = unparen_expr(ce->proc);
if (proc_mode == Addressing_Builtin) {
Entity *e = entity_of_node(p);
BuiltinProcId id = BuiltinProc_Invalid;
if (e != nullptr) {
id = cast(BuiltinProcId)e->Builtin.id;
} else {
id = BuiltinProc_DIRECTIVE;
}
return ir_build_builtin_proc(proc, expr, tv, id);
}
// NOTE(bill): Regular call
irValue *value = nullptr;
Ast *proc_expr = unparen_expr(ce->proc);
if (proc_expr->tav.mode == Addressing_Constant) {
ExactValue v = proc_expr->tav.value;
switch (v.kind) {
case ExactValue_Integer:
{
u64 u = big_int_to_u64(&v.value_integer);
irValue *x = ir_const_uintptr(u);
x = ir_emit_conv(proc, x, t_rawptr);
value = ir_emit_conv(proc, x, proc_expr->tav.type);
break;
}
case ExactValue_Pointer:
{
u64 u = cast(u64)v.value_pointer;
irValue *x = ir_const_uintptr(u);
x = ir_emit_conv(proc, x, t_rawptr);
value = ir_emit_conv(proc, x, proc_expr->tav.type);
break;
}
}
}
if (value == nullptr) {
value = ir_build_expr(proc, proc_expr);
}
GB_ASSERT(value != nullptr);
Type *proc_type_ = base_type(ir_type(value));
GB_ASSERT(proc_type_->kind == Type_Proc);
TypeProc *pt = &proc_type_->Proc;
set_procedure_abi_types(heap_allocator(), proc_type_);
if (is_call_expr_field_value(ce)) {
auto args = array_make<irValue *>(ir_allocator(), pt->param_count);
for_array(arg_index, ce->args) {
Ast *arg = ce->args[arg_index];
ast_node(fv, FieldValue, arg);
GB_ASSERT(fv->field->kind == Ast_Ident);
String name = fv->field->Ident.token.string;
isize index = lookup_procedure_parameter(pt, name);
GB_ASSERT(index >= 0);
TypeAndValue tav = type_and_value_of_expr(fv->value);
if (tav.mode == Addressing_Type) {
args[index] = ir_value_nil(tav.type);
} else {
args[index] = ir_build_expr(proc, fv->value);
}
}
TypeTuple *params = &pt->params->Tuple;
for (isize i = 0; i < args.count; i++) {
Entity *e = params->variables[i];
if (e->kind == Entity_TypeName) {
args[i] = ir_value_nil(e->type);
} else if (e->kind == Entity_Constant) {
continue;
} else {
GB_ASSERT(e->kind == Entity_Variable);
if (args[i] == nullptr) {
switch (e->Variable.param_value.kind) {
case ParameterValue_Constant:
args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
break;
case ParameterValue_Nil:
args[i] = ir_value_nil(e->type);
break;
case ParameterValue_Location:
args[i] = ir_emit_source_code_location(proc, proc->entity->token.string, ast_token(expr).pos);
break;
case ParameterValue_Value:
args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
break;
}
} else {
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
}
return ir_emit_call(proc, value, args, ce->inlining, proc->return_ptr_hint_ast == expr);
}
isize arg_index = 0;
isize arg_count = 0;
for_array(i, ce->args) {
Ast *arg = ce->args[i];
TypeAndValue tav = type_and_value_of_expr(arg);
GB_ASSERT_MSG(tav.mode != Addressing_Invalid, "%s %s", expr_to_string(arg), expr_to_string(expr));
GB_ASSERT_MSG(tav.mode != Addressing_ProcGroup, "%s", expr_to_string(arg));
Type *at = tav.type;
if (at->kind == Type_Tuple) {
arg_count += at->Tuple.variables.count;
} else {
arg_count++;
}
}
isize param_count = 0;
if (pt->params) {
GB_ASSERT(pt->params->kind == Type_Tuple);
param_count = pt->params->Tuple.variables.count;
}
auto args = array_make<irValue *>(ir_allocator(), cast(isize)gb_max(param_count, arg_count));
isize variadic_index = pt->variadic_index;
bool variadic = pt->variadic && variadic_index >= 0;
bool vari_expand = ce->ellipsis.pos.line != 0;
bool is_c_vararg = pt->c_vararg;
String proc_name = {};
if (proc->entity != nullptr) {
proc_name = proc->entity->token.string;
}
TokenPos pos = ast_token(ce->proc).pos;
TypeTuple *param_tuple = nullptr;
if (pt->params) {
GB_ASSERT(pt->params->kind == Type_Tuple);
param_tuple = &pt->params->Tuple;
}
for_array(i, ce->args) {
Ast *arg = ce->args[i];
TypeAndValue arg_tv = type_and_value_of_expr(arg);
if (arg_tv.mode == Addressing_Type) {
args[arg_index++] = ir_value_nil(arg_tv.type);
} else {
irValue *a = ir_build_expr(proc, arg);
Type *at = ir_type(a);
if (at->kind == Type_Tuple) {
for_array(i, at->Tuple.variables) {
Entity *e = at->Tuple.variables[i];
irValue *v = ir_emit_struct_ev(proc, a, cast(i32)i);
args[arg_index++] = v;
}
} else {
args[arg_index++] = a;
}
}
}
if (param_count > 0) {
GB_ASSERT_MSG(pt->params != nullptr, "%s %td", expr_to_string(expr), pt->param_count);
GB_ASSERT(param_count < 1000000);
if (arg_count < param_count) {
isize end = cast(isize)param_count;
if (variadic) {
end = variadic_index;
}
while (arg_index < end) {
Entity *e = param_tuple->variables[arg_index];
GB_ASSERT(e->kind == Entity_Variable);
switch (e->Variable.param_value.kind) {
case ParameterValue_Constant:
args[arg_index++] = ir_value_constant(e->type, e->Variable.param_value.value);
break;
case ParameterValue_Nil:
args[arg_index++] = ir_value_nil(e->type);
break;
case ParameterValue_Location:
args[arg_index++] = ir_emit_source_code_location(proc, proc_name, pos);
break;
case ParameterValue_Value:
args[arg_index++] = ir_build_expr(proc, e->Variable.param_value.ast_value);
break;
}
}
}
if (is_c_vararg) {
GB_ASSERT(variadic);
GB_ASSERT(!vari_expand);
isize i = 0;
for (; i < variadic_index; i++) {
Entity *e = param_tuple->variables[i];
if (e->kind == Entity_Variable) {
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
Type *variadic_type = param_tuple->variables[i]->type;
GB_ASSERT(is_type_slice(variadic_type));
variadic_type = base_type(variadic_type)->Slice.elem;
if (!is_type_any(variadic_type)) {
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], variadic_type);
}
} else {
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], default_type(ir_type(args[i])));
}
}
} else if (variadic) {
isize i = 0;
for (; i < variadic_index; i++) {
Entity *e = param_tuple->variables[i];
if (e->kind == Entity_Variable) {
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
if (!vari_expand) {
Type *variadic_type = param_tuple->variables[i]->type;
GB_ASSERT(is_type_slice(variadic_type));
variadic_type = base_type(variadic_type)->Slice.elem;
for (; i < arg_count; i++) {
args[i] = ir_emit_conv(proc, args[i], variadic_type);
}
}
} else {
for (isize i = 0; i < param_count; i++) {
Entity *e = param_tuple->variables[i];
if (e->kind == Entity_Variable) {
GB_ASSERT(args[i] != nullptr);
args[i] = ir_emit_conv(proc, args[i], e->type);
}
}
}
if (variadic && !vari_expand && !is_c_vararg) {
ir_emit_comment(proc, str_lit("variadic call argument generation"));
gbAllocator allocator = ir_allocator();
Type *slice_type = param_tuple->variables[variadic_index]->type;
Type *elem_type = base_type(slice_type)->Slice.elem;
irValue *slice = ir_add_local_generated(proc, slice_type, true);
isize slice_len = arg_count+1 - (variadic_index+1);
if (slice_len > 0) {
irValue *base_array = ir_add_local_generated(proc, alloc_type_array(elem_type, slice_len), true);
for (isize i = variadic_index, j = 0; i < arg_count; i++, j++) {
irValue *addr = ir_emit_array_epi(proc, base_array, cast(i32)j);
ir_emit_store(proc, addr, args[i]);
}
irValue *base_elem = ir_emit_array_epi(proc, base_array, 0);
irValue *len = ir_const_int(slice_len);
ir_fill_slice(proc, slice, base_elem, len);
}
arg_count = param_count;
args[variadic_index] = ir_emit_load(proc, slice);
}
}
if (variadic && variadic_index+1 < param_count) {
for (isize i = variadic_index+1; i < param_count; i++) {
Entity *e = param_tuple->variables[i];
switch (e->Variable.param_value.kind) {
case ParameterValue_Constant:
args[i] = ir_value_constant(e->type, e->Variable.param_value.value);
break;
case ParameterValue_Nil:
args[i] = ir_value_nil(e->type);
break;
case ParameterValue_Location:
args[i] = ir_emit_source_code_location(proc, proc_name, pos);
break;
case ParameterValue_Value:
args[i] = ir_build_expr(proc, e->Variable.param_value.ast_value);
break;
}
}
}
isize final_count = param_count;
if (is_c_vararg) {
final_count = arg_count;
}
auto call_args = array_slice(args, 0, final_count);
return ir_emit_call(proc, value, call_args, ce->inlining, proc->return_ptr_hint_ast == expr);
return res;
case_end;
case_ast_node(se, SliceExpr, expr);
+7 -1
View File
@@ -8903,7 +8903,13 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr) {
case_end;
case_ast_node(ce, CallExpr, expr);
return lb_build_call_expr(p, expr);
lbValue res = lb_build_call_expr(p, expr);
if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures
GB_ASSERT(is_type_tuple(res.type));
GB_ASSERT(res.type->Tuple.variables.count == 2);
return lb_emit_struct_ev(p, res, 0);
}
return res;
case_end;
case_ast_node(se, SliceExpr, expr);
+1
View File
@@ -1618,6 +1618,7 @@ void parse_proc_tags(AstFile *f, u64 *tags) {
}
if (false) {}
ELSE_IF_ADD_TAG(optional_ok)
ELSE_IF_ADD_TAG(require_results)
ELSE_IF_ADD_TAG(bounds_check)
ELSE_IF_ADD_TAG(no_bounds_check)
+3
View File
@@ -165,7 +165,9 @@ enum ProcInlining {
enum ProcTag {
ProcTag_bounds_check = 1<<0,
ProcTag_no_bounds_check = 1<<1,
ProcTag_require_results = 1<<4,
ProcTag_optional_ok = 1<<5,
};
enum ProcCallingConvention {
@@ -282,6 +284,7 @@ AST_KIND(_ExprBegin, "", bool) \
Token close; \
Token ellipsis; \
ProcInlining inlining; \
bool optional_ok_one; \
}) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(TernaryExpr, "ternary expression", struct { Ast *cond, *x, *y; }) \
+6 -3
View File
@@ -232,6 +232,7 @@ struct TypeUnion {
Array<Type *> abi_compat_params; \
Type * abi_compat_result_type; \
i32 variadic_index; \
/* TODO(bill): Make this a flag set rather than bools */ \
bool variadic; \
bool abi_types_set; \
bool require_results; \
@@ -242,6 +243,7 @@ struct TypeUnion {
bool has_named_results; \
bool diverging; /* no return */ \
bool return_by_pointer; \
bool optional_ok; \
u64 tags; \
isize specialization_count; \
ProcCallingConvention calling_convention; \
@@ -1979,9 +1981,10 @@ bool are_types_identical(Type *x, Type *y) {
case Type_Proc:
if (y->kind == Type_Proc) {
return x->Proc.calling_convention == y->Proc.calling_convention &&
x->Proc.c_vararg == y->Proc.c_vararg &&
x->Proc.variadic == y->Proc.variadic &&
x->Proc.diverging == y->Proc.diverging &&
x->Proc.c_vararg == y->Proc.c_vararg &&
x->Proc.variadic == y->Proc.variadic &&
x->Proc.diverging == y->Proc.diverging &&
x->Proc.optional_ok == y->Proc.optional_ok &&
are_types_identical(x->Proc.params, y->Proc.params) &&
are_types_identical(x->Proc.results, y->Proc.results);
}