Merge branch 'master' into stdlib-parser-fixes

This commit is contained in:
gingerBill
2023-08-07 11:02:01 +01:00
committed by GitHub
109 changed files with 16366 additions and 2077 deletions
+14 -1
View File
@@ -51,7 +51,10 @@ set compiler_flags= -nologo -Oi -TP -fp:precise -Gm- -MP -FC -EHsc- -GR- -GF
set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\" set compiler_defines= -DODIN_VERSION_RAW=\"%odin_version_raw%\"
if not exist .git\ goto skip_git_hash if not exist .git\ goto skip_git_hash
for /f %%i in ('git rev-parse --short HEAD') do set GIT_SHA=%%i for /f "tokens=1,2" %%i IN ('git show "--pretty=%%cd %%h" "--date=format:%%Y-%%m" --no-patch --no-notes HEAD') do (
set odin_version_raw=dev-%%i
set GIT_SHA=%%j
)
if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\" if %ERRORLEVEL% equ 0 set compiler_defines=%compiler_defines% -DGIT_SHA=\"%GIT_SHA%\"
:skip_git_hash :skip_git_hash
@@ -77,6 +80,16 @@ set libs= ^
Synchronization.lib ^ Synchronization.lib ^
bin\llvm\windows\LLVM-C.lib bin\llvm\windows\LLVM-C.lib
rem DO NOT TOUCH!
rem THIS TILDE STUFF IS FOR DEVELOPMENT ONLY!
set tilde_backend=0
if %tilde_backend% EQU 1 (
set libs=%libs% src\tilde\tb.lib
set compiler_defines=%compiler_defines% -DODIN_TILDE_BACKEND
)
rem DO NOT TOUCH!
set linker_flags= -incremental:no -opt:ref -subsystem:console set linker_flags= -incremental:no -opt:ref -subsystem:console
if %release_mode% EQU 0 ( rem Debug if %release_mode% EQU 0 ( rem Debug
+7 -4
View File
@@ -8,17 +8,20 @@ set -eu
: ${ODIN_VERSION=dev-$(date +"%Y-%m")} : ${ODIN_VERSION=dev-$(date +"%Y-%m")}
: ${GIT_SHA=} : ${GIT_SHA=}
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
CXXFLAGS="$CXXFLAGS -std=c++14" CXXFLAGS="$CXXFLAGS -std=c++14"
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++" LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
if [ -d ".git" ]; then if [ -d ".git" ] && [ $(which git) ]; then
GIT_SHA=$(git rev-parse --short HEAD || :) versionTag=( $(git show --pretty='%cd %h' --date=format:%Y-%m --no-patch --no-notes HEAD) )
if [ "$GIT_SHA" ]; then if [ $? -eq 0 ]; then
ODIN_VERSION="dev-${versionTag[0]}"
GIT_SHA="${versionTag[1]}"
CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\"" CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
fi fi
fi fi
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value" DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
OS=$(uname) OS=$(uname)
+1
View File
@@ -1,3 +1,4 @@
//+vet !using-param
package zlib package zlib
/* /*
+17 -20
View File
@@ -184,28 +184,26 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
advance :: proc(t: ^Tokenizer) -> (err: Error) { advance :: proc(t: ^Tokenizer) -> (err: Error) {
if t == nil { return .Tokenizer_Is_Nil } if t == nil { return .Tokenizer_Is_Nil }
using t
#no_bounds_check { #no_bounds_check {
if read_offset < len(src) { if t.read_offset < len(t.src) {
offset = read_offset t.offset = t.read_offset
r, w = rune(src[read_offset]), 1 t.r, t.w = rune(t.src[t.read_offset]), 1
switch { switch {
case r == 0: case t.r == 0:
return .Illegal_NUL_Character return .Illegal_NUL_Character
case r >= utf8.RUNE_SELF: case t.r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune_in_string(src[read_offset:]) t.r, t.w = utf8.decode_rune_in_string(t.src[t.read_offset:])
if r == utf8.RUNE_ERROR && w == 1 { if t.r == utf8.RUNE_ERROR && t.w == 1 {
return .Illegal_UTF_Encoding return .Illegal_UTF_Encoding
} else if r == utf8.RUNE_BOM && offset > 0 { } else if t.r == utf8.RUNE_BOM && t.offset > 0 {
return .Illegal_BOM return .Illegal_BOM
} }
} }
read_offset += w t.read_offset += t.w
return .None return .None
} else { } else {
offset = len(src) t.offset = len(t.src)
r = -1 t.r = -1
return return
} }
} }
@@ -273,26 +271,25 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
All of these would be in the ASCII range. All of these would be in the ASCII range.
Even if one is not, it doesn't matter. All characters we need to compare to extract are. Even if one is not, it doesn't matter. All characters we need to compare to extract are.
*/ */
using t
length := len(t.src) length := len(t.src)
found := false found := false
#no_bounds_check { #no_bounds_check {
for read_offset < length { for t.read_offset < length {
if src[read_offset] == ';' { if t.src[t.read_offset] == ';' {
t.read_offset += 1
found = true found = true
read_offset += 1
break break
} }
read_offset += 1 t.read_offset += 1
} }
} }
if found { if found {
return string(src[offset + 1 : read_offset - 1]), .None return string(t.src[t.offset + 1 : t.read_offset - 1]), .None
} }
return string(src[offset : read_offset]), .Invalid_Entity_Encoding return string(t.src[t.offset : t.read_offset]), .Invalid_Entity_Encoding
} }
/* /*
+1 -1
View File
@@ -207,7 +207,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
case runtime.Type_Info_Relative_Pointer: case runtime.Type_Info_Relative_Pointer:
return .Unsupported_Type return .Unsupported_Type
case runtime.Type_Info_Relative_Slice: case runtime.Type_Info_Relative_Multi_Pointer:
return .Unsupported_Type return .Unsupported_Type
case runtime.Type_Info_Matrix: case runtime.Type_Info_Matrix:
+21 -23
View File
@@ -19,43 +19,39 @@ import "core:fmt"
*/ */
print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) { print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
if doc == nil { return } if doc == nil { return }
using fmt written += fmt.wprintf(writer, "[XML Prolog]\n")
written += wprintf(writer, "[XML Prolog]\n")
for attr in doc.prologue { for attr in doc.prologue {
written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val) written += fmt.wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
} }
written += wprintf(writer, "[Encoding] %v\n", doc.encoding) written += fmt.wprintf(writer, "[Encoding] %v\n", doc.encoding)
if len(doc.doctype.ident) > 0 { if len(doc.doctype.ident) > 0 {
written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident) written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
if len(doc.doctype.rest) > 0 { if len(doc.doctype.rest) > 0 {
wprintf(writer, "\t%v\n", doc.doctype.rest) fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
} }
} }
for comment in doc.comments { for comment in doc.comments {
written += wprintf(writer, "[Pre-root comment] %v\n", comment) written += fmt.wprintf(writer, "[Pre-root comment] %v\n", comment)
} }
if len(doc.elements) > 0 { if len(doc.elements) > 0 {
wprintln(writer, " --- ") fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0) print_element(writer, doc, 0)
wprintln(writer, " --- ") fmt.wprintln(writer, " --- ")
} }
return written, .None return written, .None
} }
print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) { print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID, indent := 0) -> (written: int, err: io.Error) {
using fmt
tab :: proc(writer: io.Writer, indent: int) { tab :: proc(writer: io.Writer, indent: int) {
for _ in 0..=indent { for _ in 0..=indent {
wprintf(writer, "\t") fmt.wprintf(writer, "\t")
} }
} }
@@ -64,22 +60,24 @@ print_element :: proc(writer: io.Writer, doc: ^Document, element_id: Element_ID,
element := doc.elements[element_id] element := doc.elements[element_id]
if element.kind == .Element { if element.kind == .Element {
wprintf(writer, "<%v>\n", element.ident) fmt.wprintf(writer, "<%v>\n", element.ident)
if len(element.value) > 0 {
tab(writer, indent + 1) for value in element.value {
wprintf(writer, "[Value] %v\n", element.value) switch v in value {
case string:
tab(writer, indent + 1)
fmt.wprintf(writer, "[Value] %v\n", v)
case Element_ID:
print_element(writer, doc, v, indent + 1)
}
} }
for attr in element.attribs { for attr in element.attribs {
tab(writer, indent + 1) tab(writer, indent + 1)
wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val) fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
}
for child in element.children {
print_element(writer, doc, child, indent + 1)
} }
} else if element.kind == .Comment { } else if element.kind == .Comment {
wprintf(writer, "[COMMENT] %v\n", element.value) fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
} }
return written, .None return written, .None
+3 -3
View File
@@ -72,10 +72,10 @@ example :: proc() {
return return
} }
printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].children), docs[0].element_count) printf("Found `<charlist>` with %v children, %v elements total\n", len(docs[0].elements[charlist].value), docs[0].element_count)
crc32 := doc_hash(docs[0]) crc32 := doc_hash(docs[0], false)
printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32) printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0x420dbac5 else "🤬", crc32)
for round in 0..<N { for round in 0..<N {
defer xml.destroy(docs[round]) defer xml.destroy(docs[round])
+17 -12
View File
@@ -13,20 +13,25 @@ find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string
tag := doc.elements[parent_id] tag := doc.elements[parent_id]
count := 0 count := 0
for child_id in tag.children { for v in tag.value {
child := doc.elements[child_id] switch child_id in v {
/* case string: continue
Skip commments. They have no name. case Element_ID:
*/ child := doc.elements[child_id]
if child.kind != .Element { continue } /*
Skip commments. They have no name.
*/
if child.kind != .Element { continue }
/* /*
If the ident matches and it's the nth such child, return it. If the ident matches and it's the nth such child, return it.
*/ */
if child.ident == ident { if child.ident == ident {
if count == nth { return child_id, true } if count == nth { return child_id, true }
count += 1 count += 1
}
} }
} }
return 0, false return 0, false
} }
+16 -16
View File
@@ -125,38 +125,38 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
} }
@(optimization_mode="speed") @(optimization_mode="speed")
advance_rune :: proc(using t: ^Tokenizer) { advance_rune :: proc(t: ^Tokenizer) {
#no_bounds_check { #no_bounds_check {
/* /*
Already bounds-checked here. Already bounds-checked here.
*/ */
if read_offset < len(src) { if t.read_offset < len(t.src) {
offset = read_offset t.offset = t.read_offset
if ch == '\n' { if t.ch == '\n' {
line_offset = offset t.line_offset = t.offset
line_count += 1 t.line_count += 1
} }
r, w := rune(src[read_offset]), 1 r, w := rune(t.src[t.read_offset]), 1
switch { switch {
case r == 0: case r == 0:
error(t, t.offset, "illegal character NUL") error(t, t.offset, "illegal character NUL")
case r >= utf8.RUNE_SELF: case r >= utf8.RUNE_SELF:
r, w = #force_inline utf8.decode_rune_in_string(src[read_offset:]) r, w = #force_inline utf8.decode_rune_in_string(t.src[t.read_offset:])
if r == utf8.RUNE_ERROR && w == 1 { if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding") error(t, t.offset, "illegal UTF-8 encoding")
} else if r == utf8.RUNE_BOM && offset > 0 { } else if r == utf8.RUNE_BOM && t.offset > 0 {
error(t, t.offset, "illegal byte order mark") error(t, t.offset, "illegal byte order mark")
} }
} }
read_offset += w t.read_offset += w
ch = r t.ch = r
} else { } else {
offset = len(src) t.offset = len(t.src)
if ch == '\n' { if t.ch == '\n' {
line_offset = offset t.line_offset = t.offset
line_count += 1 t.line_count += 1
} }
ch = -1 t.ch = -1
} }
} }
} }
+15 -27
View File
@@ -125,16 +125,19 @@ Document :: struct {
Element :: struct { Element :: struct {
ident: string, ident: string,
value: string, value: [dynamic]Value,
attribs: Attributes, attribs: Attributes,
kind: enum { kind: enum {
Element = 0, Element = 0,
Comment, Comment,
}, },
parent: Element_ID, parent: Element_ID,
children: [dynamic]Element_ID, }
Value :: union {
string,
Element_ID,
} }
Attribute :: struct { Attribute :: struct {
@@ -247,9 +250,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
err = .Unexpected_Token err = .Unexpected_Token
element, parent: Element_ID element, parent: Element_ID
tag_is_open := false
first_element := true
open: Token open: Token
/* /*
@@ -275,16 +275,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
e.g. <odin - Start of new element. e.g. <odin - Start of new element.
*/ */
element = new_element(doc) element = new_element(doc)
tag_is_open = true if element == 0 { // First Element
parent = element
if first_element {
/*
First element.
*/
parent = element
first_element = false
} else { } else {
append(&doc.elements[parent].children, element) append(&doc.elements[parent].value, element)
} }
doc.elements[element].parent = parent doc.elements[element].parent = parent
@@ -324,7 +318,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
expect(t, .Gt) or_return expect(t, .Gt) or_return
parent = doc.elements[element].parent parent = doc.elements[element].parent
element = parent element = parent
tag_is_open = false
case: case:
error(t, t.offset, "Expected close tag, got: %#v\n", end_token) error(t, t.offset, "Expected close tag, got: %#v\n", end_token)
@@ -344,7 +337,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
} }
parent = doc.elements[element].parent parent = doc.elements[element].parent
element = parent element = parent
tag_is_open = false
} else if open.kind == .Exclaim { } else if open.kind == .Exclaim {
/* /*
@@ -392,8 +384,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
el := new_element(doc) el := new_element(doc)
doc.elements[el].parent = element doc.elements[el].parent = element
doc.elements[el].kind = .Comment doc.elements[el].kind = .Comment
doc.elements[el].value = comment append(&doc.elements[el].value, comment)
append(&doc.elements[element].children, el) append(&doc.elements[element].value, el)
} }
} }
@@ -436,9 +428,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
/* /*
End of file. End of file.
*/ */
if tag_is_open {
return doc, .Premature_EOF
}
break loop break loop
case: case:
@@ -450,7 +439,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
needs_processing |= .Decode_SGML_Entities in opts.flags needs_processing |= .Decode_SGML_Entities in opts.flags
if !needs_processing { if !needs_processing {
doc.elements[element].value = body_text append(&doc.elements[element].value, body_text)
continue continue
} }
@@ -472,10 +461,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
decoded, decode_err := entity.decode_xml(body_text, decode_opts) decoded, decode_err := entity.decode_xml(body_text, decode_opts)
if decode_err == .None { if decode_err == .None {
doc.elements[element].value = decoded append(&doc.elements[element].value, decoded)
append(&doc.strings_to_free, decoded) append(&doc.strings_to_free, decoded)
} else { } else {
doc.elements[element].value = body_text append(&doc.elements[element].value, body_text)
} }
} }
} }
@@ -518,7 +507,7 @@ destroy :: proc(doc: ^Document) {
for el in doc.elements { for el in doc.elements {
delete(el.attribs) delete(el.attribs)
delete(el.children) delete(el.value)
} }
delete(doc.elements) delete(doc.elements)
@@ -710,6 +699,5 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) {
cur := doc.element_count cur := doc.element_count
doc.element_count += 1 doc.element_count += 1
return cur return cur
} }
+14 -35
View File
@@ -835,22 +835,22 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
// - fi: A pointer to an Info structure // - fi: A pointer to an Info structure
// - verb: The invalid format verb // - verb: The invalid format verb
// //
fmt_bad_verb :: proc(using fi: ^Info, verb: rune) { fmt_bad_verb :: proc(fi: ^Info, verb: rune) {
prev_in_bad := fi.in_bad prev_in_bad := fi.in_bad
defer fi.in_bad = prev_in_bad defer fi.in_bad = prev_in_bad
fi.in_bad = true fi.in_bad = true
io.write_string(writer, "%!", &fi.n) io.write_string(fi.writer, "%!", &fi.n)
io.write_rune(writer, verb, &fi.n) io.write_rune(fi.writer, verb, &fi.n)
io.write_byte(writer, '(', &fi.n) io.write_byte(fi.writer, '(', &fi.n)
if arg.id != nil { if fi.arg.id != nil {
reflect.write_typeid(writer, arg.id, &fi.n) reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
io.write_byte(writer, '=', &fi.n) io.write_byte(fi.writer, '=', &fi.n)
fmt_value(fi, arg, 'v') fmt_value(fi, fi.arg, 'v')
} else { } else {
io.write_string(writer, "<nil>", &fi.n) io.write_string(fi.writer, "<nil>", &fi.n)
} }
io.write_byte(writer, ')', &fi.n) io.write_byte(fi.writer, ')', &fi.n)
} }
// Formats a boolean value according to the specified format verb // Formats a boolean value according to the specified format verb
// //
@@ -859,7 +859,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
// - b: The boolean value to format // - b: The boolean value to format
// - verb: The format verb // - verb: The format verb
// //
fmt_bool :: proc(using fi: ^Info, b: bool, verb: rune) { fmt_bool :: proc(fi: ^Info, b: bool, verb: rune) {
switch verb { switch verb {
case 't', 'v': case 't', 'v':
fmt_string(fi, b ? "true" : "false", 's') fmt_string(fi, b ? "true" : "false", 's')
@@ -2535,32 +2535,11 @@ fmt_value :: proc(fi: ^Info, v: any, verb: rune) {
fmt_value(fi, absolute_ptr, verb) fmt_value(fi, absolute_ptr, verb)
case runtime.Type_Info_Relative_Slice: case runtime.Type_Info_Relative_Multi_Pointer:
ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id) ptr := reflect.relative_pointer_to_absolute_raw(v.data, info.base_integer.id)
absolute_ptr := any{ptr, info.pointer.id}
if verb == 'p' { fmt_value(fi, absolute_ptr, verb)
fmt_pointer(fi, ptr, 'p')
} else if ptr == nil {
io.write_string(fi.writer, "[]", &fi.n)
} else {
len_ptr := uintptr(v.data) + uintptr(info.base_integer.size)
len_any := any{rawptr(len_ptr), info.base_integer.id}
len, _ := reflect.as_int(len_any)
slice_type := reflect.type_info_base(info.slice).variant.(runtime.Type_Info_Slice)
fi.record_level += 1
defer fi.record_level -= 1
io.write_byte(fi.writer, '[', &fi.n)
defer io.write_byte(fi.writer, ']', &fi.n)
for i in 0..<len {
if i > 0 { io.write_string(fi.writer, ", ", &fi.n) }
data := uintptr(ptr) + uintptr(i*slice_type.elem_size)
fmt_arg(fi, any{rawptr(data), slice_type.elem.id}, verb)
}
}
case runtime.Type_Info_Matrix: case runtime.Type_Info_Matrix:
fmt_matrix(fi, v, verb, info) fmt_matrix(fi, v, verb, info)
+8 -7
View File
@@ -4,13 +4,14 @@ import "core:bytes"
import "core:image" import "core:image"
destroy :: proc(img: ^image.Image) -> bool { destroy :: proc(img: ^image.Image) -> bool {
if img == nil do return false if img == nil {
return false
}
defer free(img) defer free(img)
bytes.buffer_destroy(&img.pixels) bytes.buffer_destroy(&img.pixels)
info, ok := img.metadata.(^image.Netpbm_Info) info := img.metadata.(^image.Netpbm_Info) or_return
if !ok do return false
header_destroy(&info.header) header_destroy(&info.header)
free(info) free(info)
@@ -19,9 +20,9 @@ destroy :: proc(img: ^image.Image) -> bool {
return true return true
} }
header_destroy :: proc(using header: ^Header) { header_destroy :: proc(header: ^Header) {
if format == .P7 && tupltype != "" { if header.format == .P7 && header.tupltype != "" {
delete(tupltype) delete(header.tupltype)
tupltype = "" header.tupltype = ""
} }
} }
+1
View File
@@ -1,3 +1,4 @@
//+vet !using-stmt
package netpbm package netpbm
import "core:bytes" import "core:bytes"
+3 -4
View File
@@ -80,11 +80,10 @@ time :: proc(c: image.PNG_Chunk) -> (res: tIME, ok: bool) {
} }
core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) { core_time :: proc(c: image.PNG_Chunk) -> (t: coretime.Time, ok: bool) {
if png_time, png_ok := time(c); png_ok { if t, png_ok := time(c); png_ok {
using png_time
return coretime.datetime_to_time( return coretime.datetime_to_time(
int(year), int(month), int(day), int(t.year), int(t.month), int(t.day),
int(hour), int(minute), int(second), int(t.hour), int(t.minute), int(t.second),
) )
} else { } else {
return {}, false return {}, false
+8 -8
View File
@@ -11,6 +11,7 @@
// package png implements a PNG image reader // package png implements a PNG image reader
// //
// The PNG specification is at https://www.w3.org/TR/PNG/. // The PNG specification is at https://www.w3.org/TR/PNG/.
//+vet !using-stmt
package png package png
import "core:compress" import "core:compress"
@@ -444,15 +445,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
img.width = int(header.width) img.width = int(header.width)
img.height = int(header.height) img.height = int(header.height)
using header
h := image.PNG_IHDR{ h := image.PNG_IHDR{
width = width, width = header.width,
height = height, height = header.height,
bit_depth = bit_depth, bit_depth = header.bit_depth,
color_type = color_type, color_type = header.color_type,
compression_method = compression_method, compression_method = header.compression_method,
filter_method = filter_method, filter_method = header.filter_method,
interlace_method = interlace_method, interlace_method = header.interlace_method,
} }
info.header = h info.header = h
+7 -5
View File
@@ -29,13 +29,13 @@ Fixed52_12 :: distinct Fixed(i64, 12)
init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) { init_from_f64 :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), val: f64) {
i, f := math.modf(val) i, f := math.modf(math.abs(val))
x.i = Backing(f * (1<<Fraction_Width)) x.i = Backing(f * (1<<Fraction_Width))
x.i &= 1<<Fraction_Width - 1 x.i &= 1<<Fraction_Width - 1
x.i |= Backing(i) << Fraction_Width x.i |= Backing(i) << Fraction_Width
if val < 0 do x.i *= -1
} }
init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) { init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fraction: Backing) {
i, f := math.modf(val) i, f := math.modf(val)
x.i = fraction x.i = fraction
@@ -44,9 +44,11 @@ init_from_parts :: proc(x: ^$T/Fixed($Backing, $Fraction_Width), integer, fracti
} }
to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 { to_f64 :: proc(x: $T/Fixed($Backing, $Fraction_Width)) -> f64 {
res := f64(x.i >> Fraction_Width) sign := -1.0 if x.i < 0 else 1.0
res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width) num := math.abs(x.i)
return res res := f64(num >> Fraction_Width)
res += f64(num & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
return res * sign
} }
+12 -12
View File
@@ -2286,20 +2286,20 @@ F64_MASK :: 0x7ff
F64_SHIFT :: 64 - 12 F64_SHIFT :: 64 - 12
F64_BIAS :: 0x3ff F64_BIAS :: 0x3ff
INF_F16 :f16: 0h7C00 INF_F16 :: f16(0h7C00)
NEG_INF_F16 :f16: 0hFC00 NEG_INF_F16 :: f16(0hFC00)
SNAN_F16 :f16: 0h7C01 SNAN_F16 :: f16(0h7C01)
QNAN_F16 :f16: 0h7E01 QNAN_F16 :: f16(0h7E01)
INF_F32 :f32: 0h7F80_0000 INF_F32 :: f32(0h7F80_0000)
NEG_INF_F32 :f32: 0hFF80_0000 NEG_INF_F32 :: f32(0hFF80_0000)
SNAN_F32 :f32: 0hFF80_0001 SNAN_F32 :: f32(0hFF80_0001)
QNAN_F32 :f32: 0hFFC0_0001 QNAN_F32 :: f32(0hFFC0_0001)
INF_F64 :f64: 0h7FF0_0000_0000_0000 INF_F64 :: f64(0h7FF0_0000_0000_0000)
NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000 NEG_INF_F64 :: f64(0hFFF0_0000_0000_0000)
SNAN_F64 :f64: 0h7FF0_0000_0000_0001 SNAN_F64 :: f64(0h7FF0_0000_0000_0001)
QNAN_F64 :f64: 0h7FF8_0000_0000_0001 QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
+52 -52
View File
@@ -111,11 +111,11 @@ begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
return tmp return tmp
} }
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) { end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {
assert(arena.offset >= prev_offset) assert(tmp.arena.offset >= tmp.prev_offset)
assert(arena.temp_count > 0) assert(tmp.arena.temp_count > 0)
arena.offset = prev_offset tmp.arena.offset = tmp.prev_offset
arena.temp_count -= 1 tmp.arena.temp_count -= 1
} }
@@ -702,11 +702,11 @@ dynamic_pool_init :: proc(pool: ^Dynamic_Pool,
pool. used_blocks.allocator = array_allocator pool. used_blocks.allocator = array_allocator
} }
dynamic_pool_destroy :: proc(using pool: ^Dynamic_Pool) { dynamic_pool_destroy :: proc(pool: ^Dynamic_Pool) {
dynamic_pool_free_all(pool) dynamic_pool_free_all(pool)
delete(unused_blocks) delete(pool.unused_blocks)
delete(used_blocks) delete(pool.used_blocks)
delete(out_band_allocations) delete(pool.out_band_allocations)
zero(pool, size_of(pool^)) zero(pool, size_of(pool^))
} }
@@ -719,90 +719,90 @@ dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocato
} }
@(require_results) @(require_results)
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) { dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) { cycle_new_block :: proc(p: ^Dynamic_Pool) -> (err: Allocator_Error) {
if block_allocator.procedure == nil { if p.block_allocator.procedure == nil {
panic("You must call pool_init on a Pool before using it") panic("You must call pool_init on a Pool before using it")
} }
if current_block != nil { if p.current_block != nil {
append(&used_blocks, current_block) append(&p.used_blocks, p.current_block)
} }
new_block: rawptr new_block: rawptr
if len(unused_blocks) > 0 { if len(p.unused_blocks) > 0 {
new_block = pop(&unused_blocks) new_block = pop(&p.unused_blocks)
} else { } else {
data: []byte data: []byte
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc, data, err = p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment, p.block_size, p.alignment,
nil, 0) nil, 0)
new_block = raw_data(data) new_block = raw_data(data)
} }
bytes_left = block_size p.bytes_left = p.block_size
current_pos = new_block p.current_pos = new_block
current_block = new_block p.current_block = new_block
return return
} }
n := bytes n := bytes
extra := alignment - (n % alignment) extra := p.alignment - (n % p.alignment)
n += extra n += extra
if n >= out_band_size { if n >= p.out_band_size {
assert(block_allocator.procedure != nil) assert(p.block_allocator.procedure != nil)
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc, memory, err := p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
block_size, alignment, p.block_size, p.alignment,
nil, 0) nil, 0)
if memory != nil { if memory != nil {
append(&out_band_allocations, raw_data(memory)) append(&p.out_band_allocations, raw_data(memory))
} }
return memory, err return memory, err
} }
if bytes_left < n { if p.bytes_left < n {
err := cycle_new_block(pool) err := cycle_new_block(p)
if err != nil { if err != nil {
return nil, err return nil, err
} }
if current_block == nil { if p.current_block == nil {
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
} }
} }
memory := current_pos memory := p.current_pos
current_pos = ptr_offset((^byte)(current_pos), n) p.current_pos = ([^]byte)(p.current_pos)[n:]
bytes_left -= n p.bytes_left -= n
return byte_slice(memory, bytes), nil return ([^]byte)(memory)[:bytes], nil
} }
dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) { dynamic_pool_reset :: proc(p: ^Dynamic_Pool) {
if current_block != nil { if p.current_block != nil {
append(&unused_blocks, current_block) append(&p.unused_blocks, p.current_block)
current_block = nil p.current_block = nil
} }
for block in used_blocks { for block in p.used_blocks {
append(&unused_blocks, block) append(&p.unused_blocks, block)
} }
clear(&used_blocks) clear(&p.used_blocks)
for a in out_band_allocations { for a in p.out_band_allocations {
free(a, block_allocator) free(a, p.block_allocator)
} }
clear(&out_band_allocations) clear(&p.out_band_allocations)
bytes_left = 0 // Make new allocations call `cycle_new_block` again. p.bytes_left = 0 // Make new allocations call `cycle_new_block` again.
} }
dynamic_pool_free_all :: proc(using pool: ^Dynamic_Pool) { dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) {
dynamic_pool_reset(pool) dynamic_pool_reset(p)
for block in unused_blocks { for block in p.unused_blocks {
free(block, block_allocator) free(block, p.block_allocator)
} }
clear(&unused_blocks) clear(&p.unused_blocks)
} }
+35 -35
View File
@@ -63,100 +63,100 @@ split_url :: proc(url: string, allocator := context.allocator) -> (scheme, host,
} }
join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string { join_url :: proc(scheme, host, path: string, queries: map[string]string, allocator := context.allocator) -> string {
using strings b := strings.builder_make(allocator)
strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
b := builder_make(allocator) strings.write_string(&b, scheme)
builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path)) strings.write_string(&b, "://")
strings.write_string(&b, strings.trim_space(host))
write_string(&b, scheme)
write_string(&b, "://")
write_string(&b, trim_space(host))
if path != "" { if path != "" {
if path[0] != '/' do write_string(&b, "/") if path[0] != '/' {
write_string(&b, trim_space(path)) strings.write_string(&b, "/")
}
strings.write_string(&b, strings.trim_space(path))
} }
query_length := len(queries) query_length := len(queries)
if query_length > 0 do write_string(&b, "?") if query_length > 0 {
strings.write_string(&b, "?")
}
i := 0 i := 0
for query_name, query_value in queries { for query_name, query_value in queries {
write_string(&b, query_name) strings.write_string(&b, query_name)
if query_value != "" { if query_value != "" {
write_string(&b, "=") strings.write_string(&b, "=")
write_string(&b, query_value) strings.write_string(&b, query_value)
} }
if i < query_length - 1 { if i < query_length - 1 {
write_string(&b, "&") strings.write_string(&b, "&")
} }
i += 1 i += 1
} }
return to_string(b) return strings.to_string(b)
} }
percent_encode :: proc(s: string, allocator := context.allocator) -> string { percent_encode :: proc(s: string, allocator := context.allocator) -> string {
using strings b := strings.builder_make(allocator)
strings.builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
b := builder_make(allocator)
builder_grow(&b, len(s) + 16) // NOTE(tetra): A reasonable number to allow for the number of things we need to escape.
for ch in s { for ch in s {
switch ch { switch ch {
case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~': case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~':
write_rune(&b, ch) strings.write_rune(&b, ch)
case: case:
bytes, n := utf8.encode_rune(ch) bytes, n := utf8.encode_rune(ch)
for byte in bytes[:n] { for byte in bytes[:n] {
buf: [2]u8 = --- buf: [2]u8 = ---
t := strconv.append_int(buf[:], i64(byte), 16) t := strconv.append_int(buf[:], i64(byte), 16)
write_rune(&b, '%') strings.write_rune(&b, '%')
write_string(&b, t) strings.write_string(&b, t)
} }
} }
} }
return to_string(b) return strings.to_string(b)
} }
percent_decode :: proc(encoded_string: string, allocator := context.allocator) -> (decoded_string: string, ok: bool) { percent_decode :: proc(encoded_string: string, allocator := context.allocator) -> (decoded_string: string, ok: bool) {
using strings b := strings.builder_make(allocator)
strings.builder_grow(&b, len(encoded_string))
b := builder_make(allocator) defer if !ok do strings.builder_destroy(&b)
builder_grow(&b, len(encoded_string))
defer if !ok do builder_destroy(&b)
s := encoded_string s := encoded_string
for len(s) > 0 { for len(s) > 0 {
i := index_byte(s, '%') i := strings.index_byte(s, '%')
if i == -1 { if i == -1 {
write_string(&b, s) // no '%'s; the string is already decoded strings.write_string(&b, s) // no '%'s; the string is already decoded
break break
} }
write_string(&b, s[:i]) strings.write_string(&b, s[:i])
s = s[i:] s = s[i:]
if len(s) == 0 do return // percent without anything after it if len(s) == 0 do return // percent without anything after it
s = s[1:] s = s[1:]
if s[0] == '%' { if s[0] == '%' {
write_byte(&b, '%') strings.write_byte(&b, '%')
s = s[1:] s = s[1:]
continue continue
} }
if len(s) < 2 do return // percent without encoded value if len(s) < 2 {
return // percent without encoded value
}
val := hex.decode_sequence(s[:2]) or_return val := hex.decode_sequence(s[:2]) or_return
write_byte(&b, val) strings.write_byte(&b, val)
s = s[2:] s = s[2:]
} }
ok = true ok = true
decoded_string = to_string(b) decoded_string = strings.to_string(b)
return return
} }
+25 -25
View File
@@ -162,31 +162,31 @@ Attribute :: struct {
} }
Type_Kind :: enum u32le { Type_Kind :: enum u32le {
Invalid = 0, Invalid = 0,
Basic = 1, Basic = 1,
Named = 2, Named = 2,
Generic = 3, Generic = 3,
Pointer = 4, Pointer = 4,
Array = 5, Array = 5,
Enumerated_Array = 6, Enumerated_Array = 6,
Slice = 7, Slice = 7,
Dynamic_Array = 8, Dynamic_Array = 8,
Map = 9, Map = 9,
Struct = 10, Struct = 10,
Union = 11, Union = 11,
Enum = 12, Enum = 12,
Tuple = 13, Tuple = 13,
Proc = 14, Proc = 14,
Bit_Set = 15, Bit_Set = 15,
Simd_Vector = 16, Simd_Vector = 16,
SOA_Struct_Fixed = 17, SOA_Struct_Fixed = 17,
SOA_Struct_Slice = 18, SOA_Struct_Slice = 18,
SOA_Struct_Dynamic = 19, SOA_Struct_Dynamic = 19,
Relative_Pointer = 20, Relative_Pointer = 20,
Relative_Slice = 21, Relative_Multi_Pointer = 21,
Multi_Pointer = 22, Multi_Pointer = 22,
Matrix = 23, Matrix = 23,
Soa_Pointer = 24, Soa_Pointer = 24,
} }
Type_Elems_Cap :: 4 Type_Elems_Cap :: 4
+1 -1
View File
@@ -2943,7 +2943,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
seen_ellipsis := false seen_ellipsis := false
for p.curr_tok.kind != .Close_Paren && for p.curr_tok.kind != .Close_Paren &&
p.curr_tok.kind != .EOF { p.curr_tok.kind != .EOF {
if p.curr_tok.kind == .Comma { if p.curr_tok.kind == .Comma {
error(p, p.curr_tok.pos, "expected an expression not ,") error(p, p.curr_tok.pos, "expected an expression not ,")
+86 -92
View File
@@ -336,22 +336,20 @@ hint_current_line :: proc(p: ^Printer, hint: Line_Type) {
@(private) @(private)
visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) { visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
using ast
if decl == nil { if decl == nil {
return return
} }
#partial switch v in decl.derived_stmt { #partial switch v in decl.derived_stmt {
case ^Expr_Stmt: case ^ast.Expr_Stmt:
move_line(p, decl.pos) move_line(p, decl.pos)
visit_expr(p, v.expr) visit_expr(p, v.expr)
if p.config.semicolons { if p.config.semicolons {
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
} }
case ^When_Stmt: case ^ast.When_Stmt:
visit_stmt(p, cast(^Stmt)decl) visit_stmt(p, cast(^ast.Stmt)decl)
case ^Foreign_Import_Decl: case ^ast.Foreign_Import_Decl:
if len(v.attributes) > 0 { if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes)) sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos) move_line(p, v.attributes[0].pos)
@@ -370,7 +368,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for path in v.fullpaths { for path in v.fullpaths {
push_ident_token(p, path, 0) push_ident_token(p, path, 0)
} }
case ^Foreign_Block_Decl: case ^ast.Foreign_Block_Decl:
if len(v.attributes) > 0 { if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes)) sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos) move_line(p, v.attributes[0].pos)
@@ -383,7 +381,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
visit_expr(p, v.foreign_library) visit_expr(p, v.foreign_library)
visit_stmt(p, v.body) visit_stmt(p, v.body)
case ^Import_Decl: case ^ast.Import_Decl:
move_line(p, decl.pos) move_line(p, decl.pos)
if v.name.text != "" { if v.name.text != "" {
@@ -395,7 +393,7 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
push_ident_token(p, v.fullpath, 1) push_ident_token(p, v.fullpath, 1)
} }
case ^Value_Decl: case ^ast.Value_Decl:
if len(v.attributes) > 0 { if len(v.attributes) > 0 {
sort.sort(sort_attribute(&v.attributes)) sort.sort(sort_attribute(&v.attributes))
move_line(p, v.attributes[0].pos) move_line(p, v.attributes[0].pos)
@@ -447,9 +445,9 @@ visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
for value in v.values { for value in v.values {
#partial switch a in value.derived { #partial switch a in value.derived {
case ^Union_Type, ^Enum_Type, ^Struct_Type: case ^ast.Union_Type, ^ast.Enum_Type, ^ast.Struct_Type:
add_semicolon = false || called_in_stmt add_semicolon = false || called_in_stmt
case ^Proc_Lit: case ^ast.Proc_Lit:
add_semicolon = false add_semicolon = false
} }
} }
@@ -510,40 +508,38 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) {
@(private) @(private)
visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) { visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) {
using ast
if stmt == nil { if stmt == nil {
return return
} }
switch v in stmt.derived_stmt { switch v in stmt.derived_stmt {
case ^Bad_Stmt: case ^ast.Bad_Stmt:
case ^Bad_Decl: case ^ast.Bad_Decl:
case ^Package_Decl: case ^ast.Package_Decl:
case ^Empty_Stmt: case ^ast.Empty_Stmt:
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
case ^Tag_Stmt: case ^ast.Tag_Stmt:
push_generic_token(p, .Hash, 1) push_generic_token(p, .Hash, 1)
push_generic_token(p, v.op.kind, 1, v.op.text) push_generic_token(p, v.op.kind, 1, v.op.text)
visit_stmt(p, v.stmt) visit_stmt(p, v.stmt)
case ^Import_Decl: case ^ast.Import_Decl:
visit_decl(p, cast(^Decl)stmt, true) visit_decl(p, cast(^ast.Decl)stmt, true)
return return
case ^Value_Decl: case ^ast.Value_Decl:
visit_decl(p, cast(^Decl)stmt, true) visit_decl(p, cast(^ast.Decl)stmt, true)
return return
case ^Foreign_Import_Decl: case ^ast.Foreign_Import_Decl:
visit_decl(p, cast(^Decl)stmt, true) visit_decl(p, cast(^ast.Decl)stmt, true)
return return
case ^Foreign_Block_Decl: case ^ast.Foreign_Block_Decl:
visit_decl(p, cast(^Decl)stmt, true) visit_decl(p, cast(^ast.Decl)stmt, true)
return return
case ^Using_Stmt: case ^ast.Using_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
push_generic_token(p, .Using, 1) push_generic_token(p, .Using, 1)
@@ -553,7 +549,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons { if p.config.semicolons {
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
} }
case ^Block_Stmt: case ^ast.Block_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
if v.pos.line == v.end.line { if v.pos.line == v.end.line {
@@ -583,7 +579,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_end_brace(p, v.end) visit_end_brace(p, v.end)
} }
} }
case ^If_Stmt: case ^ast.If_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
if v.label != nil { if v.label != nil {
@@ -606,7 +602,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
uses_do := false uses_do := false
if check_stmt, ok := v.body.derived.(^Block_Stmt); ok && check_stmt.uses_do { if check_stmt, ok := v.body.derived.(^ast.Block_Stmt); ok && check_stmt.uses_do {
uses_do = true uses_do = true
} }
@@ -637,7 +633,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt) visit_stmt(p, v.else_stmt)
} }
case ^Switch_Stmt: case ^ast.Switch_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
if v.label != nil { if v.label != nil {
@@ -665,7 +661,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.cond) visit_expr(p, v.cond)
visit_stmt(p, v.body) visit_stmt(p, v.body)
case ^Case_Clause: case ^ast.Case_Clause:
move_line(p, v.pos) move_line(p, v.pos)
if !p.config.indent_cases { if !p.config.indent_cases {
@@ -689,7 +685,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if !p.config.indent_cases { if !p.config.indent_cases {
indent(p) indent(p)
} }
case ^Type_Switch_Stmt: case ^ast.Type_Switch_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
hint_current_line(p, {.Switch_Stmt}) hint_current_line(p, {.Switch_Stmt})
@@ -707,7 +703,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.tag) visit_stmt(p, v.tag)
visit_stmt(p, v.body) visit_stmt(p, v.body)
case ^Assign_Stmt: case ^ast.Assign_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
hint_current_line(p, {.Assign}) hint_current_line(p, {.Assign})
@@ -721,13 +717,13 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons { if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
} }
case ^Expr_Stmt: case ^ast.Expr_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
visit_expr(p, v.expr) visit_expr(p, v.expr)
if block_stmt && p.config.semicolons { if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
} }
case ^For_Stmt: case ^ast.For_Stmt:
// this should be simplified // this should be simplified
move_line(p, v.pos) move_line(p, v.pos)
@@ -764,7 +760,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.body) visit_stmt(p, v.body)
case ^Inline_Range_Stmt: case ^ast.Inline_Range_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
if v.label != nil { if v.label != nil {
@@ -790,7 +786,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr) visit_expr(p, v.expr)
visit_stmt(p, v.body) visit_stmt(p, v.body)
case ^Range_Stmt: case ^ast.Range_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
if v.label != nil { if v.label != nil {
@@ -816,7 +812,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_expr(p, v.expr) visit_expr(p, v.expr)
visit_stmt(p, v.body) visit_stmt(p, v.body)
case ^Return_Stmt: case ^ast.Return_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
push_generic_token(p, .Return, 1) push_generic_token(p, .Return, 1)
@@ -828,7 +824,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if block_stmt && p.config.semicolons { if block_stmt && p.config.semicolons {
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
} }
case ^Defer_Stmt: case ^ast.Defer_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
push_generic_token(p, .Defer, 0) push_generic_token(p, .Defer, 0)
@@ -837,7 +833,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
if p.config.semicolons { if p.config.semicolons {
push_generic_token(p, .Semicolon, 0) push_generic_token(p, .Semicolon, 0)
} }
case ^When_Stmt: case ^ast.When_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
push_generic_token(p, .When, 1) push_generic_token(p, .When, 1)
visit_expr(p, v.cond) visit_expr(p, v.cond)
@@ -857,7 +853,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
visit_stmt(p, v.else_stmt) visit_stmt(p, v.else_stmt)
} }
case ^Branch_Stmt: case ^ast.Branch_Stmt:
move_line(p, v.pos) move_line(p, v.pos)
push_generic_token(p, v.tok.kind, 0) push_generic_token(p, v.tok.kind, 0)
@@ -921,8 +917,6 @@ push_poly_params :: proc(p: ^Printer, poly_params: ^ast.Field_List) {
@(private) @(private)
visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) { visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
using ast
if expr == nil { if expr == nil {
return return
} }
@@ -930,14 +924,14 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
set_source_position(p, expr.pos) set_source_position(p, expr.pos)
switch v in expr.derived_expr { switch v in expr.derived_expr {
case ^Bad_Expr: case ^ast.Bad_Expr:
case ^Tag_Expr: case ^ast.Tag_Expr:
push_generic_token(p, .Hash, 1) push_generic_token(p, .Hash, 1)
push_generic_token(p, v.op.kind, 1, v.op.text) push_generic_token(p, v.op.kind, 1, v.op.text)
visit_expr(p, v.expr) visit_expr(p, v.expr)
case ^Inline_Asm_Expr: case ^ast.Inline_Asm_Expr:
push_generic_token(p, v.tok.kind, 1, v.tok.text) push_generic_token(p, v.tok.kind, 1, v.tok.text)
push_generic_token(p, .Open_Paren, 1) push_generic_token(p, .Open_Paren, 1)
@@ -954,42 +948,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Comma, 0) push_generic_token(p, .Comma, 0)
visit_expr(p, v.constraints_string) visit_expr(p, v.constraints_string)
push_generic_token(p, .Close_Brace, 0) push_generic_token(p, .Close_Brace, 0)
case ^Undef: case ^ast.Undef:
push_generic_token(p, .Undef, 1) push_generic_token(p, .Undef, 1)
case ^Auto_Cast: case ^ast.Auto_Cast:
push_generic_token(p, v.op.kind, 1) push_generic_token(p, v.op.kind, 1)
visit_expr(p, v.expr) visit_expr(p, v.expr)
case ^Ternary_If_Expr: case ^ast.Ternary_If_Expr:
visit_expr(p, v.x) visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1) push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond) visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1) push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y) visit_expr(p, v.y)
case ^Ternary_When_Expr: case ^ast.Ternary_When_Expr:
visit_expr(p, v.x) visit_expr(p, v.x)
push_generic_token(p, v.op1.kind, 1) push_generic_token(p, v.op1.kind, 1)
visit_expr(p, v.cond) visit_expr(p, v.cond)
push_generic_token(p, v.op2.kind, 1) push_generic_token(p, v.op2.kind, 1)
visit_expr(p, v.y) visit_expr(p, v.y)
case ^Or_Else_Expr: case ^ast.Or_Else_Expr:
visit_expr(p, v.x) visit_expr(p, v.x)
push_generic_token(p, v.token.kind, 1) push_generic_token(p, v.token.kind, 1)
visit_expr(p, v.y) visit_expr(p, v.y)
case ^Or_Return_Expr: case ^ast.Or_Return_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, v.token.kind, 1) push_generic_token(p, v.token.kind, 1)
case ^Selector_Call_Expr: case ^ast.Selector_Call_Expr:
visit_expr(p, v.call.expr) visit_expr(p, v.call.expr)
push_generic_token(p, .Open_Paren, 1) push_generic_token(p, .Open_Paren, 1)
visit_exprs(p, v.call.args, {.Add_Comma}) visit_exprs(p, v.call.args, {.Add_Comma})
push_generic_token(p, .Close_Paren, 0) push_generic_token(p, .Close_Paren, 0)
case ^Ellipsis: case ^ast.Ellipsis:
push_generic_token(p, .Ellipsis, 1) push_generic_token(p, .Ellipsis, 1)
visit_expr(p, v.expr) visit_expr(p, v.expr)
case ^Relative_Type: case ^ast.Relative_Type:
visit_expr(p, v.tag) visit_expr(p, v.tag)
visit_expr(p, v.type) visit_expr(p, v.type)
case ^Slice_Expr: case ^ast.Slice_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0) push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.low) visit_expr(p, v.low)
@@ -999,37 +993,37 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_expr(p, v.high) visit_expr(p, v.high)
} }
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
case ^Ident: case ^ast.Ident:
if .Enforce_Poly_Names in options { if .Enforce_Poly_Names in options {
push_generic_token(p, .Dollar, 1) push_generic_token(p, .Dollar, 1)
push_ident_token(p, v.name, 0) push_ident_token(p, v.name, 0)
} else { } else {
push_ident_token(p, v.name, 1) push_ident_token(p, v.name, 1)
} }
case ^Deref_Expr: case ^ast.Deref_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0) push_generic_token(p, v.op.kind, 0)
case ^Type_Cast: case ^ast.Type_Cast:
push_generic_token(p, v.tok.kind, 1) push_generic_token(p, v.tok.kind, 1)
push_generic_token(p, .Open_Paren, 0) push_generic_token(p, .Open_Paren, 0)
visit_expr(p, v.type) visit_expr(p, v.type)
push_generic_token(p, .Close_Paren, 0) push_generic_token(p, .Close_Paren, 0)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.expr) visit_expr(p, v.expr)
case ^Basic_Directive: case ^ast.Basic_Directive:
push_generic_token(p, v.tok.kind, 1) push_generic_token(p, v.tok.kind, 1)
push_ident_token(p, v.name, 0) push_ident_token(p, v.name, 0)
case ^Distinct_Type: case ^ast.Distinct_Type:
push_generic_token(p, .Distinct, 1) push_generic_token(p, .Distinct, 1)
visit_expr(p, v.type) visit_expr(p, v.type)
case ^Dynamic_Array_Type: case ^ast.Dynamic_Array_Type:
visit_expr(p, v.tag) visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1) push_generic_token(p, .Open_Bracket, 1)
push_generic_token(p, .Dynamic, 0) push_generic_token(p, .Dynamic, 0)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.elem) visit_expr(p, v.elem)
case ^Bit_Set_Type: case ^ast.Bit_Set_Type:
push_generic_token(p, .Bit_Set, 1) push_generic_token(p, .Bit_Set, 1)
push_generic_token(p, .Open_Bracket, 0) push_generic_token(p, .Open_Bracket, 0)
@@ -1041,7 +1035,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} }
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
case ^Union_Type: case ^ast.Union_Type:
push_generic_token(p, .Union, 1) push_generic_token(p, .Union, 1)
push_poly_params(p, v.poly_params) push_poly_params(p, v.poly_params)
@@ -1066,7 +1060,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_exprs(p, v.variants, {.Add_Comma, .Trailing}) visit_exprs(p, v.variants, {.Add_Comma, .Trailing})
visit_end_brace(p, v.end) visit_end_brace(p, v.end)
} }
case ^Enum_Type: case ^ast.Enum_Type:
push_generic_token(p, .Enum, 1) push_generic_token(p, .Enum, 1)
hint_current_line(p, {.Enum}) hint_current_line(p, {.Enum})
@@ -1089,7 +1083,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} }
set_source_position(p, v.end) set_source_position(p, v.end)
case ^Struct_Type: case ^ast.Struct_Type:
push_generic_token(p, .Struct, 1) push_generic_token(p, .Struct, 1)
hint_current_line(p, {.Struct}) hint_current_line(p, {.Struct})
@@ -1124,7 +1118,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} }
set_source_position(p, v.end) set_source_position(p, v.end)
case ^Proc_Lit: case ^ast.Proc_Lit:
switch v.inlining { switch v.inlining {
case .None: case .None:
case .Inline: case .Inline:
@@ -1143,16 +1137,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
} else { } else {
push_generic_token(p, .Undef, 1) push_generic_token(p, .Undef, 1)
} }
case ^Proc_Type: case ^ast.Proc_Type:
visit_proc_type(p, v) visit_proc_type(p, v)
case ^Basic_Lit: case ^ast.Basic_Lit:
push_generic_token(p, v.tok.kind, 1, v.tok.text) push_generic_token(p, v.tok.kind, 1, v.tok.text)
case ^Binary_Expr: case ^ast.Binary_Expr:
visit_binary_expr(p, v) visit_binary_expr(p, v)
case ^Implicit_Selector_Expr: case ^ast.Implicit_Selector_Expr:
push_generic_token(p, .Period, 1) push_generic_token(p, .Period, 1)
push_ident_token(p, v.field.name, 0) push_ident_token(p, v.field.name, 0)
case ^Call_Expr: case ^ast.Call_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_format_token(p, push_format_token(p,
@@ -1167,34 +1161,34 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis) visit_call_exprs(p, v.args, v.ellipsis.kind == .Ellipsis)
push_generic_token(p, .Close_Paren, 0) push_generic_token(p, .Close_Paren, 0)
case ^Typeid_Type: case ^ast.Typeid_Type:
push_generic_token(p, .Typeid, 1) push_generic_token(p, .Typeid, 1)
if v.specialization != nil { if v.specialization != nil {
push_generic_token(p, .Quo, 0) push_generic_token(p, .Quo, 0)
visit_expr(p, v.specialization) visit_expr(p, v.specialization)
} }
case ^Selector_Expr: case ^ast.Selector_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, v.op.kind, 0) push_generic_token(p, v.op.kind, 0)
visit_expr(p, v.field) visit_expr(p, v.field)
case ^Paren_Expr: case ^ast.Paren_Expr:
push_generic_token(p, .Open_Paren, 1) push_generic_token(p, .Open_Paren, 1)
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, .Close_Paren, 0) push_generic_token(p, .Close_Paren, 0)
case ^Index_Expr: case ^ast.Index_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0) push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.index) visit_expr(p, v.index)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
case ^Matrix_Index_Expr: case ^ast.Matrix_Index_Expr:
visit_expr(p, v.expr) visit_expr(p, v.expr)
push_generic_token(p, .Open_Bracket, 0) push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.row_index) visit_expr(p, v.row_index)
push_generic_token(p, .Comma, 0) push_generic_token(p, .Comma, 0)
visit_expr(p, v.column_index) visit_expr(p, v.column_index)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
case ^Proc_Group: case ^ast.Proc_Group:
push_generic_token(p, v.tok.kind, 1) push_generic_token(p, v.tok.kind, 1)
if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line { if len(v.args) != 0 && v.pos.line != v.args[len(v.args) - 1].pos.line {
@@ -1209,7 +1203,7 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0) push_generic_token(p, .Close_Brace, 0)
} }
case ^Comp_Lit: case ^ast.Comp_Lit:
if v.type != nil { if v.type != nil {
visit_expr(p, v.type) visit_expr(p, v.type)
} }
@@ -1226,18 +1220,18 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Brace, 0) push_generic_token(p, .Close_Brace, 0)
} }
case ^Unary_Expr: case ^ast.Unary_Expr:
push_generic_token(p, v.op.kind, 1) push_generic_token(p, v.op.kind, 1)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.expr) visit_expr(p, v.expr)
case ^Field_Value: case ^ast.Field_Value:
visit_expr(p, v.field) visit_expr(p, v.field)
push_generic_token(p, .Eq, 1) push_generic_token(p, .Eq, 1)
visit_expr(p, v.value) visit_expr(p, v.value)
case ^Type_Assertion: case ^ast.Type_Assertion:
visit_expr(p, v.expr) visit_expr(p, v.expr)
if unary, ok := v.type.derived.(^Unary_Expr); ok && unary.op.text == "?" { if unary, ok := v.type.derived.(^ast.Unary_Expr); ok && unary.op.text == "?" {
push_generic_token(p, .Period, 0) push_generic_token(p, .Period, 0)
visit_expr(p, v.type) visit_expr(p, v.type)
} else { } else {
@@ -1247,13 +1241,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
push_generic_token(p, .Close_Paren, 0) push_generic_token(p, .Close_Paren, 0)
} }
case ^Pointer_Type: case ^ast.Pointer_Type:
push_generic_token(p, .Pointer, 1) push_generic_token(p, .Pointer, 1)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.elem) visit_expr(p, v.elem)
case ^Implicit: case ^ast.Implicit:
push_generic_token(p, v.tok.kind, 1) push_generic_token(p, v.tok.kind, 1)
case ^Poly_Type: case ^ast.Poly_Type:
push_generic_token(p, .Dollar, 1) push_generic_token(p, .Dollar, 1)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.type) visit_expr(p, v.type)
@@ -1263,28 +1257,28 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.specialization) visit_expr(p, v.specialization)
} }
case ^Array_Type: case ^ast.Array_Type:
visit_expr(p, v.tag) visit_expr(p, v.tag)
push_generic_token(p, .Open_Bracket, 1) push_generic_token(p, .Open_Bracket, 1)
visit_expr(p, v.len) visit_expr(p, v.len)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.elem) visit_expr(p, v.elem)
case ^Map_Type: case ^ast.Map_Type:
push_generic_token(p, .Map, 1) push_generic_token(p, .Map, 1)
push_generic_token(p, .Open_Bracket, 0) push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.key) visit_expr(p, v.key)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
merge_next_token(p) merge_next_token(p)
visit_expr(p, v.value) visit_expr(p, v.value)
case ^Helper_Type: case ^ast.Helper_Type:
visit_expr(p, v.type) visit_expr(p, v.type)
case ^Multi_Pointer_Type: case ^ast.Multi_Pointer_Type:
push_generic_token(p, .Open_Bracket, 1) push_generic_token(p, .Open_Bracket, 1)
push_generic_token(p, .Pointer, 0) push_generic_token(p, .Pointer, 0)
push_generic_token(p, .Close_Bracket, 0) push_generic_token(p, .Close_Bracket, 0)
visit_expr(p, v.elem) visit_expr(p, v.elem)
case ^Matrix_Type: case ^ast.Matrix_Type:
push_generic_token(p, .Matrix, 1) push_generic_token(p, .Matrix, 1)
push_generic_token(p, .Open_Bracket, 0) push_generic_token(p, .Open_Bracket, 0)
visit_expr(p, v.row_count) visit_expr(p, v.row_count)
+16 -16
View File
@@ -75,34 +75,34 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
t.error_count += 1 t.error_count += 1
} }
advance_rune :: proc(using t: ^Tokenizer) { advance_rune :: proc(t: ^Tokenizer) {
if read_offset < len(src) { if t.read_offset < len(t.src) {
offset = read_offset t.offset = t.read_offset
if ch == '\n' { if t.ch == '\n' {
line_offset = offset t.line_offset = t.offset
line_count += 1 t.line_count += 1
} }
r, w := rune(src[read_offset]), 1 r, w := rune(t.src[t.read_offset]), 1
switch { switch {
case r == 0: case r == 0:
error(t, t.offset, "illegal character NUL") error(t, t.offset, "illegal character NUL")
case r >= utf8.RUNE_SELF: case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune_in_string(src[read_offset:]) r, w = utf8.decode_rune_in_string(t.src[t.read_offset:])
if r == utf8.RUNE_ERROR && w == 1 { if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding") error(t, t.offset, "illegal UTF-8 encoding")
} else if r == utf8.RUNE_BOM && offset > 0 { } else if r == utf8.RUNE_BOM && t.offset > 0 {
error(t, t.offset, "illegal byte order mark") error(t, t.offset, "illegal byte order mark")
} }
} }
read_offset += w t.read_offset += w
ch = r t.ch = r
} else { } else {
offset = len(src) t.offset = len(t.src)
if ch == '\n' { if t.ch == '\n' {
line_offset = offset t.line_offset = t.offset
line_count += 1 t.line_count += 1
} }
ch = -1 t.ch = -1
} }
} }
+59 -60
View File
@@ -8,35 +8,35 @@ _ :: intrinsics
Type_Info :: runtime.Type_Info Type_Info :: runtime.Type_Info
Type_Info_Named :: runtime.Type_Info_Named Type_Info_Named :: runtime.Type_Info_Named
Type_Info_Integer :: runtime.Type_Info_Integer Type_Info_Integer :: runtime.Type_Info_Integer
Type_Info_Rune :: runtime.Type_Info_Rune Type_Info_Rune :: runtime.Type_Info_Rune
Type_Info_Float :: runtime.Type_Info_Float Type_Info_Float :: runtime.Type_Info_Float
Type_Info_Complex :: runtime.Type_Info_Complex Type_Info_Complex :: runtime.Type_Info_Complex
Type_Info_Quaternion :: runtime.Type_Info_Quaternion Type_Info_Quaternion :: runtime.Type_Info_Quaternion
Type_Info_String :: runtime.Type_Info_String Type_Info_String :: runtime.Type_Info_String
Type_Info_Boolean :: runtime.Type_Info_Boolean Type_Info_Boolean :: runtime.Type_Info_Boolean
Type_Info_Any :: runtime.Type_Info_Any Type_Info_Any :: runtime.Type_Info_Any
Type_Info_Type_Id :: runtime.Type_Info_Type_Id Type_Info_Type_Id :: runtime.Type_Info_Type_Id
Type_Info_Pointer :: runtime.Type_Info_Pointer Type_Info_Pointer :: runtime.Type_Info_Pointer
Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer
Type_Info_Procedure :: runtime.Type_Info_Procedure Type_Info_Procedure :: runtime.Type_Info_Procedure
Type_Info_Array :: runtime.Type_Info_Array Type_Info_Array :: runtime.Type_Info_Array
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array
Type_Info_Slice :: runtime.Type_Info_Slice Type_Info_Slice :: runtime.Type_Info_Slice
Type_Info_Parameters :: runtime.Type_Info_Parameters Type_Info_Parameters :: runtime.Type_Info_Parameters
Type_Info_Tuple :: runtime.Type_Info_Parameters Type_Info_Tuple :: runtime.Type_Info_Parameters
Type_Info_Struct :: runtime.Type_Info_Struct Type_Info_Struct :: runtime.Type_Info_Struct
Type_Info_Union :: runtime.Type_Info_Union Type_Info_Union :: runtime.Type_Info_Union
Type_Info_Enum :: runtime.Type_Info_Enum Type_Info_Enum :: runtime.Type_Info_Enum
Type_Info_Map :: runtime.Type_Info_Map Type_Info_Map :: runtime.Type_Info_Map
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
Type_Info_Matrix :: runtime.Type_Info_Matrix Type_Info_Matrix :: runtime.Type_Info_Matrix
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
@@ -69,7 +69,7 @@ Type_Kind :: enum {
Bit_Set, Bit_Set,
Simd_Vector, Simd_Vector,
Relative_Pointer, Relative_Pointer,
Relative_Slice, Relative_Multi_Pointer,
Matrix, Matrix,
Soa_Pointer, Soa_Pointer,
} }
@@ -80,34 +80,34 @@ type_kind :: proc(T: typeid) -> Type_Kind {
ti := type_info_of(T) ti := type_info_of(T)
if ti != nil { if ti != nil {
switch _ in ti.variant { switch _ in ti.variant {
case Type_Info_Named: return .Named case Type_Info_Named: return .Named
case Type_Info_Integer: return .Integer case Type_Info_Integer: return .Integer
case Type_Info_Rune: return .Rune case Type_Info_Rune: return .Rune
case Type_Info_Float: return .Float case Type_Info_Float: return .Float
case Type_Info_Complex: return .Complex case Type_Info_Complex: return .Complex
case Type_Info_Quaternion: return .Quaternion case Type_Info_Quaternion: return .Quaternion
case Type_Info_String: return .String case Type_Info_String: return .String
case Type_Info_Boolean: return .Boolean case Type_Info_Boolean: return .Boolean
case Type_Info_Any: return .Any case Type_Info_Any: return .Any
case Type_Info_Type_Id: return .Type_Id case Type_Info_Type_Id: return .Type_Id
case Type_Info_Pointer: return .Pointer case Type_Info_Pointer: return .Pointer
case Type_Info_Multi_Pointer: return .Multi_Pointer case Type_Info_Multi_Pointer: return .Multi_Pointer
case Type_Info_Procedure: return .Procedure case Type_Info_Procedure: return .Procedure
case Type_Info_Array: return .Array case Type_Info_Array: return .Array
case Type_Info_Enumerated_Array: return .Enumerated_Array case Type_Info_Enumerated_Array: return .Enumerated_Array
case Type_Info_Dynamic_Array: return .Dynamic_Array case Type_Info_Dynamic_Array: return .Dynamic_Array
case Type_Info_Slice: return .Slice case Type_Info_Slice: return .Slice
case Type_Info_Parameters: return .Tuple case Type_Info_Parameters: return .Tuple
case Type_Info_Struct: return .Struct case Type_Info_Struct: return .Struct
case Type_Info_Union: return .Union case Type_Info_Union: return .Union
case Type_Info_Enum: return .Enum case Type_Info_Enum: return .Enum
case Type_Info_Map: return .Map case Type_Info_Map: return .Map
case Type_Info_Bit_Set: return .Bit_Set case Type_Info_Bit_Set: return .Bit_Set
case Type_Info_Simd_Vector: return .Simd_Vector case Type_Info_Simd_Vector: return .Simd_Vector
case Type_Info_Relative_Pointer: return .Relative_Pointer case Type_Info_Relative_Pointer: return .Relative_Pointer
case Type_Info_Relative_Slice: return .Relative_Slice case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
case Type_Info_Matrix: return .Matrix case Type_Info_Matrix: return .Matrix
case Type_Info_Soa_Pointer: return .Soa_Pointer case Type_Info_Soa_Pointer: return .Soa_Pointer
} }
} }
@@ -1457,8 +1457,6 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
return equal(va, vb, including_indirect_array_recursion, recursion_level+1) return equal(va, vb, including_indirect_array_recursion, recursion_level+1)
case Type_Info_Map: case Type_Info_Map:
return false return false
case Type_Info_Relative_Slice:
return false
case case
Type_Info_Boolean, Type_Info_Boolean,
Type_Info_Integer, Type_Info_Integer,
@@ -1474,6 +1472,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
Type_Info_Enum, Type_Info_Enum,
Type_Info_Simd_Vector, Type_Info_Simd_Vector,
Type_Info_Relative_Pointer, Type_Info_Relative_Pointer,
Type_Info_Relative_Multi_Pointer,
Type_Info_Soa_Pointer, Type_Info_Soa_Pointer,
Type_Info_Matrix: Type_Info_Matrix:
return runtime.memory_compare(a.data, b.data, t.size) == 0 return runtime.memory_compare(a.data, b.data, t.size) == 0
+7 -8
View File
@@ -165,9 +165,9 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
y := b.variant.(Type_Info_Relative_Pointer) or_return y := b.variant.(Type_Info_Relative_Pointer) or_return
return x.base_integer == y.base_integer && x.pointer == y.pointer return x.base_integer == y.base_integer && x.pointer == y.pointer
case Type_Info_Relative_Slice: case Type_Info_Relative_Multi_Pointer:
y := b.variant.(Type_Info_Relative_Slice) or_return y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
return x.base_integer == y.base_integer && x.slice == y.slice return x.base_integer == y.base_integer && x.pointer == y.pointer
case Type_Info_Matrix: case Type_Info_Matrix:
y := b.variant.(Type_Info_Matrix) or_return y := b.variant.(Type_Info_Matrix) or_return
@@ -383,9 +383,9 @@ is_relative_pointer :: proc(info: ^Type_Info) -> bool {
return ok return ok
} }
@(require_results) @(require_results)
is_relative_slice :: proc(info: ^Type_Info) -> bool { is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
if info == nil { return false } if info == nil { return false }
_, ok := type_info_base(info).variant.(Type_Info_Relative_Slice) _, ok := type_info_base(info).variant.(Type_Info_Relative_Multi_Pointer)
return ok return ok
} }
@@ -395,7 +395,6 @@ is_relative_slice :: proc(info: ^Type_Info) -> bool {
write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) { write_typeid_builder :: proc(buf: ^strings.Builder, id: typeid, n_written: ^int = nil) -> (n: int, err: io.Error) {
return write_type_writer(strings.to_writer(buf), type_info_of(id)) return write_type_writer(strings.to_writer(buf), type_info_of(id))
} }
@@ -652,11 +651,11 @@ write_type_writer :: proc(w: io.Writer, ti: ^Type_Info, n_written: ^int = nil) -
io.write_string(w, ") ", &n) or_return io.write_string(w, ") ", &n) or_return
write_type(w, info.pointer, &n) or_return write_type(w, info.pointer, &n) or_return
case Type_Info_Relative_Slice: case Type_Info_Relative_Multi_Pointer:
io.write_string(w, "#relative(", &n) or_return io.write_string(w, "#relative(", &n) or_return
write_type(w, info.base_integer, &n) or_return write_type(w, info.base_integer, &n) or_return
io.write_string(w, ") ", &n) or_return io.write_string(w, ") ", &n) or_return
write_type(w, info.slice, &n) or_return write_type(w, info.pointer, &n) or_return
case Type_Info_Matrix: case Type_Info_Matrix:
io.write_string(w, "matrix[", &n) or_return io.write_string(w, "matrix[", &n) or_return
+5 -5
View File
@@ -162,11 +162,11 @@ Type_Info_Simd_Vector :: struct {
count: int, count: int,
} }
Type_Info_Relative_Pointer :: struct { Type_Info_Relative_Pointer :: struct {
pointer: ^Type_Info, pointer: ^Type_Info, // ^T
base_integer: ^Type_Info, base_integer: ^Type_Info,
} }
Type_Info_Relative_Slice :: struct { Type_Info_Relative_Multi_Pointer :: struct {
slice: ^Type_Info, pointer: ^Type_Info, // [^]T
base_integer: ^Type_Info, base_integer: ^Type_Info,
} }
Type_Info_Matrix :: struct { Type_Info_Matrix :: struct {
@@ -219,7 +219,7 @@ Type_Info :: struct {
Type_Info_Bit_Set, Type_Info_Bit_Set,
Type_Info_Simd_Vector, Type_Info_Simd_Vector,
Type_Info_Relative_Pointer, Type_Info_Relative_Pointer,
Type_Info_Relative_Slice, Type_Info_Relative_Multi_Pointer,
Type_Info_Matrix, Type_Info_Matrix,
Type_Info_Soa_Pointer, Type_Info_Soa_Pointer,
}, },
@@ -252,7 +252,7 @@ Typeid_Kind :: enum u8 {
Bit_Set, Bit_Set,
Simd_Vector, Simd_Vector,
Relative_Pointer, Relative_Pointer,
Relative_Slice, Relative_Multi_Pointer,
Matrix, Matrix,
} }
#assert(len(Typeid_Kind) < 32) #assert(len(Typeid_Kind) < 32)
+5 -2
View File
@@ -4,13 +4,16 @@
//+build !js //+build !js
package runtime package runtime
// TODO(bill): reimplement these procedures in the os_specific stuff
import "core:os"
when ODIN_DEFAULT_TO_NIL_ALLOCATOR { when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
_ :: os
// mem.nil_allocator reimplementation // mem.nil_allocator reimplementation
default_allocator_proc :: nil_allocator_proc default_allocator_proc :: nil_allocator_proc
default_allocator :: nil_allocator default_allocator :: nil_allocator
} else { } else {
// TODO(bill): reimplement these procedures in the os_specific stuff
import "core:os"
default_allocator_proc :: os.heap_allocator_proc default_allocator_proc :: os.heap_allocator_proc
+118 -98
View File
@@ -414,68 +414,21 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
tk := map_cell_index_dynamic(sk, info.ks, 1) tk := map_cell_index_dynamic(sk, info.ks, 1)
tv := map_cell_index_dynamic(sv, info.vs, 1) tv := map_cell_index_dynamic(sv, info.vs, 1)
for { swap_loop: for {
hp := &hs[pos] element_hash := hs[pos]
element_hash := hp^
if map_hash_is_empty(element_hash) { if map_hash_is_empty(element_hash) {
kp := map_cell_index_dynamic(ks, info.ks, pos) k_dst := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos) v_dst := map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k) intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v) intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hp^ = h hs[pos] = h
return result if result != 0 else vp return result if result != 0 else v_dst
} }
if map_hash_is_deleted(element_hash) { if map_hash_is_deleted(element_hash) {
next_pos := (pos + 1) & mask break swap_loop
// backward shift
for !map_hash_is_empty(hs[next_pos]) {
probe_distance := map_probe_distance(m^, hs[next_pos], next_pos)
if probe_distance == 0 {
break
}
probe_distance -= 1
kp := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos)
kn := map_cell_index_dynamic(ks, info.ks, next_pos)
vn := map_cell_index_dynamic(vs, info.vs, next_pos)
if distance > probe_distance {
if result == 0 {
result = vp
}
// move stored into pos; store next
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
hs[pos] = h
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(kn), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(vn), size_of_v)
h = hs[next_pos]
} else {
// move next back 1
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(kn), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(vn), size_of_v)
hs[pos] = hs[next_pos]
distance = probe_distance
}
hs[next_pos] = 0
pos = (pos + 1) & mask
next_pos = (next_pos + 1) & mask
distance += 1
}
kp := map_cell_index_dynamic(ks, info.ks, pos)
vp := map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(kp), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(v), size_of_v)
hs[pos] = h
return result if result != 0 else vp
} }
if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance { if probe_distance := map_probe_distance(m^, element_hash, pos); distance > probe_distance {
@@ -495,8 +448,8 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v) intrinsics.mem_copy_non_overlapping(rawptr(vp), rawptr(tv), size_of_v)
th := h th := h
h = hp^ h = hs[pos]
hp^ = th hs[pos] = th
distance = probe_distance distance = probe_distance
} }
@@ -504,6 +457,103 @@ map_insert_hash_dynamic :: proc "odin" (#no_alias m: ^Raw_Map, #no_alias info: ^
pos = (pos + 1) & mask pos = (pos + 1) & mask
distance += 1 distance += 1
} }
// backward shift loop
hs[pos] = 0
look_ahead: uintptr = 1
for {
la_pos := (pos + look_ahead) & mask
element_hash := hs[la_pos]
if map_hash_is_deleted(element_hash) {
look_ahead += 1
hs[la_pos] = 0
continue
}
k_dst := map_cell_index_dynamic(ks, info.ks, pos)
v_dst := map_cell_index_dynamic(vs, info.vs, pos)
if map_hash_is_empty(element_hash) {
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
return result if result != 0 else v_dst
}
k_src := map_cell_index_dynamic(ks, info.ks, la_pos)
v_src := map_cell_index_dynamic(vs, info.vs, la_pos)
probe_distance := map_probe_distance(m^, element_hash, la_pos)
if probe_distance < look_ahead {
// probed can be made ideal while placing saved (ending condition)
if result == 0 {
result = v_dst
}
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
// This will be an ideal move
pos = (la_pos - probe_distance) & mask
look_ahead -= probe_distance
// shift until we hit ideal/empty
for probe_distance != 0 {
k_dst = map_cell_index_dynamic(ks, info.ks, pos)
v_dst = map_cell_index_dynamic(vs, info.vs, pos)
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v)
hs[pos] = element_hash
hs[la_pos] = 0
pos = (pos + 1) & mask
la_pos = (la_pos + 1) & mask
look_ahead = (la_pos - pos) & mask
element_hash = hs[la_pos]
if map_hash_is_empty(element_hash) {
return
}
probe_distance = map_probe_distance(m^, element_hash, la_pos)
if probe_distance == 0 {
return
}
// can be ideal?
if probe_distance < look_ahead {
pos = (la_pos - probe_distance) & mask
}
k_src = map_cell_index_dynamic(ks, info.ks, la_pos)
v_src = map_cell_index_dynamic(vs, info.vs, la_pos)
}
return
} else if distance < probe_distance - look_ahead {
// shift back probed
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k_src), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v_src), size_of_v)
hs[pos] = element_hash
hs[la_pos] = 0
} else {
// place saved, save probed
if result == 0 {
result = v_dst
}
intrinsics.mem_copy_non_overlapping(rawptr(k_dst), rawptr(k), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v_dst), rawptr(v), size_of_v)
hs[pos] = h
intrinsics.mem_copy_non_overlapping(rawptr(k), rawptr(k_src), size_of_k)
intrinsics.mem_copy_non_overlapping(rawptr(v), rawptr(v_src), size_of_v)
h = hs[la_pos]
hs[la_pos] = 0
distance = probe_distance - look_ahead
}
pos = (pos + 1) & mask
distance += 1
}
} }
@(require_results) @(require_results)
@@ -696,49 +746,19 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
m.len -= 1 m.len -= 1
ok = true ok = true
{ // coalesce tombstones mask := (uintptr(1)<<map_log2_cap(m^)) - 1
// HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots curr_index := uintptr(index)
mask := (uintptr(1)<<map_log2_cap(m^)) - 1 next_index := (curr_index + 1) & mask
curr_index := uintptr(index)
// TODO(bill): determine a good value for this empirically
// if we do not implement backward shift deletion
PROBE_COUNT :: 8
for _ in 0..<PROBE_COUNT {
next_index := (curr_index + 1) & mask
if next_index == index {
// looped around
break
}
// if the next element is empty or has zero probe distance, then any lookup
// will always fail on the next, so we can clear both of them
hash := hs[next_index]
if map_hash_is_empty(hash) || map_probe_distance(m^, hash, next_index) == 0 {
hs[curr_index] = 0
return
}
// now the next element will have a probe count of at least one,
// so it can use the delete slot instead
hs[curr_index] = hs[next_index]
mem_copy_non_overlapping(
rawptr(map_cell_index_dynamic(ks, info.ks, curr_index)),
rawptr(map_cell_index_dynamic(ks, info.ks, next_index)),
int(info.ks.size_of_type),
)
mem_copy_non_overlapping(
rawptr(map_cell_index_dynamic(vs, info.vs, curr_index)),
rawptr(map_cell_index_dynamic(vs, info.vs, next_index)),
int(info.vs.size_of_type),
)
curr_index = next_index
}
// if the next element is empty or has zero probe distance, then any lookup
// will always fail on the next, so we can clear both of them
hash := hs[next_index]
if map_hash_is_empty(hash) || map_probe_distance(m^, hash, next_index) == 0 {
hs[curr_index] = 0
} else {
hs[curr_index] |= TOMBSTONE_MASK hs[curr_index] |= TOMBSTONE_MASK
} }
return return
} }
+9 -9
View File
@@ -235,7 +235,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
handle_error(loc, len) handle_error(loc, len)
} }
make_dynamic_array_error_loc :: #force_inline proc "contextless" (using loc := #caller_location, len, cap: int) { make_dynamic_array_error_loc :: #force_inline proc "contextless" (loc := #caller_location, len, cap: int) {
if 0 <= len && len <= cap { if 0 <= len && len <= cap {
return return
} }
@@ -271,18 +271,18 @@ make_map_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_loca
bounds_check_error_loc :: #force_inline proc "contextless" (using loc := #caller_location, index, count: int) { bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
bounds_check_error(file_path, line, column, index, count) bounds_check_error(loc.file_path, loc.line, loc.column, index, count)
} }
slice_expr_error_hi_loc :: #force_inline proc "contextless" (using loc := #caller_location, hi: int, len: int) { slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
slice_expr_error_hi(file_path, line, column, hi, len) slice_expr_error_hi(loc.file_path, loc.line, loc.column, hi, len)
} }
slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (using loc := #caller_location, lo, hi: int, len: int) { slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
slice_expr_error_lo_hi(file_path, line, column, lo, hi, len) slice_expr_error_lo_hi(loc.file_path, loc.line, loc.column, lo, hi, len)
} }
dynamic_array_expr_error_loc :: #force_inline proc "contextless" (using loc := #caller_location, low, high, max: int) { dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
dynamic_array_expr_error(file_path, line, column, low, high, max) dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
} }
+11 -2
View File
@@ -11,7 +11,7 @@ RUNTIME_LINKAGE :: "strong" when (
ODIN_BUILD_MODE == .Dynamic || ODIN_BUILD_MODE == .Dynamic ||
!ODIN_NO_CRT) && !ODIN_NO_CRT) &&
!IS_WASM) else "internal" !IS_WASM) else "internal"
RUNTIME_REQUIRE :: true RUNTIME_REQUIRE :: !ODIN_TILDE
@(private) @(private)
@@ -218,10 +218,18 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
case n == 0: return true case n == 0: return true
case x == y: return true case x == y: return true
} }
a, b := ([^]byte)(x), ([^]byte)(y) a, b := ([^]byte)(x), ([^]byte)(y)
length := uint(n) length := uint(n)
for i := uint(0); i < length; i += 1 {
if a[i] != b[i] {
return false
}
}
return true
/*
when size_of(uint) == 8 { when size_of(uint) == 8 {
if word_length := length >> 3; word_length != 0 { if word_length := length >> 3; word_length != 0 {
for _ in 0..<word_length { for _ in 0..<word_length {
@@ -276,6 +284,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
return true return true
} }
*/
} }
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check { memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
+6 -9
View File
@@ -1,9 +1,6 @@
//+private
//+build windows //+build windows
package runtime package runtime
import "core:intrinsics"
foreign import kernel32 "system:Kernel32.lib" foreign import kernel32 "system:Kernel32.lib"
@(private="file") @(private="file")
@@ -102,12 +99,12 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
allocated_mem: rawptr allocated_mem: rawptr
if old_ptr != nil { if old_ptr != nil {
original_old_ptr := intrinsics.ptr_offset((^rawptr)(old_ptr), -1)^ original_old_ptr := ([^]rawptr)(old_ptr)[-1]
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr)) allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr))
} else { } else {
allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory) allocated_mem = heap_alloc(space+size_of(rawptr), zero_memory)
} }
aligned_mem := rawptr(intrinsics.ptr_offset((^u8)(allocated_mem), size_of(rawptr))) aligned_mem := ([^]u8)(allocated_mem)[size_of(rawptr):]
ptr := uintptr(aligned_mem) ptr := uintptr(aligned_mem)
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a) aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a)
@@ -116,10 +113,10 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
return nil, .Out_Of_Memory return nil, .Out_Of_Memory
} }
aligned_mem = rawptr(aligned_ptr) aligned_mem = ([^]byte)(aligned_ptr)
intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem ([^]rawptr)(aligned_mem)[-1] = allocated_mem
return byte_slice(aligned_mem, size), nil return aligned_mem[:size], nil
} }
_windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) { _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory := true) -> ([]byte, Allocator_Error) {
@@ -129,7 +126,7 @@ _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory
_windows_default_free :: proc "contextless" (ptr: rawptr) { _windows_default_free :: proc "contextless" (ptr: rawptr) {
if ptr != nil { if ptr != nil {
heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^) heap_free(([^]rawptr)(ptr)[-1])
} }
} }
+17 -9
View File
@@ -8,6 +8,11 @@ _INTEGER_DIGITS_VAR := _INTEGER_DIGITS
when !ODIN_NO_RTTI { when !ODIN_NO_RTTI {
print_any_single :: proc "contextless" (arg: any) { print_any_single :: proc "contextless" (arg: any) {
x := arg x := arg
if x.data == nil {
print_string("nil")
return
}
if loc, ok := x.(Source_Code_Location); ok { if loc, ok := x.(Source_Code_Location); ok {
print_caller_location(loc) print_caller_location(loc)
return return
@@ -48,6 +53,7 @@ when !ODIN_NO_RTTI {
case int: print_int(v) case int: print_int(v)
case uint: print_uint(v) case uint: print_uint(v)
case uintptr: print_uintptr(v) case uintptr: print_uintptr(v)
case rawptr: print_uintptr(uintptr(v))
case bool: print_string("true" if v else "false") case bool: print_string("true" if v else "false")
case b8: print_string("true" if v else "false") case b8: print_string("true" if v else "false")
@@ -58,7 +64,7 @@ when !ODIN_NO_RTTI {
case: case:
ti := type_info_of(x.id) ti := type_info_of(x.id)
#partial switch v in ti.variant { #partial switch v in ti.variant {
case Type_Info_Pointer: case Type_Info_Pointer, Type_Info_Multi_Pointer:
print_uintptr((^uintptr)(x.data)^) print_uintptr((^uintptr)(x.data)^)
return return
} }
@@ -67,7 +73,9 @@ when !ODIN_NO_RTTI {
} }
} }
println_any :: proc "contextless" (args: ..any) { println_any :: proc "contextless" (args: ..any) {
context = default_context()
loop: for arg, i in args { loop: for arg, i in args {
assert(arg.id != nil)
if i != 0 { if i != 0 {
print_string(" ") print_string(" ")
} }
@@ -215,19 +223,19 @@ print_uint :: proc "contextless" (x: uint) { print_u64(u64(x)) }
print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) } print_uintptr :: proc "contextless" (x: uintptr) { print_u64(u64(x)) }
print_int :: proc "contextless" (x: int) { print_i64(i64(x)) } print_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
print_caller_location :: proc "contextless" (using loc: Source_Code_Location) { print_caller_location :: proc "contextless" (loc: Source_Code_Location) {
print_string(file_path) print_string(loc.file_path)
when ODIN_ERROR_POS_STYLE == .Default { when ODIN_ERROR_POS_STYLE == .Default {
print_byte('(') print_byte('(')
print_u64(u64(line)) print_u64(u64(loc.line))
print_byte(':') print_byte(':')
print_u64(u64(column)) print_u64(u64(loc.column))
print_byte(')') print_byte(')')
} else when ODIN_ERROR_POS_STYLE == .Unix { } else when ODIN_ERROR_POS_STYLE == .Unix {
print_byte(':') print_byte(':')
print_u64(u64(line)) print_u64(u64(loc.line))
print_byte(':') print_byte(':')
print_u64(u64(column)) print_u64(u64(loc.column))
print_byte(':') print_byte(':')
} else { } else {
#panic("unhandled ODIN_ERROR_POS_STYLE") #panic("unhandled ODIN_ERROR_POS_STYLE")
@@ -463,11 +471,11 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
print_string(") ") print_string(") ")
print_type(info.pointer) print_type(info.pointer)
case Type_Info_Relative_Slice: case Type_Info_Relative_Multi_Pointer:
print_string("#relative(") print_string("#relative(")
print_type(info.base_integer) print_type(info.base_integer)
print_string(") ") print_string(") ")
print_type(info.slice) print_type(info.pointer)
case Type_Info_Matrix: case Type_Info_Matrix:
print_string("matrix[") print_string("matrix[")
+2 -2
View File
@@ -31,7 +31,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
if ptr != nil && len != 0 { if ptr != nil && len != 0 {
b := byte(val) b := byte(val)
p := ([^]byte)(ptr) p := ([^]byte)(ptr)
for i in 0..<len { for i := 0; i < len; i += 1 {
p[i] = b p[i] = b
} }
} }
@@ -75,7 +75,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
if ptr != nil && len != 0 { if ptr != nil && len != 0 {
b := byte(val) b := byte(val)
p := ([^]byte)(ptr) p := ([^]byte)(ptr)
for i in 0..<len { for i := 0; i < len; i += 1 {
p[i] = b p[i] = b
} }
} }
+6
View File
@@ -1709,6 +1709,12 @@ MONITORINFO :: struct {
} }
LPMONITORINFO :: ^MONITORINFO LPMONITORINFO :: ^MONITORINFO
CCHDEVICENAME :: 32
MONITORINFOEXW :: struct {
using _: MONITORINFO,
szDevice: [CCHDEVICENAME]WCHAR,
}
// SetWindowsHook() codes // SetWindowsHook() codes
WH_MIN :: -1 WH_MIN :: -1
WH_MSGFILTER :: -1 WH_MSGFILTER :: -1
+3
View File
@@ -228,6 +228,9 @@ foreign user32 {
SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int --- SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN --- CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int --- GetSystemMetricsForDpi :: proc(nIndex: int, dpi: UINT) -> int ---
GetSystemMenu :: proc(hWnd: HWND, bRevert: BOOL) -> HMENU ---
EnableMenuItem :: proc(hMenu: HMENU, uIDEnableItem: UINT, uEnable: UINT) -> BOOL ---
} }
CreateWindowW :: #force_inline proc "stdcall" ( CreateWindowW :: #force_inline proc "stdcall" (
+2
View File
@@ -71,6 +71,8 @@ Error :: enum {
TS_File_Expected_Source, TS_File_Expected_Source,
TS_File_Expected_Translation, TS_File_Expected_Translation,
TS_File_Expected_NumerusForm, TS_File_Expected_NumerusForm,
Bad_Str,
Bad_Id,
} }
+34 -8
View File
@@ -30,10 +30,26 @@ TS_XML_Options := xml.Options{
parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) { parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTIONS, pluralizer: proc(int) -> int = nil, allocator := context.allocator) -> (translation: ^Translation, err: Error) {
context.allocator = allocator context.allocator = allocator
get_str :: proc(val: xml.Value) -> (str: string, err: Error) {
v, ok := val.(string)
if ok {
return v, .None
}
return "", .Bad_Str
}
get_id :: proc(val: xml.Value) -> (str: xml.Element_ID, err: Error) {
v, ok := val.(xml.Element_ID)
if ok {
return v, .None
}
return 0, .Bad_Id
}
ts, xml_err := xml.parse(data, TS_XML_Options) ts, xml_err := xml.parse(data, TS_XML_Options)
defer xml.destroy(ts) defer xml.destroy(ts)
if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].children) == 0 { if xml_err != .None || ts.element_count < 1 || ts.elements[0].ident != "TS" || len(ts.elements[0].value) == 0 {
return nil, .TS_File_Parse_Error return nil, .TS_File_Parse_Error
} }
@@ -46,10 +62,12 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
section: ^Section section: ^Section
for child_id in ts.elements[0].children { for value in ts.elements[0].value {
child_id := get_id(value) or_return
// These should be <context>s. // These should be <context>s.
child := ts.elements[child_id]
if child.ident != "context" { if ts.elements[child_id].ident != "context" {
return translation, .TS_File_Expected_Context return translation, .TS_File_Expected_Context
} }
@@ -61,7 +79,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
section_name, _ := strings.intern_get(&translation.intern, "") section_name, _ := strings.intern_get(&translation.intern, "")
if !options.merge_sections { if !options.merge_sections {
section_name, _ = strings.intern_get(&translation.intern, ts.elements[section_name_id].value) value_text := get_str(ts.elements[section_name_id].value[0]) or_return
section_name, _ = strings.intern_get(&translation.intern, value_text)
} }
if section_name not_in translation.k_v { if section_name not_in translation.k_v {
@@ -92,8 +111,14 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
return translation, .TS_File_Expected_Translation return translation, .TS_File_Expected_Translation
} }
source, _ := strings.intern_get(&translation.intern, ts.elements[source_id].value) source := get_str(ts.elements[source_id].value[0]) or_return
xlat, _ := strings.intern_get(&translation.intern, ts.elements[translation_id].value) source, _ = strings.intern_get(&translation.intern, source)
xlat := ""
if !has_plurals {
xlat = get_str(ts.elements[translation_id].value[0]) or_return
xlat, _ = strings.intern_get(&translation.intern, xlat)
}
if source in section { if source in section {
return translation, .Duplicate_Key return translation, .Duplicate_Key
@@ -124,7 +149,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
if !numerus_found { if !numerus_found {
break break
} }
numerus, _ := strings.intern_get(&translation.intern, ts.elements[numerus_id].value) numerus := get_str(ts.elements[numerus_id].value[0]) or_return
numerus, _ = strings.intern_get(&translation.intern, numerus)
section[source][num_plurals] = numerus section[source][num_plurals] = numerus
num_plurals += 1 num_plurals += 1
+2 -2
View File
@@ -129,8 +129,8 @@ _destroy :: proc(thread: ^Thread) {
free(thread, thread.creation_allocator) free(thread, thread.creation_allocator)
} }
_terminate :: proc(using thread : ^Thread, exit_code: int) { _terminate :: proc(thread: ^Thread, exit_code: int) {
win32.TerminateThread(win32_thread, u32(exit_code)) win32.TerminateThread(thread.win32_thread, u32(exit_code))
} }
_yield :: proc() { _yield :: proc() {
+19 -17
View File
@@ -59,28 +59,30 @@ sleep :: proc "contextless" (d: Duration) {
_sleep(d) _sleep(d)
} }
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) { stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
if !running { if !stopwatch.running {
_start_time = tick_now() stopwatch._start_time = tick_now()
running = true stopwatch.running = true
} }
} }
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) { stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
if running { if stopwatch.running {
_accumulation += tick_diff(_start_time, tick_now()) stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
running = false stopwatch.running = false
} }
} }
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) { stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
_accumulation = {} stopwatch._accumulation = {}
running = false stopwatch.running = false
} }
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration { stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
if !running { return _accumulation } if !stopwatch.running {
return _accumulation + tick_diff(_start_time, tick_now()) return stopwatch._accumulation
}
return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
} }
diff :: proc "contextless" (start, end: Time) -> Duration { diff :: proc "contextless" (start, end: Time) -> Duration {
@@ -171,9 +173,9 @@ day :: proc "contextless" (t: Time) -> (day: int) {
} }
weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) { weekday :: proc "contextless" (t: Time) -> (weekday: Weekday) {
abs := _time_abs(t) abs := _time_abs(t)
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
return Weekday(int(sec) / SECONDS_PER_DAY) return Weekday(int(sec) / SECONDS_PER_DAY)
} }
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch } clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
+28 -6
View File
@@ -1,3 +1,4 @@
//+vet !using-stmt !using-param
package main package main
import "core:fmt" import "core:fmt"
@@ -458,6 +459,27 @@ named_proc_return_parameters :: proc() {
fmt.println("foo2 =", foo2()) // 567 321 fmt.println("foo2 =", foo2()) // 567 321
} }
variadic_procedures :: proc() {
fmt.println("\n# variadic procedures")
sum :: proc(nums: ..int, init_value:= 0) -> (result: int) {
result = init_value
for n in nums {
result += n
}
return
}
fmt.println("sum(()) =", sum())
fmt.println("sum(1, 2) =", sum(1, 2))
fmt.println("sum(1, 2, 3, 4, 5) =", sum(1, 2, 3, 4, 5))
fmt.println("sum(1, 2, 3, 4, 5, init_value = 5) =", sum(1, 2, 3, 4, 5, init_value = 5))
// pass a slice as varargs
odds := []int{1, 3, 5}
fmt.println("odds =", odds)
fmt.println("sum(..odds) =", sum(..odds))
fmt.println("sum(..odds, init_value = 5) =", sum(..odds, init_value = 5))
}
explicit_procedure_overloading :: proc() { explicit_procedure_overloading :: proc() {
fmt.println("\n# explicit procedure overloading") fmt.println("\n# explicit procedure overloading")
@@ -2023,12 +2045,11 @@ relative_data_types :: proc() {
fmt.println(ptr^) fmt.println(ptr^)
arr := [3]int{1, 2, 3} arr := [3]int{1, 2, 3}
s := arr[:] multi_ptr: #relative(i16) [^]int
rel_slice: #relative(i16) []int multi_ptr = &arr[0]
rel_slice = s fmt.println(multi_ptr)
fmt.println(rel_slice) fmt.println(multi_ptr[:3])
fmt.println(rel_slice[:]) fmt.println(multi_ptr[1])
fmt.println(rel_slice[1])
} }
or_else_operator :: proc() { or_else_operator :: proc() {
@@ -2462,6 +2483,7 @@ main :: proc() {
the_basics() the_basics()
control_flow() control_flow()
named_proc_return_parameters() named_proc_return_parameters()
variadic_procedures()
explicit_procedure_overloading() explicit_procedure_overloading()
struct_type() struct_type()
union_type() union_type()
+11
View File
@@ -168,6 +168,17 @@ gb_internal gb_inline Slice<T> slice(Slice<T> const &array, isize lo, isize hi)
} }
return out; return out;
} }
template <typename T>
gb_internal gb_inline Slice<T> slice(Array<T> const &array, isize lo, isize hi) {
GB_ASSERT(0 <= lo && lo <= hi && hi <= array.count);
Slice<T> out = {};
isize len = hi-lo;
if (len > 0) {
out.data = array.data+lo;
out.count = len;
}
return out;
}
template <typename T> template <typename T>
+41 -3
View File
@@ -216,6 +216,43 @@ enum BuildPath : u8 {
BuildPathCOUNT, BuildPathCOUNT,
}; };
enum VetFlags : u64 {
VetFlag_NONE = 0,
VetFlag_Unused = 1u<<0, // 1
VetFlag_Shadowing = 1u<<1, // 2
VetFlag_UsingStmt = 1u<<2, // 4
VetFlag_UsingParam = 1u<<3, // 8
VetFlag_Style = 1u<<4, // 16
VetFlag_Semicolon = 1u<<5, // 32
VetFlag_Extra = 1u<<16,
VetFlag_All = VetFlag_Unused|VetFlag_Shadowing|VetFlag_UsingStmt, // excluding extra
VetFlag_Using = VetFlag_UsingStmt|VetFlag_UsingParam,
};
u64 get_vet_flag_from_name(String const &name) {
if (name == "unused") {
return VetFlag_Unused;
} else if (name == "shadowing") {
return VetFlag_Shadowing;
} else if (name == "using-stmt") {
return VetFlag_UsingStmt;
} else if (name == "using-param") {
return VetFlag_UsingParam;
} else if (name == "style") {
return VetFlag_Style;
} else if (name == "semicolon") {
return VetFlag_Semicolon;
} else if (name == "extra") {
return VetFlag_Extra;
}
return VetFlag_NONE;
}
// This stores the information for the specify architecture of this build // This stores the information for the specify architecture of this build
struct BuildContext { struct BuildContext {
// Constants // Constants
@@ -255,6 +292,8 @@ struct BuildContext {
String resource_filepath; String resource_filepath;
String pdb_filepath; String pdb_filepath;
u64 vet_flags;
bool has_resource; bool has_resource;
String link_flags; String link_flags;
String extra_linker_flags; String extra_linker_flags;
@@ -280,15 +319,12 @@ struct BuildContext {
bool no_entry_point; bool no_entry_point;
bool no_thread_local; bool no_thread_local;
bool use_lld; bool use_lld;
bool vet;
bool vet_extra;
bool cross_compiling; bool cross_compiling;
bool different_os; bool different_os;
bool keep_object_files; bool keep_object_files;
bool disallow_do; bool disallow_do;
bool strict_style; bool strict_style;
bool strict_style_init_only;
bool ignore_warnings; bool ignore_warnings;
bool warnings_as_errors; bool warnings_as_errors;
@@ -318,6 +354,8 @@ struct BuildContext {
isize max_error_count; isize max_error_count;
bool tilde_backend;
u32 cmd_doc_flags; u32 cmd_doc_flags;
Array<String> extra_packages; Array<String> extra_packages;
+5 -2
View File
@@ -1406,7 +1406,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
} }
return false; return false;
} else if (name == "load_or") { } else if (name == "load_or") {
warning(call, "'#load_or' is deprecated in favour of '#load(path) or_else default'"); error(call, "'#load_or' has now been removed in favour of '#load(path) or_else default'");
if (ce->args.count != 2) { if (ce->args.count != 2) {
if (ce->args.count == 0) { if (ce->args.count == 0) {
@@ -1748,7 +1748,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
mode = Addressing_Constant; mode = Addressing_Constant;
value = exact_value_i64(at->EnumeratedArray.count); value = exact_value_i64(at->EnumeratedArray.count);
type = t_untyped_integer; type = t_untyped_integer;
} else if ((is_type_slice(op_type) || is_type_relative_slice(op_type)) && id == BuiltinProc_len) { } else if (is_type_slice(op_type) && id == BuiltinProc_len) {
mode = Addressing_Value; mode = Addressing_Value;
} else if (is_type_dynamic_array(op_type)) { } else if (is_type_dynamic_array(op_type)) {
mode = Addressing_Value; mode = Addressing_Value;
@@ -3692,6 +3692,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
case Type_SimdVector: case Type_SimdVector:
operand->type = alloc_type_multi_pointer(base_array_type(base)); operand->type = alloc_type_multi_pointer(base_array_type(base));
break; break;
case Type_Matrix:
operand->type = alloc_type_multi_pointer(base->Matrix.elem);
break;
} }
} }
break; break;
+77 -90
View File
@@ -7,13 +7,15 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
e->type == t_invalid) { e->type == t_invalid) {
if (operand->mode == Addressing_Builtin) { if (operand->mode == Addressing_Builtin) {
ERROR_BLOCK();
gbString expr_str = expr_to_string(operand->expr); gbString expr_str = expr_to_string(operand->expr);
// TODO(bill): is this a good enough error message?
error(operand->expr, error(operand->expr,
"Cannot assign built-in procedure '%s' in %.*s", "Cannot assign built-in procedure '%s' in %.*s",
expr_str, expr_str,
LIT(context_name)); LIT(context_name));
error_line("\tBuilt-in procedures are implemented by the compiler and might not be actually instantiated procedure\n");
operand->mode = Addressing_Invalid; operand->mode = Addressing_Invalid;
@@ -159,9 +161,8 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op
} }
if (operand->mode != Addressing_Constant) { if (operand->mode != Addressing_Constant) {
// TODO(bill): better error
gbString str = expr_to_string(operand->expr); gbString str = expr_to_string(operand->expr);
error(operand->expr, "'%s' is not a constant", str); error(operand->expr, "'%s' is not a compile-time known constant", str);
gb_string_free(str); gb_string_free(str);
if (e->type == nullptr) { if (e->type == nullptr) {
e->type = t_invalid; e->type = t_invalid;
@@ -354,31 +355,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
// using decl // using decl
if (decl->is_using) { if (decl->is_using) {
warning(init_expr, "'using' an enum declaration is not allowed, prefer using implicit selector expressions e.g. '.A'"); error(init_expr, "'using' an enum declaration is not allowed, prefer using implicit selector expressions e.g. '.A'");
#if 1
// NOTE(bill): Must be an enum declaration
if (te->kind == Ast_EnumType) {
Scope *parent = e->scope;
if (parent->flags&ScopeFlag_File) {
// NOTE(bill): Use package scope
parent = parent->parent;
}
Type *t = base_type(e->type);
if (t->kind == Type_Enum) {
for (Entity *f : t->Enum.fields) {
if (f->kind != Entity_Constant) {
continue;
}
String name = f->token.string;
if (is_blank_ident(name)) {
continue;
}
add_entity(ctx, parent, nullptr, f);
}
}
}
#endif
} }
} }
@@ -757,6 +734,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
return link_name; return link_name;
} }
gb_internal void check_objc_methods(CheckerContext *ctx, Entity *e, AttributeContext const &ac) {
if (!(ac.objc_name.len || ac.objc_is_class_method || ac.objc_type)) {
return;
}
if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
} else if (ac.objc_type == nullptr) {
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
} else if (ac.objc_name.len == 0 && ac.objc_type) {
error(e->token, "@(objc_name) is required with @(objc_type)");
} else {
Type *t = ac.objc_type;
if (t->kind == Type_Named) {
Entity *tn = t->Named.type_name;
GB_ASSERT(tn->kind == Entity_TypeName);
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
mutex_lock(&global_type_name_objc_metadata_mutex);
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
if (!tn->TypeName.objc_metadata) {
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
}
auto *md = tn->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
if (!ac.objc_is_class_method) {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
} else {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
}
}
}
}
}
gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) { gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
GB_ASSERT(e->type == nullptr); GB_ASSERT(e->type == nullptr);
if (d->proc_lit->kind != Ast_ProcLit) { if (d->proc_lit->kind != Ast_ProcLit) {
@@ -840,62 +877,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
} }
e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode; e->Procedure.optimization_mode = cast(ProcedureOptimizationMode)ac.optimization_mode;
if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) { check_objc_methods(ctx, e, ac);
if (ac.objc_name.len == 0 && ac.objc_is_class_method) {
error(e->token, "@(objc_name) is required with @(objc_is_class_method)");
} else if (ac.objc_type == nullptr) {
error(e->token, "@(objc_name) requires that @(objc_type) to be set");
} else if (ac.objc_name.len == 0 && ac.objc_type) {
error(e->token, "@(objc_name) is required with @(objc_type)");
} else {
Type *t = ac.objc_type;
if (t->kind == Type_Named) {
Entity *tn = t->Named.type_name;
GB_ASSERT(tn->kind == Entity_TypeName);
if (tn->scope != e->scope) {
error(e->token, "@(objc_name) attribute may only be applied to procedures and types within the same scope");
} else {
mutex_lock(&global_type_name_objc_metadata_mutex);
defer (mutex_unlock(&global_type_name_objc_metadata_mutex));
if (!tn->TypeName.objc_metadata) {
tn->TypeName.objc_metadata = create_type_name_obj_c_metadata();
}
auto *md = tn->TypeName.objc_metadata;
mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex));
if (!ac.objc_is_class_method) {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->value_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
} else {
bool ok = true;
for (TypeNameObjCMetadataEntry const &entry : md->type_entries) {
if (entry.name == ac.objc_name) {
error(e->token, "Previous declaration of @(objc_name=\"%.*s\")", LIT(ac.objc_name));
ok = false;
break;
}
}
if (ok) {
array_add(&md->type_entries, TypeNameObjCMetadataEntry{ac.objc_name, e});
}
}
}
}
}
}
if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) { if (ac.require_target_feature.len != 0 && ac.enable_target_feature.len != 0) {
error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together"); error(e->token, "Attributes @(require_target_feature=...) and @(enable_target_feature=...) cannot be used together");
@@ -1059,7 +1041,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
auto *fp = &ctx->info->foreigns; auto *fp = &ctx->info->foreigns;
StringHashKey key = string_hash_string(name); StringHashKey key = string_hash_string(name);
Entity **found = string_map_get(fp, key); Entity **found = string_map_get(fp, key);
if (found) { if (found && e != *found) {
Entity *f = *found; Entity *f = *found;
TokenPos pos = f->token.pos; TokenPos pos = f->token.pos;
Type *this_type = base_type(e->type); Type *this_type = base_type(e->type);
@@ -1241,7 +1223,7 @@ gb_internal void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast
check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed"); check_rtti_type_disallowed(e->token, e->type, "A variable declaration is using a type, %s, which has been disallowed");
} }
gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity, DeclInfo *d) { gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *pg_entity, DeclInfo *d) {
GB_ASSERT(pg_entity->kind == Entity_ProcGroup); GB_ASSERT(pg_entity->kind == Entity_ProcGroup);
auto *pge = &pg_entity->ProcGroup; auto *pge = &pg_entity->ProcGroup;
String proc_group_name = pg_entity->token.string; String proc_group_name = pg_entity->token.string;
@@ -1366,6 +1348,11 @@ gb_internal void check_proc_group_decl(CheckerContext *ctx, Entity *&pg_entity,
} }
} }
AttributeContext ac = {};
check_decl_attributes(ctx, d->attributes, proc_group_attribute, &ac);
check_objc_methods(ctx, pg_entity, ac);
} }
gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) { gb_internal void check_entity_decl(CheckerContext *ctx, Entity *e, DeclInfo *d, Type *named_type) {
@@ -1626,7 +1613,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
} }
check_close_scope(ctx); check_close_scope(ctx);
check_scope_usage(ctx->checker, ctx->scope); check_scope_usage(ctx->checker, ctx->scope, check_vet_flags(body));
add_deps_from_child_to_parent(decl); add_deps_from_child_to_parent(decl);
+188 -154
View File
@@ -349,6 +349,10 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
return false; return false;
} }
if (base_entity->flags & EntityFlag_Disabled) {
return false;
}
String name = base_entity->token.string; String name = base_entity->token.string;
Type *src = base_type(base_entity->type); Type *src = base_type(base_entity->type);
@@ -462,7 +466,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
{ {
// LEAK TODO(bill): This is technically a memory leak as it has to generate the type twice // LEAK NOTE(bill): This is technically a memory leak as it has to generate the type twice
bool prev_no_polymorphic_errors = nctx.no_polymorphic_errors; bool prev_no_polymorphic_errors = nctx.no_polymorphic_errors;
defer (nctx.no_polymorphic_errors = prev_no_polymorphic_errors); defer (nctx.no_polymorphic_errors = prev_no_polymorphic_errors);
nctx.no_polymorphic_errors = false; nctx.no_polymorphic_errors = false;
@@ -470,7 +474,7 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
// NOTE(bill): Reset scope from the failed procedure type // NOTE(bill): Reset scope from the failed procedure type
scope_reset(scope); scope_reset(scope);
// LEAK TODO(bill): Cloning this AST may be leaky // LEAK NOTE(bill): Cloning this AST may be leaky but this is not really an issue due to arena-based allocation
Ast *cloned_proc_type_node = clone_ast(pt->node); Ast *cloned_proc_type_node = clone_ast(pt->node);
success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands); success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
if (!success) { if (!success) {
@@ -778,16 +782,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
} }
} }
// ^T <- rawptr
#if 0
// TODO(bill): Should C-style (not C++) pointer cast be allowed?
if (is_type_pointer(dst) && is_type_rawptr(src)) {
return true;
}
#endif
#if 1
// rawptr <- ^T // rawptr <- ^T
if (are_types_identical(type, t_rawptr) && is_type_pointer(src)) { if (are_types_identical(type, t_rawptr) && is_type_pointer(src)) {
return 5; return 5;
@@ -808,7 +802,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
return 4; return 4;
} }
} }
#endif
if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) { if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) {
bool modify_type = !c->no_polymorphic_errors; bool modify_type = !c->no_polymorphic_errors;
@@ -824,7 +817,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
} }
} }
// TODO(bill): Determine which rule is a better on in practice
if (dst->Union.variants.count == 1) { if (dst->Union.variants.count == 1) {
Type *vt = dst->Union.variants[0]; Type *vt = dst->Union.variants[0];
i64 score = check_distance_between_types(c, operand, vt); i64 score = check_distance_between_types(c, operand, vt);
@@ -864,8 +856,8 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
} }
} }
if (is_type_relative_slice(dst)) { if (is_type_relative_multi_pointer(dst)) {
i64 score = check_distance_between_types(c, operand, dst->RelativeSlice.slice_type); i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
if (score >= 0) { if (score >= 0) {
return score+2; return score+2;
} }
@@ -1013,8 +1005,8 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
case Type_RelativePointer: case Type_RelativePointer:
type = type->RelativePointer.pointer_type; type = type->RelativePointer.pointer_type;
continue; continue;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
type = type->RelativeSlice.slice_type; type = type->RelativeMultiPointer.pointer_type;
continue; continue;
} }
return nullptr; return nullptr;
@@ -1093,7 +1085,7 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
// TODO(bill): is this a good enough error message? // TODO(bill): is this a good enough error message?
error(operand->expr, error(operand->expr,
"Cannot assign overloaded procedure '%s' to '%s' in %.*s", "Cannot assign overloaded procedure group '%s' to '%s' in %.*s",
expr_str, expr_str,
op_type_str, op_type_str,
LIT(context_name)); LIT(context_name));
@@ -1120,7 +1112,6 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
switch (operand->mode) { switch (operand->mode) {
case Addressing_Builtin: case Addressing_Builtin:
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
error(operand->expr, error(operand->expr,
"Cannot assign built-in procedure '%s' in %.*s", "Cannot assign built-in procedure '%s' in %.*s",
expr_str, expr_str,
@@ -1412,9 +1403,6 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
return false; return false;
case Type_Proc: case Type_Proc:
if (source->kind == Type_Proc) { if (source->kind == Type_Proc) {
// return check_is_assignable_to(c, &o, poly);
// TODO(bill): Polymorphic type assignment
#if 1
TypeProc *x = &poly->Proc; TypeProc *x = &poly->Proc;
TypeProc *y = &source->Proc; TypeProc *y = &source->Proc;
if (x->calling_convention != y->calling_convention) { if (x->calling_convention != y->calling_convention) {
@@ -1447,7 +1435,6 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
} }
return true; return true;
#endif
} }
return false; return false;
case Type_Map: case Type_Map:
@@ -1699,7 +1686,6 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
gb_string_free(str); gb_string_free(str);
return false; return false;
} }
// TODO(bill): Handle errors correctly
Type *type = base_type(core_array_type(o->type)); Type *type = base_type(core_array_type(o->type));
gbString str = nullptr; gbString str = nullptr;
switch (op.kind) { switch (op.kind) {
@@ -1743,7 +1729,6 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) { gb_internal bool check_binary_op(CheckerContext *c, Operand *o, Token op) {
Type *main_type = o->type; Type *main_type = o->type;
// TODO(bill): Handle errors correctly
Type *type = base_type(core_array_type(main_type)); Type *type = base_type(core_array_type(main_type));
Type *ct = core_type(type); Type *ct = core_type(type);
@@ -2261,7 +2246,7 @@ gb_internal bool check_is_not_addressable(CheckerContext *c, Operand *o) {
} }
gb_internal void check_old_for_or_switch_value_usage(Ast *expr) { gb_internal void check_old_for_or_switch_value_usage(Ast *expr) {
if (!build_context.strict_style) { if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) {
return; return;
} }
@@ -2351,7 +2336,7 @@ gb_internal void check_unary_expr(CheckerContext *c, Operand *o, Token op, Ast *
o->type = alloc_type_pointer(o->type); o->type = alloc_type_pointer(o->type);
} }
} else { } else {
if (build_context.strict_style && ast_node_expect(node, Ast_UnaryExpr)) { if (ast_node_expect(node, Ast_UnaryExpr)) {
ast_node(ue, UnaryExpr, node); ast_node(ue, UnaryExpr, node);
check_old_for_or_switch_value_usage(ue->expr); check_old_for_or_switch_value_usage(ue->expr);
} }
@@ -2775,8 +2760,6 @@ gb_internal void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *nod
gb_string_free(err_str); gb_string_free(err_str);
} }
// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
if (!is_type_integer(x->type)) { if (!is_type_integer(x->type)) {
gbString err_str = expr_to_string(x->expr); gbString err_str = expr_to_string(x->expr);
error(node, "Shift operand '%s' must be an integer", err_str); error(node, "Shift operand '%s' must be an integer", err_str);
@@ -3099,7 +3082,7 @@ gb_internal void check_cast(CheckerContext *c, Operand *x, Type *type) {
update_untyped_expr_type(c, x->expr, final_type, true); update_untyped_expr_type(c, x->expr, final_type, true);
} }
if (build_context.vet_extra) { if (check_vet_flags(x->expr) & VetFlag_Extra) {
if (are_types_identical(x->type, type)) { if (are_types_identical(x->type, type)) {
gbString str = type_to_string(type); gbString str = type_to_string(type);
warning(x->expr, "Unneeded cast to the same type '%s'", str); warning(x->expr, "Unneeded cast to the same type '%s'", str);
@@ -3171,7 +3154,7 @@ gb_internal bool check_transmute(CheckerContext *c, Ast *node, Operand *o, Type
return false; return false;
} }
if (build_context.vet_extra) { if (check_vet_flags(node) & VetFlag_Extra) {
if (are_types_identical(o->type, dst_t)) { if (are_types_identical(o->type, dst_t)) {
gbString str = type_to_string(dst_t); gbString str = type_to_string(dst_t);
warning(o->expr, "Unneeded transmute to the same type '%s'", str); warning(o->expr, "Unneeded transmute to the same type '%s'", str);
@@ -4437,7 +4420,6 @@ gb_internal ExactValue get_constant_field_single(CheckerContext *c, ExactValue v
case_end; case_end;
default: default:
// TODO(bill): Should this be a general fallback?
if (success_) *success_ = true; if (success_) *success_ = true;
if (finish_) *finish_ = true; if (finish_) *finish_ = true;
return empty_exact_value; return empty_exact_value;
@@ -4793,8 +4775,6 @@ gb_internal Entity *check_selector(CheckerContext *c, Operand *operand, Ast *nod
} }
if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) { if (entity == nullptr && selector->kind == Ast_Ident && is_type_array(type_deref(operand->type))) {
// TODO(bill): Simd_Vector swizzling
String field_name = selector->Ident.token.string; String field_name = selector->Ident.token.string;
if (1 < field_name.len && field_name.len <= 4) { if (1 < field_name.len && field_name.len <= 4) {
u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'}; u8 swizzles_xyzw[4] = {'x', 'y', 'z', 'w'};
@@ -5112,27 +5092,6 @@ gb_internal bool check_identifier_exists(Scope *s, Ast *node, bool nested = fals
return false; return false;
} }
gb_internal isize add_dependencies_from_unpacking(CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) {
if (lhs != nullptr && c->decl != nullptr) {
for (isize j = 0; (tuple_index + j) < lhs_count && j < tuple_count; j++) {
Entity *e = lhs[tuple_index + j];
if (e != nullptr) {
DeclInfo *decl = decl_info_of_entity(e);
if (decl != nullptr) {
rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&c->decl->deps_mutex);
for (Entity *dep : decl->deps) {
ptr_set_add(&c->decl->deps, dep);
}
rw_mutex_unlock(&c->decl->deps_mutex);
rw_mutex_shared_unlock(&decl->deps_mutex);
}
}
}
}
return tuple_count;
}
gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) { gb_internal bool check_no_copy_assignment(Operand const &o, String const &context) {
if (o.type && is_type_no_copy(o.type)) { if (o.type && is_type_no_copy(o.type)) {
Ast *expr = unparen_expr(o.expr); Ast *expr = unparen_expr(o.expr);
@@ -5240,6 +5199,31 @@ enum UnpackFlag : u32 {
gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) { gb_internal bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count, Array<Operand> *operands, Slice<Ast *> const &rhs_arguments, UnpackFlags flags) {
auto const &add_dependencies_from_unpacking = [](CheckerContext *c, Entity **lhs, isize lhs_count, isize tuple_index, isize tuple_count) -> isize {
if (lhs == nullptr || c->decl == nullptr) {
return tuple_count;
}
for (isize j = 0; (tuple_index + j) < lhs_count && j < tuple_count; j++) {
Entity *e = lhs[tuple_index + j];
if (e == nullptr) {
continue;
}
DeclInfo *decl = decl_info_of_entity(e);
if (decl == nullptr) {
continue;
}
rw_mutex_shared_lock(&decl->deps_mutex);
rw_mutex_lock(&c->decl->deps_mutex);
for (Entity *dep : decl->deps) {
ptr_set_add(&c->decl->deps, dep);
}
rw_mutex_unlock(&c->decl->deps_mutex);
rw_mutex_shared_unlock(&decl->deps_mutex);
}
return tuple_count;
};
bool allow_ok = (flags & UnpackFlag_AllowOk) != 0; bool allow_ok = (flags & UnpackFlag_AllowOk) != 0;
bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0; bool is_variadic = (flags & UnpackFlag_IsVariadic) != 0;
bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0; bool allow_undef = (flags & UnpackFlag_AllowUndef) != 0;
@@ -5494,6 +5478,8 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count); auto variadic_operands = slice(slice_from_array(positional_operands), positional_operand_count, positional_operands.count);
bool named_variadic_param = false;
if (named_operands.count != 0) { if (named_operands.count != 0) {
GB_ASSERT(ce->split_args->named.count == named_operands.count); GB_ASSERT(ce->split_args->named.count == named_operands.count);
for_array(i, ce->split_args->named) { for_array(i, ce->split_args->named) {
@@ -5519,6 +5505,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
err = CallArgumentError_ParameterNotFound; err = CallArgumentError_ParameterNotFound;
continue; continue;
} }
if (pt->variadic && param_index == pt->variadic_index) {
named_variadic_param = true;
}
if (visited[param_index]) { if (visited[param_index]) {
if (show_error) { if (show_error) {
error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name)); error(arg, "Duplicate parameter '%.*s' in procedure call", LIT(name));
@@ -5720,11 +5709,6 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
} }
continue; continue;
} }
if (param_is_variadic) {
continue;
}
score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error); score += eval_param_and_score(c, o, e->type, err, param_is_variadic, e, show_error);
} }
} }
@@ -6136,7 +6120,6 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
{ {
// NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups // NOTE(bill, 2019-07-13): This code is used to improve the type inference for procedure groups
// where the same positional parameter has the same type value (and ellipsis) // where the same positional parameter has the same type value (and ellipsis)
bool proc_arg_count_all_equal = true;
isize proc_arg_count = -1; isize proc_arg_count = -1;
for (Entity *p : procs) { for (Entity *p : procs) {
Type *pt = base_type(p->type); Type *pt = base_type(p->type);
@@ -6144,15 +6127,12 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
if (proc_arg_count < 0) { if (proc_arg_count < 0) {
proc_arg_count = pt->Proc.param_count; proc_arg_count = pt->Proc.param_count;
} else { } else {
if (proc_arg_count != pt->Proc.param_count) { proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count);
proc_arg_count_all_equal = false;
break;
}
} }
} }
} }
if (proc_arg_count >= 0 && proc_arg_count_all_equal) { if (proc_arg_count >= 0) {
lhs_count = proc_arg_count; lhs_count = proc_arg_count;
if (lhs_count > 0) { if (lhs_count > 0) {
lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count); lhs = gb_alloc_array(heap_allocator(), Entity *, lhs_count);
@@ -6258,14 +6238,18 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
} }
isize index = i; isize index = i;
ValidIndexAndScore item = {};
item.score = data.score;
if (data.gen_entity != nullptr) { if (data.gen_entity != nullptr) {
array_add(&proc_entities, data.gen_entity); array_add(&proc_entities, data.gen_entity);
index = proc_entities.count-1; index = proc_entities.count-1;
// prefer non-polymorphic procedures over polymorphic
item.score += assign_score_function(1);
} }
ValidIndexAndScore item = {};
item.index = index; item.index = index;
item.score = data.score;
array_add(&valids, item); array_add(&valids, item);
} }
} }
@@ -6328,9 +6312,44 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
print_argument_types(); print_argument_types();
} }
if (procs.count == 0) {
procs = proc_group_entities_cloned(c, *operand);
}
if (procs.count > 0) { if (procs.count > 0) {
error_line("Did you mean to use one of the following:\n"); error_line("Did you mean to use one of the following:\n");
} }
isize max_name_length = 0;
isize max_type_length = 0;
for (Entity *proc : procs) {
Type *t = base_type(proc->type);
if (t == t_invalid) continue;
String prefix = {};
String prefix_sep = {};
if (proc->pkg) {
prefix = proc->pkg->name;
prefix_sep = str_lit(".");
}
String name = proc->token.string;
max_name_length = gb_max(max_name_length, prefix.len + prefix_sep.len + name.len);
gbString pt;
if (t->Proc.node != nullptr) {
pt = expr_to_string(t->Proc.node);
} else {
pt = type_to_string(t);
}
max_type_length = gb_max(max_type_length, gb_string_length(pt));
gb_string_free(pt);
}
isize max_spaces = gb_max(max_name_length, max_type_length);
char *spaces = gb_alloc_array(temporary_allocator(), char, max_spaces+1);
for (isize i = 0; i < max_spaces; i++) {
spaces[i] = ' ';
}
spaces[max_spaces] = 0;
for (Entity *proc : procs) { for (Entity *proc : procs) {
TokenPos pos = proc->token.pos; TokenPos pos = proc->token.pos;
Type *t = base_type(proc->type); Type *t = base_type(proc->type);
@@ -6350,12 +6369,23 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
prefix_sep = str_lit("."); prefix_sep = str_lit(".");
} }
String name = proc->token.string; String name = proc->token.string;
isize len = prefix.len + prefix_sep.len + name.len;
int name_padding = cast(int)gb_max(max_name_length - len, 0);
int type_padding = cast(int)gb_max(max_type_length - gb_string_length(pt), 0);
char const *sep = "::"; char const *sep = "::";
if (proc->kind == Entity_Variable) { if (proc->kind == Entity_Variable) {
sep = ":="; sep = ":=";
} }
error_line("\t%.*s%.*s%.*s %s %s at %s\n", LIT(prefix), LIT(prefix_sep), LIT(name), sep, pt, token_pos_to_string(pos)); error_line("\t%.*s%.*s%.*s %.*s%s %s %.*sat %s\n",
LIT(prefix), LIT(prefix_sep), LIT(name),
name_padding, spaces,
sep,
pt,
type_padding, spaces,
token_pos_to_string(pos)
);
} }
if (procs.count > 0) { if (procs.count > 0) {
error_line("\n"); error_line("\n");
@@ -6369,8 +6399,8 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name); error(operand->expr, "Ambiguous procedure group call '%s' that match with the given arguments", expr_name);
print_argument_types(); print_argument_types();
for (isize i = 0; i < valids.count; i++) { for (auto const &valid : valids) {
Entity *proc = proc_entities[valids[i].index]; Entity *proc = proc_entities[valid.index];
GB_ASSERT(proc != nullptr); GB_ASSERT(proc != nullptr);
TokenPos pos = proc->token.pos; TokenPos pos = proc->token.pos;
Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc); Type *t = base_type(proc->type); GB_ASSERT(t->kind == Type_Proc);
@@ -7107,7 +7137,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
i32 id = operand->builtin_id; i32 id = operand->builtin_id;
Entity *e = entity_of_node(operand->expr); Entity *e = entity_of_node(operand->expr);
if (e != nullptr && e->token.string == "expand_to_tuple") { if (e != nullptr && e->token.string == "expand_to_tuple") {
warning(operand->expr, "'expand_to_tuple' has been replaced with 'expand_values'"); error(operand->expr, "'expand_to_tuple' has been replaced with 'expand_values'");
} }
if (!check_builtin_procedure(c, operand, call, id, type_hint)) { if (!check_builtin_procedure(c, operand, call, id, type_hint)) {
operand->mode = Addressing_Invalid; operand->mode = Addressing_Invalid;
@@ -7128,6 +7158,7 @@ gb_internal ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *c
c->decl->defer_used += 1; c->decl->defer_used += 1;
} }
} }
add_entity_use(c, operand->expr, initial_entity);
} }
if (operand->mode != Addressing_ProcGroup) { if (operand->mode != Addressing_ProcGroup) {
@@ -7335,11 +7366,11 @@ gb_internal bool check_set_index_data(Operand *o, Type *t, bool indirection, i64
} }
return true; return true;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
{ {
Type *slice_type = base_type(t->RelativeSlice.slice_type); Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
GB_ASSERT(slice_type->kind == Type_Slice); GB_ASSERT(pointer_type->kind == Type_MultiPointer);
o->type = slice_type->Slice.elem; o->type = pointer_type->MultiPointer.elem;
if (o->mode != Addressing_Constant) { if (o->mode != Addressing_Constant) {
o->mode = Addressing_Variable; o->mode = Addressing_Variable;
} }
@@ -9315,13 +9346,13 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
ExprKind kind = check_expr_base(c, &x, se->expr, nullptr); ExprKind kind = check_expr_base(c, &x, se->expr, nullptr);
c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr; c->allow_arrow_right_selector_expr = allow_arrow_right_selector_expr;
if (x.mode == Addressing_Invalid || x.type == t_invalid) { if (x.mode == Addressing_Invalid || (x.type == t_invalid && x.mode != Addressing_ProcGroup)) {
o->mode = Addressing_Invalid; o->mode = Addressing_Invalid;
o->type = t_invalid; o->type = t_invalid;
o->expr = node; o->expr = node;
return kind; return kind;
} }
if (!is_type_proc(x.type)) { if (!is_type_proc(x.type) && x.mode != Addressing_ProcGroup) {
gbString type_str = type_to_string(x.type); gbString type_str = type_to_string(x.type);
error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str); error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", type_str);
gb_string_free(type_str); gb_string_free(type_str);
@@ -9344,76 +9375,76 @@ gb_internal ExprKind check_selector_call_expr(CheckerContext *c, Operand *o, Ast
first_arg->state_flags |= StateFlag_SelectorCallExpr; first_arg->state_flags |= StateFlag_SelectorCallExpr;
} }
Type *pt = base_type(x.type); if (e->kind != Entity_ProcGroup) {
GB_ASSERT(pt->kind == Type_Proc); Type *pt = base_type(x.type);
Type *first_type = nullptr; GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type));
String first_arg_name = {}; Type *first_type = nullptr;
if (pt->Proc.param_count > 0) { String first_arg_name = {};
Entity *f = pt->Proc.params->Tuple.variables[0]; if (pt->Proc.param_count > 0) {
first_type = f->type; Entity *f = pt->Proc.params->Tuple.variables[0];
first_arg_name = f->token.string; first_type = f->type;
} first_arg_name = f->token.string;
if (first_arg_name.len == 0) { }
first_arg_name = str_lit("_"); if (first_arg_name.len == 0) {
} first_arg_name = str_lit("_");
}
if (first_type == nullptr) { if (first_type == nullptr) {
error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter"); error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
o->mode = Addressing_Invalid; o->mode = Addressing_Invalid;
o->type = t_invalid; o->type = t_invalid;
o->expr = node; o->expr = node;
return Expr_Stmt; return Expr_Stmt;
} }
Operand y = {}; Operand y = {};
y.mode = first_arg->tav.mode; y.mode = first_arg->tav.mode;
y.type = first_arg->tav.type; y.type = first_arg->tav.type;
y.value = first_arg->tav.value; y.value = first_arg->tav.value;
if (check_is_assignable_to(c, &y, first_type)) { if (check_is_assignable_to(c, &y, first_type)) {
// Do nothing, it's valid // Do nothing, it's valid
} else { } else {
Operand z = y; Operand z = y;
z.type = type_deref(y.type); z.type = type_deref(y.type);
if (check_is_assignable_to(c, &z, first_type)) { if (check_is_assignable_to(c, &z, first_type)) {
// NOTE(bill): AST GENERATION HACK!
Token op = {Token_Pointer};
first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
} else if (y.mode == Addressing_Variable) {
Operand w = y;
w.type = alloc_type_pointer(y.type);
if (check_is_assignable_to(c, &w, first_type)) {
// NOTE(bill): AST GENERATION HACK! // NOTE(bill): AST GENERATION HACK!
Token op = {Token_And}; Token op = {Token_Pointer};
first_arg = ast_unary_expr(first_arg->file(), op, first_arg); first_arg = ast_deref_expr(first_arg->file(), first_arg, op);
} else if (y.mode == Addressing_Variable) {
Operand w = y;
w.type = alloc_type_pointer(y.type);
if (check_is_assignable_to(c, &w, first_type)) {
// NOTE(bill): AST GENERATION HACK!
Token op = {Token_And};
first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
}
}
}
if (ce->args.count > 0) {
bool fail = false;
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
for (Ast *arg : ce->args) {
bool mix = false;
if (first_is_field_value) {
mix = arg->kind != Ast_FieldValue;
} else {
mix = arg->kind == Ast_FieldValue;
}
if (mix) {
fail = true;
break;
}
}
if (!fail && first_is_field_value) {
Token op = {Token_Eq};
AstFile *f = first_arg->file();
first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
} }
} }
} }
if (ce->args.count > 0) {
bool fail = false;
bool first_is_field_value = (ce->args[0]->kind == Ast_FieldValue);
for (Ast *arg : ce->args) {
bool mix = false;
if (first_is_field_value) {
mix = arg->kind != Ast_FieldValue;
} else {
mix = arg->kind == Ast_FieldValue;
}
if (mix) {
fail = true;
break;
}
}
if (!fail && first_is_field_value) {
Token op = {Token_Eq};
AstFile *f = first_arg->file();
first_arg = ast_field_value(f, ast_ident(f, make_token_ident(first_arg_name)), first_arg, op);
}
}
auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1); auto modified_args = slice_make<Ast *>(heap_allocator(), ce->args.count+1);
modified_args[0] = first_arg; modified_args[0] = first_arg;
slice_copy(&modified_args, ce->args, 1); slice_copy(&modified_args, ce->args, 1);
@@ -9471,14 +9502,14 @@ gb_internal ExprKind check_index_expr(CheckerContext *c, Operand *o, Ast *node,
if (is_const) { if (is_const) {
if (is_type_array(t)) { if (is_type_array(t)) {
// OKay // Okay
} else if (is_type_slice(t)) { } else if (is_type_slice(t)) {
// Okay // Okay
} else if (is_type_enumerated_array(t)) { } else if (is_type_enumerated_array(t)) {
// Okay // Okay
} else if (is_type_string(t)) { } else if (is_type_string(t)) {
// Okay // Okay
} else if (is_type_relative_slice(t)) { } else if (is_type_relative_multi_pointer(t)) {
// Okay // Okay
} else if (is_type_matrix(t)) { } else if (is_type_matrix(t)) {
// Okay // Okay
@@ -9616,17 +9647,9 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
} }
break; break;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
valid = true; valid = true;
o->type = t->RelativeSlice.slice_type; o->type = type_deref(o->type);
if (o->mode != Addressing_Variable) {
gbString str = expr_to_string(node);
error(node, "Cannot relative slice '%s', as value is not addressable", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
break; break;
case Type_EnumeratedArray: case Type_EnumeratedArray:
@@ -9705,8 +9728,19 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
x[i:n] -> []T x[i:n] -> []T
*/ */
o->type = alloc_type_slice(t->MultiPointer.elem); o->type = alloc_type_slice(t->MultiPointer.elem);
} else if (t->kind == Type_RelativeMultiPointer && se->high != nullptr) {
/*
x[:] -> [^]T
x[i:] -> [^]T
x[:n] -> []T
x[i:n] -> []T
*/
Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
GB_ASSERT(pointer_type->kind == Type_MultiPointer);
o->type = alloc_type_slice(pointer_type->MultiPointer.elem);
} }
o->mode = Addressing_Value; o->mode = Addressing_Value;
if (is_type_string(t) && max_count >= 0) { if (is_type_string(t) && max_count >= 0) {
@@ -9982,7 +10016,7 @@ gb_internal ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast
Type *type = type_of_expr(ac->expr); Type *type = type_of_expr(ac->expr);
check_cast(c, o, type_hint); check_cast(c, o, type_hint);
if (is_type_typed(type) && are_types_identical(type, type_hint)) { if (is_type_typed(type) && are_types_identical(type, type_hint)) {
if (build_context.vet_extra) { if (check_vet_flags(node) & VetFlag_Extra) {
error(node, "Redundant 'auto_cast' applied to expression"); error(node, "Redundant 'auto_cast' applied to expression");
} }
} }
+11 -6
View File
@@ -384,7 +384,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
} }
if (e != nullptr) { if (e != nullptr) {
// HACK TODO(bill): Should the entities be freed as it's technically a leak
rhs->mode = Addressing_Value; rhs->mode = Addressing_Value;
rhs->type = e->type; rhs->type = e->type;
rhs->proc_group = nullptr; rhs->proc_group = nullptr;
@@ -394,7 +393,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
ast_node(i, Ident, node); ast_node(i, Ident, node);
e = scope_lookup(ctx->scope, i->token.string); e = scope_lookup(ctx->scope, i->token.string);
if (e != nullptr && e->kind == Entity_Variable) { if (e != nullptr && e->kind == Entity_Variable) {
used = (e->flags & EntityFlag_Used) != 0; // TODO(bill): Make backup just in case used = (e->flags & EntityFlag_Used) != 0; // NOTE(bill): Make backup just in case
} }
} }
@@ -888,7 +887,7 @@ gb_internal void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags
check_open_scope(ctx, node); check_open_scope(ctx, node);
defer (check_close_scope(ctx)); defer (check_close_scope(ctx));
check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be? check_label(ctx, ss->label, node);
if (ss->init != nullptr) { if (ss->init != nullptr) {
check_stmt(ctx, ss->init, 0); check_stmt(ctx, ss->init, 0);
@@ -1125,7 +1124,7 @@ gb_internal void check_type_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_
check_open_scope(ctx, node); check_open_scope(ctx, node);
defer (check_close_scope(ctx)); defer (check_close_scope(ctx));
check_label(ctx, ss->label, node); // TODO(bill): What should the label's "scope" be? check_label(ctx, ss->label, node);
if (ss->tag->kind != Ast_AssignStmt) { if (ss->tag->kind != Ast_AssignStmt) {
error(ss->tag, "Expected an 'in' assignment for this type switch statement"); error(ss->tag, "Expected an 'in' assignment for this type switch statement");
@@ -1960,7 +1959,7 @@ gb_internal void check_value_decl_stmt(CheckerContext *ctx, Ast *node, u32 mod_f
Token token = ast_token(node); Token token = ast_token(node);
if (vd->type != nullptr && entity_count > 1) { if (vd->type != nullptr && entity_count > 1) {
error(token, "'using' can only be applied to one variable of the same type"); error(token, "'using' can only be applied to one variable of the same type");
// TODO(bill): Should a 'continue' happen here? // NOTE(bill): `using` will only be applied to a single declaration
} }
for (isize entity_index = 0; entity_index < 1; entity_index++) { for (isize entity_index = 0; entity_index < 1; entity_index++) {
@@ -2294,7 +2293,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed; mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed;
check_open_scope(ctx, node); check_open_scope(ctx, node);
check_label(ctx, fs->label, node); // TODO(bill): What should the label's "scope" be? check_label(ctx, fs->label, node);
if (fs->init != nullptr) { if (fs->init != nullptr) {
check_stmt(ctx, fs->init, 0); check_stmt(ctx, fs->init, 0);
@@ -2464,6 +2463,12 @@ gb_internal void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags)
error(us->token, "Empty 'using' list"); error(us->token, "Empty 'using' list");
return; return;
} }
if (check_vet_flags(node) & VetFlag_UsingStmt) {
ERROR_BLOCK();
error(node, "'using' as a statement is not allowed when '-vet' or '-vet-using' is applied");
error_line("\t'using' is considered bad practice to use as a statement outside of immediate refactoring\n");
}
for (Ast *expr : us->list) { for (Ast *expr : us->list) {
expr = unparen_expr(expr); expr = unparen_expr(expr);
Entity *e = nullptr; Entity *e = nullptr;
+10 -4
View File
@@ -1474,6 +1474,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
Type *specialization = nullptr; Type *specialization = nullptr;
bool is_using = (p->flags&FieldFlag_using) != 0; bool is_using = (p->flags&FieldFlag_using) != 0;
if ((check_vet_flags(param) & VetFlag_UsingParam) && is_using) {
ERROR_BLOCK();
error(param, "'using' on a procedure parameter is now allowed when '-vet' or '-vet-using-param' is applied");
error_line("\t'using' is considered bad practice to use as a statement/procedure parameter outside of immediate refactoring\n");
}
if (type_expr == nullptr) { if (type_expr == nullptr) {
param_value = handle_parameter_value(ctx, nullptr, &type, default_value, true); param_value = handle_parameter_value(ctx, nullptr, &type, default_value, true);
@@ -2772,16 +2778,16 @@ gb_internal bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, T
Type *relative_type = nullptr; Type *relative_type = nullptr;
Type *base_type = check_type(ctx, rt->type); Type *base_type = check_type(ctx, rt->type);
if (!is_type_pointer(base_type) && !is_type_slice(base_type)) { if (!is_type_pointer(base_type) && !is_type_multi_pointer(base_type)) {
error(rt->type, "#relative types can only be a pointer or slice"); error(rt->type, "#relative types can only be a pointer or multi-pointer");
relative_type = base_type; relative_type = base_type;
} else if (base_integer == nullptr) { } else if (base_integer == nullptr) {
relative_type = base_type; relative_type = base_type;
} else { } else {
if (is_type_pointer(base_type)) { if (is_type_pointer(base_type)) {
relative_type = alloc_type_relative_pointer(base_type, base_integer); relative_type = alloc_type_relative_pointer(base_type, base_integer);
} else if (is_type_slice(base_type)) { } else if (is_type_multi_pointer(base_type)) {
relative_type = alloc_type_relative_slice(base_type, base_integer); relative_type = alloc_type_relative_multi_pointer(base_type, base_integer);
} }
} }
GB_ASSERT(relative_type != nullptr); GB_ASSERT(relative_type != nullptr);
+123 -44
View File
@@ -521,6 +521,28 @@ GB_COMPARE_PROC(entity_variable_pos_cmp) {
} }
gb_internal u64 check_vet_flags(CheckerContext *c) {
AstFile *file = c->file;
if (file == nullptr &&
c->curr_proc_decl &&
c->curr_proc_decl->proc_lit) {
file = c->curr_proc_decl->proc_lit->file();
}
if (file && file->vet_flags_set) {
return file->vet_flags;
}
return build_context.vet_flags;
}
gb_internal u64 check_vet_flags(Ast *node) {
AstFile *file = node->file();
if (file && file->vet_flags_set) {
return file->vet_flags;
}
return build_context.vet_flags;
}
enum VettedEntityKind { enum VettedEntityKind {
VettedEntity_Invalid, VettedEntity_Invalid,
@@ -655,9 +677,9 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
return false; return false;
} }
gb_internal void check_scope_usage(Checker *c, Scope *scope) { gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
bool vet_unused = true; bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
bool vet_shadowing = true; bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
Array<VettedEntity> vetted_entities = {}; Array<VettedEntity> vetted_entities = {};
array_init(&vetted_entities, heap_allocator()); array_init(&vetted_entities, heap_allocator());
@@ -691,15 +713,17 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope) {
if (ve.kind == VettedEntity_Shadowed_And_Unused) { if (ve.kind == VettedEntity_Shadowed_And_Unused) {
error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line); error(e->token, "'%.*s' declared but not used, possibly shadows declaration at line %d", LIT(name), other->token.pos.line);
} else if (build_context.vet) { } else if (vet_flags) {
switch (ve.kind) { switch (ve.kind) {
case VettedEntity_Unused: case VettedEntity_Unused:
error(e->token, "'%.*s' declared but not used", LIT(name)); if (vet_flags & VetFlag_Unused) {
error(e->token, "'%.*s' declared but not used", LIT(name));
}
break; break;
case VettedEntity_Shadowed: case VettedEntity_Shadowed:
if (e->flags&EntityFlag_Using) { if ((vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0 && e->flags&EntityFlag_Using) {
error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line); error(e->token, "Declaration of '%.*s' from 'using' shadows declaration at line %d", LIT(name), other->token.pos.line);
} else { } else if ((vet_flags & (VetFlag_Shadowing)) != 0) {
error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line); error(e->token, "Declaration of '%.*s' shadows declaration at line %d", LIT(name), other->token.pos.line);
} }
break; break;
@@ -726,7 +750,7 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope) {
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) { if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
// Ignore these // Ignore these
} else { } else {
check_scope_usage(c, child); check_scope_usage(c, child, vet_flags);
} }
} }
} }
@@ -943,7 +967,6 @@ gb_internal void init_universal(void) {
add_global_bool_constant("true", true); add_global_bool_constant("true", true);
add_global_bool_constant("false", false); add_global_bool_constant("false", false);
// TODO(bill): Set through flags in the compiler
add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR); add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION); add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT); add_global_string_constant("ODIN_ROOT", bc->ODIN_ROOT);
@@ -1046,6 +1069,7 @@ gb_internal void init_universal(void) {
add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti); add_global_bool_constant("ODIN_NO_RTTI", bc->no_rtti);
add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT); add_global_bool_constant("ODIN_VALGRIND_SUPPORT", bc->ODIN_VALGRIND_SUPPORT);
add_global_bool_constant("ODIN_TILDE", bc->tilde_backend);
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()));
@@ -1114,6 +1138,7 @@ gb_internal void init_universal(void) {
t_u8_ptr = alloc_type_pointer(t_u8); t_u8_ptr = alloc_type_pointer(t_u8);
t_u8_multi_ptr = alloc_type_multi_pointer(t_u8);
t_int_ptr = alloc_type_pointer(t_int); t_int_ptr = alloc_type_pointer(t_int);
t_i64_ptr = alloc_type_pointer(t_i64); t_i64_ptr = alloc_type_pointer(t_i64);
t_f64_ptr = alloc_type_pointer(t_f64); t_f64_ptr = alloc_type_pointer(t_f64);
@@ -1453,7 +1478,6 @@ gb_internal void add_type_and_value(CheckerContext *ctx, Ast *expr, AddressingMo
if (ctx->decl) { if (ctx->decl) {
mutex = &ctx->decl->type_and_value_mutex; mutex = &ctx->decl->type_and_value_mutex;
} else if (ctx->pkg) { } else if (ctx->pkg) {
// TODO(bill): is a per package mutex is a good idea here?
mutex = &ctx->pkg->type_and_value_mutex; mutex = &ctx->pkg->type_and_value_mutex;
} }
@@ -1581,30 +1605,28 @@ gb_internal void add_entity_use(CheckerContext *c, Ast *identifier, Entity *enti
if (entity == nullptr) { if (entity == nullptr) {
return; return;
} }
if (identifier != nullptr) {
if (identifier->kind != Ast_Ident) {
return;
}
Ast *empty_ident = nullptr;
entity->identifier.compare_exchange_strong(empty_ident, identifier);
identifier->Ident.entity = entity;
String dmsg = entity->deprecated_message;
if (dmsg.len > 0) {
warning(identifier, "%.*s is deprecated: %.*s", LIT(entity->token.string), LIT(dmsg));
}
String wmsg = entity->warning_message;
if (wmsg.len > 0) {
warning(identifier, "%.*s: %.*s", LIT(entity->token.string), LIT(wmsg));
}
}
entity->flags |= EntityFlag_Used;
add_declaration_dependency(c, entity); add_declaration_dependency(c, entity);
entity->flags |= EntityFlag_Used;
if (entity_has_deferred_procedure(entity)) { if (entity_has_deferred_procedure(entity)) {
Entity *deferred = entity->Procedure.deferred_procedure.entity; Entity *deferred = entity->Procedure.deferred_procedure.entity;
add_entity_use(c, nullptr, deferred); add_entity_use(c, nullptr, deferred);
} }
if (identifier == nullptr || identifier->kind != Ast_Ident) {
return;
}
Ast *empty_ident = nullptr;
entity->identifier.compare_exchange_strong(empty_ident, identifier);
identifier->Ident.entity = entity;
String dmsg = entity->deprecated_message;
if (dmsg.len > 0) {
warning(identifier, "%.*s is deprecated: %.*s", LIT(entity->token.string), LIT(dmsg));
}
String wmsg = entity->warning_message;
if (wmsg.len > 0) {
warning(identifier, "%.*s: %.*s", LIT(entity->token.string), LIT(wmsg));
}
} }
@@ -1947,9 +1969,9 @@ gb_internal void add_type_info_type_internal(CheckerContext *c, Type *t) {
add_type_info_type_internal(c, bt->RelativePointer.base_integer); add_type_info_type_internal(c, bt->RelativePointer.base_integer);
break; break;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
add_type_info_type_internal(c, bt->RelativeSlice.slice_type); add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type);
add_type_info_type_internal(c, bt->RelativeSlice.base_integer); add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer);
break; break;
case Type_Matrix: case Type_Matrix:
@@ -2188,9 +2210,9 @@ gb_internal void add_min_dep_type_info(Checker *c, Type *t) {
add_min_dep_type_info(c, bt->RelativePointer.base_integer); add_min_dep_type_info(c, bt->RelativePointer.base_integer);
break; break;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
add_min_dep_type_info(c, bt->RelativeSlice.slice_type); add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type);
add_min_dep_type_info(c, bt->RelativeSlice.base_integer); add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer);
break; break;
case Type_Matrix: case Type_Matrix:
@@ -2310,7 +2332,9 @@ gb_internal void generate_minimum_dependency_set(Checker *c, Entity *start) {
str_lit("memory_equal"), str_lit("memory_equal"),
str_lit("memory_compare"), str_lit("memory_compare"),
str_lit("memory_compare_zero"), str_lit("memory_compare_zero"),
);
FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
// Extended data type internal procedures // Extended data type internal procedures
str_lit("umodti3"), str_lit("umodti3"),
str_lit("udivti3"), str_lit("udivti3"),
@@ -2556,9 +2580,6 @@ gb_internal Array<EntityGraphNode *> generate_entity_dependency_graph(CheckerInf
} }
} }
// TODO(bill): This could be multithreaded to improve performance
// This means that the entity graph node set will have to be thread safe
TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2"); TIME_SECTION("generate_entity_dependency_graph: Calculate edges for graph M - Part 2");
auto G = array_make<EntityGraphNode *>(allocator, 0, M.count); auto G = array_make<EntityGraphNode *>(allocator, 0, M.count);
@@ -2779,7 +2800,7 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set")); t_type_info_bit_set = find_core_type(c, str_lit("Type_Info_Bit_Set"));
t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector")); t_type_info_simd_vector = find_core_type(c, str_lit("Type_Info_Simd_Vector"));
t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer")); t_type_info_relative_pointer = find_core_type(c, str_lit("Type_Info_Relative_Pointer"));
t_type_info_relative_slice = find_core_type(c, str_lit("Type_Info_Relative_Slice")); t_type_info_relative_multi_pointer = find_core_type(c, str_lit("Type_Info_Relative_Multi_Pointer"));
t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix")); t_type_info_matrix = find_core_type(c, str_lit("Type_Info_Matrix"));
t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer")); t_type_info_soa_pointer = find_core_type(c, str_lit("Type_Info_Soa_Pointer"));
@@ -2808,7 +2829,7 @@ gb_internal void init_core_type_info(Checker *c) {
t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set); t_type_info_bit_set_ptr = alloc_type_pointer(t_type_info_bit_set);
t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector); t_type_info_simd_vector_ptr = alloc_type_pointer(t_type_info_simd_vector);
t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer); t_type_info_relative_pointer_ptr = alloc_type_pointer(t_type_info_relative_pointer);
t_type_info_relative_slice_ptr = alloc_type_pointer(t_type_info_relative_slice); t_type_info_relative_multi_pointer_ptr = alloc_type_pointer(t_type_info_relative_multi_pointer);
t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix); t_type_info_matrix_ptr = alloc_type_pointer(t_type_info_matrix);
t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer); t_type_info_soa_pointer_ptr = alloc_type_pointer(t_type_info_soa_pointer);
} }
@@ -2935,6 +2956,60 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_block_decl_attribute) {
return false; return false;
} }
gb_internal DECL_ATTRIBUTE_PROC(proc_group_attribute) {
if (name == ATTRIBUTE_USER_TAG_NAME) {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind != ExactValue_String) {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_name") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_String) {
if (string_is_valid_identifier(ev.value_string)) {
ac->objc_name = ev.value_string;
} else {
error(elem, "Invalid identifier for '%.*s', got '%.*s'", LIT(name), LIT(ev.value_string));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_is_class_method") {
ExactValue ev = check_decl_attribute_value(c, value);
if (ev.kind == ExactValue_Bool) {
ac->objc_is_class_method = ev.value_bool;
} else {
error(elem, "Expected a boolean value for '%.*s'", LIT(name));
}
return true;
} else if (name == "objc_type") {
if (value == nullptr) {
error(elem, "Expected a type for '%.*s'", LIT(name));
} else {
Type *objc_type = check_type(c, value);
if (objc_type != nullptr) {
if (!has_type_got_objc_class_attribute(objc_type)) {
gbString t = type_to_string(objc_type);
error(value, "'%.*s' expected a named type with the attribute @(obj_class=<string>), got type %s", LIT(name), t);
gb_string_free(t);
} else {
ac->objc_type = objc_type;
}
}
}
return true;
} else if (name == "require_results") {
if (value != nullptr) {
error(elem, "Expected no value for '%.*s'", LIT(name));
}
ac->require_results = true;
return true;
}
return false;
}
gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) { gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
if (name == ATTRIBUTE_USER_TAG_NAME) { if (name == ATTRIBUTE_USER_TAG_NAME) {
ExactValue ev = check_decl_attribute_value(c, value); ExactValue ev = check_decl_attribute_value(c, value);
@@ -3007,7 +3082,7 @@ gb_internal DECL_ATTRIBUTE_PROC(proc_decl_attribute) {
check_expr(c, &o, value); check_expr(c, &o, value);
Entity *e = entity_of_node(o.expr); Entity *e = entity_of_node(o.expr);
if (e != nullptr && e->kind == Entity_Procedure) { if (e != nullptr && e->kind == Entity_Procedure) {
warning(elem, "'%.*s' is deprecated, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name)); error(elem, "'%.*s' is not allowed any more, please use one of the following instead: 'deferred_none', 'deferred_in', 'deferred_out'", LIT(name));
if (ac->deferred_procedure.entity != nullptr) { if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of a 'deferred_*' attribute"); error(elem, "Previous usage of a 'deferred_*' attribute");
} }
@@ -4506,7 +4581,7 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
if (value != nullptr) { if (value != nullptr) {
error(elem, "Expected no parameter for '%.*s'", LIT(name)); error(elem, "Expected no parameter for '%.*s'", LIT(name));
} else if (name == "force") { } else if (name == "force") {
warning(elem, "'force' is deprecated and is identical to 'require'"); error(elem, "'force' was replaced with 'require'");
} }
ac->require_declaration = true; ac->require_declaration = true;
return true; return true;
@@ -5904,7 +5979,11 @@ gb_internal void check_parsed_files(Checker *c) {
TIME_SECTION("check scope usage"); TIME_SECTION("check scope usage");
for (auto const &entry : c->info.files) { for (auto const &entry : c->info.files) {
AstFile *f = entry.value; AstFile *f = entry.value;
check_scope_usage(c, f->scope); u64 vet_flags = build_context.vet_flags;
if (f->vet_flags_set) {
vet_flags = f->vet_flags;
}
check_scope_usage(c, f->scope, vet_flags);
} }
TIME_SECTION("add basic type information"); TIME_SECTION("add basic type information");
@@ -6022,7 +6101,7 @@ gb_internal void check_parsed_files(Checker *c) {
while (mpsc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) { while (mpsc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
if (c->info.entry_point == nullptr && node != nullptr) { if (c->info.entry_point == nullptr && node != nullptr) {
if (node->file()->pkg->kind != Package_Runtime) { if (node->file()->pkg->kind != Package_Runtime) {
warning(node, "usage of intrinsics.__entry_point will be a no-op"); error(node, "usage of intrinsics.__entry_point will be a no-op");
} }
} }
} }
+3 -2
View File
@@ -387,8 +387,6 @@ struct CheckerInfo {
BlockingMutex foreign_mutex; // NOT recursive BlockingMutex foreign_mutex; // NOT recursive
StringMap<Entity *> foreigns; StringMap<Entity *> foreigns;
// NOTE(bill): These are actually MPSC queues
// TODO(bill): Convert them to be MPSC queues
MPSCQueue<Entity *> definition_queue; MPSCQueue<Entity *> definition_queue;
MPSCQueue<Entity *> entity_queue; MPSCQueue<Entity *> entity_queue;
MPSCQueue<Entity *> required_global_variable_queue; MPSCQueue<Entity *> required_global_variable_queue;
@@ -449,6 +447,9 @@ struct CheckerContext {
Ast *assignment_lhs_hint; Ast *assignment_lhs_hint;
}; };
gb_internal u64 check_vet_flags(CheckerContext *c);
gb_internal u64 check_vet_flags(Ast *node);
struct Checker { struct Checker {
Parser * parser; Parser * parser;
+25 -25
View File
@@ -59,31 +59,31 @@ struct OdinDocPosition {
}; };
enum OdinDocTypeKind : u32 { enum OdinDocTypeKind : u32 {
OdinDocType_Invalid = 0, OdinDocType_Invalid = 0,
OdinDocType_Basic = 1, OdinDocType_Basic = 1,
OdinDocType_Named = 2, OdinDocType_Named = 2,
OdinDocType_Generic = 3, OdinDocType_Generic = 3,
OdinDocType_Pointer = 4, OdinDocType_Pointer = 4,
OdinDocType_Array = 5, OdinDocType_Array = 5,
OdinDocType_EnumeratedArray = 6, OdinDocType_EnumeratedArray = 6,
OdinDocType_Slice = 7, OdinDocType_Slice = 7,
OdinDocType_DynamicArray = 8, OdinDocType_DynamicArray = 8,
OdinDocType_Map = 9, OdinDocType_Map = 9,
OdinDocType_Struct = 10, OdinDocType_Struct = 10,
OdinDocType_Union = 11, OdinDocType_Union = 11,
OdinDocType_Enum = 12, OdinDocType_Enum = 12,
OdinDocType_Tuple = 13, OdinDocType_Tuple = 13,
OdinDocType_Proc = 14, OdinDocType_Proc = 14,
OdinDocType_BitSet = 15, OdinDocType_BitSet = 15,
OdinDocType_SimdVector = 16, OdinDocType_SimdVector = 16,
OdinDocType_SOAStructFixed = 17, OdinDocType_SOAStructFixed = 17,
OdinDocType_SOAStructSlice = 18, OdinDocType_SOAStructSlice = 18,
OdinDocType_SOAStructDynamic = 19, OdinDocType_SOAStructDynamic = 19,
OdinDocType_RelativePointer = 20, OdinDocType_RelativePointer = 20,
OdinDocType_RelativeSlice = 21, OdinDocType_RelativeMultiPointer = 21,
OdinDocType_MultiPointer = 22, OdinDocType_MultiPointer = 22,
OdinDocType_Matrix = 23, OdinDocType_Matrix = 23,
OdinDocType_SoaPointer = 24, OdinDocType_SoaPointer = 24,
}; };
enum OdinDocTypeFlag_Basic : u32 { enum OdinDocTypeFlag_Basic : u32 {
+4 -4
View File
@@ -771,12 +771,12 @@ gb_internal OdinDocTypeIndex odin_doc_type(OdinDocWriter *w, Type *type) {
doc_type.types = odin_write_slice(w, types, gb_count_of(types)); doc_type.types = odin_write_slice(w, types, gb_count_of(types));
} }
break; break;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
doc_type.kind = OdinDocType_RelativeSlice; doc_type.kind = OdinDocType_RelativeMultiPointer;
{ {
OdinDocTypeIndex types[2] = {}; OdinDocTypeIndex types[2] = {};
types[0] = odin_doc_type(w, type->RelativeSlice.slice_type); types[0] = odin_doc_type(w, type->RelativeMultiPointer.pointer_type);
types[1] = odin_doc_type(w, type->RelativeSlice.base_integer); types[1] = odin_doc_type(w, type->RelativeMultiPointer.base_integer);
doc_type.types = odin_write_slice(w, types, gb_count_of(types)); doc_type.types = odin_write_slice(w, types, gb_count_of(types));
} }
break; break;
+11 -8
View File
@@ -2,8 +2,6 @@ struct Scope;
struct Checker; struct Checker;
struct Type; struct Type;
struct DeclInfo; struct DeclInfo;
struct lbModule;
struct lbProcedure;
#define ENTITY_KINDS \ #define ENTITY_KINDS \
@@ -183,8 +181,14 @@ struct Entity {
Entity * aliased_of; Entity * aliased_of;
lbModule * code_gen_module; union {
lbProcedure *code_gen_procedure; struct lbModule *code_gen_module;
struct cgModule *cg_module;
};
union {
struct lbProcedure *code_gen_procedure;
struct cgProcedure *cg_procedure;
};
u64 order_in_src; u64 order_in_src;
String deprecated_message; String deprecated_message;
@@ -287,7 +291,6 @@ gb_internal bool is_entity_kind_exported(EntityKind kind, bool allow_builtin = f
} }
gb_internal bool is_entity_exported(Entity *e, bool allow_builtin = false) { gb_internal bool is_entity_exported(Entity *e, bool allow_builtin = false) {
// TODO(bill): Determine the actual exportation rules for imports of entities
GB_ASSERT(e != nullptr); GB_ASSERT(e != nullptr);
if (!is_entity_kind_exported(e->kind, allow_builtin)) { if (!is_entity_kind_exported(e->kind, allow_builtin)) {
return false; return false;
@@ -401,7 +404,7 @@ gb_internal Entity *alloc_entity_array_elem(Scope *scope, Token token, Type *typ
return entity; return entity;
} }
gb_internal Entity *alloc_entity_procedure(Scope *scope, Token token, Type *signature_type, u64 tags) { gb_internal Entity *alloc_entity_procedure(Scope *scope, Token token, Type *signature_type, u64 tags=0) {
Entity *entity = alloc_entity(Entity_Procedure, scope, token, signature_type); Entity *entity = alloc_entity(Entity_Procedure, scope, token, signature_type);
entity->Procedure.tags = tags; entity->Procedure.tags = tags;
return entity; return entity;
@@ -418,7 +421,7 @@ gb_internal Entity *alloc_entity_import_name(Scope *scope, Token token, Type *ty
entity->ImportName.path = path; entity->ImportName.path = path;
entity->ImportName.name = name; entity->ImportName.name = name;
entity->ImportName.scope = import_scope; entity->ImportName.scope = import_scope;
entity->state = EntityState_Resolved; // TODO(bill): Is this correct? entity->state = EntityState_Resolved;
return entity; return entity;
} }
@@ -427,7 +430,7 @@ gb_internal Entity *alloc_entity_library_name(Scope *scope, Token token, Type *t
Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type); Entity *entity = alloc_entity(Entity_LibraryName, scope, token, type);
entity->LibraryName.paths = paths; entity->LibraryName.paths = paths;
entity->LibraryName.name = name; entity->LibraryName.name = name;
entity->state = EntityState_Resolved; // TODO(bill): Is this correct? entity->state = EntityState_Resolved;
return entity; return entity;
} }
+2 -2
View File
@@ -411,7 +411,7 @@ gb_internal void error_line_va(char const *fmt, va_list va) {
gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) { gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
mutex_lock(&global_error_collector.mutex); mutex_lock(&global_error_collector.mutex);
global_error_collector.count++; global_error_collector.count.fetch_add(1);
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (pos.line == 0) { if (pos.line == 0) {
error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red); error_out_coloured("Error: ", TerminalStyle_Normal, TerminalColour_Red);
@@ -425,7 +425,7 @@ gb_internal void error_no_newline_va(TokenPos const &pos, char const *fmt, va_li
error_out_va(fmt, va); error_out_va(fmt, va);
} }
mutex_unlock(&global_error_collector.mutex); mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT()) { if (global_error_collector.count.load() > MAX_ERROR_COLLECTOR_COUNT()) {
gb_exit(1); gb_exit(1);
} }
} }
+2 -5
View File
@@ -26,8 +26,8 @@ enum ExactValueKind {
ExactValue_Complex = 5, ExactValue_Complex = 5,
ExactValue_Quaternion = 6, ExactValue_Quaternion = 6,
ExactValue_Pointer = 7, ExactValue_Pointer = 7,
ExactValue_Compound = 8, // TODO(bill): Is this good enough? ExactValue_Compound = 8,
ExactValue_Procedure = 9, // TODO(bill): Is this good enough? ExactValue_Procedure = 9,
ExactValue_Typeid = 10, ExactValue_Typeid = 10,
ExactValue_Count, ExactValue_Count,
@@ -101,7 +101,6 @@ gb_internal ExactValue exact_value_bool(bool b) {
} }
gb_internal ExactValue exact_value_string(String string) { gb_internal ExactValue exact_value_string(String string) {
// TODO(bill): Allow for numbers with underscores in them
ExactValue result = {ExactValue_String}; ExactValue result = {ExactValue_String};
result.value_string = string; result.value_string = string;
return result; return result;
@@ -702,7 +701,6 @@ gb_internal void match_exact_values(ExactValue *x, ExactValue *y) {
compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind); compiler_error("match_exact_values: How'd you get here? Invalid ExactValueKind %d", x->kind);
} }
// TODO(bill): Allow for pointer arithmetic? Or are pointer slices good enough?
gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) { gb_internal ExactValue exact_binary_operator_value(TokenKind op, ExactValue x, ExactValue y) {
match_exact_values(&x, &y); match_exact_values(&x, &y);
@@ -943,7 +941,6 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
case ExactValue_String: { case ExactValue_String: {
String a = x.value_string; String a = x.value_string;
String b = y.value_string; String b = y.value_string;
// TODO(bill): gb_memcompare is used because the strings are UTF-8
switch (op) { switch (op) {
case Token_CmpEq: return a == b; case Token_CmpEq: return a == b;
case Token_NotEq: return a != b; case Token_NotEq: return a != b;
+460
View File
@@ -0,0 +1,460 @@
struct LinkerData {
BlockingMutex foreign_mutex;
PtrSet<Entity *> foreign_libraries_set;
Array<Entity *> foreign_libraries;
Array<String> output_object_paths;
Array<String> output_temp_paths;
String output_base;
String output_name;
};
gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt, ...);
gb_internal void linker_data_init(LinkerData *ld, CheckerInfo *info, String const &init_fullpath) {
gbAllocator ha = heap_allocator();
array_init(&ld->output_object_paths, ha);
array_init(&ld->output_temp_paths, ha);
array_init(&ld->foreign_libraries, ha, 0, 1024);
ptr_set_init(&ld->foreign_libraries_set, 1024);
if (build_context.out_filepath.len == 0) {
ld->output_name = remove_directory_from_path(init_fullpath);
ld->output_name = remove_extension_from_path(ld->output_name);
ld->output_name = string_trim_whitespace(ld->output_name);
if (ld->output_name.len == 0) {
ld->output_name = info->init_scope->pkg->name;
}
ld->output_base = ld->output_name;
} else {
ld->output_name = build_context.out_filepath;
ld->output_name = string_trim_whitespace(ld->output_name);
if (ld->output_name.len == 0) {
ld->output_name = info->init_scope->pkg->name;
}
isize pos = string_extension_position(ld->output_name);
if (pos < 0) {
ld->output_base = ld->output_name;
} else {
ld->output_base = substring(ld->output_name, 0, pos);
}
}
ld->output_base = path_to_full_path(ha, ld->output_base);
}
gb_internal i32 linker_stage(LinkerData *gen) {
i32 result = 0;
Timings *timings = &global_timings;
String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
debugf("Linking %.*s\n", LIT(output_filename));
// TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
LIT(target_arch_names[build_context.metrics.arch])
);
#endif
} else if (build_context.cross_compiling && build_context.different_os) {
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
LIT(target_arch_names[build_context.metrics.arch])
);
build_context.keep_object_files = true;
} else {
#if defined(GB_SYSTEM_WINDOWS)
bool is_windows = true;
#else
bool is_windows = false;
#endif
#if defined(GB_SYSTEM_OSX)
bool is_osx = true;
#else
bool is_osx = false;
#endif
if (is_windows) {
String section_name = str_lit("msvc-link");
if (build_context.use_lld) {
section_name = str_lit("lld-link");
}
timings_start_section(timings, section_name);
gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
defer (gb_string_free(link_settings));
// Add library search paths.
if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
String path = {};
auto add_path = [&](String path) {
if (path[path.len-1] == '\\') {
path.len -= 1;
}
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
};
add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
}
StringSet libs = {};
string_set_init(&libs, 64);
defer (string_set_destroy(&libs));
StringSet asm_files = {};
string_set_init(&asm_files, 64);
defer (string_set_destroy(&asm_files));
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
for_array(i, e->LibraryName.paths) {
String lib = string_trim_whitespace(e->LibraryName.paths[i]);
// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
// we will never uses these strings afterwards
string_to_lower(&lib);
if (lib.len == 0) {
continue;
}
if (has_asm_extension(lib)) {
if (!string_set_update(&asm_files, lib)) {
String asm_file = asm_files.entries[i].value;
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
result = system_exec_command_line_app("nasm",
"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
"-f win64 "
"-o \"%.*s\" "
"%.*s "
"",
LIT(build_context.ODIN_ROOT), LIT(asm_file),
LIT(obj_file),
LIT(build_context.extra_assembler_flags)
);
if (result) {
return result;
}
array_add(&gen->output_object_paths, obj_file);
}
} else {
if (!string_set_update(&libs, lib)) {
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
}
}
}
}
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
if (e->LibraryName.extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
}
}
if (build_context.build_mode == BuildMode_DynamicLibrary) {
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
}
if (build_context.pdb_filepath != "") {
String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
}
if (build_context.no_crt) {
link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
} else {
link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
}
if (build_context.ODIN_DEBUG) {
link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
}
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
for (String const &object_path : gen->output_object_paths) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
defer (gb_free(heap_allocator(), vs_exe_path.text));
String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
String res_path = {};
defer (gb_free(heap_allocator(), res_path.text));
if (build_context.has_resource) {
String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
gb_free(heap_allocator(), temp_res_path.text);
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
defer (gb_free(heap_allocator(), rc_path.text));
result = system_exec_command_line_app("msvc-link",
"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
LIT(windows_sdk_bin_path),
LIT(res_path),
LIT(rc_path)
);
if (result) {
return result;
}
}
switch (build_context.build_mode) {
case BuildMode_Executable:
link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
break;
}
result = system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
"%.*s "
"%.*s "
"%s "
"",
LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
} else { // lld
result = system_exec_command_line_app("msvc-lld-link",
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
"%.*s "
"%.*s "
"%s "
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
}
} else {
timings_start_section(timings, str_lit("ld-link"));
// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
char cwd[256];
#if !defined(GB_SYSTEM_WINDOWS)
getcwd(&cwd[0], 256);
#endif
//printf("%s\n", cwd);
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
// files can be passed with -l:
gbString lib_str = gb_string_make(heap_allocator(), "-L/");
defer (gb_string_free(lib_str));
StringSet libs = {};
string_set_init(&libs, 64);
defer (string_set_destroy(&libs));
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
for (String lib : e->LibraryName.paths) {
lib = string_trim_whitespace(lib);
if (lib.len == 0) {
continue;
}
if (string_set_update(&libs, lib)) {
continue;
}
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
// This allows you to specify '-f' in a #foreign_system_library,
// without having to implement any new syntax specifically for MacOS.
if (build_context.metrics.os == TargetOs_darwin) {
if (string_ends_with(lib, str_lit(".framework"))) {
// framework thingie
String lib_name = lib;
lib_name = remove_extension_from_path(lib_name);
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
// For:
// object
// dynamic lib
// static libs, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
} else {
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
// since those are statically linked to at link time. shared libraries (.so) has to be
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
// static libs and object files, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
} else if (string_ends_with(lib, str_lit(".so"))) {
// dynamic lib, relative path to executable
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
// at runtime to the executable
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
}
}
}
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
if (e->LibraryName.extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
}
}
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
for (String object_path : gen->output_object_paths) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
if (build_context.no_crt) {
link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
}
// NOTE(dweiler): We use clang as a frontend for the linker as there are
// other runtime and compiler support libraries that need to be linked in
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
// These are not always typically inside /lib, /lib64, or /usr versions
// of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
// the distribution of Linux even. The gcc or clang specs is the only
// reliable way to query this information to call ld directly.
if (build_context.build_mode == BuildMode_DynamicLibrary) {
// NOTE(dweiler): Let the frontend know we're building a shared library
// so it doesn't generate symbols which cannot be relocated.
link_settings = gb_string_appendc(link_settings, "-shared ");
// NOTE(dweiler): _odin_entry_point must be called at initialization
// time of the shared object, similarly, _odin_exit_point must be called
// at deinitialization. We can pass both -init and -fini to the linker by
// using a comma separated list of arguments to -Wl.
//
// This previously used ld but ld cannot actually build a shared library
// correctly this way since all the other dependencies provided implicitly
// by the compiler frontend are still needed and most of the command
// line arguments prepared previously are incompatible with ld.
if (build_context.metrics.os == TargetOs_darwin) {
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
// NOTE(weshardee): __odin_exit_point should also be added, but -fini
// does not exist on MacOS
} else {
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
} else if (build_context.metrics.os != TargetOs_openbsd) {
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
}
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
} else {
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
}
if (build_context.metrics.os == TargetOs_darwin) {
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
if (build_context.minimum_os_version_string.len) {
link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
} else if (build_context.metrics.arch == TargetArch_arm64) {
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
} else {
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
}
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, " -e _main ");
}
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
defer (gb_string_free(link_command_line));
link_command_line = gb_string_appendc(link_command_line, object_files);
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
result = system_exec_command_line_app("ld-link", link_command_line);
if (result) {
return result;
}
if (is_osx && build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
if (result) {
return result;
}
}
}
}
return result;
}
+3 -10
View File
@@ -69,7 +69,6 @@ enum lbAddrKind {
lbAddr_SoaVariable, lbAddr_SoaVariable,
lbAddr_RelativePointer, lbAddr_RelativePointer,
lbAddr_RelativeSlice,
lbAddr_Swizzle, lbAddr_Swizzle,
lbAddr_SwizzleLarge, lbAddr_SwizzleLarge,
@@ -190,13 +189,9 @@ struct lbModule {
LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT]; LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
}; };
struct lbGenerator { struct lbGenerator : LinkerData {
CheckerInfo *info; CheckerInfo *info;
Array<String> output_object_paths;
Array<String> output_temp_paths;
String output_base;
String output_name;
PtrMap<void *, lbModule *> modules; // key is `AstPackage *` (`void *` is used for future use) PtrMap<void *, lbModule *> modules; // key is `AstPackage *` (`void *` is used for future use)
PtrMap<LLVMContextRef, lbModule *> modules_through_ctx; PtrMap<LLVMContextRef, lbModule *> modules_through_ctx;
lbModule default_module; lbModule default_module;
@@ -204,10 +199,6 @@ struct lbGenerator {
RecursiveMutex anonymous_proc_lits_mutex; RecursiveMutex anonymous_proc_lits_mutex;
PtrMap<Ast *, lbProcedure *> anonymous_proc_lits; PtrMap<Ast *, lbProcedure *> anonymous_proc_lits;
BlockingMutex foreign_mutex;
PtrSet<Entity *> foreign_libraries_set;
Array<Entity *> foreign_libraries;
std::atomic<u32> global_array_index; std::atomic<u32> global_array_index;
std::atomic<u32> global_generated_index; std::atomic<u32> global_generated_index;
@@ -346,7 +337,9 @@ struct lbProcedure {
}; };
#ifndef ABI_PKG_NAME_SEPARATOR
#define ABI_PKG_NAME_SEPARATOR "." #define ABI_PKG_NAME_SEPARATOR "."
#endif
#if !ODIN_LLVM_MINIMUM_VERSION_14 #if !ODIN_LLVM_MINIMUM_VERSION_14
+67 -69
View File
@@ -567,7 +567,7 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
} }
} else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) { } else if (is_type_array(type) && value.kind == ExactValue_String && !is_type_u8(core_array_type(type))) {
if (is_type_rune_array(type) && value.kind == ExactValue_String) { if (is_type_rune_array(type)) {
i64 count = type->Array.count; i64 count = type->Array.count;
Type *elem = type->Array.elem; Type *elem = type->Array.elem;
LLVMTypeRef et = lb_type(m, elem); LLVMTypeRef et = lb_type(m, elem);
@@ -1036,86 +1036,84 @@ gb_internal lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bo
LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count); LLVMValueRef *values = gb_alloc_array(temporary_allocator(), LLVMValueRef, value_count);
bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count); bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count);
if (cl->elems.count > 0) { if (cl->elems[0]->kind == Ast_FieldValue) {
if (cl->elems[0]->kind == Ast_FieldValue) { isize elem_count = cl->elems.count;
isize elem_count = cl->elems.count; for (isize i = 0; i < elem_count; i++) {
for (isize i = 0; i < elem_count; i++) { ast_node(fv, FieldValue, cl->elems[i]);
ast_node(fv, FieldValue, cl->elems[i]); String name = fv->field->Ident.token.string;
String name = fv->field->Ident.token.string;
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
GB_ASSERT(tav.mode != Addressing_Invalid); GB_ASSERT(tav.mode != Addressing_Invalid);
Selection sel = lookup_field(type, name, false); Selection sel = lookup_field(type, name, false);
GB_ASSERT(!sel.indirect); GB_ASSERT(!sel.indirect);
Entity *f = type->Struct.fields[sel.index[0]]; Entity *f = type->Struct.fields[sel.index[0]];
i32 index = field_remapping[f->Variable.field_index]; i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) { if (elem_type_can_be_constant(f->type)) {
if (sel.index.count == 1) { if (sel.index.count == 1) {
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value; values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
visited[index] = true;
} else {
if (!visited[index]) {
values[index] = lb_const_value(m, f->type, {}, false).value;
visited[index] = true; visited[index] = true;
} else { }
if (!visited[index]) { unsigned idx_list_len = cast(unsigned)sel.index.count-1;
values[index] = lb_const_value(m, f->type, {}, false).value; unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
visited[index] = true;
}
unsigned idx_list_len = cast(unsigned)sel.index.count-1;
unsigned *idx_list = gb_alloc_array(temporary_allocator(), unsigned, idx_list_len);
if (lb_is_nested_possibly_constant(type, sel, fv->value)) { if (lb_is_nested_possibly_constant(type, sel, fv->value)) {
bool is_constant = true; bool is_constant = true;
Type *cv_type = f->type; Type *cv_type = f->type;
for (isize j = 1; j < sel.index.count; j++) { for (isize j = 1; j < sel.index.count; j++) {
i32 index = sel.index[j]; i32 index = sel.index[j];
Type *cvt = base_type(cv_type); Type *cvt = base_type(cv_type);
if (cvt->kind == Type_Struct) { if (cvt->kind == Type_Struct) {
if (cvt->Struct.is_raw_union) { if (cvt->Struct.is_raw_union) {
// sanity check which should have been caught by `lb_is_nested_possibly_constant` // sanity check which should have been caught by `lb_is_nested_possibly_constant`
is_constant = false; is_constant = false;
break; break;
}
cv_type = cvt->Struct.fields[index]->type;
if (is_type_struct(cvt)) {
auto cv_field_remapping = lb_get_struct_remapping(m, cvt);
unsigned remapped_index = cast(unsigned)cv_field_remapping[index];
idx_list[j-1] = remapped_index;
} else {
idx_list[j-1] = cast(unsigned)index;
}
} else if (cvt->kind == Type_Array) {
cv_type = cvt->Array.elem;
idx_list[j-1] = cast(unsigned)index;
} else {
GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type));
} }
cv_type = cvt->Struct.fields[index]->type;
if (is_type_struct(cvt)) {
auto cv_field_remapping = lb_get_struct_remapping(m, cvt);
unsigned remapped_index = cast(unsigned)cv_field_remapping[index];
idx_list[j-1] = remapped_index;
} else {
idx_list[j-1] = cast(unsigned)index;
}
} else if (cvt->kind == Type_Array) {
cv_type = cvt->Array.elem;
idx_list[j-1] = cast(unsigned)index;
} else {
GB_PANIC("UNKNOWN TYPE: %s", type_to_string(cv_type));
} }
if (is_constant) { }
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value; if (is_constant) {
GB_ASSERT(LLVMIsConstant(elem_value)); LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len); GB_ASSERT(LLVMIsConstant(elem_value));
} values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
} }
} }
} }
} }
} else { }
for_array(i, cl->elems) { } else {
Entity *f = type->Struct.fields[i]; for_array(i, cl->elems) {
TypeAndValue tav = cl->elems[i]->tav; Entity *f = type->Struct.fields[i];
ExactValue val = {}; TypeAndValue tav = cl->elems[i]->tav;
if (tav.mode != Addressing_Invalid) { ExactValue val = {};
val = tav.value; if (tav.mode != Addressing_Invalid) {
} val = tav.value;
}
i32 index = field_remapping[f->Variable.field_index];
if (elem_type_can_be_constant(f->type)) { i32 index = field_remapping[f->Variable.field_index];
values[index] = lb_const_value(m, f->type, val, allow_local).value; if (elem_type_can_be_constant(f->type)) {
visited[index] = true; values[index] = lb_const_value(m, f->type, val, allow_local).value;
} visited[index] = true;
} }
} }
} }
+5 -12
View File
@@ -442,19 +442,12 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
gbString name = type_to_string(type, temporary_allocator()); gbString name = type_to_string(type, temporary_allocator());
return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type))); return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
} }
case Type_RelativeMultiPointer: {
LLVMMetadataRef base_integer = lb_debug_type(m, type->RelativeMultiPointer.base_integer);
gbString name = type_to_string(type, temporary_allocator());
return LLVMDIBuilderCreateTypedef(m->debug_builder, base_integer, name, gb_string_length(name), nullptr, 0, nullptr, cast(u32)(8*type_align_of(type)));
}
case Type_RelativeSlice:
{
unsigned element_count = 0;
LLVMMetadataRef elements[2] = {};
Type *base_integer = type->RelativeSlice.base_integer;
unsigned base_bits = cast(unsigned)(8*type_size_of(base_integer));
elements[0] = lb_debug_struct_field(m, str_lit("data_offset"), base_integer, 0);
elements[1] = lb_debug_struct_field(m, str_lit("len"), base_integer, base_bits);
gbString name = type_to_string(type, temporary_allocator());
return LLVMDIBuilderCreateStructType(m->debug_builder, nullptr, name, gb_string_length(name), nullptr, 0, 2*base_bits, base_bits, LLVMDIFlagZero, nullptr, elements, element_count, 0, nullptr, "", 0);
}
case Type_Matrix: { case Type_Matrix: {
LLVMMetadataRef subscripts[1] = {}; LLVMMetadataRef subscripts[1] = {};
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder, subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
+27 -17
View File
@@ -2863,7 +2863,6 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
ast_node(ue, UnaryExpr, expr); ast_node(ue, UnaryExpr, expr);
auto tv = type_and_value_of_expr(expr); auto tv = type_and_value_of_expr(expr);
Ast *ue_expr = unparen_expr(ue->expr); Ast *ue_expr = unparen_expr(ue->expr);
if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_type_tuple(tv.type)) { if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_type_tuple(tv.type)) {
Type *tuple = tv.type; Type *tuple = tv.type;
@@ -3803,25 +3802,32 @@ gb_internal lbAddr lb_build_addr_index_expr(lbProcedure *p, Ast *expr) {
lbValue v = {}; lbValue v = {};
LLVMValueRef indices[1] = {index.value}; LLVMValueRef indices[1] = {index.value};
v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "foo"); v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, t->MultiPointer.elem), multi_ptr.value, indices, 1, "");
v.type = alloc_type_pointer(t->MultiPointer.elem); v.type = alloc_type_pointer(t->MultiPointer.elem);
return lb_addr(v); return lb_addr(v);
} }
case Type_RelativeSlice: { case Type_RelativeMultiPointer: {
lbAddr slice_addr = {}; lbAddr rel_ptr_addr = {};
if (deref) { if (deref) {
slice_addr = lb_addr(lb_build_expr(p, ie->expr)); lbValue rel_ptr_ptr = lb_build_expr(p, ie->expr);
rel_ptr_addr = lb_addr(rel_ptr_ptr);
} else { } else {
slice_addr = lb_build_addr(p, ie->expr); rel_ptr_addr = lb_build_addr(p, ie->expr);
} }
lbValue slice = lb_addr_load(p, slice_addr); lbValue rel_ptr = lb_relative_pointer_to_pointer(p, rel_ptr_addr);
lbValue elem = lb_slice_elem(p, slice); lbValue index = lb_build_expr(p, ie->index);
lbValue index = lb_emit_conv(p, lb_build_expr(p, ie->index), t_int); index = lb_emit_conv(p, index, t_int);
lbValue len = lb_slice_len(p, slice); lbValue v = {};
lb_emit_bounds_check(p, ast_token(ie->index), index, len);
lbValue v = lb_emit_ptr_offset(p, elem, index); Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
GB_ASSERT(pointer_type->kind == Type_MultiPointer);
Type *elem = pointer_type->MultiPointer.elem;
LLVMValueRef indices[1] = {index.value};
v.value = LLVMBuildGEP2(p->builder, lb_type(p->module, elem), rel_ptr.value, indices, 1, "");
v.type = alloc_type_pointer(elem);
return lb_addr(v); return lb_addr(v);
} }
@@ -3925,8 +3931,11 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
return slice; return slice;
} }
case Type_RelativeSlice: case Type_RelativePointer:
GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load"); GB_PANIC("TODO(bill): Type_RelativePointer should be handled above already on the lb_addr_load");
break;
case Type_RelativeMultiPointer:
GB_PANIC("TODO(bill): Type_RelativeMultiPointer should be handled above already on the lb_addr_load");
break; break;
case Type_DynamicArray: { case Type_DynamicArray: {
@@ -3996,7 +4005,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
} }
case Type_Basic: { case Type_Basic: {
GB_ASSERT(type == t_string); GB_ASSERT_MSG(type == t_string, "got %s", type_to_string(type));
lbValue len = lb_string_len(p, base); lbValue len = lb_string_len(p, base);
if (high.value == nullptr) high = len; if (high.value == nullptr) high = len;
@@ -4171,7 +4180,7 @@ gb_internal lbAddr lb_build_addr_compound_lit(lbProcedure *p, Ast *expr) {
// HACK TODO(bill): THIS IS A MASSIVE HACK!!!! // HACK TODO(bill): THIS IS A MASSIVE HACK!!!!
if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) { if (is_type_union(ft) && !are_types_identical(fet, ft) && !is_type_untyped(fet)) {
GB_ASSERT_MSG(union_variant_index(ft, fet) > 0, "%s", type_to_string(fet)); GB_ASSERT_MSG(union_variant_index(ft, fet) >= 0, "%s", type_to_string(fet));
lb_emit_store_union_variant(p, gep, field_expr, fet); lb_emit_store_union_variant(p, gep, field_expr, fet);
} else { } else {
@@ -4519,8 +4528,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
Selection sel = lookup_field(type, selector, false); Selection sel = lookup_field(type, selector, false);
GB_ASSERT(sel.entity != nullptr); GB_ASSERT(sel.entity != nullptr);
if (sel.pseudo_field) { if (sel.pseudo_field) {
GB_ASSERT(sel.entity->kind == Entity_Procedure); GB_ASSERT(sel.entity->kind == Entity_Procedure || sel.entity->kind == Entity_ProcGroup);
Entity *e = entity_of_node(sel_node); Entity *e = entity_of_node(sel_node);
GB_ASSERT(e->kind == Entity_Procedure);
return lb_addr(lb_find_value_from_entity(p->module, e)); return lb_addr(lb_find_value_from_entity(p->module, e));
} }
+87 -155
View File
@@ -103,37 +103,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
} }
String init_fullpath = c->parser->init_fullpath; String init_fullpath = c->parser->init_fullpath;
linker_data_init(gen, &c->info, init_fullpath);
if (build_context.out_filepath.len == 0) {
gen->output_name = remove_directory_from_path(init_fullpath);
gen->output_name = remove_extension_from_path(gen->output_name);
gen->output_name = string_trim_whitespace(gen->output_name);
if (gen->output_name.len == 0) {
gen->output_name = c->info.init_scope->pkg->name;
}
gen->output_base = gen->output_name;
} else {
gen->output_name = build_context.out_filepath;
gen->output_name = string_trim_whitespace(gen->output_name);
if (gen->output_name.len == 0) {
gen->output_name = c->info.init_scope->pkg->name;
}
isize pos = string_extension_position(gen->output_name);
if (pos < 0) {
gen->output_base = gen->output_name;
} else {
gen->output_base = substring(gen->output_name, 0, pos);
}
}
gbAllocator ha = heap_allocator();
array_init(&gen->output_object_paths, ha);
array_init(&gen->output_temp_paths, ha);
gen->output_base = path_to_full_path(ha, gen->output_base);
gbString output_file_path = gb_string_make_length(ha, gen->output_base.text, gen->output_base.len);
output_file_path = gb_string_appendc(output_file_path, ".obj");
defer (gb_string_free(output_file_path));
gen->info = &c->info; gen->info = &c->info;
@@ -141,10 +111,6 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
map_init(&gen->modules_through_ctx, gen->info->packages.count*2); map_init(&gen->modules_through_ctx, gen->info->packages.count*2);
map_init(&gen->anonymous_proc_lits, 1024); map_init(&gen->anonymous_proc_lits, 1024);
array_init(&gen->foreign_libraries, heap_allocator(), 0, 1024);
ptr_set_init(&gen->foreign_libraries_set, 1024);
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
for (auto const &entry : gen->info->packages) { for (auto const &entry : gen->info->packages) {
AstPackage *pkg = entry.value; AstPackage *pkg = entry.value;
@@ -383,9 +349,10 @@ gb_internal lbAddr lb_addr(lbValue addr) {
if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) { if (addr.type != nullptr && is_type_relative_pointer(type_deref(addr.type))) {
GB_ASSERT(is_type_pointer(addr.type)); GB_ASSERT(is_type_pointer(addr.type));
v.kind = lbAddr_RelativePointer; v.kind = lbAddr_RelativePointer;
} else if (addr.type != nullptr && is_type_relative_slice(type_deref(addr.type))) { } else if (addr.type != nullptr && is_type_relative_multi_pointer(type_deref(addr.type))) {
GB_ASSERT(is_type_pointer(addr.type)); GB_ASSERT(is_type_pointer(addr.type) ||
v.kind = lbAddr_RelativeSlice; is_type_multi_pointer(addr.type));
v.kind = lbAddr_RelativePointer;
} }
return v; return v;
} }
@@ -458,6 +425,43 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) {
return type_deref(addr.addr.type); return type_deref(addr.addr.type);
} }
gb_internal lbValue lb_relative_pointer_to_pointer(lbProcedure *p, lbAddr const &addr) {
GB_ASSERT(addr.kind == lbAddr_RelativePointer);
Type *t = base_type(lb_addr_type(addr));
GB_ASSERT(is_type_relative_pointer(t) || is_type_relative_multi_pointer(t));
Type *pointer_type = nullptr;
Type *base_integer = nullptr;
if (t->kind == Type_RelativePointer) {
pointer_type = t->RelativePointer.pointer_type;
base_integer = t->RelativePointer.base_integer;
} else if (t->kind == Type_RelativeMultiPointer) {
pointer_type = t->RelativeMultiPointer.pointer_type;
base_integer = t->RelativeMultiPointer.base_integer;
}
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
offset = lb_emit_load(p, offset);
if (!is_type_unsigned(base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
}
offset = lb_emit_conv(p, offset, t_uintptr);
lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
// NOTE(bill): nil check
lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr);
return final_ptr;
}
gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) { gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
if (addr.addr.value == nullptr) { if (addr.addr.value == nullptr) {
GB_PANIC("Illegal addr -> nullptr"); GB_PANIC("Illegal addr -> nullptr");
@@ -468,28 +472,8 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
case lbAddr_Map: case lbAddr_Map:
return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key); return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
case lbAddr_RelativePointer: { case lbAddr_RelativePointer:
Type *rel_ptr = base_type(lb_addr_type(addr)); return lb_relative_pointer_to_pointer(p, addr);
GB_ASSERT(rel_ptr->kind == Type_RelativePointer);
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
offset = lb_emit_load(p, offset);
if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
}
offset = lb_emit_conv(p, offset, t_uintptr);
lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
absolute_ptr = lb_emit_conv(p, absolute_ptr, rel_ptr->RelativePointer.pointer_type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer));
// NOTE(bill): nil check
lbValue nil_ptr = lb_const_nil(p->module, rel_ptr->RelativePointer.pointer_type);
lbValue final_ptr = lb_emit_select(p, cond, nil_ptr, absolute_ptr);
return final_ptr;
}
case lbAddr_SoaVariable: case lbAddr_SoaVariable:
// TODO(bill): FIX THIS HACK // TODO(bill): FIX THIS HACK
@@ -511,6 +495,9 @@ gb_internal lbValue lb_addr_get_ptr(lbProcedure *p, lbAddr const &addr) {
gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) { gb_internal lbValue lb_build_addr_ptr(lbProcedure *p, Ast *expr) {
lbAddr addr = lb_build_addr(p, expr); lbAddr addr = lb_build_addr(p, expr);
if (addr.kind == lbAddr_RelativePointer) {
return addr.addr;
}
return lb_addr_get_ptr(p, addr); return lb_addr_get_ptr(p, addr);
} }
@@ -719,9 +706,20 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
if (addr.kind == lbAddr_RelativePointer) { if (addr.kind == lbAddr_RelativePointer) {
Type *rel_ptr = base_type(lb_addr_type(addr)); Type *rel_ptr = base_type(lb_addr_type(addr));
GB_ASSERT(rel_ptr->kind == Type_RelativePointer); GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
rel_ptr->kind == Type_RelativeMultiPointer);
Type *pointer_type = nullptr;
Type *base_integer = nullptr;
value = lb_emit_conv(p, value, rel_ptr->RelativePointer.pointer_type); if (rel_ptr->kind == Type_RelativePointer) {
pointer_type = rel_ptr->RelativePointer.pointer_type;
base_integer = rel_ptr->RelativePointer.base_integer;
} else if (rel_ptr->kind == Type_RelativeMultiPointer) {
pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
base_integer = rel_ptr->RelativeMultiPointer.base_integer;
}
value = lb_emit_conv(p, value, pointer_type);
GB_ASSERT(is_type_pointer(addr.addr.type)); GB_ASSERT(is_type_pointer(addr.addr.type));
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr); lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
@@ -730,54 +728,20 @@ gb_internal void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, ""); offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
offset.type = t_uintptr; offset.type = t_uintptr;
if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) { if (!is_type_unsigned(base_integer)) {
offset = lb_emit_conv(p, offset, t_i64); offset = lb_emit_conv(p, offset, t_i64);
} }
offset = lb_emit_conv(p, offset, rel_ptr->RelativePointer.base_integer); offset = lb_emit_conv(p, offset, base_integer);
lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer)); lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(base_integer));
offset = lb_emit_select(p, offset = lb_emit_select(p,
lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)), lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer), lb_const_nil(p->module, base_integer),
offset offset
); );
LLVMBuildStore(p->builder, offset.value, offset_ptr.value); LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
return; return;
} else if (addr.kind == lbAddr_RelativeSlice) {
Type *rel_ptr = base_type(lb_addr_type(addr));
GB_ASSERT(rel_ptr->kind == Type_RelativeSlice);
value = lb_emit_conv(p, value, rel_ptr->RelativeSlice.slice_type);
GB_ASSERT(is_type_pointer(addr.addr.type));
lbValue ptr = lb_emit_conv(p, lb_emit_struct_ep(p, addr.addr, 0), t_uintptr);
lbValue val_ptr = lb_emit_conv(p, lb_slice_elem(p, value), t_uintptr);
lbValue offset = {};
offset.value = LLVMBuildSub(p->builder, val_ptr.value, ptr.value, "");
offset.type = t_uintptr;
if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
}
offset = lb_emit_conv(p, offset, rel_ptr->RelativePointer.base_integer);
lbValue offset_ptr = lb_emit_conv(p, addr.addr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer));
offset = lb_emit_select(p,
lb_emit_comp(p, Token_CmpEq, val_ptr, lb_const_nil(p->module, t_uintptr)),
lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer),
offset
);
LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
lbValue len = lb_slice_len(p, value);
len = lb_emit_conv(p, len, rel_ptr->RelativePointer.base_integer);
lbValue len_ptr = lb_emit_struct_ep(p, addr.addr, 1);
LLVMBuildStore(p->builder, len.value, len_ptr.value);
return;
} else if (addr.kind == lbAddr_Map) { } else if (addr.kind == lbAddr_Map) {
lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt); lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
return; return;
@@ -1054,67 +1018,43 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
if (addr.kind == lbAddr_RelativePointer) { if (addr.kind == lbAddr_RelativePointer) {
Type *rel_ptr = base_type(lb_addr_type(addr)); Type *rel_ptr = base_type(lb_addr_type(addr));
GB_ASSERT(rel_ptr->kind == Type_RelativePointer); Type *base_integer = nullptr;
Type *pointer_type = nullptr;
GB_ASSERT(rel_ptr->kind == Type_RelativePointer ||
rel_ptr->kind == Type_RelativeMultiPointer);
if (rel_ptr->kind == Type_RelativePointer) {
base_integer = rel_ptr->RelativePointer.base_integer;
pointer_type = rel_ptr->RelativePointer.pointer_type;
} else if (rel_ptr->kind == Type_RelativeMultiPointer) {
base_integer = rel_ptr->RelativeMultiPointer.base_integer;
pointer_type = rel_ptr->RelativeMultiPointer.pointer_type;
}
lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr); lbValue ptr = lb_emit_conv(p, addr.addr, t_uintptr);
lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(rel_ptr->RelativePointer.base_integer)); lbValue offset = lb_emit_conv(p, ptr, alloc_type_pointer(base_integer));
offset = lb_emit_load(p, offset); offset = lb_emit_load(p, offset);
if (!is_type_unsigned(rel_ptr->RelativePointer.base_integer)) { if (!is_type_unsigned(base_integer)) {
offset = lb_emit_conv(p, offset, t_i64); offset = lb_emit_conv(p, offset, t_i64);
} }
offset = lb_emit_conv(p, offset, t_uintptr); offset = lb_emit_conv(p, offset, t_uintptr);
lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr); lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
absolute_ptr = lb_emit_conv(p, absolute_ptr, rel_ptr->RelativePointer.pointer_type); absolute_ptr = lb_emit_conv(p, absolute_ptr, pointer_type);
lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, rel_ptr->RelativePointer.base_integer)); lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, base_integer));
// NOTE(bill): nil check // NOTE(bill): nil check
lbValue nil_ptr = lb_const_nil(p->module, rel_ptr->RelativePointer.pointer_type); lbValue nil_ptr = lb_const_nil(p->module, pointer_type);
lbValue final_ptr = {}; lbValue final_ptr = {};
final_ptr.type = absolute_ptr.type; final_ptr.type = absolute_ptr.type;
final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, ""); final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
return lb_emit_load(p, final_ptr); if (rel_ptr->kind == Type_RelativeMultiPointer) {
return final_ptr;
} else if (addr.kind == lbAddr_RelativeSlice) {
Type *rel_ptr = base_type(lb_addr_type(addr));
GB_ASSERT(rel_ptr->kind == Type_RelativeSlice);
lbValue offset_ptr = lb_emit_struct_ep(p, addr.addr, 0);
lbValue ptr = lb_emit_conv(p, offset_ptr, t_uintptr);
lbValue offset = lb_emit_load(p, offset_ptr);
if (!is_type_unsigned(rel_ptr->RelativeSlice.base_integer)) {
offset = lb_emit_conv(p, offset, t_i64);
} }
offset = lb_emit_conv(p, offset, t_uintptr); return lb_emit_load(p, final_ptr);
lbValue absolute_ptr = lb_emit_arith(p, Token_Add, ptr, offset, t_uintptr);
Type *slice_type = base_type(rel_ptr->RelativeSlice.slice_type);
GB_ASSERT(rel_ptr->RelativeSlice.slice_type->kind == Type_Slice);
Type *slice_elem = slice_type->Slice.elem;
Type *slice_elem_ptr = alloc_type_pointer(slice_elem);
absolute_ptr = lb_emit_conv(p, absolute_ptr, slice_elem_ptr);
lbValue cond = lb_emit_comp(p, Token_CmpEq, offset, lb_const_nil(p->module, rel_ptr->RelativeSlice.base_integer));
// NOTE(bill): nil check
lbValue nil_ptr = lb_const_nil(p->module, slice_elem_ptr);
lbValue data = {};
data.type = absolute_ptr.type;
data.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
lbValue len = lb_emit_load(p, lb_emit_struct_ep(p, addr.addr, 1));
len = lb_emit_conv(p, len, t_int);
lbAddr slice = lb_add_local_generated(p, slice_type, false);
lb_fill_slice(p, slice, data, len);
return lb_addr_load(p, slice);
} else if (addr.kind == lbAddr_Map) { } else if (addr.kind == lbAddr_Map) {
Type *map_type = base_type(type_deref(addr.addr.type)); Type *map_type = base_type(type_deref(addr.addr.type));
@@ -1895,8 +1835,8 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_SimdVector: case Type_SimdVector:
return lb_type_internal(m, base); return lb_type_internal(m, base);
// TODO(bill): Deal with this correctly. Can this be named?
case Type_Proc: case Type_Proc:
// TODO(bill): Deal with this correctly. Can this be named?
return lb_type_internal(m, base); return lb_type_internal(m, base);
case Type_Tuple: case Type_Tuple:
@@ -2173,17 +2113,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
case Type_RelativePointer: case Type_RelativePointer:
return lb_type_internal(m, type->RelativePointer.base_integer); return lb_type_internal(m, type->RelativePointer.base_integer);
case Type_RelativeMultiPointer:
return lb_type_internal(m, type->RelativeMultiPointer.base_integer);
case Type_RelativeSlice:
{
LLVMTypeRef base_integer = lb_type_internal(m, type->RelativeSlice.base_integer);
unsigned field_count = 2;
LLVMTypeRef *fields = gb_alloc_array(permanent_allocator(), LLVMTypeRef, field_count);
fields[0] = base_integer;
fields[1] = base_integer;
return LLVMStructTypeInContext(ctx, fields, field_count, false);
}
case Type_Matrix: case Type_Matrix:
{ {
@@ -2869,7 +2802,6 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
if (USE_SEPARATE_MODULES) { if (USE_SEPARATE_MODULES) {
lbModule *other_module = lb_module_of_entity(m->gen, e); lbModule *other_module = lb_module_of_entity(m->gen, e);
// TODO(bill): correct this logic
bool is_external = other_module != m; bool is_external = other_module != m;
if (!is_external) { if (!is_external) {
if (e->code_gen_module != nullptr) { if (e->code_gen_module != nullptr) {
+12 -9
View File
@@ -362,7 +362,6 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
Type *pt = p->type; Type *pt = p->type;
lbCallingConventionKind cc_kind = lbCallingConvention_C; lbCallingConventionKind cc_kind = lbCallingConvention_C;
// TODO(bill): Clean up this logic
if (!is_arch_wasm()) { if (!is_arch_wasm()) {
cc_kind = lb_calling_convention_map[pt->Proc.calling_convention]; cc_kind = lb_calling_convention_map[pt->Proc.calling_convention];
} }
@@ -1702,7 +1701,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
lbValue v = lb_build_expr(p, ce->args[0]); lbValue v = lb_build_expr(p, ce->args[0]);
Type *t = base_type(v.type); Type *t = base_type(v.type);
if (is_type_pointer(t)) { if (is_type_pointer(t)) {
// IMPORTANT TODO(bill): Should there be a nil pointer check?
v = lb_emit_load(p, v); v = lb_emit_load(p, v);
t = type_deref(t); t = type_deref(t);
} }
@@ -1712,7 +1710,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
return lb_string_len(p, v); return lb_string_len(p, v);
} else if (is_type_array(t)) { } else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant"); GB_PANIC("Array lengths are constant");
} else if (is_type_slice(t) || is_type_relative_slice(t)) { } else if (is_type_slice(t)) {
return lb_slice_len(p, v); return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) { } else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_len(p, v); return lb_dynamic_array_len(p, v);
@@ -1730,7 +1728,6 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
lbValue v = lb_build_expr(p, ce->args[0]); lbValue v = lb_build_expr(p, ce->args[0]);
Type *t = base_type(v.type); Type *t = base_type(v.type);
if (is_type_pointer(t)) { if (is_type_pointer(t)) {
// IMPORTANT TODO(bill): Should there be a nil pointer check?
v = lb_emit_load(p, v); v = lb_emit_load(p, v);
t = type_deref(t); t = type_deref(t);
} }
@@ -1738,7 +1735,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
GB_PANIC("Unreachable"); GB_PANIC("Unreachable");
} else if (is_type_array(t)) { } else if (is_type_array(t)) {
GB_PANIC("Array lengths are constant"); GB_PANIC("Array lengths are constant");
} else if (is_type_slice(t) || is_type_relative_slice(t)) { } else if (is_type_slice(t)) {
return lb_slice_len(p, v); return lb_slice_len(p, v);
} else if (is_type_dynamic_array(t)) { } else if (is_type_dynamic_array(t)) {
return lb_dynamic_array_cap(p, v); return lb_dynamic_array_cap(p, v);
@@ -3144,7 +3141,7 @@ gb_internal lbValue lb_build_call_expr(lbProcedure *p, Ast *expr) {
lbValue res = lb_build_call_expr_internal(p, expr); lbValue res = lb_build_call_expr_internal(p, expr);
if (ce->optional_ok_one) { // TODO(bill): Minor hack for #optional_ok procedures if (ce->optional_ok_one) {
GB_ASSERT(is_type_tuple(res.type)); GB_ASSERT(is_type_tuple(res.type));
GB_ASSERT(res.type->Tuple.variables.count == 2); GB_ASSERT(res.type->Tuple.variables.count == 2);
return lb_emit_struct_ev(p, res, 0); return lb_emit_struct_ev(p, res, 0);
@@ -3332,9 +3329,15 @@ gb_internal lbValue lb_build_call_expr_internal(lbProcedure *p, Ast *expr) {
isize param_index = lookup_procedure_parameter(pt, name); isize param_index = lookup_procedure_parameter(pt, name);
GB_ASSERT(param_index >= 0); GB_ASSERT(param_index >= 0);
lbValue value = lb_build_expr(p, fv->value); Entity *e = pt->params->Tuple.variables[param_index];
GB_ASSERT(!is_type_tuple(value.type)); if (e->kind == Entity_TypeName) {
args[param_index] = value; lbValue value = lb_const_nil(p->module, e->type);
args[param_index] = value;
} else {
lbValue value = lb_build_expr(p, fv->value);
GB_ASSERT(!is_type_tuple(value.type));
args[param_index] = value;
}
} }
TokenPos pos = ast_token(ce->proc).pos; TokenPos pos = ast_token(ce->proc).pos;
+1 -4
View File
@@ -1688,7 +1688,6 @@ gb_internal void lb_build_type_switch_stmt(lbProcedure *p, AstTypeSwitchStmt *ss
lb_add_entity(p->module, case_entity, ptr); lb_add_entity(p->module, case_entity, ptr);
lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token); lb_add_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
} else { } else {
// TODO(bill): is the correct expected behaviour?
lb_store_type_case_implicit(p, clause, parent_value); lb_store_type_case_implicit(p, clause, parent_value);
} }
@@ -2014,12 +2013,10 @@ gb_internal void lb_build_if_stmt(lbProcedure *p, Ast *node) {
defer (lb_close_scope(p, lbDeferExit_Default, nullptr)); defer (lb_close_scope(p, lbDeferExit_Default, nullptr));
if (is->init != nullptr) { if (is->init != nullptr) {
// TODO(bill): Should this have a separate block to begin with?
#if 1
lbBlock *init = lb_create_block(p, "if.init"); lbBlock *init = lb_create_block(p, "if.init");
lb_emit_jump(p, init); lb_emit_jump(p, init);
lb_start_block(p, init); lb_start_block(p, init);
#endif
lb_build_stmt(p, is->init); lb_build_stmt(p, is->init);
} }
lbBlock *then = lb_create_block(p, "if.then"); lbBlock *then = lb_create_block(p, "if.then");
+7 -6
View File
@@ -57,7 +57,7 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
case Type_BitSet: kind = Typeid_Bit_Set; break; case Type_BitSet: kind = Typeid_Bit_Set; break;
case Type_SimdVector: kind = Typeid_Simd_Vector; break; case Type_SimdVector: kind = Typeid_Simd_Vector; break;
case Type_RelativePointer: kind = Typeid_Relative_Pointer; break; case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
case Type_RelativeSlice: kind = Typeid_Relative_Slice; break; case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
case Type_SoaPointer: kind = Typeid_SoaPointer; break; case Type_SoaPointer: kind = Typeid_SoaPointer; break;
} }
@@ -731,7 +731,6 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet type_set_offsets(t); // NOTE(bill): Just incase the offsets have not been set yet
for (isize source_index = 0; source_index < count; source_index++) { for (isize source_index = 0; source_index < count; source_index++) {
// TODO(bill): Order fields in source order not layout order
Entity *f = t->Struct.fields[source_index]; Entity *f = t->Struct.fields[source_index];
lbValue tip = lb_type_info(m, f->type); lbValue tip = lb_type_info(m, f->type);
i64 foffset = 0; i64 foffset = 0;
@@ -858,12 +857,13 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
lb_emit_store(p, tag, res); lb_emit_store(p, tag, res);
} }
break; break;
case Type_RelativeSlice:
case Type_RelativeMultiPointer:
{ {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_slice_ptr); tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_multi_pointer_ptr);
LLVMValueRef vals[2] = { LLVMValueRef vals[2] = {
lb_type_info(m, t->RelativeSlice.slice_type).value, lb_type_info(m, t->RelativeMultiPointer.pointer_type).value,
lb_type_info(m, t->RelativeSlice.base_integer).value, lb_type_info(m, t->RelativeMultiPointer.base_integer).value,
}; };
lbValue res = {}; lbValue res = {};
@@ -872,6 +872,7 @@ gb_internal void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup
lb_emit_store(p, tag, res); lb_emit_store(p, tag, res);
} }
break; break;
case Type_Matrix: case Type_Matrix:
{ {
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr); tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);
+1 -6
View File
@@ -1124,11 +1124,6 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
} }
} else if (is_type_array(t)) { } else if (is_type_array(t)) {
return lb_emit_array_epi(p, s, index); return lb_emit_array_epi(p, s, index);
} else if (is_type_relative_slice(t)) {
switch (index) {
case 0: result_type = t->RelativeSlice.base_integer; break;
case 1: result_type = t->RelativeSlice.base_integer; break;
}
} else if (is_type_soa_pointer(t)) { } else if (is_type_soa_pointer(t)) {
switch (index) { switch (index) {
case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break; case 0: result_type = alloc_type_pointer(t->SoaPointer.elem); break;
@@ -1547,7 +1542,7 @@ gb_internal lbValue lb_slice_elem(lbProcedure *p, lbValue slice) {
return lb_emit_struct_ev(p, slice, 0); return lb_emit_struct_ev(p, slice, 0);
} }
gb_internal lbValue lb_slice_len(lbProcedure *p, lbValue slice) { gb_internal lbValue lb_slice_len(lbProcedure *p, lbValue slice) {
GB_ASSERT(is_type_slice(slice.type) || is_type_relative_slice(slice.type)); GB_ASSERT(is_type_slice(slice.type));
return lb_emit_struct_ev(p, slice, 1); return lb_emit_struct_ev(p, slice, 1);
} }
gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) { gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
+139 -460
View File
@@ -1,5 +1,4 @@
// #define NO_ARRAY_BOUNDS_CHECK // #define NO_ARRAY_BOUNDS_CHECK
#include "common.cpp" #include "common.cpp"
#include "timings.cpp" #include "timings.cpp"
#include "tokenizer.cpp" #include "tokenizer.cpp"
@@ -71,6 +70,18 @@ gb_global Timings global_timings = {0};
#include "checker.cpp" #include "checker.cpp"
#include "docs.cpp" #include "docs.cpp"
#include "linker.cpp"
#if defined(GB_SYSTEM_WINDOWS) && defined(ODIN_TILDE_BACKEND)
#define ALLOW_TILDE 1
#else
#define ALLOW_TILDE 0
#endif
#if ALLOW_TILDE
#include "tilde.cpp"
#endif
#include "llvm_backend.cpp" #include "llvm_backend.cpp"
#if defined(GB_SYSTEM_OSX) #if defined(GB_SYSTEM_OSX)
@@ -147,422 +158,6 @@ gb_internal i32 system_exec_command_line_app(char const *name, char const *fmt,
return exit_code; return exit_code;
} }
gb_internal i32 linker_stage(lbGenerator *gen) {
i32 result = 0;
Timings *timings = &global_timings;
String output_filename = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Output]);
debugf("Linking %.*s\n", LIT(output_filename));
// TOOD(Jeroen): Make a `build_paths[BuildPath_Object] to avoid `%.*s.o`.
if (is_arch_wasm()) {
timings_start_section(timings, str_lit("wasm-ld"));
#if defined(GB_SYSTEM_WINDOWS)
result = system_exec_command_line_app("wasm-ld",
"\"%.*s\\bin\\wasm-ld\" \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(build_context.ODIN_ROOT),
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
result = system_exec_command_line_app("wasm-ld",
"wasm-ld \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#endif
return result;
}
if (build_context.cross_compiling && selected_target_metrics->metrics == &target_essence_amd64) {
#if defined(GB_SYSTEM_UNIX)
result = system_exec_command_line_app("linker", "x86_64-essence-gcc \"%.*s.o\" -o \"%.*s\" %.*s %.*s",
LIT(output_filename), LIT(output_filename), LIT(build_context.link_flags), LIT(build_context.extra_linker_flags));
#else
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
LIT(target_arch_names[build_context.metrics.arch])
);
#endif
} else if (build_context.cross_compiling && build_context.different_os) {
gb_printf_err("Linking for cross compilation for this platform is not yet supported (%.*s %.*s)\n",
LIT(target_os_names[build_context.metrics.os]),
LIT(target_arch_names[build_context.metrics.arch])
);
build_context.keep_object_files = true;
} else {
#if defined(GB_SYSTEM_WINDOWS)
bool is_windows = true;
#else
bool is_windows = false;
#endif
#if defined(GB_SYSTEM_OSX)
bool is_osx = true;
#else
bool is_osx = false;
#endif
if (is_windows) {
String section_name = str_lit("msvc-link");
if (build_context.use_lld) {
section_name = str_lit("lld-link");
}
timings_start_section(timings, section_name);
gbString lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(lib_str));
gbString link_settings = gb_string_make_reserve(heap_allocator(), 256);
defer (gb_string_free(link_settings));
// Add library search paths.
if (build_context.build_paths[BuildPath_VS_LIB].basename.len > 0) {
String path = {};
auto add_path = [&](String path) {
if (path[path.len-1] == '\\') {
path.len -= 1;
}
link_settings = gb_string_append_fmt(link_settings, " /LIBPATH:\"%.*s\"", LIT(path));
};
add_path(build_context.build_paths[BuildPath_Win_SDK_UM_Lib].basename);
add_path(build_context.build_paths[BuildPath_Win_SDK_UCRT_Lib].basename);
add_path(build_context.build_paths[BuildPath_VS_LIB].basename);
}
StringSet libs = {};
string_set_init(&libs, 64);
defer (string_set_destroy(&libs));
StringSet asm_files = {};
string_set_init(&asm_files, 64);
defer (string_set_destroy(&asm_files));
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
for_array(i, e->LibraryName.paths) {
String lib = string_trim_whitespace(e->LibraryName.paths[i]);
// IMPORTANT NOTE(bill): calling `string_to_lower` here is not an issue because
// we will never uses these strings afterwards
string_to_lower(&lib);
if (lib.len == 0) {
continue;
}
if (has_asm_extension(lib)) {
if (!string_set_update(&asm_files, lib)) {
String asm_file = asm_files.entries[i].value;
String obj_file = concatenate_strings(permanent_allocator(), asm_file, str_lit(".obj"));
result = system_exec_command_line_app("nasm",
"\"%.*s\\bin\\nasm\\windows\\nasm.exe\" \"%.*s\" "
"-f win64 "
"-o \"%.*s\" "
"%.*s "
"",
LIT(build_context.ODIN_ROOT), LIT(asm_file),
LIT(obj_file),
LIT(build_context.extra_assembler_flags)
);
if (result) {
return result;
}
array_add(&gen->output_object_paths, obj_file);
}
} else {
if (!string_set_update(&libs, lib)) {
lib_str = gb_string_append_fmt(lib_str, " \"%.*s\"", LIT(lib));
}
}
}
}
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
if (e->LibraryName.extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
}
}
if (build_context.build_mode == BuildMode_DynamicLibrary) {
link_settings = gb_string_append_fmt(link_settings, " /DLL");
} else {
link_settings = gb_string_append_fmt(link_settings, " /ENTRY:mainCRTStartup");
}
if (build_context.pdb_filepath != "") {
String pdb_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_PDB]);
link_settings = gb_string_append_fmt(link_settings, " /PDB:%.*s", LIT(pdb_path));
}
if (build_context.no_crt) {
link_settings = gb_string_append_fmt(link_settings, " /nodefaultlib");
} else {
link_settings = gb_string_append_fmt(link_settings, " /defaultlib:libcmt");
}
if (build_context.ODIN_DEBUG) {
link_settings = gb_string_append_fmt(link_settings, " /DEBUG");
}
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
for (String const &object_path : gen->output_object_paths) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
String vs_exe_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_VS_EXE]);
defer (gb_free(heap_allocator(), vs_exe_path.text));
String windows_sdk_bin_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_Win_SDK_Bin_Path]);
defer (gb_free(heap_allocator(), windows_sdk_bin_path.text));
char const *subsystem_str = build_context.use_subsystem_windows ? "WINDOWS" : "CONSOLE";
if (!build_context.use_lld) { // msvc
String res_path = {};
defer (gb_free(heap_allocator(), res_path.text));
if (build_context.has_resource) {
String temp_res_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RES]);
res_path = concatenate3_strings(heap_allocator(), str_lit("\""), temp_res_path, str_lit("\""));
gb_free(heap_allocator(), temp_res_path.text);
String rc_path = path_to_string(heap_allocator(), build_context.build_paths[BuildPath_RC]);
defer (gb_free(heap_allocator(), rc_path.text));
result = system_exec_command_line_app("msvc-link",
"\"%.*src.exe\" /nologo /fo \"%.*s\" \"%.*s\"",
LIT(windows_sdk_bin_path),
LIT(res_path),
LIT(rc_path)
);
if (result) {
return result;
}
}
switch (build_context.build_mode) {
case BuildMode_Executable:
link_settings = gb_string_append_fmt(link_settings, " /NOIMPLIB /NOEXP");
break;
}
result = system_exec_command_line_app("msvc-link",
"\"%.*slink.exe\" %s %.*s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
"%.*s "
"%.*s "
"%s "
"",
LIT(vs_exe_path), object_files, LIT(res_path), LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
} else { // lld
result = system_exec_command_line_app("msvc-lld-link",
"\"%.*s\\bin\\lld-link\" %s -OUT:\"%.*s\" %s "
"/nologo /incremental:no /opt:ref /subsystem:%s "
"%.*s "
"%.*s "
"%s "
"",
LIT(build_context.ODIN_ROOT), object_files, LIT(output_filename),
link_settings,
subsystem_str,
LIT(build_context.link_flags),
LIT(build_context.extra_linker_flags),
lib_str
);
if (result) {
return result;
}
}
} else {
timings_start_section(timings, str_lit("ld-link"));
// NOTE(vassvik): get cwd, for used for local shared libs linking, since those have to be relative to the exe
char cwd[256];
#if !defined(GB_SYSTEM_WINDOWS)
getcwd(&cwd[0], 256);
#endif
//printf("%s\n", cwd);
// NOTE(vassvik): needs to add the root to the library search paths, so that the full filenames of the library
// files can be passed with -l:
gbString lib_str = gb_string_make(heap_allocator(), "-L/");
defer (gb_string_free(lib_str));
StringSet libs = {};
string_set_init(&libs, 64);
defer (string_set_destroy(&libs));
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
for (String lib : e->LibraryName.paths) {
lib = string_trim_whitespace(lib);
if (lib.len == 0) {
continue;
}
if (string_set_update(&libs, lib)) {
continue;
}
// NOTE(zangent): Sometimes, you have to use -framework on MacOS.
// This allows you to specify '-f' in a #foreign_system_library,
// without having to implement any new syntax specifically for MacOS.
if (build_context.metrics.os == TargetOs_darwin) {
if (string_ends_with(lib, str_lit(".framework"))) {
// framework thingie
String lib_name = lib;
lib_name = remove_extension_from_path(lib_name);
lib_str = gb_string_append_fmt(lib_str, " -framework %.*s ", LIT(lib_name));
} else if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o")) || string_ends_with(lib, str_lit(".dylib"))) {
// For:
// object
// dynamic lib
// static libs, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " %.*s ", LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
} else {
// NOTE(vassvik): static libraries (.a files) in linux can be linked to directly using the full path,
// since those are statically linked to at link time. shared libraries (.so) has to be
// available at runtime wherever the executable is run, so we make require those to be
// local to the executable (unless the system collection is used, in which case we search
// the system library paths for the library file).
if (string_ends_with(lib, str_lit(".a")) || string_ends_with(lib, str_lit(".o"))) {
// static libs and object files, absolute full path relative to the file in which the lib was imported from
lib_str = gb_string_append_fmt(lib_str, " -l:\"%.*s\" ", LIT(lib));
} else if (string_ends_with(lib, str_lit(".so"))) {
// dynamic lib, relative path to executable
// NOTE(vassvik): it is the user's responsibility to make sure the shared library files are visible
// at runtime to the executable
lib_str = gb_string_append_fmt(lib_str, " -l:\"%s/%.*s\" ", cwd, LIT(lib));
} else {
// dynamic or static system lib, just link regularly searching system library paths
lib_str = gb_string_append_fmt(lib_str, " -l%.*s ", LIT(lib));
}
}
}
}
for (Entity *e : gen->foreign_libraries) {
GB_ASSERT(e->kind == Entity_LibraryName);
if (e->LibraryName.extra_linker_flags.len != 0) {
lib_str = gb_string_append_fmt(lib_str, " %.*s", LIT(e->LibraryName.extra_linker_flags));
}
}
gbString object_files = gb_string_make(heap_allocator(), "");
defer (gb_string_free(object_files));
for (String object_path : gen->output_object_paths) {
object_files = gb_string_append_fmt(object_files, "\"%.*s\" ", LIT(object_path));
}
gbString link_settings = gb_string_make_reserve(heap_allocator(), 32);
if (build_context.no_crt) {
link_settings = gb_string_append_fmt(link_settings, "-nostdlib ");
}
// NOTE(dweiler): We use clang as a frontend for the linker as there are
// other runtime and compiler support libraries that need to be linked in
// very specific orders such as libgcc_s, ld-linux-so, unwind, etc.
// These are not always typically inside /lib, /lib64, or /usr versions
// of that, e.g libgcc.a is in /usr/lib/gcc/{version}, and can vary on
// the distribution of Linux even. The gcc or clang specs is the only
// reliable way to query this information to call ld directly.
if (build_context.build_mode == BuildMode_DynamicLibrary) {
// NOTE(dweiler): Let the frontend know we're building a shared library
// so it doesn't generate symbols which cannot be relocated.
link_settings = gb_string_appendc(link_settings, "-shared ");
// NOTE(dweiler): _odin_entry_point must be called at initialization
// time of the shared object, similarly, _odin_exit_point must be called
// at deinitialization. We can pass both -init and -fini to the linker by
// using a comma separated list of arguments to -Wl.
//
// This previously used ld but ld cannot actually build a shared library
// correctly this way since all the other dependencies provided implicitly
// by the compiler frontend are still needed and most of the command
// line arguments prepared previously are incompatible with ld.
if (build_context.metrics.os == TargetOs_darwin) {
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'__odin_entry_point' ");
// NOTE(weshardee): __odin_exit_point should also be added, but -fini
// does not exist on MacOS
} else {
link_settings = gb_string_appendc(link_settings, "-Wl,-init,'_odin_entry_point' ");
link_settings = gb_string_appendc(link_settings, "-Wl,-fini,'_odin_exit_point' ");
}
} else if (build_context.metrics.os != TargetOs_openbsd) {
// OpenBSD defaults to PIE executable. do not pass -no-pie for it.
link_settings = gb_string_appendc(link_settings, "-no-pie ");
}
gbString platform_lib_str = gb_string_make(heap_allocator(), "");
defer (gb_string_free(platform_lib_str));
if (build_context.metrics.os == TargetOs_darwin) {
platform_lib_str = gb_string_appendc(platform_lib_str, "-lSystem -lm -Wl,-syslibroot /Library/Developer/CommandLineTools/SDKs/MacOSX.sdk -L/usr/local/lib");
} else {
platform_lib_str = gb_string_appendc(platform_lib_str, "-lc -lm");
}
if (build_context.metrics.os == TargetOs_darwin) {
// This sets a requirement of Mountain Lion and up, but the compiler doesn't work without this limit.
if (build_context.minimum_os_version_string.len) {
link_settings = gb_string_append_fmt(link_settings, " -mmacosx-version-min=%.*s ", LIT(build_context.minimum_os_version_string));
} else if (build_context.metrics.arch == TargetArch_arm64) {
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=12.0.0 ");
} else {
link_settings = gb_string_appendc(link_settings, " -mmacosx-version-min=10.12.0 ");
}
// This points the linker to where the entry point is
link_settings = gb_string_appendc(link_settings, " -e _main ");
}
gbString link_command_line = gb_string_make(heap_allocator(), "clang -Wno-unused-command-line-argument ");
defer (gb_string_free(link_command_line));
link_command_line = gb_string_appendc(link_command_line, object_files);
link_command_line = gb_string_append_fmt(link_command_line, " -o \"%.*s\" ", LIT(output_filename));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", platform_lib_str);
link_command_line = gb_string_append_fmt(link_command_line, " %s ", lib_str);
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.link_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %.*s ", LIT(build_context.extra_linker_flags));
link_command_line = gb_string_append_fmt(link_command_line, " %s ", link_settings);
result = system_exec_command_line_app("ld-link", link_command_line);
if (result) {
return result;
}
if (is_osx && build_context.ODIN_DEBUG) {
// NOTE: macOS links DWARF symbols dynamically. Dsymutil will map the stubs in the exe
// to the symbols in the object file
result = system_exec_command_line_app("dsymutil", "dsymutil %.*s", LIT(output_filename));
if (result) {
return result;
}
}
}
}
return result;
}
gb_internal Array<String> setup_args(int argc, char const **argv) { gb_internal Array<String> setup_args(int argc, char const **argv) {
gbAllocator a = heap_allocator(); gbAllocator a = heap_allocator();
@@ -653,8 +248,16 @@ enum BuildFlagKind {
BuildFlag_UseSeparateModules, BuildFlag_UseSeparateModules,
BuildFlag_NoThreadedChecker, BuildFlag_NoThreadedChecker,
BuildFlag_ShowDebugMessages, BuildFlag_ShowDebugMessages,
BuildFlag_Vet, BuildFlag_Vet,
BuildFlag_VetShadowing,
BuildFlag_VetUnused,
BuildFlag_VetUsingStmt,
BuildFlag_VetUsingParam,
BuildFlag_VetStyle,
BuildFlag_VetSemicolon,
BuildFlag_VetExtra, BuildFlag_VetExtra,
BuildFlag_IgnoreUnknownAttributes, BuildFlag_IgnoreUnknownAttributes,
BuildFlag_ExtraLinkerFlags, BuildFlag_ExtraLinkerFlags,
BuildFlag_ExtraAssemblerFlags, BuildFlag_ExtraAssemblerFlags,
@@ -671,7 +274,6 @@ enum BuildFlagKind {
BuildFlag_DisallowDo, BuildFlag_DisallowDo,
BuildFlag_DefaultToNilAllocator, BuildFlag_DefaultToNilAllocator,
BuildFlag_StrictStyle, BuildFlag_StrictStyle,
BuildFlag_StrictStyleInitOnly,
BuildFlag_ForeignErrorProcedures, BuildFlag_ForeignErrorProcedures,
BuildFlag_NoRTTI, BuildFlag_NoRTTI,
BuildFlag_DynamicMapCalls, BuildFlag_DynamicMapCalls,
@@ -695,6 +297,8 @@ enum BuildFlagKind {
BuildFlag_InternalIgnoreLazy, BuildFlag_InternalIgnoreLazy,
BuildFlag_InternalIgnoreLLVMBuild, BuildFlag_InternalIgnoreLLVMBuild,
BuildFlag_Tilde,
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
BuildFlag_IgnoreVsSearch, BuildFlag_IgnoreVsSearch,
BuildFlag_ResourceFile, BuildFlag_ResourceFile,
@@ -830,8 +434,16 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_UseSeparateModules, str_lit("use-separate-modules"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoThreadedChecker, str_lit("no-threaded-checker"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_ShowDebugMessages, str_lit("show-debug-messages"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_Vet, str_lit("vet"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUnused, str_lit("vet-unused"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetShadowing, str_lit("vet-shadowing"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingStmt, str_lit("vet-using-stmt"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetUsingParam, str_lit("vet-using-param"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetStyle, str_lit("vet-style"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetSemicolon, str_lit("vet-semicolon"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_VetExtra, str_lit("vet-extra"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_IgnoreUnknownAttributes, str_lit("ignore-unknown-attributes"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ExtraLinkerFlags, str_lit("extra-linker-flags"), BuildFlagParam_String, Command__does_build);
add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ExtraAssemblerFlags, str_lit("extra-assembler-flags"), BuildFlagParam_String, Command__does_build);
@@ -847,7 +459,6 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DisallowDo, str_lit("disallow-do"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_DefaultToNilAllocator, str_lit("default-to-nil-allocator"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_StrictStyle, str_lit("strict-style"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_StrictStyleInitOnly, str_lit("strict-style-init-only"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_ForeignErrorProcedures, str_lit("foreign-error-procedures"), BuildFlagParam_None, Command__does_check);
add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("no-rtti"), BuildFlagParam_None, Command__does_check); add_flag(&build_flags, BuildFlag_NoRTTI, str_lit("no-rtti"), BuildFlagParam_None, Command__does_check);
@@ -869,6 +480,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLazy, str_lit("internal-ignore-lazy"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_InternalIgnoreLLVMBuild, str_lit("internal-ignore-llvm-build"),BuildFlagParam_None, Command_all);
#if ALLOW_TILDE
add_flag(&build_flags, BuildFlag_Tilde, str_lit("tilde"), BuildFlagParam_None, Command__does_build);
#endif
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build); add_flag(&build_flags, BuildFlag_ResourceFile, str_lit("resource"), BuildFlagParam_String, Command__does_build);
@@ -1362,13 +977,25 @@ gb_internal bool parse_build_flags(Array<String> args) {
build_context.show_debug_messages = true; build_context.show_debug_messages = true;
break; break;
case BuildFlag_Vet: case BuildFlag_Vet:
build_context.vet = true; if (build_context.vet_flags & VetFlag_Extra) {
build_context.vet_flags |= VetFlag_All;
} else {
build_context.vet_flags &= ~VetFlag_Extra;
build_context.vet_flags |= VetFlag_All;
}
break; break;
case BuildFlag_VetExtra: {
build_context.vet = true; case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break;
build_context.vet_extra = true; case BuildFlag_VetShadowing: build_context.vet_flags |= VetFlag_Shadowing; break;
case BuildFlag_VetUsingStmt: build_context.vet_flags |= VetFlag_UsingStmt; break;
case BuildFlag_VetUsingParam: build_context.vet_flags |= VetFlag_UsingParam; break;
case BuildFlag_VetStyle: build_context.vet_flags |= VetFlag_Style; break;
case BuildFlag_VetSemicolon: build_context.vet_flags |= VetFlag_Semicolon; break;
case BuildFlag_VetExtra:
build_context.vet_flags = VetFlag_All | VetFlag_Extra;
break; break;
}
case BuildFlag_IgnoreUnknownAttributes: case BuildFlag_IgnoreUnknownAttributes:
build_context.ignore_unknown_attributes = true; build_context.ignore_unknown_attributes = true;
break; break;
@@ -1456,20 +1083,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_ForeignErrorProcedures: case BuildFlag_ForeignErrorProcedures:
build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true; build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
break; break;
case BuildFlag_StrictStyle: { case BuildFlag_StrictStyle:
if (build_context.strict_style_init_only) {
gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n");
}
build_context.strict_style = true; build_context.strict_style = true;
break; break;
}
case BuildFlag_StrictStyleInitOnly: {
if (build_context.strict_style) {
gb_printf_err("-strict-style and -strict-style-init-only cannot be used together\n");
}
build_context.strict_style_init_only = true;
break;
}
case BuildFlag_Short: case BuildFlag_Short:
build_context.cmd_doc_flags |= CmdDocFlag_Short; build_context.cmd_doc_flags |= CmdDocFlag_Short;
break; break;
@@ -1536,6 +1152,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
case BuildFlag_InternalIgnoreLLVMBuild: case BuildFlag_InternalIgnoreLLVMBuild:
build_context.ignore_llvm_build = true; build_context.ignore_llvm_build = true;
break; break;
case BuildFlag_Tilde:
build_context.tilde_backend = true;
break;
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
case BuildFlag_IgnoreVsSearch: { case BuildFlag_IgnoreVsSearch: {
GB_ASSERT(value.kind == ExactValue_Invalid); GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1572,7 +1192,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
if (path_is_directory(path)) { if (path_is_directory(path)) {
gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path)); gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
bad_flags = true; bad_flags = true;
break; break;
} }
// #if defined(GB_SYSTEM_WINDOWS) // #if defined(GB_SYSTEM_WINDOWS)
// String ext = path_extension(path); // String ext = path_extension(path);
@@ -2005,6 +1625,10 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds"); print_usage_line(2, "Shows an advanced overview of the timings of different stages within the compiler in milliseconds");
print_usage_line(0, ""); print_usage_line(0, "");
print_usage_line(1, "-show-system-calls");
print_usage_line(2, "Prints the whole command and arguments for calls to external tools like linker and assembler");
print_usage_line(0, "");
print_usage_line(1, "-export-timings:<format>"); print_usage_line(1, "-export-timings:<format>");
print_usage_line(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`"); print_usage_line(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`");
print_usage_line(2, "Available options:"); print_usage_line(2, "Available options:");
@@ -2114,29 +1738,55 @@ gb_internal void print_show_help(String const arg0, String const &command) {
} }
if (check) { if (check) {
#if defined(GB_SYSTEM_WINDOWS)
print_usage_line(1, "-no-threaded-checker"); print_usage_line(1, "-no-threaded-checker");
print_usage_line(2, "Disabled multithreading in the semantic checker stage"); print_usage_line(2, "Disabled multithreading in the semantic checker stage");
print_usage_line(0, ""); print_usage_line(0, "");
#else }
print_usage_line(1, "-threaded-checker");
print_usage_line(1, "[EXPERIMENTAL]");
print_usage_line(2, "Multithread the semantic checker stage");
print_usage_line(0, "");
#endif
if (check) {
print_usage_line(1, "-vet"); print_usage_line(1, "-vet");
print_usage_line(2, "Do extra checks on the code"); print_usage_line(2, "Do extra checks on the code");
print_usage_line(2, "Extra checks include:"); print_usage_line(2, "Extra checks include:");
print_usage_line(3, "Variable shadowing within procedures"); print_usage_line(2, "-vet-unused");
print_usage_line(3, "Unused declarations"); print_usage_line(2, "-vet-shadowing");
print_usage_line(2, "-vet-using-stmt");
print_usage_line(0, "");
print_usage_line(1, "-vet-unused");
print_usage_line(2, "Checks for unused declarations");
print_usage_line(0, "");
print_usage_line(1, "-vet-shadowing");
print_usage_line(2, "Checks for variable shadowing within procedures");
print_usage_line(0, "");
print_usage_line(1, "-vet-using-stmt");
print_usage_line(2, "Checks for the use of 'using' as a statement");
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring");
print_usage_line(0, "");
print_usage_line(1, "-vet-using-param");
print_usage_line(2, "Checks for the use of 'using' on procedure parameters");
print_usage_line(2, "'using' is considered bad practice outside of immediate refactoring");
print_usage_line(0, "");
print_usage_line(1, "-vet-style");
print_usage_line(2, "Errs on missing trailing commas followed by a newline");
print_usage_line(2, "Errs on deprecated syntax");
print_usage_line(2, "Does not err on unneeded tokens (unlike -strict-style)");
print_usage_line(0, "");
print_usage_line(1, "-vet-semicolon");
print_usage_line(2, "Errs on unneeded semicolons");
print_usage_line(0, ""); print_usage_line(0, "");
print_usage_line(1, "-vet-extra"); print_usage_line(1, "-vet-extra");
print_usage_line(2, "Do even more checks than standard vet on the code"); print_usage_line(2, "Do even more checks than standard vet on the code");
print_usage_line(2, "To treat the extra warnings as errors, use -warnings-as-errors"); print_usage_line(2, "To treat the extra warnings as errors, use -warnings-as-errors");
print_usage_line(0, ""); print_usage_line(0, "");
}
if (check) {
print_usage_line(1, "-ignore-unknown-attributes"); print_usage_line(1, "-ignore-unknown-attributes");
print_usage_line(2, "Ignores unknown attributes"); print_usage_line(2, "Ignores unknown attributes");
print_usage_line(2, "This can be used with metaprogramming tools"); print_usage_line(2, "This can be used with metaprogramming tools");
@@ -2206,10 +1856,8 @@ gb_internal void print_show_help(String const arg0, String const &command) {
print_usage_line(1, "-strict-style"); print_usage_line(1, "-strict-style");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons"); print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons");
print_usage_line(0, ""); print_usage_line(2, "Errs on missing trailing commas followed by a newline");
print_usage_line(2, "Errs on deprecated syntax");
print_usage_line(1, "-strict-style-init-only");
print_usage_line(2, "Errs on unneeded tokens, such as unneeded semicolons, only on the initial project");
print_usage_line(0, ""); print_usage_line(0, "");
print_usage_line(1, "-ignore-warnings"); print_usage_line(1, "-ignore-warnings");
@@ -2347,6 +1995,8 @@ gb_internal void print_show_unused(Checker *c) {
} }
gb_internal bool check_env(void) { gb_internal bool check_env(void) {
TIME_SECTION("init check env");
gbAllocator a = heap_allocator(); gbAllocator a = heap_allocator();
char const *odin_root = gb_get_env("ODIN_ROOT", a); char const *odin_root = gb_get_env("ODIN_ROOT", a);
defer (gb_free(a, cast(void *)odin_root)); defer (gb_free(a, cast(void *)odin_root));
@@ -2548,6 +2198,7 @@ gb_internal int strip_semicolons(Parser *parser) {
} }
gb_internal void init_terminal(void) { gb_internal void init_terminal(void) {
TIME_SECTION("init terminal");
build_context.has_ansi_terminal_colours = false; build_context.has_ansi_terminal_colours = false;
gbAllocator a = heap_allocator(); gbAllocator a = heap_allocator();
@@ -2614,11 +2265,13 @@ int main(int arg_count, char const **arg_ptr) {
return 1; return 1;
} }
TIME_SECTION("init default library collections");
array_init(&library_collections, heap_allocator()); array_init(&library_collections, heap_allocator());
// NOTE(bill): 'core' cannot be (re)defined by the user // NOTE(bill): 'core' cannot be (re)defined by the user
add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core"))); add_library_collection(str_lit("core"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("core")));
add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor"))); add_library_collection(str_lit("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
TIME_SECTION("init args");
map_init(&build_context.defined_values); map_init(&build_context.defined_values);
build_context.extra_packages.allocator = heap_allocator(); build_context.extra_packages.allocator = heap_allocator();
string_set_init(&build_context.test_names); string_set_init(&build_context.test_names);
@@ -2814,12 +2467,14 @@ int main(int arg_count, char const **arg_ptr) {
for_array(i, build_context.build_paths) { for_array(i, build_context.build_paths) {
String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]); String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path)); debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
} }
} }
TIME_SECTION("init thread pool");
init_global_thread_pool(); init_global_thread_pool();
defer (thread_pool_destroy(&global_thread_pool)); defer (thread_pool_destroy(&global_thread_pool));
TIME_SECTION("init universal");
init_universal(); init_universal();
// TODO(bill): prevent compiling without a linker // TODO(bill): prevent compiling without a linker
@@ -2882,16 +2537,18 @@ int main(int arg_count, char const **arg_ptr) {
return 0; return 0;
} }
MAIN_TIME_SECTION("LLVM API Code Gen"); #if ALLOW_TILDE
lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator); if (build_context.tilde_backend) {
if (!lb_init_generator(gen, checker)) { LinkerData linker_data = {};
return 1; MAIN_TIME_SECTION("Tilde Code Gen");
} if (!cg_generate_code(checker, &linker_data)) {
if (lb_generate_code(gen)) { return 1;
}
switch (build_context.build_mode) { switch (build_context.build_mode) {
case BuildMode_Executable: case BuildMode_Executable:
case BuildMode_DynamicLibrary: case BuildMode_DynamicLibrary:
i32 result = linker_stage(gen); i32 result = linker_stage(&linker_data);
if (result) { if (result) {
if (build_context.show_timings) { if (build_context.show_timings) {
show_timings(checker, &global_timings); show_timings(checker, &global_timings);
@@ -2900,9 +2557,31 @@ int main(int arg_count, char const **arg_ptr) {
} }
break; break;
} }
} } else
#endif
{
MAIN_TIME_SECTION("LLVM API Code Gen");
lbGenerator *gen = gb_alloc_item(permanent_allocator(), lbGenerator);
if (!lb_init_generator(gen, checker)) {
return 1;
}
if (lb_generate_code(gen)) {
switch (build_context.build_mode) {
case BuildMode_Executable:
case BuildMode_DynamicLibrary:
i32 result = linker_stage(gen);
if (result) {
if (build_context.show_timings) {
show_timings(checker, &global_timings);
}
return result;
}
break;
}
}
remove_temp_files(gen); remove_temp_files(gen);
}
if (build_context.show_timings) { if (build_context.show_timings) {
show_timings(checker, &global_timings); show_timings(checker, &global_timings);
+139 -34
View File
@@ -1,7 +1,21 @@
#include "parser_pos.cpp" #include "parser_pos.cpp"
// #undef at the bottom of this file gb_internal u64 ast_file_vet_flags(AstFile *f) {
#define ALLOW_NEWLINE (!build_context.strict_style) if (f->vet_flags_set) {
return f->vet_flags;
}
return build_context.vet_flags;
}
gb_internal bool ast_file_vet_style(AstFile *f) {
return (ast_file_vet_flags(f) & VetFlag_Style) != 0;
}
gb_internal bool file_allow_newline(AstFile *f) {
bool is_strict = build_context.strict_style || ast_file_vet_style(f);
return !is_strict;
}
gb_internal Token token_end_of_line(AstFile *f, Token tok) { gb_internal Token token_end_of_line(AstFile *f, Token tok) {
u8 const *start = f->tokenizer.start + tok.pos.offset; u8 const *start = f->tokenizer.start + tok.pos.offset;
@@ -1567,29 +1581,29 @@ gb_internal void assign_removal_flag_to_semicolon(AstFile *f) {
Token *prev_token = &f->tokens[f->prev_token_index]; Token *prev_token = &f->tokens[f->prev_token_index];
Token *curr_token = &f->tokens[f->curr_token_index]; Token *curr_token = &f->tokens[f->curr_token_index];
GB_ASSERT(prev_token->kind == Token_Semicolon); GB_ASSERT(prev_token->kind == Token_Semicolon);
if (prev_token->string == ";") { if (prev_token->string != ";") {
bool ok = false; return;
if (curr_token->pos.line > prev_token->pos.line) { }
bool ok = false;
if (curr_token->pos.line > prev_token->pos.line) {
ok = true;
} else if (curr_token->pos.line == prev_token->pos.line) {
switch (curr_token->kind) {
case Token_CloseBrace:
case Token_CloseParen:
case Token_EOF:
ok = true; ok = true;
} else if (curr_token->pos.line == prev_token->pos.line) { break;
switch (curr_token->kind) {
case Token_CloseBrace:
case Token_CloseParen:
case Token_EOF:
ok = true;
break;
}
}
if (ok) {
if (build_context.strict_style) {
syntax_error(*prev_token, "Found unneeded semicolon");
} else if (build_context.strict_style_init_only && f->pkg->kind == Package_Init) {
syntax_error(*prev_token, "Found unneeded semicolon");
}
prev_token->flags |= TokenFlag_Remove;
} }
} }
if (!ok) {
return;
}
if (build_context.strict_style || (ast_file_vet_flags(f) & VetFlag_Semicolon)) {
syntax_error(*prev_token, "Found unneeded semicolon");
}
prev_token->flags |= TokenFlag_Remove;
} }
gb_internal void expect_semicolon(AstFile *f) { gb_internal void expect_semicolon(AstFile *f) {
@@ -2221,7 +2235,11 @@ gb_internal Ast *parse_operand(AstFile *f, bool lhs) {
return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert); return parse_check_directive_for_statement(operand, name, StateFlag_no_type_assert);
} else if (name.string == "relative") { } else if (name.string == "relative") {
Ast *tag = ast_basic_directive(f, token, name); Ast *tag = ast_basic_directive(f, token, name);
tag = parse_call_expr(f, tag); if (f->curr_token.kind != Token_OpenParen) {
syntax_error(tag, "expected #relative(<integer type>) <type>");
} else {
tag = parse_call_expr(f, tag);
}
Ast *type = parse_type(f); Ast *type = parse_type(f);
return ast_relative_type(f, tag, type); return ast_relative_type(f, tag, type);
} else if (name.string == "force_inline" || } else if (name.string == "force_inline" ||
@@ -2748,7 +2766,7 @@ gb_internal Ast *parse_call_expr(AstFile *f, Ast *operand) {
isize prev_expr_level = f->expr_level; isize prev_expr_level = f->expr_level;
bool prev_allow_newline = f->allow_newline; bool prev_allow_newline = f->allow_newline;
f->expr_level = 0; f->expr_level = 0;
f->allow_newline = ALLOW_NEWLINE; f->allow_newline = file_allow_newline(f);
open_paren = expect_token(f, Token_OpenParen); open_paren = expect_token(f, Token_OpenParen);
@@ -3147,7 +3165,7 @@ gb_internal Ast *parse_expr(AstFile *f, bool lhs) {
gb_internal Array<Ast *> parse_expr_list(AstFile *f, bool lhs) { gb_internal Array<Ast *> parse_expr_list(AstFile *f, bool lhs) {
bool allow_newline = f->allow_newline; bool allow_newline = f->allow_newline;
f->allow_newline = ALLOW_NEWLINE; f->allow_newline = file_allow_newline(f);
auto list = array_make<Ast *>(heap_allocator()); auto list = array_make<Ast *>(heap_allocator());
for (;;) { for (;;) {
@@ -3472,7 +3490,7 @@ gb_internal Ast *parse_results(AstFile *f, bool *diverging) {
Ast *list = nullptr; Ast *list = nullptr;
expect_token(f, Token_OpenParen); expect_token(f, Token_OpenParen);
list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false); list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false);
if (ALLOW_NEWLINE) { if (file_allow_newline(f)) {
skip_possible_newline(f); skip_possible_newline(f);
} }
expect_token_after(f, Token_CloseParen, "parameter list"); expect_token_after(f, Token_CloseParen, "parameter list");
@@ -3532,7 +3550,7 @@ gb_internal Ast *parse_proc_type(AstFile *f, Token proc_token) {
expect_token(f, Token_OpenParen); expect_token(f, Token_OpenParen);
params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true); params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
if (ALLOW_NEWLINE) { if (file_allow_newline(f)) {
skip_possible_newline(f); skip_possible_newline(f);
} }
expect_token_after(f, Token_CloseParen, "parameter list"); expect_token_after(f, Token_CloseParen, "parameter list");
@@ -3754,7 +3772,7 @@ gb_internal bool allow_field_separator(AstFile *f) {
} }
if (token.kind == Token_Semicolon) { if (token.kind == Token_Semicolon) {
bool ok = false; bool ok = false;
if (ALLOW_NEWLINE && token_is_newline(token)) { if (file_allow_newline(f) && token_is_newline(token)) {
TokenKind next = peek_token(f).kind; TokenKind next = peek_token(f).kind;
switch (next) { switch (next) {
case Token_CloseBrace: case Token_CloseBrace:
@@ -3818,7 +3836,7 @@ gb_internal bool check_procedure_name_list(Array<Ast *> const &names) {
gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) { gb_internal Ast *parse_field_list(AstFile *f, isize *name_count_, u32 allowed_flags, TokenKind follow, bool allow_default_parameters, bool allow_typeid_token) {
bool prev_allow_newline = f->allow_newline; bool prev_allow_newline = f->allow_newline;
defer (f->allow_newline = prev_allow_newline); defer (f->allow_newline = prev_allow_newline);
f->allow_newline = ALLOW_NEWLINE; f->allow_newline = file_allow_newline(f);
Token start_token = f->curr_token; Token start_token = f->curr_token;
@@ -4169,6 +4187,8 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
syntax_error(f->curr_token, "Expected condition for when statement"); syntax_error(f->curr_token, "Expected condition for when statement");
} }
bool was_in_when_statement = f->in_when_statement;
f->in_when_statement = true;
if (allow_token(f, Token_do)) { if (allow_token(f, Token_do)) {
body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement"); body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement");
} else { } else {
@@ -4195,6 +4215,7 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
break; break;
} }
} }
f->in_when_statement = was_in_when_statement;
return ast_when_stmt(f, token, cond, body, else_stmt); return ast_when_stmt(f, token, cond, body, else_stmt);
} }
@@ -4456,6 +4477,10 @@ gb_internal Ast *parse_import_decl(AstFile *f, ImportDeclKind kind) {
array_add(&f->imports, s); array_add(&f->imports, s);
} }
if (f->in_when_statement) {
syntax_error(import_name, "Cannot use 'import' within a 'when' statement. Prefer using the file suffixes (e.g. foo_windows.odin) or '//+build' tags");
}
if (kind != ImportDecl_Standard) { if (kind != ImportDecl_Standard) {
syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly"); syntax_error(import_name, "'using import' is not allowed, please use the import name explicitly");
} }
@@ -4954,7 +4979,6 @@ gb_internal bool init_parser(Parser *p) {
gb_internal void destroy_parser(Parser *p) { gb_internal void destroy_parser(Parser *p) {
GB_ASSERT(p != nullptr); GB_ASSERT(p != nullptr);
// TODO(bill): Fix memory leak
for (AstPackage *pkg : p->packages) { for (AstPackage *pkg : p->packages) {
for (AstFile *file : pkg->files) { for (AstFile *file : pkg->files) {
destroy_ast_file(file); destroy_ast_file(file);
@@ -4998,7 +5022,6 @@ gb_internal WORKER_TASK_PROC(parser_worker_proc) {
gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) { gb_internal void parser_add_file_to_process(Parser *p, AstPackage *pkg, FileInfo fi, TokenPos pos) {
// TODO(bill): Use a better allocator
ImportedFile f = {pkg, fi, pos, p->file_to_process_count++}; ImportedFile f = {pkg, fi, pos, p->file_to_process_count++};
auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData); auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData);
wd->parser = p; wd->parser = p;
@@ -5528,6 +5551,88 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
return any_correct; return any_correct;
} }
gb_internal String vet_tag_get_token(String s, String *out) {
s = string_trim_whitespace(s);
isize n = 0;
while (n < s.len) {
Rune rune = 0;
isize width = utf8_decode(&s[n], s.len-n, &rune);
if (n == 0 && rune == '!') {
} else if (!rune_is_letter(rune) && !rune_is_digit(rune) && rune != '-') {
isize k = gb_max(gb_max(n, width), 1);
*out = substring(s, k, s.len);
return substring(s, 0, k);
}
n += width;
}
out->len = 0;
return s;
}
gb_internal u64 parse_vet_tag(Token token_for_pos, String s) {
String const prefix = str_lit("+vet");
GB_ASSERT(string_starts_with(s, prefix));
s = string_trim_whitespace(substring(s, prefix.len, s.len));
if (s.len == 0) {
return VetFlag_All;
}
u64 vet_flags = 0;
u64 vet_not_flags = 0;
while (s.len > 0) {
String p = string_trim_whitespace(vet_tag_get_token(s, &s));
if (p.len == 0) {
break;
}
bool is_notted = false;
if (p[0] == '!') {
is_notted = true;
p = substring(p, 1, p.len);
if (p.len == 0) {
syntax_error(token_for_pos, "Expected a vet flag name after '!'");
return build_context.vet_flags;
}
}
u64 flag = get_vet_flag_from_name(p);
if (flag != VetFlag_NONE) {
if (is_notted) {
vet_not_flags |= flag;
} else {
vet_flags |= flag;
}
} else {
ERROR_BLOCK();
syntax_error(token_for_pos, "Invalid vet flag name: %.*s", LIT(p));
error_line("\tExpected one of the following\n");
error_line("\tunused\n");
error_line("\tshadowing\n");
error_line("\tusing-stmt\n");
error_line("\tusing-param\n");
error_line("\textra\n");
return build_context.vet_flags;
}
}
if (vet_flags == 0 && vet_not_flags == 0) {
return build_context.vet_flags;
}
if (vet_flags == 0 && vet_not_flags != 0) {
return build_context.vet_flags &~ vet_not_flags;
}
if (vet_flags != 0 && vet_not_flags == 0) {
return vet_flags;
}
GB_ASSERT(vet_flags != 0 && vet_not_flags != 0);
return vet_flags &~ vet_not_flags;
}
gb_internal String dir_from_path(String path) { gb_internal String dir_from_path(String path) {
String base_dir = path; String base_dir = path;
for (isize i = path.len-1; i >= 0; i--) { for (isize i = path.len-1; i >= 0; i--) {
@@ -5679,6 +5784,9 @@ gb_internal bool parse_file(Parser *p, AstFile *f) {
if (!parse_build_tag(tok, lc)) { if (!parse_build_tag(tok, lc)) {
return false; return false;
} }
} else if (string_starts_with(lc, str_lit("+vet"))) {
f->vet_flags = parse_vet_tag(tok, lc);
f->vet_flags_set = true;
} else if (string_starts_with(lc, str_lit("+ignore"))) { } else if (string_starts_with(lc, str_lit("+ignore"))) {
return false; return false;
} else if (string_starts_with(lc, str_lit("+private"))) { } else if (string_starts_with(lc, str_lit("+private"))) {
@@ -5920,6 +6028,3 @@ gb_internal ParseFileError parse_packages(Parser *p, String init_filename) {
return ParseFile_None; return ParseFile_None;
} }
#undef ALLOW_NEWLINE
+3
View File
@@ -104,6 +104,8 @@ struct AstFile {
Token package_token; Token package_token;
String package_name; String package_name;
u64 vet_flags;
bool vet_flags_set;
// >= 0: In Expression // >= 0: In Expression
// < 0: In Control Clause // < 0: In Control Clause
@@ -114,6 +116,7 @@ struct AstFile {
bool allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases bool allow_in_expr; // NOTE(bill): in expression are only allowed in certain cases
bool in_foreign_block; bool in_foreign_block;
bool allow_type; bool allow_type;
bool in_when_statement;
isize total_file_decl_count; isize total_file_decl_count;
isize delayed_decl_count; isize delayed_decl_count;
+813
View File
@@ -0,0 +1,813 @@
#include "tilde.hpp"
gb_global Slice<TB_Arena> global_tb_arenas;
gb_internal TB_Arena *cg_arena(void) {
return &global_tb_arenas[current_thread_index()];
}
gb_internal void cg_global_arena_init(void) {
global_tb_arenas = slice_make<TB_Arena>(permanent_allocator(), global_thread_pool.threads.count);
for_array(i, global_tb_arenas) {
tb_arena_create(&global_tb_arenas[i], 2ull<<20);
}
}
// returns TB_TYPE_VOID if not trivially possible
gb_internal TB_DataType cg_data_type(Type *t) {
GB_ASSERT(t != nullptr);
t = core_type(t);
i64 sz = type_size_of(t);
switch (t->kind) {
case Type_Basic:
switch (t->Basic.kind) {
case Basic_bool:
case Basic_b8:
case Basic_b16:
case Basic_b32:
case Basic_b64:
case Basic_i8:
case Basic_u8:
case Basic_i16:
case Basic_u16:
case Basic_i32:
case Basic_u32:
case Basic_i64:
case Basic_u64:
case Basic_i128:
case Basic_u128:
case Basic_rune:
case Basic_int:
case Basic_uint:
case Basic_uintptr:
case Basic_typeid:
return TB_TYPE_INTN(cast(u16)gb_min(8*sz, 64));
case Basic_f16: return TB_TYPE_F16;
case Basic_f32: return TB_TYPE_F32;
case Basic_f64: return TB_TYPE_F64;
case Basic_rawptr: return TB_TYPE_PTR;
case Basic_cstring: return TB_TYPE_PTR;
// Endian Specific Types
case Basic_i16le:
case Basic_u16le:
case Basic_i32le:
case Basic_u32le:
case Basic_i64le:
case Basic_u64le:
case Basic_i128le:
case Basic_u128le:
case Basic_i16be:
case Basic_u16be:
case Basic_i32be:
case Basic_u32be:
case Basic_i64be:
case Basic_u64be:
case Basic_i128be:
case Basic_u128be:
return TB_TYPE_INTN(cast(u16)gb_min(8*sz, 64));
case Basic_f16le: return TB_TYPE_F16;
case Basic_f32le: return TB_TYPE_F32;
case Basic_f64le: return TB_TYPE_F64;
case Basic_f16be: return TB_TYPE_F16;
case Basic_f32be: return TB_TYPE_F32;
case Basic_f64be: return TB_TYPE_F64;
}
break;
case Type_Pointer:
case Type_MultiPointer:
case Type_Proc:
return TB_TYPE_PTR;
case Type_BitSet:
return cg_data_type(bit_set_to_int(t));
case Type_RelativePointer:
return cg_data_type(t->RelativePointer.base_integer);
}
// unknown
return {};
}
gb_internal cgValue cg_value(TB_Global *g, Type *type) {
return cg_value((TB_Symbol *)g, type);
}
gb_internal cgValue cg_value(TB_External *e, Type *type) {
return cg_value((TB_Symbol *)e, type);
}
gb_internal cgValue cg_value(TB_Function *f, Type *type) {
return cg_value((TB_Symbol *)f, type);
}
gb_internal cgValue cg_value(TB_Symbol *s, Type *type) {
cgValue v = {};
v.kind = cgValue_Symbol;
v.type = type;
v.symbol = s;
return v;
}
gb_internal cgValue cg_value(TB_Node *node, Type *type) {
cgValue v = {};
v.kind = cgValue_Value;
v.type = type;
v.node = node;
return v;
}
gb_internal cgValue cg_lvalue_addr(TB_Node *node, Type *type) {
GB_ASSERT(node->dt.type == TB_PTR);
cgValue v = {};
v.kind = cgValue_Addr;
v.type = type;
v.node = node;
return v;
}
gb_internal cgValue cg_lvalue_addr_to_value(cgValue v) {
if (v.kind == cgValue_Value) {
GB_ASSERT(is_type_pointer(v.type));
GB_ASSERT(v.node->dt.type == TB_PTR);
} else {
GB_ASSERT(v.kind == cgValue_Addr);
GB_ASSERT(v.node->dt.type == TB_PTR);
v.kind = cgValue_Value;
v.type = alloc_type_pointer(v.type);
}
return v;
}
gb_internal cgValue cg_value_multi(cgValueMulti *multi, Type *type) {
GB_ASSERT(type->kind == Type_Tuple);
GB_ASSERT(multi != nullptr);
GB_ASSERT(type->Tuple.variables.count > 1);
GB_ASSERT(multi->values.count == type->Tuple.variables.count);
cgValue v = {};
v.kind = cgValue_Multi;
v.type = type;
v.multi = multi;
return v;
}
gb_internal cgValue cg_value_multi(Slice<cgValue> const &values, Type *type) {
cgValueMulti *multi = gb_alloc_item(permanent_allocator(), cgValueMulti);
multi->values = values;
return cg_value_multi(multi, type);
}
gb_internal cgValue cg_value_multi2(cgValue const &x, cgValue const &y, Type *type) {
GB_ASSERT(type->kind == Type_Tuple);
GB_ASSERT(type->Tuple.variables.count == 2);
cgValueMulti *multi = gb_alloc_item(permanent_allocator(), cgValueMulti);
multi->values = slice_make<cgValue>(permanent_allocator(), 2);
multi->values[0] = x;
multi->values[1] = y;
return cg_value_multi(multi, type);
}
gb_internal cgAddr cg_addr(cgValue const &value) {
GB_ASSERT(value.kind != cgValue_Multi);
cgAddr addr = {};
addr.kind = cgAddr_Default;
addr.addr = value;
if (addr.addr.kind == cgValue_Addr) {
GB_ASSERT(addr.addr.node != nullptr);
addr.addr.kind = cgValue_Value;
addr.addr.type = alloc_type_pointer(addr.addr.type);
}
return addr;
}
gb_internal void cg_set_debug_pos_from_node(cgProcedure *p, Ast *node) {
if (node) {
TokenPos pos = ast_token(node).pos;
TB_FileID *file_id = map_get(&p->module->file_id_map, cast(uintptr)pos.file_id);
if (file_id) {
tb_inst_set_location(p->func, *file_id, pos.line);
}
}
}
gb_internal void cg_add_symbol(cgModule *m, Entity *e, TB_Symbol *symbol) {
if (e) {
rw_mutex_lock(&m->values_mutex);
map_set(&m->symbols, e, symbol);
rw_mutex_unlock(&m->values_mutex);
}
}
gb_internal void cg_add_entity(cgModule *m, Entity *e, cgValue const &val) {
if (e) {
rw_mutex_lock(&m->values_mutex);
GB_ASSERT(val.node != nullptr);
map_set(&m->values, e, val);
rw_mutex_unlock(&m->values_mutex);
}
}
gb_internal void cg_add_member(cgModule *m, String const &name, cgValue const &val) {
if (name.len > 0) {
rw_mutex_lock(&m->values_mutex);
string_map_set(&m->members, name, val);
rw_mutex_unlock(&m->values_mutex);
}
}
gb_internal void cg_add_procedure_value(cgModule *m, cgProcedure *p) {
rw_mutex_lock(&m->values_mutex);
if (p->entity != nullptr) {
map_set(&m->procedure_values, p->func, p->entity);
if (p->symbol != nullptr) {
map_set(&m->symbols, p->entity, p->symbol);
}
}
string_map_set(&m->procedures, p->name, p);
rw_mutex_unlock(&m->values_mutex);
}
gb_internal TB_Symbol *cg_find_symbol_from_entity(cgModule *m, Entity *e) {
GB_ASSERT(e != nullptr);
rw_mutex_lock(&m->values_mutex);
TB_Symbol **found = map_get(&m->symbols, e);
if (found) {
rw_mutex_unlock(&m->values_mutex);
return *found;
}
String link_name = cg_get_entity_name(m, e);
cgProcedure **proc_found = string_map_get(&m->procedures, link_name);
if (proc_found) {
TB_Symbol *symbol = (*proc_found)->symbol;
map_set(&m->symbols, e, symbol);
rw_mutex_unlock(&m->values_mutex);
return symbol;
}
rw_mutex_unlock(&m->values_mutex);
if (e->kind == Entity_Procedure) {
debugf("[Tilde] try to generate procedure %.*s as it was not in the minimum_dependency_set", LIT(e->token.string));
// IMPORTANT TODO(bill): This is an utter bodge, try and fix this shit
cgProcedure *p = cg_procedure_create(m, e);
if (p != nullptr) {
GB_ASSERT(p->symbol != nullptr);
cg_add_procedure_to_queue(p);
return p->symbol;
}
}
GB_PANIC("could not find entity's symbol %.*s", LIT(e->token.string));
return nullptr;
}
struct cgGlobalVariable {
cgValue var;
cgValue init;
DeclInfo *decl;
bool is_initialized;
};
// Returns already_has_entry_point
gb_internal bool cg_global_variables_create(cgModule *m, Array<cgGlobalVariable> *global_variables) {
isize global_variable_max_count = 0;
bool already_has_entry_point = false;
for (Entity *e : m->info->entities) {
String name = e->token.string;
if (e->kind == Entity_Variable) {
global_variable_max_count++;
} else if (e->kind == Entity_Procedure) {
if ((e->scope->flags&ScopeFlag_Init) && name == "main") {
GB_ASSERT(e == m->info->entry_point);
}
if (build_context.command_kind == Command_test &&
(e->Procedure.is_export || e->Procedure.link_name.len > 0)) {
String link_name = e->Procedure.link_name;
if (e->pkg->kind == Package_Runtime) {
if (link_name == "main" ||
link_name == "DllMain" ||
link_name == "WinMain" ||
link_name == "wWinMain" ||
link_name == "mainCRTStartup" ||
link_name == "_start") {
already_has_entry_point = true;
}
}
}
}
}
*global_variables = array_make<cgGlobalVariable>(permanent_allocator(), 0, global_variable_max_count);
auto *min_dep_set = &m->info->minimum_dependency_set;
for (DeclInfo *d : m->info->variable_init_order) {
Entity *e = d->entity;
if ((e->scope->flags & ScopeFlag_File) == 0) {
continue;
}
if (!ptr_set_exists(min_dep_set, e)) {
continue;
}
DeclInfo *decl = decl_info_of_entity(e);
if (decl == nullptr) {
continue;
}
GB_ASSERT(e->kind == Entity_Variable);
bool is_foreign = e->Variable.is_foreign;
bool is_export = e->Variable.is_export;
String name = cg_get_entity_name(m, e);
TB_Linkage linkage = TB_LINKAGE_PRIVATE;
if (is_foreign) {
linkage = TB_LINKAGE_PUBLIC;
// lb_add_foreign_library_path(m, e->Variable.foreign_library);
// lb_set_wasm_import_attributes(g.value, e, name);
} else if (is_export) {
linkage = TB_LINKAGE_PUBLIC;
}
// lb_set_linkage_from_entity_flags(m, g.value, e->flags);
TB_DebugType *debug_type = cg_debug_type(m, e->type);
TB_Global *global = tb_global_create(m->mod, name.len, cast(char const *)name.text, debug_type, linkage);
cgValue g = cg_value(global, alloc_type_pointer(e->type));
TB_ModuleSection *section = tb_module_get_data(m->mod);
if (e->Variable.thread_local_model != "") {
section = tb_module_get_tls(m->mod);
}
if (e->Variable.link_section.len > 0) {
// TODO(bill): custom module sections
// LLVMSetSection(g.value, alloc_cstring(permanent_allocator(), e->Variable.link_section));
}
cgGlobalVariable var = {};
var.var = g;
var.decl = decl;
if (decl->init_expr != nullptr) {
TypeAndValue tav = type_and_value_of_expr(decl->init_expr);
isize max_regions = cg_global_const_calculate_region_count(tav.value, e->type);
tb_global_set_storage(m->mod, section, global, type_size_of(e->type), type_align_of(e->type), max_regions);
if (tav.mode == Addressing_Constant &&
tav.value.kind != ExactValue_Invalid) {
cg_global_const_add_region(m, tav.value, e->type, global, 0);
var.is_initialized = true;
}
if (!var.is_initialized && is_type_untyped_nil(tav.type)) {
var.is_initialized = true;
}
} else {
var.is_initialized = true;
// TODO(bill): is this even needed;
i64 max_regions = cg_global_const_calculate_region_count_from_basic_type(e->type);
tb_global_set_storage(m->mod, section, global, type_size_of(e->type), type_align_of(e->type), max_regions);
}
array_add(global_variables, var);
cg_add_symbol(m, e, cast(TB_Symbol *)global);
cg_add_entity(m, e, g);
cg_add_member(m, name, g);
}
cg_setup_type_info_data(m);
return already_has_entry_point;
}
gb_internal void cg_global_variables_initialize(cgProcedure *p, Array<cgGlobalVariable> *global_variables) {
for (cgGlobalVariable &var : *global_variables) {
if (var.is_initialized) {
continue;
}
cgValue src = cg_build_expr(p, var.decl->init_expr);
cgValue dst = cg_flatten_value(p, var.var);
cg_emit_store(p, dst, src);
}
}
gb_internal cgModule *cg_module_create(Checker *c) {
cgModule *m = gb_alloc_item(permanent_allocator(), cgModule);
m->checker = c;
m->info = &c->info;
TB_FeatureSet feature_set = {};
bool is_jit = false;
m->mod = tb_module_create(TB_ARCH_X86_64, TB_SYSTEM_WINDOWS, &feature_set, is_jit);
tb_module_set_tls_index(m->mod, 10, "_tls_index");
map_init(&m->values);
map_init(&m->symbols);
map_init(&m->file_id_map);
map_init(&m->debug_type_map);
map_init(&m->proc_debug_type_map);
map_init(&m->proc_proto_map);
map_init(&m->anonymous_proc_lits_map);
map_init(&m->equal_procs);
map_init(&m->hasher_procs);
map_init(&m->map_get_procs);
map_init(&m->map_set_procs);
array_init(&m->single_threaded_procedure_queue, heap_allocator());
for_array(id, global_files) {
if (AstFile *f = global_files[id]) {
char const *path = alloc_cstring(permanent_allocator(), f->fullpath);
map_set(&m->file_id_map, cast(uintptr)id, tb_file_create(m->mod, path));
}
}
return m;
}
gb_internal void cg_module_destroy(cgModule *m) {
map_destroy(&m->values);
map_destroy(&m->symbols);
map_destroy(&m->file_id_map);
map_destroy(&m->debug_type_map);
map_destroy(&m->proc_debug_type_map);
map_destroy(&m->proc_proto_map);
map_destroy(&m->anonymous_proc_lits_map);
map_destroy(&m->equal_procs);
map_destroy(&m->hasher_procs);
map_destroy(&m->map_get_procs);
map_destroy(&m->map_set_procs);
array_free(&m->single_threaded_procedure_queue);
tb_module_destroy(m->mod);
}
gb_internal String cg_set_nested_type_name_ir_mangled_name(Entity *e, cgProcedure *p) {
// NOTE(bill, 2020-03-08): A polymorphic procedure may take a nested type declaration
// and as a result, the declaration does not have time to determine what it should be
GB_ASSERT(e != nullptr && e->kind == Entity_TypeName);
if (e->TypeName.ir_mangled_name.len != 0) {
return e->TypeName.ir_mangled_name;
}
GB_ASSERT((e->scope->flags & ScopeFlag_File) == 0);
if (p == nullptr) {
Entity *proc = nullptr;
if (e->parent_proc_decl != nullptr) {
proc = e->parent_proc_decl->entity;
} else {
Scope *scope = e->scope;
while (scope != nullptr && (scope->flags & ScopeFlag_Proc) == 0) {
scope = scope->parent;
}
GB_ASSERT(scope != nullptr);
GB_ASSERT(scope->flags & ScopeFlag_Proc);
proc = scope->procedure_entity;
}
GB_ASSERT(proc->kind == Entity_Procedure);
if (proc->cg_procedure != nullptr) {
p = proc->cg_procedure;
}
}
// NOTE(bill): Generate a new name
// parent_proc.name-guid
String ts_name = e->token.string;
if (p != nullptr) {
isize name_len = p->name.len + 1 + ts_name.len + 1 + 10 + 1;
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
u32 guid = 1+p->module->nested_type_name_guid.fetch_add(1);
name_len = gb_snprintf(name_text, name_len, "%.*s" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(p->name), LIT(ts_name), guid);
String name = make_string(cast(u8 *)name_text, name_len-1);
e->TypeName.ir_mangled_name = name;
return name;
} else {
// NOTE(bill): a nested type be required before its parameter procedure exists. Just give it a temp name for now
isize name_len = 9 + 1 + ts_name.len + 1 + 10 + 1;
char *name_text = gb_alloc_array(permanent_allocator(), char, name_len);
static std::atomic<u32> guid;
name_len = gb_snprintf(name_text, name_len, "_internal" ABI_PKG_NAME_SEPARATOR "%.*s-%u", LIT(ts_name), 1+guid.fetch_add(1));
String name = make_string(cast(u8 *)name_text, name_len-1);
e->TypeName.ir_mangled_name = name;
return name;
}
}
gb_internal String cg_mangle_name(cgModule *m, Entity *e) {
String name = e->token.string;
AstPackage *pkg = e->pkg;
GB_ASSERT_MSG(pkg != nullptr, "Missing package for '%.*s'", LIT(name));
String pkgn = pkg->name;
GB_ASSERT(!rune_is_digit(pkgn[0]));
if (pkgn == "llvm") {
GB_PANIC("llvm. entities are not allowed with the tilde backend");
}
isize max_len = pkgn.len + 1 + name.len + 1;
bool require_suffix_id = is_type_polymorphic(e->type, true);
if ((e->scope->flags & (ScopeFlag_File | ScopeFlag_Pkg)) == 0) {
require_suffix_id = true;
} else if (is_blank_ident(e->token)) {
require_suffix_id = true;
}if (e->flags & EntityFlag_NotExported) {
require_suffix_id = true;
}
if (require_suffix_id) {
max_len += 21;
}
char *new_name = gb_alloc_array(permanent_allocator(), char, max_len);
isize new_name_len = gb_snprintf(
new_name, max_len,
"%.*s" ABI_PKG_NAME_SEPARATOR "%.*s", LIT(pkgn), LIT(name)
);
if (require_suffix_id) {
char *str = new_name + new_name_len-1;
isize len = max_len-new_name_len;
isize extra = gb_snprintf(str, len, "-%llu", cast(unsigned long long)e->id);
new_name_len += extra-1;
}
String mangled_name = make_string((u8 const *)new_name, new_name_len-1);
return mangled_name;
}
gb_internal String cg_get_entity_name(cgModule *m, Entity *e) {
if (e != nullptr && e->kind == Entity_TypeName && e->TypeName.ir_mangled_name.len != 0) {
return e->TypeName.ir_mangled_name;
}
GB_ASSERT(e != nullptr);
if (e->pkg == nullptr) {
return e->token.string;
}
if (e->kind == Entity_TypeName && (e->scope->flags & ScopeFlag_File) == 0) {
return cg_set_nested_type_name_ir_mangled_name(e, nullptr);
}
String name = {};
bool no_name_mangle = false;
if (e->kind == Entity_Variable) {
bool is_foreign = e->Variable.is_foreign;
bool is_export = e->Variable.is_export;
no_name_mangle = e->Variable.link_name.len > 0 || is_foreign || is_export;
if (e->Variable.link_name.len > 0) {
return e->Variable.link_name;
}
} else if (e->kind == Entity_Procedure && e->Procedure.link_name.len > 0) {
return e->Procedure.link_name;
} else if (e->kind == Entity_Procedure && e->Procedure.is_export) {
no_name_mangle = true;
}
if (!no_name_mangle) {
name = cg_mangle_name(m, e);
}
if (name.len == 0) {
name = e->token.string;
}
if (e->kind == Entity_TypeName) {
e->TypeName.ir_mangled_name = name;
} else if (e->kind == Entity_Procedure) {
e->Procedure.link_name = name;
}
return name;
}
#include "tilde_const.cpp"
#include "tilde_debug.cpp"
#include "tilde_expr.cpp"
#include "tilde_builtin.cpp"
#include "tilde_type_info.cpp"
#include "tilde_proc.cpp"
#include "tilde_stmt.cpp"
gb_internal String cg_filepath_obj_for_module(cgModule *m) {
String path = concatenate3_strings(permanent_allocator(),
build_context.build_paths[BuildPath_Output].basename,
STR_LIT("/"),
build_context.build_paths[BuildPath_Output].name
);
// if (m->file) {
// char buf[32] = {};
// isize n = gb_snprintf(buf, gb_size_of(buf), "-%u", m->file->id);
// String suffix = make_string((u8 *)buf, n-1);
// path = concatenate_strings(permanent_allocator(), path, suffix);
// } else if (m->pkg) {
// path = concatenate3_strings(permanent_allocator(), path, STR_LIT("-"), m->pkg->name);
// }
String ext = {};
if (build_context.build_mode == BuildMode_Assembly) {
ext = STR_LIT(".S");
} else {
if (is_arch_wasm()) {
ext = STR_LIT(".wasm.o");
} else {
switch (build_context.metrics.os) {
case TargetOs_windows:
ext = STR_LIT(".obj");
break;
default:
case TargetOs_darwin:
case TargetOs_linux:
case TargetOs_essence:
ext = STR_LIT(".o");
break;
case TargetOs_freestanding:
switch (build_context.metrics.abi) {
default:
case TargetABI_Default:
case TargetABI_SysV:
ext = STR_LIT(".o");
break;
case TargetABI_Win64:
ext = STR_LIT(".obj");
break;
}
break;
}
}
}
return concatenate_strings(permanent_allocator(), path, ext);
}
gb_internal WORKER_TASK_PROC(cg_procedure_generate_worker_proc) {
cgProcedure *p = cast(cgProcedure *)data;
cg_procedure_generate(p);
return 0;
}
gb_internal void cg_add_procedure_to_queue(cgProcedure *p) {
if (p == nullptr) {
return;
}
cgModule *m = p->module;
if (m->do_threading) {
thread_pool_add_task(cg_procedure_generate_worker_proc, p);
} else {
array_add(&m->single_threaded_procedure_queue, p);
}
}
gb_internal bool cg_generate_code(Checker *c, LinkerData *linker_data) {
TIME_SECTION("Tilde Module Initializtion");
CheckerInfo *info = &c->info;
linker_data_init(linker_data, info, c->parser->init_fullpath);
cg_global_arena_init();
cgModule *m = cg_module_create(c);
defer (cg_module_destroy(m));
m->do_threading = false;
TIME_SECTION("Tilde Global Variables");
Array<cgGlobalVariable> global_variables = {};
bool already_has_entry_point = cg_global_variables_create(m, &global_variables);
gb_unused(already_has_entry_point);
if (true) {
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
cgProcedure *p = cg_procedure_create_dummy(m, str_lit(CG_STARTUP_RUNTIME_PROC_NAME), proc_type);
p->is_startup = true;
cg_startup_runtime_proc = p;
}
if (true) {
Type *proc_type = alloc_type_proc(nullptr, nullptr, 0, nullptr, 0, false, ProcCC_Odin);
cgProcedure *p = cg_procedure_create_dummy(m, str_lit(CG_CLEANUP_RUNTIME_PROC_NAME), proc_type);
p->is_startup = true;
cg_cleanup_runtime_proc = p;
}
auto *min_dep_set = &info->minimum_dependency_set;
Array<cgProcedure *> procedures_to_generate = {};
array_init(&procedures_to_generate, heap_allocator());
defer (array_free(&procedures_to_generate));
for (Entity *e : info->entities) {
String name = e->token.string;
Scope *scope = e->scope;
if ((scope->flags & ScopeFlag_File) == 0) {
continue;
}
Scope *package_scope = scope->parent;
GB_ASSERT(package_scope->flags & ScopeFlag_Pkg);
if (e->kind != Entity_Procedure) {
continue;
}
if (!ptr_set_exists(min_dep_set, e)) {
// NOTE(bill): Nothing depends upon it so doesn't need to be built
continue;
}
if (cgProcedure *p = cg_procedure_create(m, e)) {
array_add(&procedures_to_generate, p);
}
}
for (cgProcedure *p : procedures_to_generate) {
cg_add_procedure_to_queue(p);
}
if (!m->do_threading) {
for (isize i = 0; i < m->single_threaded_procedure_queue.count; i++) {
cgProcedure *p = m->single_threaded_procedure_queue[i];
cg_procedure_generate(p);
}
}
thread_pool_wait();
{
cgProcedure *p = cg_startup_runtime_proc;
cg_procedure_begin(p);
cg_global_variables_initialize(p, &global_variables);
tb_inst_ret(p->func, 0, nullptr);
cg_procedure_end(p);
}
{
cgProcedure *p = cg_cleanup_runtime_proc;
cg_procedure_begin(p);
tb_inst_ret(p->func, 0, nullptr);
cg_procedure_end(p);
}
TB_DebugFormat debug_format = TB_DEBUGFMT_NONE;
if (build_context.ODIN_DEBUG) {
switch (build_context.metrics.os) {
case TargetOs_windows:
debug_format = TB_DEBUGFMT_CODEVIEW;
break;
case TargetOs_darwin:
case TargetOs_linux:
case TargetOs_essence:
case TargetOs_freebsd:
case TargetOs_openbsd:
debug_format = TB_DEBUGFMT_DWARF;
break;
}
}
TB_ExportBuffer export_buffer = tb_module_object_export(m->mod, debug_format);
defer (tb_export_buffer_free(export_buffer));
String filepath_obj = cg_filepath_obj_for_module(m);
array_add(&linker_data->output_object_paths, filepath_obj);
GB_ASSERT(tb_export_buffer_to_file(export_buffer, cast(char const *)filepath_obj.text));
return true;
}
#undef ABI_PKG_NAME_SEPARATOR
+373
View File
@@ -0,0 +1,373 @@
#if defined(GB_SYSTEM_WINDOWS)
#pragma warning(push)
#pragma warning(disable: 4200)
#pragma warning(disable: 4201)
#define restrict gb_restrict
#endif
#include "tilde/tb.h"
#include "tilde/tb_arena.h"
#define TB_TYPE_F16 TB_DataType{ { TB_INT, 0, 16 } }
#define TB_TYPE_I128 TB_DataType{ { TB_INT, 0, 128 } }
#define TB_TYPE_INT TB_TYPE_INTN(cast(u16)(8*build_context.int_size))
#define TB_TYPE_INTPTR TB_TYPE_INTN(cast(u16)(8*build_context.ptr_size))
#if defined(GB_SYSTEM_WINDOWS)
#pragma warning(pop)
#endif
#define CG_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
#define CG_CLEANUP_RUNTIME_PROC_NAME "__$cleanup_runtime"
#define CG_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
#define CG_TYPE_INFO_DATA_NAME "__$type_info_data"
#define CG_TYPE_INFO_TYPES_NAME "__$type_info_types_data"
#define CG_TYPE_INFO_NAMES_NAME "__$type_info_names_data"
#define CG_TYPE_INFO_OFFSETS_NAME "__$type_info_offsets_data"
#define CG_TYPE_INFO_USINGS_NAME "__$type_info_usings_data"
#define CG_TYPE_INFO_TAGS_NAME "__$type_info_tags_data"
#define CG_TYPE_INFO_ENUM_VALUES_NAME "__$type_info_enum_values_data"
struct cgModule;
enum cgValueKind : u32 {
cgValue_Value, // rvalue
cgValue_Addr, // lvalue
cgValue_Symbol, // global
cgValue_Multi, // multiple values
};
struct cgValueMulti;
struct cgValue {
cgValueKind kind;
Type * type;
union {
// NOTE: any value in this union must be a pointer
TB_Symbol * symbol;
TB_Node * node;
cgValueMulti *multi;
};
};
struct cgValueMulti {
Slice<cgValue> values;
};
enum cgAddrKind {
cgAddr_Default,
cgAddr_Map,
cgAddr_Context,
cgAddr_SoaVariable,
cgAddr_RelativePointer,
cgAddr_RelativeSlice,
cgAddr_Swizzle,
cgAddr_SwizzleLarge,
};
struct cgAddr {
cgAddrKind kind;
cgValue addr;
union {
struct {
cgValue key;
Type *type;
Type *result;
} map;
struct {
Selection sel;
} ctx;
struct {
cgValue index;
Ast *index_expr;
} soa;
struct {
cgValue index;
Ast *node;
} index_set;
struct {
bool deref;
} relative;
struct {
Type *type;
u8 count; // 2, 3, or 4 components
u8 indices[4];
} swizzle;
struct {
Type *type;
Slice<i32> indices;
} swizzle_large;
};
};
struct cgTargetList {
cgTargetList *prev;
bool is_block;
// control regions
TB_Node * break_;
TB_Node * continue_;
TB_Node * fallthrough_;
};
struct cgBranchRegions {
Ast * label;
TB_Node *break_;
TB_Node *continue_;
};
enum cgDeferExitKind {
cgDeferExit_Default,
cgDeferExit_Return,
cgDeferExit_Branch,
};
enum cgDeferKind {
cgDefer_Node,
cgDefer_Proc,
};
struct cgDefer {
cgDeferKind kind;
isize scope_index;
isize context_stack_count;
TB_Node * control_region;
union {
Ast *stmt;
struct {
cgValue deferred;
Slice<cgValue> result_as_args;
} proc;
};
};
struct cgContextData {
cgAddr ctx;
isize scope_index;
isize uses;
};
struct cgControlRegion {
TB_Node *control_region;
isize scope_index;
};
struct cgProcedure {
u32 flags;
u16 state_flags;
cgProcedure *parent;
Array<cgProcedure *> children;
TB_Function *func;
TB_FunctionPrototype *proto;
TB_Symbol *symbol;
Entity * entity;
cgModule *module;
String name;
Type * type;
Ast * type_expr;
Ast * body;
u64 tags;
ProcInlining inlining;
bool is_foreign;
bool is_export;
bool is_entry_point;
bool is_startup;
TB_DebugType *debug_type;
cgValue value;
Ast *curr_stmt;
cgTargetList * target_list;
Array<cgDefer> defer_stack;
Array<Scope *> scope_stack;
Array<cgContextData> context_stack;
Array<cgControlRegion> control_regions;
Array<cgBranchRegions> branch_regions;
Scope *curr_scope;
i32 scope_index;
bool in_multi_assignment;
isize split_returns_index;
bool return_by_ptr;
PtrMap<Entity *, cgAddr> variable_map;
};
struct cgModule {
TB_Module * mod;
Checker * checker;
CheckerInfo *info;
LinkerData * linker_data;
bool do_threading;
Array<cgProcedure *> single_threaded_procedure_queue;
RwMutex values_mutex;
PtrMap<Entity *, cgValue> values;
PtrMap<Entity *, TB_Symbol *> symbols;
StringMap<cgValue> members;
StringMap<cgProcedure *> procedures;
PtrMap<TB_Function *, Entity *> procedure_values;
RecursiveMutex debug_type_mutex;
PtrMap<Type *, TB_DebugType *> debug_type_map;
PtrMap<Type *, TB_DebugType *> proc_debug_type_map; // not pointer to
RecursiveMutex proc_proto_mutex;
PtrMap<Type *, TB_FunctionPrototype *> proc_proto_map;
BlockingMutex anonymous_proc_lits_mutex;
PtrMap<Ast *, cgProcedure *> anonymous_proc_lits_map;
RecursiveMutex generated_procs_mutex;
PtrMap<Type *, cgProcedure *> equal_procs;
PtrMap<Type *, cgProcedure *> hasher_procs;
PtrMap<Type *, cgProcedure *> map_get_procs;
PtrMap<Type *, cgProcedure *> map_set_procs;
// NOTE(bill): no need to protect this with a mutex
PtrMap<uintptr, TB_FileID> file_id_map; // Key: AstFile.id (i32 cast to uintptr)
std::atomic<u32> nested_type_name_guid;
std::atomic<u32> const_nil_guid;
};
#ifndef ABI_PKG_NAME_SEPARATOR
#define ABI_PKG_NAME_SEPARATOR "@"
#endif
struct GlobalTypeInfoData {
TB_Global *global;
Type * array_type;
Type * elem_type;
isize index;
};
gb_global Entity *cg_global_type_info_data_entity = {};
gb_global GlobalTypeInfoData cg_global_type_info_member_types = {};
gb_global GlobalTypeInfoData cg_global_type_info_member_names = {};
gb_global GlobalTypeInfoData cg_global_type_info_member_offsets = {};
gb_global GlobalTypeInfoData cg_global_type_info_member_usings = {};
gb_global GlobalTypeInfoData cg_global_type_info_member_tags = {};
gb_global GlobalTypeInfoData cg_global_type_info_member_enum_values = {};
gb_global cgProcedure *cg_startup_runtime_proc = nullptr;
gb_global cgProcedure *cg_cleanup_runtime_proc = nullptr;
gb_internal TB_Arena *cg_arena(void);
gb_internal cgProcedure *cg_procedure_create(cgModule *m, Entity *entity, bool ignore_body=false);
gb_internal void cg_add_procedure_to_queue(cgProcedure *p);
gb_internal void cg_setup_type_info_data(cgModule *m);
gb_internal cgProcedure *cg_procedure_generate_anonymous(cgModule *m, Ast *expr, cgProcedure *parent);
gb_internal isize cg_global_const_calculate_region_count(ExactValue const &value, Type *type);
gb_internal i64 cg_global_const_calculate_region_count_from_basic_type(Type *type);
gb_internal bool cg_global_const_add_region(cgModule *m, ExactValue const &value, Type *type, TB_Global *global, i64 offset);
gb_internal String cg_get_entity_name(cgModule *m, Entity *e);
gb_internal cgValue cg_value(TB_Global * g, Type *type);
gb_internal cgValue cg_value(TB_External *e, Type *type);
gb_internal cgValue cg_value(TB_Function *f, Type *type);
gb_internal cgValue cg_value(TB_Symbol * s, Type *type);
gb_internal cgValue cg_value(TB_Node * node, Type *type);
gb_internal cgAddr cg_addr(cgValue const &value);
gb_internal u64 cg_typeid_as_u64(cgModule *m, Type *type);
gb_internal cgValue cg_type_info(cgProcedure *p, Type *type);
gb_internal isize cg_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found=true);
gb_internal cgValue cg_const_value(cgProcedure *p, Type *type, ExactValue const &value);
gb_internal cgValue cg_const_nil(cgProcedure *p, Type *type);
gb_internal cgValue cg_flatten_value(cgProcedure *p, cgValue value);
gb_internal void cg_build_stmt(cgProcedure *p, Ast *stmt);
gb_internal void cg_build_stmt_list(cgProcedure *p, Slice<Ast *> const &stmts);
gb_internal void cg_build_when_stmt(cgProcedure *p, AstWhenStmt *ws);
gb_internal cgValue cg_build_expr(cgProcedure *p, Ast *expr);
gb_internal cgAddr cg_build_addr(cgProcedure *p, Ast *expr);
gb_internal cgValue cg_build_addr_ptr(cgProcedure *p, Ast *expr);
gb_internal cgValue cg_build_cond(cgProcedure *p, Ast *cond, TB_Node *true_block, TB_Node *false_block);
gb_internal Type * cg_addr_type(cgAddr const &addr);
gb_internal cgValue cg_addr_load(cgProcedure *p, cgAddr addr);
gb_internal void cg_addr_store(cgProcedure *p, cgAddr addr, cgValue value);
gb_internal cgValue cg_addr_get_ptr(cgProcedure *p, cgAddr const &addr);
gb_internal cgValue cg_emit_load(cgProcedure *p, cgValue const &ptr, bool is_volatile=false);
gb_internal void cg_emit_store(cgProcedure *p, cgValue dst, cgValue src, bool is_volatile=false);
gb_internal cgAddr cg_add_local (cgProcedure *p, Type *type, Entity *e, bool zero_init);
gb_internal cgAddr cg_add_global(cgProcedure *p, Type *type, Entity *e);
gb_internal cgValue cg_address_from_load_or_generate_local(cgProcedure *p, cgValue value);
gb_internal cgValue cg_copy_value_to_ptr(cgProcedure *p, cgValue value, Type *original_type, isize min_alignment);
gb_internal cgValue cg_build_call_expr(cgProcedure *p, Ast *expr);
gb_internal void cg_build_return_stmt(cgProcedure *p, Slice<Ast *> const &return_results);
gb_internal void cg_build_return_stmt_internal(cgProcedure *p, Slice<cgValue> const &results);
gb_internal void cg_build_return_stmt_internal_single(cgProcedure *p, cgValue result);
gb_internal void cg_build_range_stmt(cgProcedure *p, Ast *node);
gb_internal cgValue cg_find_value_from_entity(cgModule *m, Entity *e);
gb_internal cgValue cg_find_procedure_value_from_entity(cgModule *m, Entity *e);
gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type);
gb_internal String cg_get_entity_name(cgModule *m, Entity *e);
gb_internal cgValue cg_typeid(cgProcedure *m, Type *t);
gb_internal cgValue cg_emit_ptr_offset(cgProcedure *p, cgValue ptr, cgValue index);
gb_internal cgValue cg_emit_array_ep(cgProcedure *p, cgValue s, cgValue index);
gb_internal cgValue cg_emit_array_epi(cgProcedure *p, cgValue s, i64 index);
gb_internal cgValue cg_emit_struct_ep(cgProcedure *p, cgValue s, i64 index);
gb_internal cgValue cg_emit_deep_field_gep(cgProcedure *p, cgValue e, Selection const &sel);
gb_internal cgValue cg_emit_struct_ev(cgProcedure *p, cgValue s, i64 index);
gb_internal cgValue cg_emit_conv(cgProcedure *p, cgValue value, Type *t);
gb_internal cgValue cg_emit_comp_against_nil(cgProcedure *p, TokenKind op_kind, cgValue x);
gb_internal cgValue cg_emit_comp(cgProcedure *p, TokenKind op_kind, cgValue left, cgValue right);
gb_internal cgValue cg_emit_arith(cgProcedure *p, TokenKind op, cgValue lhs, cgValue rhs, Type *type);
gb_internal cgValue cg_emit_unary_arith(cgProcedure *p, TokenKind op, cgValue x, Type *type);
gb_internal void cg_emit_increment(cgProcedure *p, cgValue addr);
gb_internal cgProcedure *cg_equal_proc_for_type (cgModule *m, Type *type);
gb_internal cgProcedure *cg_hasher_proc_for_type(cgModule *m, Type *type);
gb_internal cgValue cg_hasher_proc_value_for_type(cgProcedure *p, Type *type);
gb_internal cgValue cg_equal_proc_value_for_type(cgProcedure *p, Type *type);
gb_internal cgValue cg_emit_call(cgProcedure * p, cgValue value, Slice<cgValue> const &args);
gb_internal cgValue cg_emit_runtime_call(cgProcedure *p, char const *name, Slice<cgValue> const &args);
gb_internal bool cg_emit_goto(cgProcedure *p, TB_Node *control_region);
gb_internal TB_Node *cg_control_region(cgProcedure *p, char const *name);
gb_internal isize cg_append_tuple_values(cgProcedure *p, Array<cgValue> *dst_values, cgValue src_value);
gb_internal cgValue cg_handle_param_value(cgProcedure *p, Type *parameter_type, ParameterValue const &param_value, TokenPos const &pos);
gb_internal cgValue cg_builtin_len(cgProcedure *p, cgValue value);
gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &x);
+1101
View File
File diff suppressed because it is too large Load Diff
BIN
View File
Binary file not shown.
+76
View File
@@ -0,0 +1,76 @@
#pragma once
#include <stddef.h>
#include <stdbool.h>
#ifndef TB_API
# ifdef __cplusplus
# define TB_EXTERN extern "C"
# else
# define TB_EXTERN
# endif
# ifdef TB_DLL
# ifdef TB_IMPORT_DLL
# define TB_API TB_EXTERN __declspec(dllimport)
# else
# define TB_API TB_EXTERN __declspec(dllexport)
# endif
# else
# define TB_API TB_EXTERN
# endif
#endif
enum {
TB_ARENA_SMALL_CHUNK_SIZE = 4 * 1024,
TB_ARENA_MEDIUM_CHUNK_SIZE = 512 * 1024,
TB_ARENA_LARGE_CHUNK_SIZE = 2 * 1024 * 1024,
TB_ARENA_ALIGNMENT = 16,
};
typedef struct TB_ArenaChunk TB_ArenaChunk;
struct TB_ArenaChunk {
TB_ArenaChunk* next;
size_t pad;
char data[];
};
typedef struct TB_Arena {
size_t chunk_size;
TB_ArenaChunk* base;
TB_ArenaChunk* top;
// top of the allocation space
char* watermark;
char* high_point; // &top->data[chunk_size]
} TB_Arena;
typedef struct TB_ArenaSavepoint {
TB_ArenaChunk* top;
char* watermark;
} TB_ArenaSavepoint;
#define TB_ARENA_FOR(it, arena) for (TB_ArenaChunk* it = (arena)->base; it != NULL; it = it->next)
#define TB_ARENA_ALLOC(arena, T) tb_arena_alloc(arena, sizeof(T))
#define TB_ARENA_ARR_ALLOC(arena, count, T) tb_arena_alloc(arena, (count) * sizeof(T))
TB_API void tb_arena_create(TB_Arena* restrict arena, size_t chunk_size);
TB_API void tb_arena_destroy(TB_Arena* restrict arena);
TB_API void* tb_arena_unaligned_alloc(TB_Arena* restrict arena, size_t size);
TB_API void* tb_arena_alloc(TB_Arena* restrict arena, size_t size);
// asserts if ptr+size != watermark
TB_API void tb_arena_pop(TB_Arena* restrict arena, void* ptr, size_t size);
// in case you wanna mix unaligned and aligned arenas
TB_API void tb_arena_realign(TB_Arena* restrict arena);
TB_API bool tb_arena_is_empty(TB_Arena* arena);
// savepoints
TB_API TB_ArenaSavepoint tb_arena_save(TB_Arena* arena);
TB_API void tb_arena_restore(TB_Arena* arena, TB_ArenaSavepoint sp);
// resets to only having one chunk
TB_API void tb_arena_clear(TB_Arena* arena);
+330
View File
@@ -0,0 +1,330 @@
// PE/COFF is the executable/object format used by Microsoft.
#ifndef TB_COFF_H
#define TB_COFF_H
#include "tb_formats.h"
#define IMAGE_SCN_LNK_NRELOC_OVFL 0x01000000
#define IMAGE_SYM_CLASS_EXTERNAL 0x0002
#define IMAGE_SYM_CLASS_STATIC 0x0003
#define IMAGE_SYM_CLASS_LABEL 0x0006
#define IMAGE_SYM_CLASS_FILE 0x0067
#define IMAGE_SYM_CLASS_SECTION 0x0068
#define IMAGE_SYM_CLASS_WEAK_EXTERNAL 0x0069
#define IMAGE_FILE_LINE_NUMS_STRIPPED 0x0004
#define IMAGE_REL_AMD64_ADDR64 0x0001
#define IMAGE_REL_AMD64_ADDR32 0x0002
#define IMAGE_REL_AMD64_ADDR32NB 0x0003
#define IMAGE_REL_AMD64_REL32 0x0004
#define IMAGE_REL_AMD64_REL32_1 0x0005
#define IMAGE_REL_AMD64_REL32_2 0x0006
#define IMAGE_REL_AMD64_REL32_3 0x0007
#define IMAGE_REL_AMD64_REL32_4 0x0008
#define IMAGE_REL_AMD64_REL32_5 0x0009
#define IMAGE_REL_AMD64_SECTION 0x000A
#define IMAGE_REL_AMD64_SECREL 0x000B
#define IMAGE_SCN_LNK_REMOVE 0x00000800
#define IMAGE_SCN_LNK_COMDAT 0x00001000
#define IMAGE_SCN_MEM_DISCARDABLE 0x02000000
#define IMAGE_SCN_MEM_EXECUTE 0x20000000
#define IMAGE_SCN_MEM_READ 0x40000000
#define IMAGE_SCN_MEM_WRITE 0x80000000
#define IMAGE_SCN_CNT_CODE 0x00000020 /* Section contains code. */
#define IMAGE_SCN_CNT_INITIALIZED_DATA 0x00000040 /* Section contains initialized data. */
#define IMAGE_SCN_CNT_UNINITIALIZED_DATA 0x00000080 /* Section contains uninitialized data. */
#define IMAGE_DIRECTORY_ENTRY_EXPORT 0 // Export Directory
#define IMAGE_DIRECTORY_ENTRY_IMPORT 1 // Import Directory
#define IMAGE_DIRECTORY_ENTRY_RESOURCE 2 // Resource Directory
#define IMAGE_DIRECTORY_ENTRY_EXCEPTION 3 // Exception Directory
#define IMAGE_DIRECTORY_ENTRY_SECURITY 4 // Security Directory
#define IMAGE_DIRECTORY_ENTRY_BASERELOC 5 // Base Relocation Table
#define IMAGE_DIRECTORY_ENTRY_DEBUG 6 // Debug Directory
#define IMAGE_DIRECTORY_ENTRY_ARCHITECTURE 7 // Architecture Specific Data
#define IMAGE_DIRECTORY_ENTRY_GLOBALPTR 8 // RVA of GP
#define IMAGE_DIRECTORY_ENTRY_TLS 9 // TLS Directory
#define IMAGE_DIRECTORY_ENTRY_LOAD_CONFIG 10 // Load Configuration Directory
#define IMAGE_DIRECTORY_ENTRY_BOUND_IMPORT 11 // Bound Import Directory in headers
#define IMAGE_DIRECTORY_ENTRY_IAT 12 // Import Address Table
#define IMAGE_DIRECTORY_ENTRY_DELAY_IMPORT 13 // Delay Load Import Descriptors
#define IMAGE_DIRECTORY_ENTRY_COM_DESCRIPTOR 14 // COM Runtime descriptor
#define IMAGE_SUBSYSTEM_WINDOWS_GUI 2
#define IMAGE_SUBSYSTEM_WINDOWS_CUI 3
#define IMAGE_SUBSYSTEM_EFI_APPLICATION 10
typedef enum {
TB_COFF_SECTION_NO_PAD = 0x00000008,
TB_COFF_SECTION_CODE = 0x00000020,
TB_COFF_SECTION_INIT = 0x00000040,
TB_COFF_SECTION_UNINIT = 0x00000080,
TB_COFF_SECTION_OTHER = 0x00000100,
TB_COFF_SECTION_INFO = 0x00000200,
TB_COFF_SECTION_REMOVE = 0x00000800,
TB_COFF_SECTION_COMDAT = 0x00001000,
// this is actually a 4bit field
TB_COFF_SECTION_ALIGN = 0x00F00000,
// if we have more than 65535 relocations we do this
TB_COFF_SECTION_RELOC_OVR = 0x00F00000,
// memory flags
TB_COFF_SECTION_DISCARDABLE = 0x02000000,
TB_COFF_SECTION_NOT_CACHED = 0x04000000,
TB_COFF_SECTION_NOT_PAGED = 0x08000000,
TB_COFF_SECTION_SHARED = 0x10000000,
TB_COFF_SECTION_EXECUTE = 0x20000000,
TB_COFF_SECTION_READ = 0x40000000,
TB_COFF_SECTION_WRITE = 0x80000000,
} TB_COFF_SectionFlags;
typedef struct TB_COFF_Parser {
// inputs
TB_Slice name, file;
// results
size_t section_count;
size_t symbol_table, symbol_count;
// private
TB_Slice string_table;
} TB_COFF_Parser;
// fills the parser with results from the COFF header
bool tb_coff_parse_init(TB_COFF_Parser* restrict parser);
bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* out_sec);
// how many symbols does this one symbol take up (basically 1 + aux symbols).
// returns 0 if error.
size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym);
#endif // TB_COFF_H
#ifdef TB_COFF_IMPL
#include <common.h>
#pragma pack(push, 2)
typedef struct COFF_SectionHeader {
char name[8];
union {
uint32_t physical_address;
uint32_t virtual_size;
} misc;
uint32_t virtual_address;
uint32_t raw_data_size;
uint32_t raw_data_pos;
uint32_t pointer_to_reloc;
uint32_t pointer_to_lineno;
uint16_t num_reloc;
uint16_t num_lineno;
uint32_t characteristics;
} COFF_SectionHeader;
typedef struct COFF_FileHeader {
uint16_t machine;
uint16_t section_count;
uint32_t timestamp;
uint32_t symbol_table;
uint32_t symbol_count;
uint16_t optional_header_size;
uint16_t flags;
} COFF_FileHeader;
typedef struct COFF_Symbol {
union {
uint8_t short_name[8];
uint32_t long_name[2];
};
uint32_t value;
int16_t section_number;
uint16_t type;
uint8_t storage_class;
uint8_t aux_symbols_count;
} COFF_Symbol;
typedef struct COFF_ImageReloc {
union {
uint32_t VirtualAddress;
uint32_t RelocCount;
};
uint32_t SymbolTableIndex;
uint16_t Type;
} COFF_ImageReloc;
#pragma pack(pop)
// sanity checks
static_assert(sizeof(COFF_SectionHeader) == 40, "COFF Section header size != 40 bytes");
static_assert(sizeof(COFF_ImageReloc) == 10, "COFF Image Relocation size != 10 bytes");
static_assert(sizeof(COFF_FileHeader) == 20, "COFF File header size != 20 bytes");
static_assert(sizeof(COFF_Symbol) == 18, "COFF Symbol size != 18 bytes");
bool tb_coff_parse_init(TB_COFF_Parser* restrict parser) {
TB_Slice file = parser->file;
if (file.length < sizeof(COFF_FileHeader)) return false;
COFF_FileHeader* header = (COFF_FileHeader*) &parser->file.data[0];
// locate string table (it spans until the end of the file)
size_t string_table_pos = header->symbol_table + (header->symbol_count * sizeof(COFF_Symbol));
if (file.length < string_table_pos) return false;
parser->symbol_count = header->symbol_count;
parser->symbol_table = header->symbol_table;
parser->section_count = header->section_count;
parser->string_table = (TB_Slice){
.length = file.length - string_table_pos,
.data = &file.data[string_table_pos]
};
return true;
}
static long long tb__parse_decimal_int(size_t n, const char* str) {
const char* end = &str[n];
int result = 0;
while (str != end) {
if (*str < '0' || *str > '9') break;
result *= 10;
result += *str - '0';
str++;
}
return result;
}
bool tb_coff_parse_section(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSection* restrict out_sec) {
TB_Slice file = parser->file;
size_t section_offset = sizeof(COFF_FileHeader) + (i * sizeof(COFF_SectionHeader));
if (file.length < section_offset + sizeof(COFF_SectionHeader)) {
return false;
}
COFF_SectionHeader* sec = (COFF_SectionHeader*) &file.data[section_offset];
*out_sec = (TB_ObjectSection) { .flags = sec->characteristics };
// Parse string table name stuff
if (sec->name[0] == '/') {
// string table access
int offset = tb__parse_decimal_int(7, &sec->name[1]);
if (file.length > offset) {
return false;
}
const uint8_t* data = &parser->string_table.data[offset];
out_sec->name = (TB_Slice){ strlen((const char*) data), data };
} else {
// normal inplace string
size_t len = strlen(sec->name);
out_sec->name = (TB_Slice){ len, (uint8_t*) sec->name };
}
// Parse relocations
if (sec->num_reloc > 0) {
out_sec->relocation_count = sec->num_reloc;
COFF_ImageReloc* src_relocs = (COFF_ImageReloc*) &file.data[sec->pointer_to_reloc];
TB_ObjectReloc* dst_relocs = tb_platform_heap_alloc(sec->num_reloc * sizeof(TB_ObjectReloc));
FOREACH_N(j, 0, sec->num_reloc) {
dst_relocs[j] = (TB_ObjectReloc){ 0 };
switch (src_relocs[j].Type) {
case IMAGE_REL_AMD64_ADDR32NB: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32NB; break;
case IMAGE_REL_AMD64_ADDR32: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR32; break;
case IMAGE_REL_AMD64_ADDR64: dst_relocs[j].type = TB_OBJECT_RELOC_ADDR64; break;
case IMAGE_REL_AMD64_SECREL: dst_relocs[j].type = TB_OBJECT_RELOC_SECREL; break;
case IMAGE_REL_AMD64_SECTION: dst_relocs[j].type = TB_OBJECT_RELOC_SECTION; break;
case IMAGE_REL_AMD64_REL32:
case IMAGE_REL_AMD64_REL32_1:
case IMAGE_REL_AMD64_REL32_2:
case IMAGE_REL_AMD64_REL32_3:
case IMAGE_REL_AMD64_REL32_4:
case IMAGE_REL_AMD64_REL32_5:
dst_relocs[j].type = TB_OBJECT_RELOC_REL32;
break;
default: tb_todo();
}
if (src_relocs[j].Type >= IMAGE_REL_AMD64_REL32 && src_relocs[j].Type <= IMAGE_REL_AMD64_REL32_5) {
dst_relocs[j].addend = 4 + (src_relocs[j].Type - IMAGE_REL_AMD64_REL32);
}
dst_relocs[j].symbol_index = src_relocs[j].SymbolTableIndex;
dst_relocs[j].virtual_address = src_relocs[j].VirtualAddress;
}
out_sec->relocations = dst_relocs;
}
// Parse virtual region
out_sec->virtual_address = sec->virtual_address;
out_sec->virtual_size = sec->misc.virtual_size;
// Read raw data (if applies)
if (sec->raw_data_size) {
assert(sec->raw_data_pos + sec->raw_data_size < file.length);
out_sec->raw_data = (TB_Slice){ sec->raw_data_size, &file.data[sec->raw_data_pos] };
}
return true;
}
TB_ObjectSymbolType classify_symbol_type(uint16_t st_class) {
switch (st_class) {
case 2: return TB_OBJECT_SYMBOL_EXTERN;
case 3: return TB_OBJECT_SYMBOL_STATIC;
case 6: return TB_OBJECT_SYMBOL_STATIC;
case 0x68: return TB_OBJECT_SYMBOL_SECTION;
case 0x69: return TB_OBJECT_SYMBOL_WEAK_EXTERN;
default: return TB_OBJECT_SYMBOL_UNKNOWN;
}
}
size_t tb_coff_parse_symbol(TB_COFF_Parser* restrict parser, size_t i, TB_ObjectSymbol* restrict out_sym) {
TB_Slice file = parser->file;
size_t symbol_offset = parser->symbol_table + (i * sizeof(COFF_Symbol));
if (file.length < symbol_offset + sizeof(COFF_Symbol)) {
return 0;
}
COFF_Symbol* sym = (COFF_Symbol*) &file.data[symbol_offset];
*out_sym = (TB_ObjectSymbol) {
.ordinal = i,
.type = classify_symbol_type(sym->storage_class),
.section_num = sym->section_number,
.value = sym->value
};
// Parse string table name stuff
if (sym->long_name[0] == 0) {
// string table access (read a cstring)
// TODO(NeGate): bounds check this
const uint8_t* data = &parser->string_table.data[sym->long_name[1]];
out_sym->name = (TB_Slice){ strlen((const char*) data), data };
} else {
// normal inplace string
size_t len = strlen((const char*) sym->short_name);
out_sym->name = (TB_Slice){ len, sym->short_name };
}
// TODO(NeGate): Process aux symbols
if (sym->aux_symbols_count) {
out_sym->extra = &sym[1];
// FOREACH_N(j, 0, sym->aux_symbols_count) {}
}
return sym->aux_symbols_count + 1;
}
#endif // TB_COFF_IMPL
+170
View File
@@ -0,0 +1,170 @@
#ifndef TB_ELF_H
#define TB_ELF_H
#include <stdint.h>
#define TB_EI_MAG0 0
#define TB_EI_MAG1 1
#define TB_EI_MAG2 2
#define TB_EI_MAG3 3
#define TB_EI_CLASS 4 /* Class of machine. */
#define TB_EI_DATA 5 /* Data format. */
#define TB_EI_VERSION 6 /* ELF format version. */
#define TB_EI_OSABI 7 /* Operating system / ABI identification */
#define TB_EI_ABIVERSION 8 /* ABI version */
#define TB_OLD_EI_BRAND 8 /* Start of architecture identification. */
#define TB_EI_PAD 9 /* Start of padding (per SVR4 ABI). */
#define TB_EI_NIDENT 16 /* Size of e_ident array. */
/* Values for e_type. */
#define TB_ET_NONE 0 /* Unknown type. */
#define TB_ET_REL 1 /* Relocatable. */
#define TB_ET_EXEC 2 /* Executable. */
#define TB_ET_DYN 3 /* Shared object. */
#define TB_ET_CORE 4 /* Core file. */
#define TB_ET_LOOS 0xfe00 /* First operating system specific. */
#define TB_ET_HIOS 0xfeff /* Last operating system-specific. */
#define TB_ET_LOPROC 0xff00 /* First processor-specific. */
#define TB_ET_HIPROC 0xffff /* Last processor-specific. */
/* Values for e_machine. */
#define TB_EM_NONE 0 /* Unknown machine. */
#define TB_EM_X86_64 62 /* Advanced Micro Devices x86-64 */
#define TB_EM_AARCH64 183 /* AArch64 (64-bit ARM) */
/* sh_type */
#define TB_SHT_NULL 0 /* inactive */
#define TB_SHT_PROGBITS 1 /* program defined information */
#define TB_SHT_SYMTAB 2 /* symbol table section */
#define TB_SHT_STRTAB 3 /* string table section */
#define TB_SHT_RELA 4 /* relocation section with addends */
#define TB_SHT_NOBITS 8 /* no space section */
/* Flags for sh_flags. */
#define TB_SHF_WRITE 0x1 /* Section contains writable data. */
#define TB_SHF_ALLOC 0x2 /* Section occupies memory. */
#define TB_SHF_EXECINSTR 0x4 /* Section contains instructions. */
#define TB_SHF_MERGE 0x10 /* Section may be merged. */
#define TB_SHF_STRINGS 0x20 /* Section contains strings. */
#define TB_SHF_INFO_LINK 0x40 /* sh_info holds section index. */
#define TB_SHF_LINK_ORDER 0x80 /* Special ordering requirements. */
#define TB_SHF_OS_NONCONFORMING 0x100 /* OS-specific processing required. */
#define TB_SHF_GROUP 0x200 /* Member of section group. */
#define TB_SHF_TLS 0x400 /* Section contains TLS data. */
#define TB_SHF_MASKOS 0x0ff00000 /* OS-specific semantics. */
#define TB_SHF_MASKPROC 0xf0000000 /* Processor-specific semantics. */
/* Values for p_flags. */
#define TB_PF_X 0x1 /* Executable. */
#define TB_PF_W 0x2 /* Writable. */
#define TB_PF_R 0x4 /* Readable. */
#define TB_PF_MASKOS 0x0ff00000 /* Operating system-specific. */
#define TB_PF_MASKPROC 0xf0000000 /* Processor-specific. */
/* Values for p_type. */
#define TB_PT_NULL 0 /* Unused entry. */
#define TB_PT_LOAD 1 /* Loadable segment. */
#define TB_PT_DYNAMIC 2 /* Dynamic linking information segment. */
#define TB_PT_INTERP 3 /* Pathname of interpreter. */
#define TB_PT_NOTE 4 /* Auxiliary information. */
#define TB_PT_SHLIB 5 /* Reserved (not used). */
#define TB_PT_PHDR 6 /* Location of program header itself. */
#define TB_PT_TLS 7 /* Thread local storage segment */
/* Values for relocation */
typedef enum {
TB_ELF_X86_64_NONE = 0,
TB_ELF_X86_64_64 = 1,
TB_ELF_X86_64_PC32 = 2,
TB_ELF_X86_64_GOT32 = 3,
TB_ELF_X86_64_PLT32 = 4,
TB_ELF_X86_64_GOTPCREL = 9,
} TB_ELF_RelocType;
// ST_TYPE
#define TB_ELF64_STT_NOTYPE 0
#define TB_ELF64_STT_OBJECT 1
#define TB_ELF64_STT_FUNC 2
#define TB_ELF64_STT_SECTION 3
// ST_INFO
#define TB_ELF64_STB_LOCAL 0
#define TB_ELF64_STB_GLOBAL 1
#define TB_ELF64_STB_WEAK 2
/* Macros for accessing the fields of st_info. */
#define TB_ELF64_ST_BIND(info) ((info) >> 4)
#define TB_ELF64_ST_TYPE(info) ((info) & 0xf)
#define TB_ELF64_ST_INFO(b, t) (((b) << 4) | ((t) & 0xF))
#define TB_ELF64_R_SYM(i) ((i) >> 32u)
#define TB_ELF64_R_TYPE(i) ((i)&0xffffffffULL)
#define TB_ELF64_R_INFO(s, t) (((uint64_t)(s) << 32ULL) + ((uint64_t)(t) & 0xffffffffULL))
// http://web.mit.edu/freebsd/head/sys/sys/elf64.h
// https://cirosantilli.com/elf-hello-world#minimal-elf-file
// https://en.wikipedia.org/wiki/Executable_and_Linkable_Format
typedef struct {
uint8_t ident[16];
uint16_t type;
uint16_t machine;
uint32_t version;
uint64_t entry;
uint64_t phoff;
uint64_t shoff;
uint32_t flags;
uint16_t ehsize;
uint16_t phentsize;
uint16_t phnum;
uint16_t shentsize;
uint16_t shnum;
uint16_t shstrndx;
} TB_Elf64_Ehdr;
typedef struct {
uint32_t name;
uint32_t type;
uint64_t flags;
uint64_t addr;
uint64_t offset;
uint64_t size;
uint32_t link;
uint32_t info;
uint64_t addralign;
uint64_t entsize;
} TB_Elf64_Shdr;
// Segment header for ELF64.
typedef struct {
uint32_t type; // Type of segment
uint32_t flags; // Segment flags
uint64_t offset; // File offset where segment is located, in bytes
uint64_t vaddr; // Virtual address of beginning of segment
uint64_t paddr; // Physical addr of beginning of segment (OS-specific)
uint64_t filesz; // Num. of bytes in file image of segment (may be zero)
uint64_t memsz; // Num. of bytes in mem image of segment (may be zero)
uint64_t align; // Segment alignment constraint
} TB_Elf64_Phdr;
typedef struct {
uint32_t name;
uint8_t info;
uint8_t other;
uint16_t shndx;
uint64_t value;
uint64_t size;
} TB_Elf64_Sym;
typedef struct {
uint64_t offset;
uint64_t info;
int64_t addend;
} TB_Elf64_Rela;
typedef struct {
uint64_t offset;
uint64_t info;
} TB_Elf64_Rel;
#endif /* TB_ELF_H */
+132
View File
@@ -0,0 +1,132 @@
// This handles the generalized executable/object format parsing stuff
#ifndef TB_OBJECT_H
#define TB_OBJECT_H
#include <stdint.h>
#include <stddef.h>
typedef enum {
TB_OBJECT_RELOC_NONE, // how?
// Target independent
TB_OBJECT_RELOC_ADDR32,
TB_OBJECT_RELOC_ADDR64, // unsupported on 32bit platforms
TB_OBJECT_RELOC_SECREL,
TB_OBJECT_RELOC_SECTION,
// COFF only
TB_OBJECT_RELOC_ADDR32NB, // Relative virtual address
// x64 only
TB_OBJECT_RELOC_REL32, // relative 32bit displacement
// Aarch64 only
TB_OBJECT_RELOC_BRANCH26, // 26bit displacement for B and BL instructions
TB_OBJECT_RELOC_REL21, // for ADR instructions
// TODO(NeGate): fill in the rest of this later
} TB_ObjectRelocType;
typedef struct {
TB_ObjectRelocType type;
uint32_t symbol_index;
size_t virtual_address;
size_t addend;
} TB_ObjectReloc;
typedef enum {
TB_OBJECT_SYMBOL_UNKNOWN,
TB_OBJECT_SYMBOL_EXTERN, // exported
TB_OBJECT_SYMBOL_WEAK_EXTERN, // weak
TB_OBJECT_SYMBOL_IMPORT, // forward decl
TB_OBJECT_SYMBOL_STATIC, // local
TB_OBJECT_SYMBOL_SECTION, // local
} TB_ObjectSymbolType;
typedef struct {
TB_ObjectSymbolType type;
int section_num;
uint32_t ordinal;
uint32_t value;
TB_Slice name;
// for COFF, this is the auxillary
void* extra;
// this is zeroed out by the loader and left for the user to do crap with
void* user_data;
} TB_ObjectSymbol;
typedef struct {
TB_Slice name;
uint32_t flags;
size_t virtual_address;
size_t virtual_size;
// You can have a virtual size without having a raw
// data size, that's how the BSS section works
TB_Slice raw_data;
size_t relocation_count;
TB_ObjectReloc* relocations;
// this is zeroed out by the loader and left for the user to do crap with
void* user_data;
} TB_ObjectSection;
typedef enum {
TB_OBJECT_FILE_UNKNOWN,
TB_OBJECT_FILE_COFF,
TB_OBJECT_FILE_ELF64
} TB_ObjectFileType;
typedef struct {
TB_ObjectFileType type;
TB_Arch arch;
TB_Slice name;
TB_Slice ar_name;
size_t symbol_count;
TB_ObjectSymbol* symbols;
size_t section_count;
TB_ObjectSection sections[];
} TB_ObjectFile;
////////////////////////////////
// Archive parser
////////////////////////////////
typedef struct {
TB_Slice name;
// if import_name is empty, we're dealing with an object file
TB_Slice import_name;
uint16_t ordinal;
TB_Slice content;
} TB_ArchiveEntry;
typedef struct {
TB_Slice file;
size_t pos;
size_t member_count;
uint32_t* members;
size_t symbol_count;
uint16_t* symbols;
TB_Slice strtbl;
} TB_ArchiveFileParser;
// We do this to parse the header
bool tb_archive_parse(TB_Slice file, TB_ArchiveFileParser* restrict out_parser);
// After that we can enumerate any symbol entries to resolve imports
size_t tb_archive_parse_entries(TB_ArchiveFileParser* restrict parser, size_t i, size_t count, TB_ArchiveEntry* out_entry);
#endif // TB_OBJECT_H
+90
View File
@@ -0,0 +1,90 @@
#ifndef TB_X64_H
#define TB_X64_H
#include <stdint.h>
#include <stdbool.h>
typedef enum {
// uses xmm registers for the reg array
TB_X86_INSTR_XMMREG = (1u << 0u),
// r/m is a memory operand
TB_X86_INSTR_USE_MEMOP = (1u << 1u),
// r/m is a rip-relative address (TB_X86_INSTR_USE_MEMOP is always set when this is set)
TB_X86_INSTR_USE_RIPMEM = (1u << 2u),
// LOCK prefix is present
TB_X86_INSTR_LOCK = (1u << 3u),
// uses a signed immediate
TB_X86_INSTR_IMMEDIATE = (1u << 4u),
// absolute means it's using the 64bit immediate (cannot be applied while a memory operand is active)
TB_X86_INSTR_ABSOLUTE = (1u << 5u),
// set if the r/m can be found on the right hand side
TB_X86_INSTR_DIRECTION = (1u << 6u),
// uses the second data type because the instruction is weird like MOVSX or MOVZX
TB_X86_INSTR_TWO_DATA_TYPES = (1u << 7u)
} TB_X86_InstFlags;
typedef enum {
TB_X86_SEGMENT_DEFAULT = 0,
TB_X86_SEGMENT_ES, TB_X86_SEGMENT_CS,
TB_X86_SEGMENT_SS, TB_X86_SEGMENT_DS,
TB_X86_SEGMENT_GS, TB_X86_SEGMENT_FS,
} TB_X86_Segment;
typedef enum {
TB_X86_TYPE_NONE = 0,
TB_X86_TYPE_BYTE, // 1
TB_X86_TYPE_WORD, // 2
TB_X86_TYPE_DWORD, // 4
TB_X86_TYPE_QWORD, // 8
TB_X86_TYPE_PBYTE, // int8 x 16 = 16
TB_X86_TYPE_PWORD, // int16 x 8 = 16
TB_X86_TYPE_PDWORD, // int32 x 4 = 16
TB_X86_TYPE_PQWORD, // int64 x 2 = 16
TB_X86_TYPE_SSE_SS, // float32 x 1 = 4
TB_X86_TYPE_SSE_SD, // float64 x 1 = 8
TB_X86_TYPE_SSE_PS, // float32 x 4 = 16
TB_X86_TYPE_SSE_PD, // float64 x 2 = 16
TB_X86_TYPE_XMMWORD, // the generic idea of them
} TB_X86_DataType;
typedef struct {
int16_t type;
// registers (there's 4 max taking up 4bit slots each)
uint16_t regs;
uint8_t flags;
// bitpacking amirite
TB_X86_DataType data_type : 4;
TB_X86_DataType data_type2 : 4;
TB_X86_Segment segment : 4;
uint8_t length : 4;
// memory operand
// X86_INSTR_USE_MEMOP
int32_t disp;
// immediate operand
// imm for INSTR_IMMEDIATE
// abs for INSTR_ABSOLUTE
union {
int32_t imm;
uint64_t abs;
};
} TB_X86_Inst;
TB_X86_Inst tb_x86_disasm(size_t length, const uint8_t data[length]);
#endif /* TB_X64_H */
+443
View File
@@ -0,0 +1,443 @@
gb_internal cgValue cg_builtin_len(cgProcedure *p, cgValue value) {
Type *t = base_type(value.type);
switch (t->kind) {
case Type_Basic:
switch (t->Basic.kind) {
case Basic_string:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
return cg_emit_load(p, len_ptr);
}
case Basic_cstring:
GB_PANIC("TODO(bill): len(cstring)");
break;
}
break;
case Type_Array:
return cg_const_int(p, t_int, t->Array.count);
case Type_EnumeratedArray:
return cg_const_int(p, t_int, t->EnumeratedArray.count);
case Type_Slice:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
return cg_emit_load(p, len_ptr);
}
case Type_DynamicArray:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
return cg_emit_load(p, len_ptr);
}
case Type_Map:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
return cg_emit_conv(p, cg_emit_load(p, len_ptr), t_int);
}
case Type_Struct:
GB_ASSERT(is_type_soa_struct(t));
break;
}
GB_PANIC("TODO(bill): cg_builtin_len %s", type_to_string(t));
return {};
}
gb_internal cgValue cg_builtin_cap(cgProcedure *p, cgValue value) {
Type *t = base_type(value.type);
switch (t->kind) {
case Type_Basic:
switch (t->Basic.kind) {
case Basic_string:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
return cg_emit_load(p, len_ptr);
}
case Basic_cstring:
GB_PANIC("TODO(bill): cap(cstring)");
break;
}
break;
case Type_Array:
return cg_const_int(p, t_int, t->Array.count);
case Type_EnumeratedArray:
return cg_const_int(p, t_int, t->EnumeratedArray.count);
case Type_Slice:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 1);
return cg_emit_load(p, len_ptr);
}
case Type_DynamicArray:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue len_ptr = cg_emit_struct_ep(p, ptr, 2);
return cg_emit_load(p, len_ptr);
}
case Type_Map:
{
TB_DataType dt_uintptr = cg_data_type(t_uintptr);
TB_Node *zero = tb_inst_uint(p->func, dt_uintptr, 0);
TB_Node *one = tb_inst_uint(p->func, dt_uintptr, 0);
TB_Node *mask = tb_inst_uint(p->func, dt_uintptr, MAP_CACHE_LINE_SIZE-1);
TB_Node *data = cg_emit_struct_ev(p, value, 0).node;
TB_Node *log2_cap = tb_inst_and(p->func, data, mask);
TB_Node *cap = tb_inst_shl(p->func, one, log2_cap, cast(TB_ArithmeticBehavior)0);
TB_Node *cmp = tb_inst_cmp_eq(p->func, data, zero);
cgValue res = cg_value(tb_inst_select(p->func, cmp, zero, cap), t_uintptr);
return cg_emit_conv(p, res, t_int);
}
case Type_Struct:
GB_ASSERT(is_type_soa_struct(t));
break;
}
GB_PANIC("TODO(bill): cg_builtin_cap %s", type_to_string(t));
return {};
}
gb_internal cgValue cg_builtin_raw_data(cgProcedure *p, cgValue const &value) {
Type *t = base_type(value.type);
cgValue res = {};
switch (t->kind) {
case Type_Slice:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0);
res = cg_emit_load(p, data_ptr);
GB_ASSERT(is_type_multi_pointer(res.type));
}
break;
case Type_DynamicArray:
{
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0);
res = cg_emit_load(p, data_ptr);
}
break;
case Type_Basic:
if (t->Basic.kind == Basic_string) {
GB_ASSERT(value.kind == cgValue_Addr);
cgValue ptr = cg_value(value.node, alloc_type_pointer(value.type));
cgValue data_ptr = cg_emit_struct_ep(p, ptr, 0);
res = cg_emit_load(p, data_ptr);
} else if (t->Basic.kind == Basic_cstring) {
res = cg_emit_conv(p, value, t_u8_multi_ptr);
}
break;
case Type_Pointer:
GB_ASSERT(is_type_array_like(t->Pointer.elem));
GB_ASSERT(value.kind == cgValue_Value);
res = cg_value(value.node, alloc_type_multi_pointer(base_array_type(t->Pointer.elem)));
break;
case Type_MultiPointer:
GB_PANIC("TODO(bill) %s", type_to_string(value.type));
// res = cg_emit_conv(p, value, tv.type);
break;
}
GB_ASSERT(res.node != nullptr);
return res;
}
gb_internal cgValue cg_builtin_min(cgProcedure *p, Type *t, cgValue x, cgValue y) {
x = cg_emit_conv(p, x, t);
y = cg_emit_conv(p, y, t);
return cg_emit_select(p, cg_emit_comp(p, Token_Lt, x, y), x, y);
}
gb_internal cgValue cg_builtin_max(cgProcedure *p, Type *t, cgValue x, cgValue y) {
x = cg_emit_conv(p, x, t);
y = cg_emit_conv(p, y, t);
return cg_emit_select(p, cg_emit_comp(p, Token_Gt, x, y), x, y);
}
gb_internal cgValue cg_builtin_abs(cgProcedure *p, cgValue const &x) {
if (is_type_unsigned(x.type)) {
return x;
}
if (is_type_quaternion(x.type)) {
GB_PANIC("TODO(bill): abs quaternion");
} else if (is_type_complex(x.type)) {
GB_PANIC("TODO(bill): abs complex");
}
TB_DataType dt = cg_data_type(x.type);
GB_ASSERT(!TB_IS_VOID_TYPE(dt));
TB_Node *zero = nullptr;
if (dt.type == TB_FLOAT) {
if (dt.data == 32) {
zero = tb_inst_float32(p->func, 0);
} else if (dt.data == 64) {
zero = tb_inst_float64(p->func, 0);
}
} else {
zero = tb_inst_uint(p->func, dt, 0);
}
GB_ASSERT(zero != nullptr);
cgValue cond = cg_emit_comp(p, Token_Lt, x, cg_value(zero, x.type));
cgValue neg = cg_emit_unary_arith(p, Token_Sub, x, x.type);
return cg_emit_select(p, cond, neg, x);
}
gb_internal cgValue cg_builtin_clamp(cgProcedure *p, Type *t, cgValue const &x, cgValue const &min, cgValue const &max) {
cgValue z = x;
z = cg_builtin_max(p, t, z, min);
z = cg_builtin_min(p, t, z, max);
return z;
}
gb_internal cgValue cg_builtin_mem_zero(cgProcedure *p, cgValue const &ptr, cgValue const &len) {
GB_ASSERT(ptr.kind == cgValue_Value);
GB_ASSERT(len.kind == cgValue_Value);
tb_inst_memzero(p->func, ptr.node, len.node, 1, false);
return ptr;
}
gb_internal cgValue cg_builtin_mem_copy(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len) {
GB_ASSERT(dst.kind == cgValue_Value);
GB_ASSERT(src.kind == cgValue_Value);
GB_ASSERT(len.kind == cgValue_Value);
// TODO(bill): This needs to be memmove
tb_inst_memcpy(p->func, dst.node, src.node, len.node, 1, false);
return dst;
}
gb_internal cgValue cg_builtin_mem_copy_non_overlapping(cgProcedure *p, cgValue const &dst, cgValue const &src, cgValue const &len) {
GB_ASSERT(dst.kind == cgValue_Value);
GB_ASSERT(src.kind == cgValue_Value);
GB_ASSERT(len.kind == cgValue_Value);
tb_inst_memcpy(p->func, dst.node, src.node, len.node, 1, false);
return dst;
}
gb_internal cgValue cg_build_builtin(cgProcedure *p, BuiltinProcId id, Ast *expr) {
ast_node(ce, CallExpr, expr);
if (BuiltinProc__simd_begin < id && id < BuiltinProc__simd_end) {
GB_PANIC("TODO(bill): cg_build_builtin_simd_proc");
// return cg_build_builtin_simd_proc(p, expr, tv, id);
}
String builtin_name = builtin_procs[id].name;
switch (id) {
case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc);
String name = bd->name.string;
GB_ASSERT(name == "location");
String procedure = p->entity->token.string;
TokenPos pos = ast_token(ce->proc).pos;
if (ce->args.count > 0) {
Ast *ident = unselector_expr(ce->args[0]);
GB_ASSERT(ident->kind == Ast_Ident);
Entity *e = entity_of_node(ident);
GB_ASSERT(e != nullptr);
if (e->parent_proc_decl != nullptr && e->parent_proc_decl->entity != nullptr) {
procedure = e->parent_proc_decl->entity->token.string;
} else {
procedure = str_lit("");
}
pos = e->token.pos;
}
return cg_emit_source_code_location_as_global(p, procedure, pos);
} break;
case BuiltinProc_len: {
cgValue v = cg_build_expr(p, ce->args[0]);
Type *t = base_type(v.type);
if (is_type_pointer(t)) {
// IMPORTANT TODO(bill): Should there be a nil pointer check?
v = cg_emit_load(p, v);
t = type_deref(t);
}
return cg_builtin_len(p, v);
}
case BuiltinProc_cap: {
cgValue v = cg_build_expr(p, ce->args[0]);
Type *t = base_type(v.type);
if (is_type_pointer(t)) {
// IMPORTANT TODO(bill): Should there be a nil pointer check?
v = cg_emit_load(p, v);
t = type_deref(t);
}
return cg_builtin_cap(p, v);
}
case BuiltinProc_raw_data:
{
cgValue v = cg_build_expr(p, ce->args[0]);
return cg_builtin_raw_data(p, v);
}
case BuiltinProc_min:
if (ce->args.count == 2) {
Type *t = type_of_expr(expr);
cgValue x = cg_build_expr(p, ce->args[0]);
cgValue y = cg_build_expr(p, ce->args[1]);
return cg_builtin_min(p, t, x, y);
} else {
Type *t = type_of_expr(expr);
cgValue x = cg_build_expr(p, ce->args[0]);
for (isize i = 1; i < ce->args.count; i++) {
cgValue y = cg_build_expr(p, ce->args[i]);
x = cg_builtin_min(p, t, x, y);
}
return x;
}
break;
case BuiltinProc_max:
if (ce->args.count == 2) {
Type *t = type_of_expr(expr);
cgValue x = cg_build_expr(p, ce->args[0]);
cgValue y = cg_build_expr(p, ce->args[1]);
return cg_builtin_max(p, t, x, y);
} else {
Type *t = type_of_expr(expr);
cgValue x = cg_build_expr(p, ce->args[0]);
for (isize i = 1; i < ce->args.count; i++) {
cgValue y = cg_build_expr(p, ce->args[i]);
x = cg_builtin_max(p, t, x, y);
}
return x;
}
break;
case BuiltinProc_abs:
{
cgValue x = cg_build_expr(p, ce->args[0]);
return cg_builtin_abs(p, x);
}
case BuiltinProc_clamp:
{
cgValue x = cg_build_expr(p, ce->args[0]);
cgValue min = cg_build_expr(p, ce->args[1]);
cgValue max = cg_build_expr(p, ce->args[2]);
return cg_builtin_clamp(p, type_of_expr(expr), x, min, max);
}
case BuiltinProc_debug_trap:
tb_inst_debugbreak(p->func);
return {};
case BuiltinProc_trap:
tb_inst_trap(p->func);
return {};
case BuiltinProc_mem_zero:
{
cgValue ptr = cg_build_expr(p, ce->args[0]);
cgValue len = cg_build_expr(p, ce->args[1]);
return cg_builtin_mem_zero(p, ptr, len);
}
case BuiltinProc_mem_copy:
{
cgValue dst = cg_build_expr(p, ce->args[0]);
cgValue src = cg_build_expr(p, ce->args[1]);
cgValue len = cg_build_expr(p, ce->args[2]);
return cg_builtin_mem_copy(p, dst, src, len);
}
case BuiltinProc_mem_copy_non_overlapping:
{
cgValue dst = cg_build_expr(p, ce->args[0]);
cgValue src = cg_build_expr(p, ce->args[1]);
cgValue len = cg_build_expr(p, ce->args[2]);
return cg_builtin_mem_copy_non_overlapping(p, dst, src, len);
}
case BuiltinProc_overflow_add:
{
Type *res_type = type_of_expr(expr);
GB_ASSERT(res_type->kind == Type_Tuple);
GB_ASSERT(res_type->Tuple.variables.count == 2);
// TODO(bill): do a proper overflow add
Type *type = res_type->Tuple.variables[0]->type;
Type *ok_type = res_type->Tuple.variables[1]->type;
cgValue x = cg_build_expr(p, ce->args[0]);
cgValue y = cg_build_expr(p, ce->args[1]);
x = cg_emit_conv(p, x, type);
y = cg_emit_conv(p, y, type);
cgValue res = cg_emit_arith(p, Token_Add, x, y, type);
cgValue ok = cg_const_int(p, ok_type, false);
return cg_value_multi2(res, ok, res_type);
}
case BuiltinProc_ptr_offset:
{
cgValue ptr = cg_build_expr(p, ce->args[0]);
cgValue len = cg_build_expr(p, ce->args[1]);
len = cg_emit_conv(p, len, t_int);
return cg_emit_ptr_offset(p, ptr, len);
}
case BuiltinProc_ptr_sub:
{
Type *elem0 = type_deref(type_of_expr(ce->args[0]));
Type *elem1 = type_deref(type_of_expr(ce->args[1]));
GB_ASSERT(are_types_identical(elem0, elem1));
Type *elem = elem0;
cgValue ptr0 = cg_emit_conv(p, cg_build_expr(p, ce->args[0]), t_uintptr);
cgValue ptr1 = cg_emit_conv(p, cg_build_expr(p, ce->args[1]), t_uintptr);
cgValue diff = cg_emit_arith(p, Token_Sub, ptr0, ptr1, t_uintptr);
diff = cg_emit_conv(p, diff, t_int);
return cg_emit_arith(p, Token_Quo, diff, cg_const_int(p, t_int, type_size_of(elem)), t_int);
}
case BuiltinProc_type_info_of:
{
Ast *arg = ce->args[0];
TypeAndValue tav = type_and_value_of_expr(arg);
if (tav.mode == Addressing_Type) {
Type *t = default_type(type_of_expr(arg));
return cg_type_info(p, t);
}
GB_ASSERT(is_type_typeid(tav.type));
auto args = slice_make<cgValue>(permanent_allocator(), 1);
args[0] = cg_build_expr(p, arg);
return cg_emit_runtime_call(p, "__type_info_of", args);
}
case BuiltinProc_type_equal_proc:
return cg_equal_proc_value_for_type(p, ce->args[0]->tav.type);
case BuiltinProc_type_hasher_proc:
return cg_hasher_proc_value_for_type(p, ce->args[0]->tav.type);
}
GB_PANIC("TODO(bill): builtin procs %d %.*s", id, LIT(builtin_name));
return {};
}
+1040
View File
File diff suppressed because it is too large Load Diff
+482
View File
@@ -0,0 +1,482 @@
gb_internal TB_DebugType *cg_debug_type_internal(cgModule *m, Type *type);
gb_internal TB_DebugType *cg_debug_type(cgModule *m, Type *type) {
type = reduce_tuple_to_single_type(type);
mutex_lock(&m->debug_type_mutex);
defer (mutex_unlock(&m->debug_type_mutex));
TB_DebugType **found = map_get(&m->debug_type_map, type);
if (found) {
return *found;
}
TB_DebugType *res = cg_debug_type_internal(m, type);
map_set(&m->debug_type_map, type, res);
return res;
}
gb_internal TB_DebugType *cg_debug_type_for_proc(cgModule *m, Type *type) {
GB_ASSERT(is_type_proc(type));
TB_DebugType **func_found = nullptr;
TB_DebugType *func_ptr = cg_debug_type(m, type);
GB_ASSERT(func_ptr != nullptr);
mutex_lock(&m->debug_type_mutex);
func_found = map_get(&m->proc_debug_type_map, type);
mutex_unlock(&m->debug_type_mutex);
GB_ASSERT(func_found != nullptr);
return *func_found;
}
gb_internal TB_DebugType *cg_debug_type_internal_record(cgModule *m, Type *type, String const &record_name) {
Type *bt = base_type(type);
switch (bt->kind) {
case Type_Struct:
{
type_set_offsets(bt);
TB_DebugType *record = nullptr;
if (bt->Struct.is_raw_union) {
record = tb_debug_create_union(m->mod, record_name.len, cast(char const *)record_name.text);
} else {
record = tb_debug_create_struct(m->mod, record_name.len, cast(char const *)record_name.text);
}
if (record_name.len != 0) {
map_set(&m->debug_type_map, type, record);
}
TB_DebugType **fields = tb_debug_record_begin(record, bt->Struct.fields.count);
for_array(i, bt->Struct.fields) {
Entity *e = bt->Struct.fields[i];
Type *type = e->type;
if (is_type_proc(type)) {
type = t_rawptr;
}
TB_DebugType *field_type = cg_debug_type(m, type);
String name = e->token.string;
TB_CharUnits offset = cast(TB_CharUnits)bt->Struct.offsets[i];
if (name.len == 0) {
name = str_lit("_");
}
fields[i] = tb_debug_create_field(m->mod, field_type, name.len, cast(char const *)name.text, offset);
}
tb_debug_record_end(
record,
cast(TB_CharUnits)type_size_of(type),
cast(TB_CharUnits)type_align_of(type)
);
return record;
}
break;
case Type_Tuple:
{
GB_ASSERT(record_name.len == 0);
type_set_offsets(bt);
TB_DebugType *record = tb_debug_create_struct(m->mod, 0, "");
isize record_count = 0;
for (Entity *e : bt->Tuple.variables) {
if (e->kind == Entity_Variable) {
record_count += 1;
}
}
TB_DebugType **fields = tb_debug_record_begin(record, record_count);
for_array(i, bt->Tuple.variables) {
Entity *e = bt->Tuple.variables[i];
if (e->kind != Entity_Variable) {
continue;
}
Type *type = e->type;
if (is_type_proc(type)) {
type = t_rawptr;
}
TB_DebugType *field_type = cg_debug_type(m, type);
String name = e->token.string;
TB_CharUnits offset = cast(TB_CharUnits)bt->Tuple.offsets[i];
if (name.len == 0) {
name = str_lit("_");
}
fields[i] = tb_debug_create_field(m->mod, field_type, name.len, cast(char const *)name.text, offset);
}
tb_debug_record_end(
record,
cast(TB_CharUnits)type_size_of(type),
cast(TB_CharUnits)type_align_of(type)
);
return record;
}
break;
case Type_Union:
{
TB_DebugType *record = tb_debug_create_struct(m->mod, record_name.len, cast(char const *)record_name.text);
if (record_name.len != 0) {
map_set(&m->debug_type_map, type, record);
}
i64 variant_count = bt->Union.variants.count;
if (is_type_union_maybe_pointer(bt)) {
// NO TAG
GB_ASSERT(variant_count == 1);
TB_DebugType **fields = tb_debug_record_begin(record, variant_count);
TB_DebugType *variant_type = cg_debug_type(m, bt->Union.variants[0]);
fields[0] = tb_debug_create_field(m->mod, variant_type, -1, "v0", 0);
tb_debug_record_end(
record,
cast(TB_CharUnits)type_size_of(type),
cast(TB_CharUnits)type_align_of(type)
);
} else {
TB_DebugType **fields = tb_debug_record_begin(record, variant_count+1);
for_array(i, bt->Union.variants) {
Type *v = bt->Union.variants[i];
TB_DebugType *variant_type = cg_debug_type(m, v);
char name[32] = {};
u32 v_index = cast(u32)i;
if (bt->Union.kind != UnionType_no_nil) {
v_index += 1;
}
gb_snprintf(name, 31, "v%u", v_index);
fields[i] = tb_debug_create_field(m->mod, variant_type, -1, name, 0);
}
TB_DebugType *tag_type = cg_debug_type(m, union_tag_type(bt));
fields[variant_count] = tb_debug_create_field(m->mod, tag_type, -1, "tag", cast(TB_CharUnits)bt->Union.variant_block_size);
}
tb_debug_record_end(
record,
cast(TB_CharUnits)type_size_of(type),
cast(TB_CharUnits)type_align_of(type)
);
return record;
}
break;
}
return nullptr;
}
gb_internal TB_DebugType *cg_debug_type_internal(cgModule *m, Type *type) {
if (type == nullptr) {
return tb_debug_get_void(m->mod);
}
Type *original_type = type;
if (type->kind == Type_Named) {
String name = type->Named.name;
TB_DebugType *res = cg_debug_type_internal_record(m, type, name);
if (res) {
return res;
}
type = base_type(type->Named.base);
}
TB_CharUnits int_size = cast(TB_CharUnits)build_context.int_size;
TB_CharUnits ptr_size = cast(TB_CharUnits)build_context.ptr_size;
TB_CharUnits size = cast(TB_CharUnits)type_size_of(type);
TB_CharUnits align = cast(TB_CharUnits)type_align_of(type);
int bits = cast(int)(8*size);
bool is_signed = is_type_integer(core_type(type)) && !is_type_unsigned(core_type(type));
switch (type->kind) {
case Type_Basic:
switch (type->Basic.kind) {
case Basic_bool: return tb_debug_get_bool(m->mod);
case Basic_b8: return tb_debug_get_bool(m->mod);
case Basic_b16: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_b32: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_b64: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i8: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u8: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i16: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u16: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i32: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u32: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i64: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u64: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i128: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u128: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_rune: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_f16: return tb_debug_get_integer(m->mod, false, bits);
case Basic_f32: return tb_debug_get_float(m->mod, TB_FLT_32);
case Basic_f64: return tb_debug_get_float(m->mod, TB_FLT_64);
case Basic_complex32:
case Basic_complex64:
case Basic_complex128:
{
String name = basic_types[type->Basic.kind].Basic.name;
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
Type *et = base_complex_elem_type(type);
TB_CharUnits elem_size = cast(TB_CharUnits)type_size_of(et);
TB_DebugType *elem = cg_debug_type(m, et);
TB_DebugType **fields = tb_debug_record_begin(record, 2);
fields[0] = tb_debug_create_field(m->mod, elem, -1, "real", 0*elem_size);
fields[1] = tb_debug_create_field(m->mod, elem, -1, "imag", 1*elem_size);
tb_debug_record_end(record, size, align);
return record;
}
case Basic_quaternion64:
case Basic_quaternion128:
case Basic_quaternion256:
{
String name = basic_types[type->Basic.kind].Basic.name;
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
Type *et = base_complex_elem_type(type);
TB_CharUnits elem_size = cast(TB_CharUnits)type_size_of(et);
TB_DebugType *elem = cg_debug_type(m, et);
// @QuaternionLayout
TB_DebugType **fields = tb_debug_record_begin(record, 4);
fields[0] = tb_debug_create_field(m->mod, elem, -1, "imag", 0*elem_size);
fields[1] = tb_debug_create_field(m->mod, elem, -1, "jmag", 1*elem_size);
fields[2] = tb_debug_create_field(m->mod, elem, -1, "kmag", 2*elem_size);
fields[3] = tb_debug_create_field(m->mod, elem, -1, "real", 3*elem_size);
tb_debug_record_end(record, size, align);
return record;
}
case Basic_int: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_uint: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_uintptr: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_rawptr:
return tb_debug_create_ptr(m->mod, tb_debug_get_void(m->mod));
case Basic_string:
{
String name = basic_types[type->Basic.kind].Basic.name;
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
// @QuaternionLayout
TB_DebugType **fields = tb_debug_record_begin(record, 2);
fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, t_u8_ptr), -1, "data", 0*int_size);
fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int), -1, "len", 1*int_size);
tb_debug_record_end(record, size, align);
return record;
}
case Basic_cstring:
return tb_debug_create_ptr(m->mod, tb_debug_get_integer(m->mod, false, 8));
case Basic_any:
{
String name = basic_types[type->Basic.kind].Basic.name;
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
// @QuaternionLayout
TB_DebugType **fields = tb_debug_record_begin(record, 2);
fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, t_rawptr), -1, "data", 0*ptr_size);
fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_typeid), -1, "id", 1*ptr_size);
tb_debug_record_end(record, size, align);
return record;
}
case Basic_typeid: return tb_debug_get_integer(m->mod, false, bits);
case Basic_i16le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u16le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i32le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u32le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i64le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u64le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i128le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u128le: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i16be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u16be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i32be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u32be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i64be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u64be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_i128be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_u128be: return tb_debug_get_integer(m->mod, is_signed, bits);
case Basic_f16le: return tb_debug_get_integer(m->mod, false, bits);
case Basic_f32le: return tb_debug_get_float(m->mod, TB_FLT_32);
case Basic_f64le: return tb_debug_get_float(m->mod, TB_FLT_64);
case Basic_f16be: return tb_debug_get_integer(m->mod, false, bits);
case Basic_f32be: return tb_debug_get_float(m->mod, TB_FLT_32);
case Basic_f64be: return tb_debug_get_float(m->mod, TB_FLT_64);
}
break;
case Type_Generic:
GB_PANIC("SHOULD NEVER HIT");
break;
case Type_Pointer:
return tb_debug_create_ptr(m->mod, cg_debug_type(m, type->Pointer.elem));
case Type_MultiPointer:
return tb_debug_create_ptr(m->mod, cg_debug_type(m, type->MultiPointer.elem));
case Type_Array:
return tb_debug_create_array(m->mod, cg_debug_type(m, type->Array.elem), type->Array.count);
case Type_EnumeratedArray:
return tb_debug_create_array(m->mod, cg_debug_type(m, type->EnumeratedArray.elem), type->EnumeratedArray.count);
case Type_Slice:
{
String name = {};
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
TB_DebugType **fields = tb_debug_record_begin(record, 2);
fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, alloc_type_pointer(type->Slice.elem)), -1, "data", 0*int_size);
fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int), -1, "len", 1*int_size);
tb_debug_record_end(record, size, align);
return record;
}
case Type_DynamicArray:
{
String name = {};
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
TB_DebugType **fields = tb_debug_record_begin(record, 4);
fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, alloc_type_pointer(type->Slice.elem)), -1, "data", 0*int_size);
fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int), -1, "len", 1*int_size);
fields[2] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int), -1, "cap", 2*int_size);
fields[3] = tb_debug_create_field(m->mod, cg_debug_type(m, t_allocator), -1, "allocator", 3*int_size);
tb_debug_record_end(record, size, align);
return record;
}
case Type_Map:
return cg_debug_type(m, t_raw_map);
case Type_Struct:
case Type_Tuple:
case Type_Union:
return cg_debug_type_internal_record(m, type, {});
case Type_Enum:
return tb_debug_get_integer(m->mod, is_signed, bits);
case Type_Proc:
{
TypeProc *pt = &type->Proc;
isize param_count = 0;
isize return_count = 0;
bool is_odin_cc = is_calling_convention_odin(pt->calling_convention);
if (pt->params) for (Entity *e : pt->params->Tuple.variables) {
if (e->kind == Entity_Variable) {
param_count += 1;
}
}
if (pt->result_count > 0) {
if (is_odin_cc) {
// Split returns
param_count += pt->result_count-1;
return_count = 1;
} else {
return_count = 1;
}
}
if (pt->calling_convention == ProcCC_Odin) {
// `context` ptr
param_count += 1;
}
TB_CallingConv tb_cc = TB_CDECL;
if (pt->calling_convention == ProcCC_StdCall) {
tb_cc = TB_STDCALL;
}
TB_DebugType *func = tb_debug_create_func(m->mod, tb_cc, param_count, return_count, pt->c_vararg);
map_set(&m->proc_debug_type_map, original_type, func);
map_set(&m->proc_debug_type_map, type, func);
TB_DebugType *func_ptr = tb_debug_create_ptr(m->mod, func);
map_set(&m->debug_type_map, original_type, func_ptr);
map_set(&m->debug_type_map, type, func_ptr);
TB_DebugType **params = tb_debug_func_params(func);
TB_DebugType **returns = tb_debug_func_returns(func);
isize param_index = 0;
isize return_index = 0;
if (pt->params) for (Entity *e : pt->params->Tuple.variables) {
if (e->kind == Entity_Variable) {
Type *type = e->type;
if (is_type_proc(type)) {
type = t_rawptr;
}
String name = e->token.string;
if (name.len == 0) {
name = str_lit("_");
}
params[param_index++] = tb_debug_create_field(m->mod, cg_debug_type(m, type), name.len, cast(char const *)name.text, 0);
}
}
if (pt->result_count) {
GB_ASSERT(pt->results);
if (is_odin_cc) {
// Split Returns
for (isize i = 0; i < pt->results->Tuple.variables.count-1; i++) {
Entity *e = pt->results->Tuple.variables[i];
GB_ASSERT(e->kind == Entity_Variable);
Type *type = e->type;
if (is_type_proc(e->type)) {
type = t_rawptr;
}
type = alloc_type_pointer(type);
String name = e->token.string;
if (name.len == 0) {
name = str_lit("_");
}
params[param_index++] = tb_debug_create_field(m->mod, cg_debug_type(m, type), name.len, cast(char const *)name.text, 0);
}
Type *last_type = pt->results->Tuple.variables[pt->results->Tuple.variables.count-1]->type;
if (is_type_proc(last_type)) {
last_type = t_rawptr;
}
returns[return_index++] = cg_debug_type(m, last_type);
} else {
returns[return_index++] = cg_debug_type(m, pt->results);
}
}
if (pt->calling_convention == ProcCC_Odin) {
Type *type = t_context_ptr;
String name = str_lit("__.context_ptr");
params[param_index++] = tb_debug_create_field(m->mod, cg_debug_type(m, type), name.len, cast(char const *)name.text, 0);
}
GB_ASSERT_MSG(param_index == param_count, "%td vs %td for %s", param_index, param_count, type_to_string(type));
GB_ASSERT_MSG(return_index == return_count, "%td vs %td for %s", return_index, return_count, type_to_string(type));
return func_ptr;
}
break;
case Type_BitSet:
return cg_debug_type(m, bit_set_to_int(type));
case Type_SimdVector:
return tb_debug_create_array(m->mod, cg_debug_type(m, type->SimdVector.elem), type->SimdVector.count);
case Type_RelativePointer:
return cg_debug_type(m, type->RelativePointer.base_integer);
case Type_RelativeMultiPointer:
return cg_debug_type(m, type->RelativeMultiPointer.base_integer);
case Type_Matrix:
{
i64 count = matrix_type_total_internal_elems(type);
return tb_debug_create_array(m->mod, cg_debug_type(m, type->Matrix.elem), count);
}
case Type_SoaPointer:
{
String name = {};
TB_DebugType *record = tb_debug_create_struct(m->mod, name.len, cast(char const *)name.text);
TB_DebugType **fields = tb_debug_record_begin(record, 2);
fields[0] = tb_debug_create_field(m->mod, cg_debug_type(m, alloc_type_pointer(type->SoaPointer.elem)), -1, "ptr", 0*int_size);
fields[1] = tb_debug_create_field(m->mod, cg_debug_type(m, t_int), -1, "offset", 1*int_size);
tb_debug_record_end(record, size, align);
return record;
}
}
// TODO(bill): cg_debug_type
return tb_debug_get_void(m->mod);
}
+3871
View File
File diff suppressed because it is too large Load Diff
+1307
View File
File diff suppressed because it is too large Load Diff
+2614
View File
File diff suppressed because it is too large Load Diff
+983
View File
@@ -0,0 +1,983 @@
gb_internal void cg_global_const_type_info_ptr(cgModule *m, Type *type, TB_Global *global, i64 offset) {
GB_ASSERT(type != nullptr);
TB_Symbol *type_table_array = cg_find_symbol_from_entity(m, cg_global_type_info_data_entity);
i64 index_in_bytes = cast(i64)cg_type_info_index(m->info, type);
index_in_bytes *= type_size_of(t_type_info);
void *ti_ptr_ptr = tb_global_add_region(m->mod, global, offset, build_context.ptr_size);
// NOTE(bill): define the byte offset for the pointer
cg_write_int_at_ptr(ti_ptr_ptr, index_in_bytes, t_uintptr);
// NOTE(bill): this will add to the byte offset set previously
tb_global_add_symbol_reloc(m->mod, global, offset, type_table_array);
}
gb_internal cgValue cg_global_type_info_data_ptr(cgProcedure *p) {
cgValue v = cg_find_value_from_entity(p->module, cg_global_type_info_data_entity);
return cg_flatten_value(p, v);
}
gb_internal isize cg_type_info_index(CheckerInfo *info, Type *type, bool err_on_not_found) {
auto *set = &info->minimum_dependency_type_info_set;
isize index = type_info_index(info, type, err_on_not_found);
if (index >= 0) {
auto *found = map_get(set, index);
if (found) {
GB_ASSERT(*found >= 0);
return *found + 1;
}
}
if (err_on_not_found) {
GB_PANIC("NOT FOUND lb_type_info_index '%s' @ index %td", type_to_string(type), index);
}
return -1;
}
gb_internal cgValue cg_type_info(cgProcedure *p, Type *type) {
GB_ASSERT(!build_context.no_rtti);
type = default_type(type);
isize index = cg_type_info_index(p->module->info, type);
GB_ASSERT(index >= 0);
cgValue data = cg_global_type_info_data_ptr(p);
return cg_emit_array_epi(p, data, index);
}
gb_internal u64 cg_typeid_as_u64(cgModule *m, Type *type) {
GB_ASSERT(!build_context.no_rtti);
type = default_type(type);
u64 id = cast(u64)cg_type_info_index(m->info, type);
GB_ASSERT(id >= 0);
u64 kind = Typeid_Invalid;
u64 named = is_type_named(type) && type->kind != Type_Basic;
u64 special = 0;
u64 reserved = 0;
Type *bt = base_type(type);
TypeKind tk = bt->kind;
switch (tk) {
case Type_Basic: {
u32 flags = bt->Basic.flags;
if (flags & BasicFlag_Boolean) kind = Typeid_Boolean;
if (flags & BasicFlag_Integer) kind = Typeid_Integer;
if (flags & BasicFlag_Unsigned) kind = Typeid_Integer;
if (flags & BasicFlag_Float) kind = Typeid_Float;
if (flags & BasicFlag_Complex) kind = Typeid_Complex;
if (flags & BasicFlag_Pointer) kind = Typeid_Pointer;
if (flags & BasicFlag_String) kind = Typeid_String;
if (flags & BasicFlag_Rune) kind = Typeid_Rune;
} break;
case Type_Pointer: kind = Typeid_Pointer; break;
case Type_MultiPointer: kind = Typeid_Multi_Pointer; break;
case Type_Array: kind = Typeid_Array; break;
case Type_Matrix: kind = Typeid_Matrix; break;
case Type_EnumeratedArray: kind = Typeid_Enumerated_Array; break;
case Type_Slice: kind = Typeid_Slice; break;
case Type_DynamicArray: kind = Typeid_Dynamic_Array; break;
case Type_Map: kind = Typeid_Map; break;
case Type_Struct: kind = Typeid_Struct; break;
case Type_Enum: kind = Typeid_Enum; break;
case Type_Union: kind = Typeid_Union; break;
case Type_Tuple: kind = Typeid_Tuple; break;
case Type_Proc: kind = Typeid_Procedure; break;
case Type_BitSet: kind = Typeid_Bit_Set; break;
case Type_SimdVector: kind = Typeid_Simd_Vector; break;
case Type_RelativePointer: kind = Typeid_Relative_Pointer; break;
case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; break;
case Type_SoaPointer: kind = Typeid_SoaPointer; break;
}
if (is_type_cstring(type)) {
special = 1;
} else if (is_type_integer(type) && !is_type_unsigned(type)) {
special = 1;
}
u64 data = 0;
if (build_context.ptr_size == 4) {
GB_ASSERT(id <= (1u<<24u));
data |= (id &~ (1u<<24)) << 0u; // index
data |= (kind &~ (1u<<5)) << 24u; // kind
data |= (named &~ (1u<<1)) << 29u; // named
data |= (special &~ (1u<<1)) << 30u; // special
data |= (reserved &~ (1u<<1)) << 31u; // reserved
} else {
GB_ASSERT(build_context.ptr_size == 8);
GB_ASSERT(id <= (1ull<<56u));
data |= (id &~ (1ull<<56)) << 0ul; // index
data |= (kind &~ (1ull<<5)) << 56ull; // kind
data |= (named &~ (1ull<<1)) << 61ull; // named
data |= (special &~ (1ull<<1)) << 62ull; // special
data |= (reserved &~ (1ull<<1)) << 63ull; // reserved
}
return data;
}
gb_internal cgValue cg_typeid(cgProcedure *p, Type *t) {
u64 x = cg_typeid_as_u64(p->module, t);
return cg_value(tb_inst_uint(p->func, cg_data_type(t_typeid), x), t_typeid);
}
gb_internal void cg_set_type_info_member_types(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, Type *(*type_proc)(isize index, void *userdata)) {
if (count == 0) {
return;
}
void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
i64 offset_in_bytes = cg_global_type_info_member_types.index * type_size_of(cg_global_type_info_member_types.elem_type);
cg_global_type_info_member_types.index += count;
cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_types.global);
for (isize i = 0; i < count; i++) {
i64 elem_size = type_size_of(cg_global_type_info_member_types.elem_type);
Type *type = type_proc(i, userdata);
i64 offset_for_elem = offset_in_bytes + i*elem_size;
cg_global_const_type_info_ptr(m, type, cg_global_type_info_member_types.global, offset_for_elem);
}
void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, count, t_int);
}
gb_internal void cg_set_type_info_member_names(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, String (*name_proc)(isize index, void *userdata)) {
if (count == 0) {
return;
}
void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
i64 offset_in_bytes = cg_global_type_info_member_names.index * type_size_of(cg_global_type_info_member_names.elem_type);
cg_global_type_info_member_names.index += count;
cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_names.global);
for (isize i = 0; i < count; i++) {
i64 elem_size = type_size_of(cg_global_type_info_member_names.elem_type);
String name = name_proc(i, userdata);
i64 offset_for_elem = offset_in_bytes + i*elem_size;
cg_global_const_string(m, name, cg_global_type_info_member_names.elem_type, cg_global_type_info_member_names.global, offset_for_elem);
}
void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, count, t_int);
}
gb_internal void cg_set_type_info_member_offsets(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, i64 (*offset_proc)(isize index, void *userdata)) {
if (count == 0) {
return;
}
void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
i64 offset_in_bytes = cg_global_type_info_member_offsets.index * type_size_of(cg_global_type_info_member_offsets.elem_type);
cg_global_type_info_member_offsets.index += count;
cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_offsets.global);
for (isize i = 0; i < count; i++) {
i64 elem_size = type_size_of(cg_global_type_info_member_offsets.elem_type);
i64 the_offset = offset_proc(i, userdata);
i64 offset_for_elem = offset_in_bytes + i*elem_size;
void *offset_ptr = tb_global_add_region(m->mod, cg_global_type_info_member_offsets.global, offset_for_elem, elem_size);
cg_write_uint_at_ptr(offset_ptr, the_offset, t_uintptr);
}
void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, count, t_int);
}
gb_internal void cg_set_type_info_member_usings(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, bool (*usings_proc)(isize index, void *userdata)) {
if (count == 0) {
return;
}
void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
i64 offset_in_bytes = cg_global_type_info_member_usings.index * type_size_of(cg_global_type_info_member_usings.elem_type);
cg_global_type_info_member_usings.index += count;
cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_usings.global);
for (isize i = 0; i < count; i++) {
i64 elem_size = type_size_of(cg_global_type_info_member_usings.elem_type);
GB_ASSERT(elem_size == 1);
bool the_usings = usings_proc(i, userdata);
i64 offset_for_elem = offset_in_bytes + i*elem_size;
bool *usings_ptr = cast(bool *)tb_global_add_region(m->mod, cg_global_type_info_member_usings.global, offset_for_elem, 1);
*usings_ptr = the_usings;
}
void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, count, t_int);
}
gb_internal void cg_set_type_info_member_tags(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, String (*tag_proc)(isize index, void *userdata)) {
if (count == 0) {
return;
}
void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
i64 offset_in_bytes = cg_global_type_info_member_tags.index * type_size_of(cg_global_type_info_member_tags.elem_type);
cg_global_type_info_member_tags.index += count;
cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_tags.global);
for (isize i = 0; i < count; i++) {
i64 elem_size = type_size_of(cg_global_type_info_member_tags.elem_type);
String tag = tag_proc(i, userdata);
i64 offset_for_elem = offset_in_bytes + i*elem_size;
cg_global_const_string(m, tag, cg_global_type_info_member_tags.elem_type, cg_global_type_info_member_tags.global, offset_for_elem);
}
void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, count, t_int);
}
gb_internal void cg_set_type_info_member_enum_values(cgModule *m, TB_Global *global, isize offset, isize count, void *userdata, i64 (*value_proc)(isize index, void *userdata)) {
if (count == 0) {
return;
}
void *data_ptr = tb_global_add_region(m->mod, global, offset+0, build_context.ptr_size);
i64 offset_in_bytes = cg_global_type_info_member_enum_values.index * type_size_of(cg_global_type_info_member_enum_values.elem_type);
cg_global_type_info_member_enum_values.index += count;
cg_write_int_at_ptr(data_ptr, offset_in_bytes, t_uintptr);
tb_global_add_symbol_reloc(m->mod, global, offset+0, cast(TB_Symbol *)cg_global_type_info_member_enum_values.global);
for (isize i = 0; i < count; i++) {
i64 elem_size = type_size_of(cg_global_type_info_member_enum_values.elem_type);
GB_ASSERT(elem_size == 8);
i64 the_value = value_proc(i, userdata);
i64 offset_for_elem = offset_in_bytes + i*elem_size;
void *offset_ptr = tb_global_add_region(m->mod, cg_global_type_info_member_enum_values.global, offset_for_elem, elem_size);
cg_write_uint_at_ptr(offset_ptr, the_value, cg_global_type_info_member_enum_values.elem_type);
}
void *len_ptr = tb_global_add_region(m->mod, global, offset+build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, count, t_int);
}
gb_internal void cg_setup_type_info_data(cgModule *m) {
if (build_context.no_rtti) {
return;
}
CheckerInfo *info = m->info;
{ // Add type info data
isize max_type_info_count = info->minimum_dependency_type_info_set.count+1;
// gb_printf_err("max_type_info_count: %td\n", max_type_info_count);
Type *t = alloc_type_array(t_type_info, max_type_info_count);
i64 max_objects = cast(i64)max_type_info_count * cg_global_const_calculate_region_count_from_basic_type(t_type_info);
TB_Global *g = tb_global_create(m->mod, -1, CG_TYPE_INFO_DATA_NAME, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, max_objects);
cgValue value = cg_value(g, alloc_type_pointer(t));
cg_global_type_info_data_entity = alloc_entity_variable(nullptr, make_token_ident(CG_TYPE_INFO_DATA_NAME), t, EntityState_Resolved);
cg_add_symbol(m, cg_global_type_info_data_entity, cast(TB_Symbol *)g);
cg_add_entity(m, cg_global_type_info_data_entity, value);
}
{ // Type info member buffer
// NOTE(bill): Removes need for heap allocation by making it global memory
isize count = 0;
isize enum_count = 0;
for (Type *t : m->info->type_info_types) {
isize index = cg_type_info_index(m->info, t, false);
if (index < 0) {
continue;
}
switch (t->kind) {
case Type_Union:
count += t->Union.variants.count;
break;
case Type_Struct:
count += t->Struct.fields.count;
break;
case Type_Tuple:
count += t->Tuple.variables.count;
break;
case Type_Enum:
enum_count += t->Enum.fields.count;
break;
}
}
if (count > 0) {
{
char const *name = CG_TYPE_INFO_TYPES_NAME;
Type *t = alloc_type_array(t_type_info_ptr, count);
TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count*2);
cg_global_type_info_member_types = GlobalTypeInfoData{g, t, t_type_info_ptr, 0};
}
{
char const *name = CG_TYPE_INFO_NAMES_NAME;
Type *t = alloc_type_array(t_string, count);
TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count*2);
cg_global_type_info_member_names = GlobalTypeInfoData{g, t, t_string, 0};
}
{
char const *name = CG_TYPE_INFO_OFFSETS_NAME;
Type *t = alloc_type_array(t_uintptr, count);
TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
cg_global_type_info_member_offsets = GlobalTypeInfoData{g, t, t_uintptr, 0};
}
{
char const *name = CG_TYPE_INFO_USINGS_NAME;
Type *t = alloc_type_array(t_bool, count);
TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count);
cg_global_type_info_member_usings = GlobalTypeInfoData{g, t, t_bool, 0};
}
{
char const *name = CG_TYPE_INFO_TAGS_NAME;
Type *t = alloc_type_array(t_string, count);
TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, count*2);
cg_global_type_info_member_tags = GlobalTypeInfoData{g, t, t_string, 0};
}
}
if (enum_count > 0) {
char const *name = CG_TYPE_INFO_ENUM_VALUES_NAME;
Type *t = alloc_type_array(t_i64, enum_count);
TB_Global *g = tb_global_create(m->mod, -1, name, nullptr, TB_LINKAGE_PRIVATE);
tb_global_set_storage(m->mod, tb_module_get_rdata(m->mod), g, type_size_of(t), 16, enum_count);
cg_global_type_info_member_enum_values = GlobalTypeInfoData{g, t, t_i64, 0};
}
}
gb_unused(info);
i64 global_type_info_data_entity_count = 0;
// NOTE(bill): Set the type_table slice with the global backing array
TB_Global *type_table_slice = cast(TB_Global *)cg_find_symbol_from_entity(m, scope_lookup_current(m->info->runtime_package->scope, str_lit("type_table")));
GB_ASSERT(type_table_slice != nullptr);
TB_Global *type_table_array = cast(TB_Global *)cg_find_symbol_from_entity(m, cg_global_type_info_data_entity);
GB_ASSERT(type_table_array != nullptr);
Type *type = base_type(cg_global_type_info_data_entity->type);
GB_ASSERT(is_type_array(type));
global_type_info_data_entity_count = type->Array.count;
tb_global_add_symbol_reloc(m->mod, type_table_slice, 0, cast(TB_Symbol *)type_table_array);
void *len_ptr = tb_global_add_region(m->mod, type_table_slice, build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(len_ptr, type->Array.count, t_int);
// Useful types
Entity *type_info_flags_entity = find_core_entity(info->checker, str_lit("Type_Info_Flags"));
Type *t_type_info_flags = type_info_flags_entity->type;
GB_ASSERT(type_size_of(t_type_info_flags) == 4);
auto entries_handled = slice_make<bool>(heap_allocator(), cast(isize)global_type_info_data_entity_count);
defer (gb_free(heap_allocator(), entries_handled.data));
entries_handled[0] = true;
i64 type_info_size = type_size_of(t_type_info);
i64 size_offset = type_offset_of(t_type_info, 0);
i64 align_offset = type_offset_of(t_type_info, 1);
i64 flags_offset = type_offset_of(t_type_info, 2);
i64 id_offset = type_offset_of(t_type_info, 3);
i64 variant_offset = type_offset_of(t_type_info, 4);
Type *type_info_union = base_type(t_type_info)->Struct.fields[4]->type;
GB_ASSERT(type_info_union->kind == Type_Union);
i64 union_tag_offset = type_info_union->Union.variant_block_size;
Type *ti_union_tag_type = union_tag_type(type_info_union);
u64 union_tag_type_size = type_size_of(ti_union_tag_type);
auto const &set_bool = [](cgModule *m, TB_Global *global, i64 offset, bool value) {
bool *ptr = cast(bool *)tb_global_add_region(m->mod, global, offset, 1);
*ptr = value;
};
for_array(type_info_type_index, info->type_info_types) {
Type *t = info->type_info_types[type_info_type_index];
if (t == nullptr || t == t_invalid) {
continue;
}
isize entry_index = cg_type_info_index(info, t, false);
if (entry_index <= 0) {
continue;
}
if (entries_handled[entry_index]) {
continue;
}
entries_handled[entry_index] = true;
TB_Global *global = type_table_array;
i64 offset = entry_index * type_info_size;
i64 size = type_size_of(t);
i64 align = type_align_of(t);
u32 flags = type_info_flags_of_type(t);
u64 id = cg_typeid_as_u64(m, t);
void *size_ptr = tb_global_add_region(m->mod, global, offset+size_offset, build_context.int_size);
void *align_ptr = tb_global_add_region(m->mod, global, offset+align_offset, build_context.int_size);
void *flags_ptr = tb_global_add_region(m->mod, global, offset+flags_offset, 4);
void *id_ptr = tb_global_add_region(m->mod, global, offset+id_offset, build_context.ptr_size);
cg_write_int_at_ptr (size_ptr, size, t_int);
cg_write_int_at_ptr (align_ptr, align, t_int);
cg_write_int_at_ptr (flags_ptr, flags, t_u32);
cg_write_uint_at_ptr(id_ptr, id, t_typeid);
// add data to the offset to make it easier to deal with later on
offset += variant_offset;
Type *tag_type = nullptr;
switch (t->kind) {
case Type_Named: {
// Type_Info_Named :: struct {
// name: string,
// base: ^Type_Info,
// pkg: string,
// loc: Source_Code_Location,
// }
tag_type = t_type_info_named;
if (t->Named.type_name->pkg) {
i64 pkg_offset = type_offset_of(tag_type, 2);
String pkg_name = t->Named.type_name->pkg->name;
cg_global_const_string(m, pkg_name, t_string, global, offset+pkg_offset);
}
String proc_name = {};
if (t->Named.type_name->parent_proc_decl) {
DeclInfo *decl = t->Named.type_name->parent_proc_decl;
if (decl->entity && decl->entity->kind == Entity_Procedure) {
i64 name_offset = type_offset_of(tag_type, 0);
proc_name = decl->entity->token.string;
cg_global_const_string(m, proc_name, t_string, global, offset+name_offset);
}
}
i64 loc_offset = type_offset_of(tag_type, 3);
TokenPos pos = t->Named.type_name->token.pos;
cg_global_source_code_location_const(m, proc_name, pos, global, offset+loc_offset);
i64 base_offset = type_offset_of(tag_type, 1);
cg_global_const_type_info_ptr(m, t->Named.base, global, offset+base_offset);
break;
}
case Type_Basic:
switch (t->Basic.kind) {
case Basic_bool:
case Basic_b8:
case Basic_b16:
case Basic_b32:
case Basic_b64:
tag_type = t_type_info_boolean;
break;
case Basic_i8:
case Basic_u8:
case Basic_i16:
case Basic_u16:
case Basic_i32:
case Basic_u32:
case Basic_i64:
case Basic_u64:
case Basic_i128:
case Basic_u128:
case Basic_i16le:
case Basic_u16le:
case Basic_i32le:
case Basic_u32le:
case Basic_i64le:
case Basic_u64le:
case Basic_i128le:
case Basic_u128le:
case Basic_i16be:
case Basic_u16be:
case Basic_i32be:
case Basic_u32be:
case Basic_i64be:
case Basic_u64be:
case Basic_i128be:
case Basic_u128be:
case Basic_int:
case Basic_uint:
case Basic_uintptr: {
tag_type = t_type_info_integer;
bool is_signed = (t->Basic.flags & BasicFlag_Unsigned) == 0;
// NOTE(bill): This is matches the runtime layout
u8 endianness_value = 0;
if (t->Basic.flags & BasicFlag_EndianLittle) {
endianness_value = 1;
} else if (t->Basic.flags & BasicFlag_EndianBig) {
endianness_value = 2;
}
u8 *signed_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+0, 1);
u8 *endianness_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+1, 1);
*signed_ptr = is_signed;
*endianness_ptr = endianness_value;
break;
}
case Basic_rune:
tag_type = t_type_info_rune;
break;
case Basic_f16:
case Basic_f32:
case Basic_f64:
case Basic_f16le:
case Basic_f32le:
case Basic_f64le:
case Basic_f16be:
case Basic_f32be:
case Basic_f64be:
{
tag_type = t_type_info_float;
// // NOTE(bill): This is matches the runtime layout
u8 endianness_value = 0;
if (t->Basic.flags & BasicFlag_EndianLittle) {
endianness_value = 1;
} else if (t->Basic.flags & BasicFlag_EndianBig) {
endianness_value = 2;
}
u8 *ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+0, 1);
*ptr = endianness_value;
}
break;
case Basic_complex32:
case Basic_complex64:
case Basic_complex128:
tag_type = t_type_info_complex;
break;
case Basic_quaternion64:
case Basic_quaternion128:
case Basic_quaternion256:
tag_type = t_type_info_quaternion;
break;
case Basic_rawptr:
tag_type = t_type_info_pointer;
break;
case Basic_string:
tag_type = t_type_info_string;
break;
case Basic_cstring:
tag_type = t_type_info_string;
set_bool(m, global, offset+0, true);
break;
case Basic_any:
tag_type = t_type_info_any;
break;
case Basic_typeid:
tag_type = t_type_info_typeid;
break;
}
break;
case Type_Pointer:
tag_type = t_type_info_pointer;
cg_global_const_type_info_ptr(m, t->Pointer.elem, global, offset+0);
break;
case Type_MultiPointer:
tag_type = t_type_info_multi_pointer;
cg_global_const_type_info_ptr(m, t->MultiPointer.elem, global, offset+0);
break;
case Type_SoaPointer:
tag_type = t_type_info_soa_pointer;
cg_global_const_type_info_ptr(m, t->SoaPointer.elem, global, offset+0);
break;
case Type_Array:
{
tag_type = t_type_info_array;
cg_global_const_type_info_ptr(m, t->Array.elem, global, offset+0);
void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+1*build_context.int_size, build_context.int_size);
void *count_ptr = tb_global_add_region(m->mod, global, offset+2*build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->Array.elem), t_int);
cg_write_int_at_ptr(count_ptr, t->Array.count, t_int);
}
break;
case Type_EnumeratedArray:
{
tag_type = t_type_info_enumerated_array;
i64 elem_offset = type_offset_of(tag_type, 0);
i64 index_offset = type_offset_of(tag_type, 1);
i64 elem_size_offset = type_offset_of(tag_type, 2);
i64 count_offset = type_offset_of(tag_type, 3);
i64 min_value_offset = type_offset_of(tag_type, 4);
i64 max_value_offset = type_offset_of(tag_type, 5);
i64 is_sparse_offset = type_offset_of(tag_type, 6);
cg_global_const_type_info_ptr(m, t->EnumeratedArray.elem, global, offset+elem_offset);
cg_global_const_type_info_ptr(m, t->EnumeratedArray.index, global, offset+index_offset);
void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+elem_size_offset, build_context.int_size);
void *count_ptr = tb_global_add_region(m->mod, global, offset+count_offset, build_context.int_size);
void *min_value_ptr = tb_global_add_region(m->mod, global, offset+min_value_offset, type_size_of(t_type_info_enum_value));
void *max_value_ptr = tb_global_add_region(m->mod, global, offset+max_value_offset, type_size_of(t_type_info_enum_value));
cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->EnumeratedArray.elem), t_int);
cg_write_int_at_ptr(count_ptr, t->EnumeratedArray.count, t_int);
cg_write_int_at_ptr(min_value_ptr, exact_value_to_i64(*t->EnumeratedArray.min_value), t_type_info_enum_value);
cg_write_int_at_ptr(max_value_ptr, exact_value_to_i64(*t->EnumeratedArray.max_value), t_type_info_enum_value);
set_bool(m, global, offset+is_sparse_offset, t->EnumeratedArray.is_sparse);
}
break;
case Type_DynamicArray:
{
tag_type = t_type_info_dynamic_array;
cg_global_const_type_info_ptr(m, t->DynamicArray.elem, global, offset+0);
void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+1*build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->DynamicArray.elem), t_int);
}
break;
case Type_Slice:
{
tag_type = t_type_info_slice;
cg_global_const_type_info_ptr(m, t->Slice.elem, global, offset+0);
void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+1*build_context.int_size, build_context.int_size);
cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->Slice.elem), t_int);
}
break;
case Type_Proc:
{
tag_type = t_type_info_procedure;
i64 params_offset = type_offset_of(tag_type, 0);
i64 results_offset = type_offset_of(tag_type, 1);
i64 variadic_offset = type_offset_of(tag_type, 2);
i64 convention_offset = type_offset_of(tag_type, 3);
if (t->Proc.params) {
cg_global_const_type_info_ptr(m, t->Proc.params, global, offset+params_offset);
}
if (t->Proc.results) {
cg_global_const_type_info_ptr(m, t->Proc.results, global, offset+results_offset);
}
set_bool(m, global, offset+variadic_offset, t->Proc.variadic);
u8 *convention_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+convention_offset, 1);
*convention_ptr = cast(u8)t->Proc.calling_convention;
}
break;
case Type_Tuple:
{
tag_type = t_type_info_parameters;
i64 types_offset = type_offset_of(tag_type, 0);
i64 names_offset = type_offset_of(tag_type, 1);
i64 count = t->Tuple.variables.count;
cg_set_type_info_member_types(m, global, offset+types_offset, count, t, [](isize i, void *userdata) -> Type * {
Type *t = cast(Type *)userdata;
return t->Tuple.variables[i]->type;
});
cg_set_type_info_member_names(m, global, offset+names_offset, count, t, [](isize i, void *userdata) -> String {
Type *t = cast(Type *)userdata;
return t->Tuple.variables[i]->token.string;
});
}
break;
case Type_Enum:
{
tag_type = t_type_info_enum;
i64 base_offset = type_offset_of(tag_type, 0);
i64 names_offset = type_offset_of(tag_type, 1);
i64 values_offset = type_offset_of(tag_type, 2);
cg_global_const_type_info_ptr(m, t->Enum.base_type, global, offset+base_offset);
i64 count = t->Enum.fields.count;
cg_set_type_info_member_names(m, global, offset+names_offset, count, t, [](isize i, void *userdata) -> String {
Type *t = cast(Type *)userdata;
return t->Enum.fields[i]->token.string;
});
cg_set_type_info_member_enum_values(m, global, offset+values_offset, count, t, [](isize i, void *userdata) -> i64 {
Type *t = cast(Type *)userdata;
Entity *e = t->Enum.fields[i];
GB_ASSERT(e->kind == Entity_Constant);
return exact_value_to_i64(e->Constant.value);
});
}
break;
case Type_Struct:
{
tag_type = t_type_info_struct;
i64 types_offset = type_offset_of(tag_type, 0);
i64 names_offset = type_offset_of(tag_type, 1);
i64 offsets_offset = type_offset_of(tag_type, 2);
i64 usings_offset = type_offset_of(tag_type, 3);
i64 tags_offset = type_offset_of(tag_type, 4);
i64 is_packed_offset = type_offset_of(tag_type, 5);
i64 is_raw_union_offset = type_offset_of(tag_type, 6);
i64 is_no_copy_offset = type_offset_of(tag_type, 7);
i64 custom_align_offset = type_offset_of(tag_type, 8);
i64 equal_offset = type_offset_of(tag_type, 9);
i64 soa_kind_offset = type_offset_of(tag_type, 10);
i64 soa_base_type_offset = type_offset_of(tag_type, 11);
i64 soa_len_offset = type_offset_of(tag_type, 12);
// TODO(bill): equal proc stuff
gb_unused(equal_offset);
i64 count = t->Struct.fields.count;
cg_set_type_info_member_types(m, global, offset+types_offset, count, t, [](isize i, void *userdata) -> Type * {
Type *t = cast(Type *)userdata;
return t->Struct.fields[i]->type;
});
cg_set_type_info_member_names(m, global, offset+names_offset, count, t, [](isize i, void *userdata) -> String {
Type *t = cast(Type *)userdata;
return t->Struct.fields[i]->token.string;
});
cg_set_type_info_member_offsets(m, global, offset+offsets_offset, count, t, [](isize i, void *userdata) -> i64 {
Type *t = cast(Type *)userdata;
return t->Struct.offsets[i];
});
cg_set_type_info_member_usings(m, global, offset+usings_offset, count, t, [](isize i, void *userdata) -> bool {
Type *t = cast(Type *)userdata;
return (t->Struct.fields[i]->flags & EntityFlag_Using) != 0;
});
cg_set_type_info_member_tags(m, global, offset+tags_offset, count, t, [](isize i, void *userdata) -> String {
Type *t = cast(Type *)userdata;
return t->Struct.tags[i];
});
set_bool(m, global, offset+is_packed_offset, t->Struct.is_packed);
set_bool(m, global, offset+is_raw_union_offset, t->Struct.is_raw_union);
set_bool(m, global, offset+is_no_copy_offset, t->Struct.is_no_copy);
set_bool(m, global, offset+custom_align_offset, t->Struct.custom_align != 0);
if (t->Struct.soa_kind != StructSoa_None) {
u8 *kind_ptr = cast(u8 *)tb_global_add_region(m->mod, global, offset+soa_kind_offset, 1);
*kind_ptr = cast(u8)t->Struct.soa_kind;
cg_global_const_type_info_ptr(m, t->Struct.soa_elem, global, offset+soa_base_type_offset);
void *soa_len_ptr = tb_global_add_region(m->mod, global, offset+soa_len_offset, build_context.int_size);
cg_write_int_at_ptr(soa_len_ptr, t->Struct.soa_count, t_int);
}
}
break;
case Type_Union:
{
tag_type = t_type_info_union;
i64 variants_offset = type_offset_of(tag_type, 0);
i64 tag_offset_offset = type_offset_of(tag_type, 1);
i64 tag_type_offset = type_offset_of(tag_type, 2);
i64 equal_offset = type_offset_of(tag_type, 3);
i64 custom_align_offset = type_offset_of(tag_type, 4);
i64 no_nil_offset = type_offset_of(tag_type, 5);
i64 shared_nil_offset = type_offset_of(tag_type, 6);
// TODO(bill): equal procs
gb_unused(equal_offset);
i64 count = t->Union.variants.count;
cg_set_type_info_member_types(m, global, offset+variants_offset, count, t, [](isize i, void *userdata) -> Type * {
Type *t = cast(Type *)userdata;
return t->Union.variants[i];
});
void *tag_offset_ptr = tb_global_add_region(m->mod, global, offset+tag_offset_offset, build_context.ptr_size);
cg_write_uint_at_ptr(tag_offset_ptr, t->Union.variant_block_size, t_uintptr);
cg_global_const_type_info_ptr(m, union_tag_type(t), global, offset+tag_type_offset);
set_bool(m, global, offset+custom_align_offset, t->Union.custom_align != 0);
set_bool(m, global, offset+no_nil_offset, t->Union.kind == UnionType_no_nil);
set_bool(m, global, offset+shared_nil_offset, t->Union.kind == UnionType_shared_nil);
}
break;
case Type_Map:
{
tag_type = t_type_info_map;
i64 key_offset = type_offset_of(tag_type, 0);
i64 value_offset = type_offset_of(tag_type, 1);
i64 map_info_offset = type_offset_of(tag_type, 2);
// TODO(bill): map info
gb_unused(map_info_offset);
cg_global_const_type_info_ptr(m, t->Map.key, global, offset+key_offset);
cg_global_const_type_info_ptr(m, t->Map.value, global, offset+value_offset);
}
break;
case Type_BitSet:
{
tag_type = t_type_info_bit_set;
i64 elem_offset = type_offset_of(tag_type, 0);
i64 underlying_offset = type_offset_of(tag_type, 1);
i64 lower_offset = type_offset_of(tag_type, 2);
i64 upper_offset = type_offset_of(tag_type, 3);
cg_global_const_type_info_ptr(m, t->BitSet.elem, global, offset+elem_offset);
if (t->BitSet.underlying) {
cg_global_const_type_info_ptr(m, t->BitSet.underlying, global, offset+underlying_offset);
}
void *lower_ptr = tb_global_add_region(m->mod, global, offset+lower_offset, 8);
void *upper_ptr = tb_global_add_region(m->mod, global, offset+upper_offset, 8);
cg_write_int_at_ptr(lower_ptr, t->BitSet.lower, t_i64);
cg_write_int_at_ptr(upper_ptr, t->BitSet.upper, t_i64);
}
break;
case Type_SimdVector:
{
tag_type = t_type_info_simd_vector;
i64 elem_offset = type_offset_of(tag_type, 0);
i64 elem_size_offset = type_offset_of(tag_type, 1);
i64 count_offset = type_offset_of(tag_type, 2);
cg_global_const_type_info_ptr(m, t->SimdVector.elem, global, offset+elem_offset);
void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+elem_size_offset, build_context.int_size);
void *count_ptr = tb_global_add_region(m->mod, global, offset+count_offset, build_context.int_size);
cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->SimdVector.elem), t_int);
cg_write_int_at_ptr(count_ptr, t->SimdVector.count, t_int);
}
break;
case Type_RelativePointer:
{
tag_type = t_type_info_relative_pointer;
i64 pointer_offset = type_offset_of(tag_type, 0);
i64 base_integer_offset = type_offset_of(tag_type, 1);
cg_global_const_type_info_ptr(m, t->RelativePointer.pointer_type, global, offset+pointer_offset);
cg_global_const_type_info_ptr(m, t->RelativePointer.base_integer, global, offset+base_integer_offset);
}
break;
case Type_RelativeMultiPointer:
{
tag_type = t_type_info_relative_multi_pointer;
i64 pointer_offset = type_offset_of(tag_type, 0);
i64 base_integer_offset = type_offset_of(tag_type, 1);
cg_global_const_type_info_ptr(m, t->RelativePointer.pointer_type, global, offset+pointer_offset);
cg_global_const_type_info_ptr(m, t->RelativePointer.base_integer, global, offset+base_integer_offset);
}
break;
case Type_Matrix:
{
tag_type = t_type_info_matrix;
i64 elem_offset = type_offset_of(tag_type, 0);
i64 elem_size_offset = type_offset_of(tag_type, 1);
i64 elem_stride_offset = type_offset_of(tag_type, 2);
i64 row_count_offset = type_offset_of(tag_type, 3);
i64 column_count_offset = type_offset_of(tag_type, 4);
cg_global_const_type_info_ptr(m, t->Matrix.elem, global, offset+elem_offset);
void *elem_size_ptr = tb_global_add_region(m->mod, global, offset+elem_size_offset, build_context.int_size);
void *elem_stride_ptr = tb_global_add_region(m->mod, global, offset+elem_stride_offset, build_context.int_size);
void *row_count_ptr = tb_global_add_region(m->mod, global, offset+row_count_offset, build_context.int_size);
void *column_count_ptr = tb_global_add_region(m->mod, global, offset+column_count_offset, build_context.int_size);
cg_write_int_at_ptr(elem_size_ptr, type_size_of(t->Matrix.elem), t_int);
cg_write_int_at_ptr(elem_stride_ptr, matrix_type_stride_in_elems(t), t_int);
cg_write_int_at_ptr(row_count_ptr, t->Matrix.row_count, t_int);
cg_write_int_at_ptr(column_count_ptr, t->Matrix.column_count, t_int);
}
break;
}
if (tag_type != nullptr) {
i64 union_index = union_variant_index(type_info_union, tag_type);
GB_ASSERT(union_index != 0);
void *tag_ptr = tb_global_add_region(m->mod, global, offset+union_tag_offset, union_tag_type_size);
cg_write_int_at_ptr(tag_ptr, union_index, ti_union_tag_type);
}
}
}
+2 -2
View File
@@ -696,8 +696,8 @@ gb_internal void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (entry->kind != Token_Invalid && entry->hash == hash) { if (entry->kind != Token_Invalid && entry->hash == hash) {
if (str_eq(entry->text, token->string)) { if (str_eq(entry->text, token->string)) {
token->kind = entry->kind; token->kind = entry->kind;
if (token->kind == Token_not_in && entry->text == "notin") { if (token->kind == Token_not_in && entry->text.len == 5) {
syntax_warning(*token, "'notin' is deprecated in favour of 'not_in'"); syntax_error(*token, "Did you mean 'not_in'?");
} }
} }
} }
+127 -70
View File
@@ -143,6 +143,7 @@ struct TypeStruct {
Type * soa_elem; Type * soa_elem;
i32 soa_count; i32 soa_count;
StructSoaKind soa_kind; StructSoaKind soa_kind;
BlockingMutex mutex; // for settings offsets
bool is_polymorphic; bool is_polymorphic;
bool are_offsets_set : 1; bool are_offsets_set : 1;
@@ -244,6 +245,7 @@ struct TypeProc {
TYPE_KIND(Tuple, struct { \ TYPE_KIND(Tuple, struct { \
Slice<Entity *> variables; /* Entity_Variable */ \ Slice<Entity *> variables; /* Entity_Variable */ \
i64 * offsets; \ i64 * offsets; \
BlockingMutex mutex; /* for settings offsets */ \
bool are_offsets_being_processed; \ bool are_offsets_being_processed; \
bool are_offsets_set; \ bool are_offsets_set; \
bool is_packed; \ bool is_packed; \
@@ -265,8 +267,8 @@ struct TypeProc {
Type *pointer_type; \ Type *pointer_type; \
Type *base_integer; \ Type *base_integer; \
}) \ }) \
TYPE_KIND(RelativeSlice, struct { \ TYPE_KIND(RelativeMultiPointer, struct { \
Type *slice_type; \ Type *pointer_type; \
Type *base_integer; \ Type *base_integer; \
}) \ }) \
TYPE_KIND(Matrix, struct { \ TYPE_KIND(Matrix, struct { \
@@ -347,7 +349,7 @@ enum Typeid_Kind : u8 {
Typeid_Bit_Set, Typeid_Bit_Set,
Typeid_Simd_Vector, Typeid_Simd_Vector,
Typeid_Relative_Pointer, Typeid_Relative_Pointer,
Typeid_Relative_Slice, Typeid_Relative_Multi_Pointer,
Typeid_Matrix, Typeid_Matrix,
Typeid_SoaPointer, Typeid_SoaPointer,
}; };
@@ -594,6 +596,7 @@ gb_global Type *t_untyped_uninit = &basic_types[Basic_UntypedUninit];
gb_global Type *t_u8_ptr = nullptr; gb_global Type *t_u8_ptr = nullptr;
gb_global Type *t_u8_multi_ptr = nullptr;
gb_global Type *t_int_ptr = nullptr; gb_global Type *t_int_ptr = nullptr;
gb_global Type *t_i64_ptr = nullptr; gb_global Type *t_i64_ptr = nullptr;
gb_global Type *t_f64_ptr = nullptr; gb_global Type *t_f64_ptr = nullptr;
@@ -632,7 +635,7 @@ gb_global Type *t_type_info_map = nullptr;
gb_global Type *t_type_info_bit_set = nullptr; gb_global Type *t_type_info_bit_set = nullptr;
gb_global Type *t_type_info_simd_vector = nullptr; gb_global Type *t_type_info_simd_vector = nullptr;
gb_global Type *t_type_info_relative_pointer = nullptr; gb_global Type *t_type_info_relative_pointer = nullptr;
gb_global Type *t_type_info_relative_slice = nullptr; gb_global Type *t_type_info_relative_multi_pointer = nullptr;
gb_global Type *t_type_info_matrix = nullptr; gb_global Type *t_type_info_matrix = nullptr;
gb_global Type *t_type_info_soa_pointer = nullptr; gb_global Type *t_type_info_soa_pointer = nullptr;
@@ -661,7 +664,7 @@ gb_global Type *t_type_info_map_ptr = nullptr;
gb_global Type *t_type_info_bit_set_ptr = nullptr; gb_global Type *t_type_info_bit_set_ptr = nullptr;
gb_global Type *t_type_info_simd_vector_ptr = nullptr; gb_global Type *t_type_info_simd_vector_ptr = nullptr;
gb_global Type *t_type_info_relative_pointer_ptr = nullptr; gb_global Type *t_type_info_relative_pointer_ptr = nullptr;
gb_global Type *t_type_info_relative_slice_ptr = nullptr; gb_global Type *t_type_info_relative_multi_pointer_ptr = nullptr;
gb_global Type *t_type_info_matrix_ptr = nullptr; gb_global Type *t_type_info_matrix_ptr = nullptr;
gb_global Type *t_type_info_soa_pointer_ptr = nullptr; gb_global Type *t_type_info_soa_pointer_ptr = nullptr;
@@ -725,7 +728,7 @@ struct TypePath;
gb_internal i64 type_size_of (Type *t); gb_internal i64 type_size_of (Type *t);
gb_internal i64 type_align_of (Type *t); gb_internal i64 type_align_of (Type *t);
gb_internal i64 type_offset_of (Type *t, i32 index); gb_internal i64 type_offset_of (Type *t, i64 index, Type **field_type_=nullptr);
gb_internal gbString type_to_string (Type *type, bool shorthand=true); gb_internal gbString type_to_string (Type *type, bool shorthand=true);
gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true); gb_internal gbString type_to_string (Type *type, gbAllocator allocator, bool shorthand=true);
gb_internal i64 type_size_of_internal(Type *t, TypePath *path); gb_internal i64 type_size_of_internal(Type *t, TypePath *path);
@@ -734,6 +737,7 @@ gb_internal Type * bit_set_to_int(Type *t);
gb_internal bool are_types_identical(Type *x, Type *y); gb_internal bool are_types_identical(Type *x, Type *y);
gb_internal bool is_type_pointer(Type *t); gb_internal bool is_type_pointer(Type *t);
gb_internal bool is_type_multi_pointer(Type *t);
gb_internal bool is_type_soa_pointer(Type *t); gb_internal bool is_type_soa_pointer(Type *t);
gb_internal bool is_type_proc(Type *t); gb_internal bool is_type_proc(Type *t);
gb_internal bool is_type_slice(Type *t); gb_internal bool is_type_slice(Type *t);
@@ -821,6 +825,9 @@ gb_internal void type_path_pop(TypePath *tp) {
#define FAILURE_ALIGNMENT 0 #define FAILURE_ALIGNMENT 0
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) { gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
if (ptr_set_exists(s, t)) { if (ptr_set_exists(s, t)) {
return true; return true;
} }
@@ -829,6 +836,10 @@ gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
} }
gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) { gb_internal bool type_ptr_set_exists(PtrSet<Type *> *s, Type *t) {
if (t == nullptr) {
return true;
}
if (ptr_set_exists(s, t)) { if (ptr_set_exists(s, t)) {
return true; return true;
} }
@@ -988,7 +999,7 @@ gb_internal Type *alloc_type_enumerated_array(Type *elem, Type *index, ExactValu
gb_internal Type *alloc_type_slice(Type *elem) { gb_internal Type *alloc_type_slice(Type *elem) {
Type *t = alloc_type(Type_Slice); Type *t = alloc_type(Type_Slice);
t->Array.elem = elem; t->Slice.elem = elem;
return t; return t;
} }
@@ -1025,12 +1036,12 @@ gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_int
return t; return t;
} }
gb_internal Type *alloc_type_relative_slice(Type *slice_type, Type *base_integer) { gb_internal Type *alloc_type_relative_multi_pointer(Type *pointer_type, Type *base_integer) {
GB_ASSERT(is_type_slice(slice_type)); GB_ASSERT(is_type_multi_pointer(pointer_type));
GB_ASSERT(is_type_integer(base_integer)); GB_ASSERT(is_type_integer(base_integer));
Type *t = alloc_type(Type_RelativeSlice); Type *t = alloc_type(Type_RelativeMultiPointer);
t->RelativeSlice.slice_type = slice_type; t->RelativeMultiPointer.pointer_type = pointer_type;
t->RelativeSlice.base_integer = base_integer; t->RelativeMultiPointer.base_integer = base_integer;
return t; return t;
} }
@@ -1541,9 +1552,9 @@ gb_internal bool is_type_relative_pointer(Type *t) {
t = base_type(t); t = base_type(t);
return t->kind == Type_RelativePointer; return t->kind == Type_RelativePointer;
} }
gb_internal bool is_type_relative_slice(Type *t) { gb_internal bool is_type_relative_multi_pointer(Type *t) {
t = base_type(t); t = base_type(t);
return t->kind == Type_RelativeSlice; return t->kind == Type_RelativeMultiPointer;
} }
gb_internal bool is_type_u8_slice(Type *t) { gb_internal bool is_type_u8_slice(Type *t) {
@@ -1960,7 +1971,7 @@ gb_internal bool is_type_indexable(Type *t) {
return true; return true;
case Type_EnumeratedArray: case Type_EnumeratedArray:
return true; return true;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
return true; return true;
case Type_Matrix: case Type_Matrix:
return true; return true;
@@ -1979,7 +1990,7 @@ gb_internal bool is_type_sliceable(Type *t) {
return true; return true;
case Type_EnumeratedArray: case Type_EnumeratedArray:
return false; return false;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
return true; return true;
case Type_Matrix: case Type_Matrix:
return false; return false;
@@ -2185,12 +2196,12 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
} }
break; break;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
if (is_type_polymorphic(t->RelativeSlice.slice_type, or_specialized)) { if (is_type_polymorphic(t->RelativeMultiPointer.pointer_type, or_specialized)) {
return true; return true;
} }
if (t->RelativeSlice.base_integer != nullptr && if (t->RelativeMultiPointer.base_integer != nullptr &&
is_type_polymorphic(t->RelativeSlice.base_integer, or_specialized)) { is_type_polymorphic(t->RelativeMultiPointer.base_integer, or_specialized)) {
return true; return true;
} }
break; break;
@@ -2248,7 +2259,7 @@ gb_internal bool type_has_nil(Type *t) {
return false; return false;
case Type_RelativePointer: case Type_RelativePointer:
case Type_RelativeSlice: case Type_RelativeMultiPointer:
return true; return true;
} }
return false; return false;
@@ -2415,7 +2426,7 @@ gb_internal bool is_type_load_safe(Type *type) {
return true; return true;
case Type_RelativePointer: case Type_RelativePointer:
case Type_RelativeSlice: case Type_RelativeMultiPointer:
return true; return true;
case Type_Pointer: case Type_Pointer:
@@ -2666,7 +2677,6 @@ gb_internal bool are_types_identical_internal(Type *x, Type *y, bool check_tuple
x->Struct.soa_kind == y->Struct.soa_kind && x->Struct.soa_kind == y->Struct.soa_kind &&
x->Struct.soa_count == y->Struct.soa_count && x->Struct.soa_count == y->Struct.soa_count &&
are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) { are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
// TODO(bill); Fix the custom alignment rule
for_array(i, x->Struct.fields) { for_array(i, x->Struct.fields) {
Entity *xf = x->Struct.fields[i]; Entity *xf = x->Struct.fields[i];
Entity *yf = y->Struct.fields[i]; Entity *yf = y->Struct.fields[i];
@@ -2807,7 +2817,6 @@ gb_internal i64 union_tag_size(Type *u) {
return 0; return 0;
} }
// TODO(bill): Is this an okay approach?
i64 max_align = 1; i64 max_align = 1;
if (u->Union.variants.count < 1ull<<8) { if (u->Union.variants.count < 1ull<<8) {
@@ -2817,7 +2826,7 @@ gb_internal i64 union_tag_size(Type *u) {
} else if (u->Union.variants.count < 1ull<<32) { } else if (u->Union.variants.count < 1ull<<32) {
max_align = 4; max_align = 4;
} else { } else {
GB_PANIC("how many variants do you have?!"); compiler_error("how many variants do you have?! %lld", cast(long long)u->Union.variants.count);
} }
for_array(i, u->Union.variants) { for_array(i, u->Union.variants) {
@@ -3081,7 +3090,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
mutex_lock(md->mutex); mutex_lock(md->mutex);
defer (mutex_unlock(md->mutex)); defer (mutex_unlock(md->mutex));
for (TypeNameObjCMetadataEntry const &entry : md->value_entries) { for (TypeNameObjCMetadataEntry const &entry : md->value_entries) {
GB_ASSERT(entry.entity->kind == Entity_Procedure); GB_ASSERT(entry.entity->kind == Entity_Procedure || entry.entity->kind == Entity_ProcGroup);
if (entry.name == field_name) { if (entry.name == field_name) {
sel.entity = entry.entity; sel.entity = entry.entity;
sel.pseudo_field = true; sel.pseudo_field = true;
@@ -3136,8 +3145,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
switch (type->Basic.kind) { switch (type->Basic.kind) {
case Basic_any: { case Basic_any: {
#if 1 #if 1
// IMPORTANT TODO(bill): Should these members be available to should I only allow them with
// `Raw_Any` type?
String data_str = str_lit("data"); String data_str = str_lit("data");
String id_str = str_lit("id"); String id_str = str_lit("id");
gb_local_persist Entity *entity__any_data = alloc_entity_field(nullptr, make_token_ident(data_str), t_rawptr, false, 0); gb_local_persist Entity *entity__any_data = alloc_entity_field(nullptr, make_token_ident(data_str), t_rawptr, false, 0);
@@ -3623,8 +3630,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
case Type_RelativePointer: case Type_RelativePointer:
return type_align_of_internal(t->RelativePointer.base_integer, path); return type_align_of_internal(t->RelativePointer.base_integer, path);
case Type_RelativeSlice: case Type_RelativeMultiPointer:
return type_align_of_internal(t->RelativeSlice.base_integer, path); return type_align_of_internal(t->RelativeMultiPointer.base_integer, path);
case Type_SoaPointer: case Type_SoaPointer:
return build_context.int_size; return build_context.int_size;
@@ -3645,28 +3652,35 @@ gb_internal i64 *type_set_offsets_of(Slice<Entity *> const &fields, bool is_pack
} }
} else if (is_packed) { } else if (is_packed) {
for_array(i, fields) { for_array(i, fields) {
i64 size = type_size_of(fields[i]->type); if (fields[i]->kind != Entity_Variable) {
offsets[i] = curr_offset; offsets[i] = -1;
curr_offset += size; } else {
i64 size = type_size_of(fields[i]->type);
offsets[i] = curr_offset;
curr_offset += size;
}
} }
} else { } else {
for_array(i, fields) { for_array(i, fields) {
Type *t = fields[i]->type; if (fields[i]->kind != Entity_Variable) {
i64 align = gb_max(type_align_of(t), 1); offsets[i] = -1;
i64 size = gb_max(type_size_of( t), 0); } else {
curr_offset = align_formula(curr_offset, align); Type *t = fields[i]->type;
offsets[i] = curr_offset; i64 align = gb_max(type_align_of(t), 1);
curr_offset += size; i64 size = gb_max(type_size_of( t), 0);
curr_offset = align_formula(curr_offset, align);
offsets[i] = curr_offset;
curr_offset += size;
}
} }
} }
return offsets; return offsets;
} }
gb_internal bool type_set_offsets(Type *t) { gb_internal bool type_set_offsets(Type *t) {
MUTEX_GUARD(&g_type_mutex); // TODO(bill): only per struct
t = base_type(t); t = base_type(t);
if (t->kind == Type_Struct) { if (t->kind == Type_Struct) {
MUTEX_GUARD(&t->Struct.mutex);
if (!t->Struct.are_offsets_set) { if (!t->Struct.are_offsets_set) {
t->Struct.are_offsets_being_processed = true; t->Struct.are_offsets_being_processed = true;
t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union); t->Struct.offsets = type_set_offsets_of(t->Struct.fields, t->Struct.is_packed, t->Struct.is_raw_union);
@@ -3675,6 +3689,7 @@ gb_internal bool type_set_offsets(Type *t) {
return true; return true;
} }
} else if (is_type_tuple(t)) { } else if (is_type_tuple(t)) {
MUTEX_GUARD(&t->Tuple.mutex);
if (!t->Tuple.are_offsets_set) { if (!t->Tuple.are_offsets_set) {
t->Tuple.are_offsets_being_processed = true; t->Tuple.are_offsets_being_processed = true;
t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false); t->Tuple.offsets = type_set_offsets_of(t->Tuple.variables, t->Tuple.is_packed, false);
@@ -3849,7 +3864,6 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
max = size; max = size;
} }
} }
// TODO(bill): Is this how it should work?
return align_formula(max, align); return align_formula(max, align);
} else { } else {
i64 count = 0, size = 0, align = 0; i64 count = 0, size = 0, align = 0;
@@ -3899,59 +3913,100 @@ gb_internal i64 type_size_of_internal(Type *t, TypePath *path) {
case Type_RelativePointer: case Type_RelativePointer:
return type_size_of_internal(t->RelativePointer.base_integer, path); return type_size_of_internal(t->RelativePointer.base_integer, path);
case Type_RelativeSlice: case Type_RelativeMultiPointer:
return 2*type_size_of_internal(t->RelativeSlice.base_integer, path); return type_size_of_internal(t->RelativeMultiPointer.base_integer, path);
} }
// Catch all // Catch all
return build_context.ptr_size; return build_context.ptr_size;
} }
gb_internal i64 type_offset_of(Type *t, i32 index) { gb_internal i64 type_offset_of(Type *t, i64 index, Type **field_type_) {
t = base_type(t); t = base_type(t);
if (t->kind == Type_Struct) { switch (t->kind) {
case Type_Struct:
type_set_offsets(t); type_set_offsets(t);
if (gb_is_between(index, 0, t->Struct.fields.count-1)) { if (gb_is_between(index, 0, t->Struct.fields.count-1)) {
GB_ASSERT(t->Struct.offsets != nullptr); GB_ASSERT(t->Struct.offsets != nullptr);
if (field_type_) *field_type_ = t->Struct.fields[index]->type;
return t->Struct.offsets[index]; return t->Struct.offsets[index];
} }
} else if (t->kind == Type_Tuple) { break;
case Type_Tuple:
type_set_offsets(t); type_set_offsets(t);
if (gb_is_between(index, 0, t->Tuple.variables.count-1)) { if (gb_is_between(index, 0, t->Tuple.variables.count-1)) {
GB_ASSERT(t->Tuple.offsets != nullptr); GB_ASSERT(t->Tuple.offsets != nullptr);
return t->Tuple.offsets[index]; if (field_type_) *field_type_ = t->Tuple.variables[index]->type;
i64 offset = t->Tuple.offsets[index];
GB_ASSERT(offset >= 0);
return offset;
} }
} else if (t->kind == Type_Basic) { break;
case Type_Array:
GB_ASSERT(0 <= index && index < t->Array.count);
return index * type_size_of(t->Array.elem);
case Type_Basic:
if (t->Basic.kind == Basic_string) { if (t->Basic.kind == Basic_string) {
switch (index) { switch (index) {
case 0: return 0; // data case 0:
case 1: return build_context.int_size; // len if (field_type_) *field_type_ = t_u8_ptr;
return 0; // data
case 1:
if (field_type_) *field_type_ = t_int;
return build_context.int_size; // len
} }
} else if (t->Basic.kind == Basic_any) { } else if (t->Basic.kind == Basic_any) {
switch (index) { switch (index) {
case 0: return 0; // type_info case 0:
case 1: return build_context.ptr_size; // data if (field_type_) *field_type_ = t_rawptr;
return 0; // data
case 1:
if (field_type_) *field_type_ = t_typeid;
return build_context.ptr_size; // id
} }
} }
} else if (t->kind == Type_Slice) { break;
case Type_Slice:
switch (index) { switch (index) {
case 0: return 0; // data case 0:
case 1: return 1*build_context.int_size; // len if (field_type_) *field_type_ = alloc_type_multi_pointer(t->Slice.elem);
case 2: return 2*build_context.int_size; // cap return 0; // data
case 1:
if (field_type_) *field_type_ = t_int;
return 1*build_context.int_size; // len
} }
} else if (t->kind == Type_DynamicArray) { break;
case Type_DynamicArray:
switch (index) { switch (index) {
case 0: return 0; // data case 0:
case 1: return 1*build_context.int_size; // len if (field_type_) *field_type_ = alloc_type_multi_pointer(t->DynamicArray.elem);
case 2: return 2*build_context.int_size; // cap return 0; // data
case 3: return 3*build_context.int_size; // allocator case 1:
if (field_type_) *field_type_ = t_int;
return 1*build_context.int_size; // len
case 2:
if (field_type_) *field_type_ = t_int;
return 2*build_context.int_size; // cap
case 3:
if (field_type_) *field_type_ = t_allocator;
return 3*build_context.int_size; // allocator
} }
} else if (t->kind == Type_Union) { break;
/* i64 s = */ type_size_of(t); case Type_Union:
switch (index) { if (!is_type_union_maybe_pointer(t)) {
case -1: return align_formula(t->Union.variant_block_size, build_context.ptr_size); // __type_info /* i64 s = */ type_size_of(t);
switch (index) {
case -1:
if (field_type_) *field_type_ = union_tag_type(t);
union_tag_size(t);
return t->Union.variant_block_size;
}
} }
break;
} }
GB_ASSERT(index == 0);
return 0; return 0;
} }
@@ -3965,8 +4020,10 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
i32 index = sel.index[i]; i32 index = sel.index[i];
t = base_type(t); t = base_type(t);
offset += type_offset_of(t, index); offset += type_offset_of(t, index);
if (t->kind == Type_Struct && !t->Struct.is_raw_union) { if (t->kind == Type_Struct) {
t = t->Struct.fields[index]->type; t = t->Struct.fields[index]->type;
} else if (t->kind == Type_Array) {
t = t->Array.elem;
} else { } else {
// NOTE(bill): No need to worry about custom types, just need the alignment // NOTE(bill): No need to worry about custom types, just need the alignment
switch (t->kind) { switch (t->kind) {
@@ -4410,11 +4467,11 @@ gb_internal gbString write_type_to_string(gbString str, Type *type, bool shortha
str = gb_string_append_fmt(str, ") "); str = gb_string_append_fmt(str, ") ");
str = write_type_to_string(str, type->RelativePointer.pointer_type); str = write_type_to_string(str, type->RelativePointer.pointer_type);
break; break;
case Type_RelativeSlice: case Type_RelativeMultiPointer:
str = gb_string_append_fmt(str, "#relative("); str = gb_string_append_fmt(str, "#relative(");
str = write_type_to_string(str, type->RelativeSlice.base_integer); str = write_type_to_string(str, type->RelativePointer.base_integer);
str = gb_string_append_fmt(str, ") "); str = gb_string_append_fmt(str, ") ");
str = write_type_to_string(str, type->RelativeSlice.slice_type); str = write_type_to_string(str, type->RelativePointer.pointer_type);
break; break;
case Type_Matrix: case Type_Matrix:
+14 -20
View File
@@ -21,16 +21,13 @@ main :: proc() {
@test @test
test_read :: proc(t: ^testing.T) { test_read :: proc(t: ^testing.T) {
using hxa
filename := tc.get_data_path(t, TEAPOT_PATH) filename := tc.get_data_path(t, TEAPOT_PATH)
defer delete(filename) defer delete(filename)
file, err := read_from_file(filename) file, err := hxa.read_from_file(filename)
e :: hxa.Read_Error.None e :: hxa.Read_Error.None
tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e)) tc.expect(t, err == e, fmt.tprintf("%v: read_from_file(%v) -> %v != %v", #procedure, filename, err, e))
defer file_destroy(file) defer hxa.file_destroy(file)
/* Header */ /* Header */
tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v", tc.expect(t, file.magic_number == 0x417848, fmt.tprintf("%v: file.magic_number %v != %v",
@@ -134,38 +131,35 @@ test_read :: proc(t: ^testing.T) {
@test @test
test_write :: proc(t: ^testing.T) { test_write :: proc(t: ^testing.T) {
n1: hxa.Node
using hxa
n1 :Node
n1_m1_value := []f64le{0.4, -1.23, 2341.6, -333.333} n1_m1_value := []f64le{0.4, -1.23, 2341.6, -333.333}
n1_m1 := Meta{"m1", n1_m1_value} n1_m1 := hxa.Meta{"m1", n1_m1_value}
n1.meta_data = []Meta{n1_m1} n1.meta_data = []hxa.Meta{n1_m1}
n1_l1 := Layer{"l1", 2, []f32le{32.1, -41.3}} n1_l1 := hxa.Layer{"l1", 2, []f32le{32.1, -41.3}}
n1_l2 := Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}} n1_l2 := hxa.Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}}
n1_content := Node_Image{Image_Type.Image_1D, [3]u32le{1, 1, 2}, Layer_Stack{n1_l1, n1_l2}} n1_content := hxa.Node_Image{.Image_1D, [3]u32le{1, 1, 2}, hxa.Layer_Stack{n1_l1, n1_l2}}
n1.content = n1_content n1.content = n1_content
w_file :File w_file: hxa.File
w_file.nodes = []Node{n1} w_file.nodes = []hxa.Node{n1}
required_size := required_write_size(w_file) required_size := hxa.required_write_size(w_file)
buf := make([]u8, required_size) buf := make([]u8, required_size)
n, write_err := write(buf, w_file) n, write_err := hxa.write(buf, w_file)
write_e :: hxa.Write_Error.None write_e :: hxa.Write_Error.None
tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e)) tc.expect(t, write_err == write_e, fmt.tprintf("%v: write_err %v != %v", #procedure, write_err, write_e))
tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size)) tc.expect(t, n == required_size, fmt.tprintf("%v: n %v != %v", #procedure, n, required_size))
file, read_err := read(buf) file, read_err := hxa.read(buf)
read_e :: hxa.Read_Error.None read_e :: hxa.Read_Error.None
tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e)) tc.expect(t, read_err == read_e, fmt.tprintf("%v: read_err %v != %v", #procedure, read_err, read_e))
defer file_destroy(file) defer hxa.file_destroy(file)
delete(buf) delete(buf)
+33 -36
View File
@@ -47,7 +47,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "恥ずべきフクロウ", expected_doctype = "恥ずべきフクロウ",
}, },
crc32 = 0x30d82264, crc32 = 0xe9b62f03,
}, },
{ {
@@ -62,7 +62,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "恥ずべきフクロウ", expected_doctype = "恥ずべきフクロウ",
}, },
crc32 = 0xad31d8e8, crc32 = 0x9c2643ed,
}, },
{ {
@@ -77,7 +77,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "TS", expected_doctype = "TS",
}, },
crc32 = 0x7bce2630, crc32 = 0x859b7443,
}, },
{ {
@@ -92,7 +92,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "xliff", expected_doctype = "xliff",
}, },
crc32 = 0x43f19d61, crc32 = 0x3deaf329,
}, },
{ {
@@ -107,7 +107,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "xliff", expected_doctype = "xliff",
}, },
crc32 = 0x961e7635, crc32 = 0x0c55e287,
}, },
{ {
@@ -118,7 +118,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "html", expected_doctype = "html",
}, },
crc32 = 0x573c1033, crc32 = 0x05373317,
}, },
{ {
@@ -129,7 +129,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "html", expected_doctype = "html",
}, },
crc32 = 0x82588917, crc32 = 0x3b6d4a90,
}, },
{ {
@@ -140,7 +140,7 @@ TESTS :: []TEST{
}, },
expected_doctype = "html", expected_doctype = "html",
}, },
crc32 = 0x5e74d8a6, crc32 = 0x5be2ffdc,
}, },
/* /*
@@ -170,7 +170,7 @@ TESTS :: []TEST{
expected_doctype = "", expected_doctype = "",
}, },
err = .None, err = .None,
crc32 = 0xcaa042b9, crc32 = 0x420dbac5,
}, },
} }
@@ -214,43 +214,40 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
*/ */
print :: proc(writer: io.Writer, doc: ^xml.Document) -> (written: int, err: io.Error) { print :: proc(writer: io.Writer, doc: ^xml.Document) -> (written: int, err: io.Error) {
if doc == nil { return } if doc == nil { return }
using fmt
written += wprintf(writer, "[XML Prolog]\n") written += fmt.wprintf(writer, "[XML Prolog]\n")
for attr in doc.prologue { for attr in doc.prologue {
written += wprintf(writer, "\t%v: %v\n", attr.key, attr.val) written += fmt.wprintf(writer, "\t%v: %v\n", attr.key, attr.val)
} }
written += wprintf(writer, "[Encoding] %v\n", doc.encoding) written += fmt.wprintf(writer, "[Encoding] %v\n", doc.encoding)
if len(doc.doctype.ident) > 0 { if len(doc.doctype.ident) > 0 {
written += wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident) written += fmt.wprintf(writer, "[DOCTYPE] %v\n", doc.doctype.ident)
if len(doc.doctype.rest) > 0 { if len(doc.doctype.rest) > 0 {
wprintf(writer, "\t%v\n", doc.doctype.rest) fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
} }
} }
for comment in doc.comments { for comment in doc.comments {
written += wprintf(writer, "[Pre-root comment] %v\n", comment) written += fmt.wprintf(writer, "[Pre-root comment] %v\n", comment)
} }
if doc.element_count > 0 { if doc.element_count > 0 {
wprintln(writer, " --- ") fmt.wprintln(writer, " --- ")
print_element(writer, doc, 0) print_element(writer, doc, 0)
wprintln(writer, " --- ") fmt.wprintln(writer, " --- ")
} }
return written, .None return written, .None
} }
print_element :: proc(writer: io.Writer, doc: ^xml.Document, element_id: xml.Element_ID, indent := 0) -> (written: int, err: io.Error) { print_element :: proc(writer: io.Writer, doc: ^xml.Document, element_id: xml.Element_ID, indent := 0) -> (written: int, err: io.Error) {
using fmt
tab :: proc(writer: io.Writer, indent: int) { tab :: proc(writer: io.Writer, indent: int) {
for _ in 0..=indent { for _ in 0..=indent {
wprintf(writer, "\t") fmt.wprintf(writer, "\t")
} }
} }
@@ -259,22 +256,24 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
element := doc.elements[element_id] element := doc.elements[element_id]
if element.kind == .Element { if element.kind == .Element {
wprintf(writer, "<%v>\n", element.ident) fmt.wprintf(writer, "<%v>\n", element.ident)
if len(element.value) > 0 {
tab(writer, indent + 1) for value in element.value {
wprintf(writer, "[Value] %v\n", element.value) switch v in value {
case string:
tab(writer, indent + 1)
fmt.wprintf(writer, "[Value] %v\n", v)
case xml.Element_ID:
print_element(writer, doc, v, indent + 1)
}
} }
for attr in element.attribs { for attr in element.attribs {
tab(writer, indent + 1) tab(writer, indent + 1)
wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val) fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
}
for child in element.children {
print_element(writer, doc, child, indent + 1)
} }
} else if element.kind == .Comment { } else if element.kind == .Comment {
wprintf(writer, "[COMMENT] %v\n", element.value) fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
} }
return written, .None return written, .None
@@ -289,8 +288,6 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
@test @test
run_tests :: proc(t: ^testing.T) { run_tests :: proc(t: ^testing.T) {
using fmt
for test in TESTS { for test in TESTS {
path := test_file_path(test.filename) path := test_file_path(test.filename)
log(t, fmt.tprintf("Trying to parse %v", path)) log(t, fmt.tprintf("Trying to parse %v", path))
@@ -305,11 +302,11 @@ run_tests :: proc(t: ^testing.T) {
crc32 := hash.crc32(tree_bytes) crc32 := hash.crc32(tree_bytes)
failed := err != test.err failed := err != test.err
err_msg := tprintf("Expected return value %v, got %v", test.err, err) err_msg := fmt.tprintf("Expected return value %v, got %v", test.err, err)
expect(t, err == test.err, err_msg) expect(t, err == test.err, err_msg)
failed |= crc32 != test.crc32 failed |= crc32 != test.crc32
err_msg = tprintf("Expected CRC 0x%08x, got 0x%08x, with options %v", test.crc32, crc32, test.options) err_msg = fmt.tprintf("Expected CRC 0x%08x, got 0x%08x, with options %v", test.crc32, crc32, test.options)
expect(t, crc32 == test.crc32, err_msg) expect(t, crc32 == test.crc32, err_msg)
if failed { if failed {
@@ -317,7 +314,7 @@ run_tests :: proc(t: ^testing.T) {
Don't fully print big trees. Don't fully print big trees.
*/ */
tree_string = tree_string[:min(2_048, len(tree_string))] tree_string = tree_string[:min(2_048, len(tree_string))]
println(tree_string) fmt.println(tree_string)
} }
} }
} }
@@ -22,9 +22,6 @@ main :: proc() {
@test @test
test_fract_f32 :: proc(t: ^testing.T) { test_fract_f32 :: proc(t: ^testing.T) {
using math
r: f32 r: f32
Datum :: struct { Datum :: struct {
@@ -35,8 +32,8 @@ test_fract_f32 :: proc(t: ^testing.T) {
@static data := []Datum{ @static data := []Datum{
{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
{ 1, -10.5, -0.5 }, { 1, -10.5, -0.5 },
{ 2, F32_MIN, F32_MIN }, // 0x1p-126 { 2, math.F32_MIN, math.F32_MIN }, // 0x1p-126
{ 3, -F32_MIN, -F32_MIN }, { 3, -math.F32_MIN, -math.F32_MIN },
{ 4, 0.0, 0.0 }, { 4, 0.0, 0.0 },
{ 5, -0.0, -0.0 }, { 5, -0.0, -0.0 },
{ 6, 1, 0.0 }, { 6, 1, 0.0 },
@@ -54,9 +51,6 @@ test_fract_f32 :: proc(t: ^testing.T) {
@test @test
test_fract_f64 :: proc(t: ^testing.T) { test_fract_f64 :: proc(t: ^testing.T) {
using math
r: f64 r: f64
Datum :: struct { Datum :: struct {
@@ -67,8 +61,8 @@ test_fract_f64 :: proc(t: ^testing.T) {
@static data := []Datum{ @static data := []Datum{
{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken { 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
{ 1, -10.5, -0.5 }, { 1, -10.5, -0.5 },
{ 2, F64_MIN, F64_MIN }, // 0x1p-1022 { 2, math.F64_MIN, math.F64_MIN }, // 0x1p-1022
{ 3, -F64_MIN, -F64_MIN }, { 3, -math.F64_MIN, -math.F64_MIN },
{ 4, 0.0, 0.0 }, { 4, 0.0, 0.0 },
{ 5, -0.0, -0.0 }, { 5, -0.0, -0.0 },
{ 6, 1, 0.0 }, { 6, 1, 0.0 },
+73 -94
View File
@@ -43,11 +43,7 @@ main :: proc() {
@test @test
test_classify_f16 :: proc(t: ^testing.T) { test_classify_f16 :: proc(t: ^testing.T) {
r: math.Float_Class
using math
using Float_Class
r: Float_Class
Datum :: struct { Datum :: struct {
i: int, i: int,
@@ -55,38 +51,34 @@ test_classify_f16 :: proc(t: ^testing.T) {
e: math.Float_Class, e: math.Float_Class,
} }
@static data := []Datum{ @static data := []Datum{
{ 0, 1.2, Normal }, { 0, 1.2, .Normal },
{ 1, 0h0001, Subnormal }, { 1, 0h0001, .Subnormal },
{ 2, 0.0, Zero }, { 2, 0.0, .Zero },
{ 3, -0.0, Neg_Zero }, { 3, -0.0, .Neg_Zero },
{ 4, SNAN_F16, NaN }, { 4, math.SNAN_F16, .NaN },
{ 5, QNAN_F16, NaN }, { 5, math.QNAN_F16, .NaN },
{ 6, INF_F16, Inf }, { 6, math.INF_F16, .Inf },
{ 7, NEG_INF_F16, Neg_Inf }, { 7, math.NEG_INF_F16, .Neg_Inf },
} }
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = classify_f16(d.v) r = math.classify_f16(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
} }
/* Check all subnormals (exponent 0, 10-bit significand non-zero) */ /* Check all subnormals (exponent 0, 10-bit significand non-zero) */
for i :u16 = 1; i < 0x400; i += 1 { for i in u16(1)..<0x400 {
v :f16 = transmute(f16)i v := transmute(f16)i
r = classify_f16(v) r = math.classify_f16(v)
e :Float_Class: Subnormal e :: math.Float_Class.Subnormal
tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e)) tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e))
} }
} }
@test @test
test_classify_f32 :: proc(t: ^testing.T) { test_classify_f32 :: proc(t: ^testing.T) {
r: math.Float_Class
using math
using Float_Class
r: Float_Class
Datum :: struct { Datum :: struct {
i: int, i: int,
@@ -94,30 +86,26 @@ test_classify_f32 :: proc(t: ^testing.T) {
e: math.Float_Class, e: math.Float_Class,
} }
@static data := []Datum{ @static data := []Datum{
{ 0, 1.2, Normal }, { 0, 1.2, .Normal },
{ 1, 0h0000_0001, Subnormal }, { 1, 0h0000_0001, .Subnormal },
{ 2, 0.0, Zero }, { 2, 0.0, .Zero },
{ 3, -0.0, Neg_Zero }, { 3, -0.0, .Neg_Zero },
{ 4, SNAN_F32, NaN }, { 4, math.SNAN_F32, .NaN },
{ 5, QNAN_F32, NaN }, { 5, math.QNAN_F32, .NaN },
{ 6, INF_F32, Inf }, { 6, math.INF_F32, .Inf },
{ 7, NEG_INF_F32, Neg_Inf }, { 7, math.NEG_INF_F32, .Neg_Inf },
} }
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = classify_f32(d.v) r = math.classify_f32(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
} }
} }
@test @test
test_classify_f64 :: proc(t: ^testing.T) { test_classify_f64 :: proc(t: ^testing.T) {
r: math.Float_Class
using math
using Float_Class
r: Float_Class
Datum :: struct { Datum :: struct {
i: int, i: int,
@@ -125,28 +113,25 @@ test_classify_f64 :: proc(t: ^testing.T) {
e: math.Float_Class, e: math.Float_Class,
} }
@static data := []Datum{ @static data := []Datum{
{ 0, 1.2, Normal }, { 0, 1.2, .Normal },
{ 1, 0h0000_0000_0000_0001, Subnormal }, { 1, 0h0000_0000_0000_0001, .Subnormal },
{ 2, 0.0, Zero }, { 2, 0.0, .Zero },
{ 3, -0.0, Neg_Zero }, { 3, -0.0, .Neg_Zero },
{ 4, SNAN_F64, NaN }, { 4, math.SNAN_F64, .NaN },
{ 5, QNAN_F64, NaN }, { 5, math.QNAN_F64, .NaN },
{ 6, INF_F64, Inf }, { 6, math.INF_F64, .Inf },
{ 7, NEG_INF_F64, Neg_Inf }, { 7, math.NEG_INF_F64, .Neg_Inf },
} }
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = classify_f64(d.v) r = math.classify_f64(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, d.v, r, d.e))
} }
} }
@test @test
test_trunc_f16 :: proc(t: ^testing.T) { test_trunc_f16 :: proc(t: ^testing.T) {
using math
r, v: f16 r, v: f16
Datum :: struct { Datum :: struct {
@@ -158,16 +143,16 @@ test_trunc_f16 :: proc(t: ^testing.T) {
{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
{ 1, -10.5, -10 }, { 1, -10.5, -10 },
{ 2, F16_MAX, F16_MAX }, { 2, math.F16_MAX, math.F16_MAX },
{ 3, -F16_MAX, -F16_MAX }, { 3, -math.F16_MAX, -math.F16_MAX },
{ 4, F16_MIN, 0.0 }, { 4, math.F16_MIN, 0.0 },
{ 5, -F16_MIN, -0.0 }, { 5, -math.F16_MIN, -0.0 },
{ 6, 0.0, 0.0 }, { 6, 0.0, 0.0 },
{ 7, -0.0, -0.0 }, { 7, -0.0, -0.0 },
{ 8, 1, 1 }, { 8, 1, 1 },
{ 9, -1, -1 }, { 9, -1, -1 },
{ 10, INF_F16, INF_F16 }, { 10, math.INF_F16, math.INF_F16 },
{ 11, NEG_INF_F16, NEG_INF_F16 }, { 11, math.NEG_INF_F16, math.NEG_INF_F16 },
/* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */ /* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */
{ 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1) { 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1)
@@ -185,24 +170,21 @@ test_trunc_f16 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = trunc_f16(d.v) r = math.trunc_f16(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
} }
v = SNAN_F16 v = math.SNAN_F16
r = trunc_f16(v) r = math.trunc_f16(v)
tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
v = QNAN_F16 v = math.QNAN_F16
r = trunc_f16(v) r = math.trunc_f16(v)
tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
} }
@test @test
test_trunc_f32 :: proc(t: ^testing.T) { test_trunc_f32 :: proc(t: ^testing.T) {
using math
r, v: f32 r, v: f32
Datum :: struct { Datum :: struct {
@@ -214,16 +196,16 @@ test_trunc_f32 :: proc(t: ^testing.T) {
{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
{ 1, -10.5, -10 }, { 1, -10.5, -10 },
{ 2, F32_MAX, F32_MAX }, { 2, math.F32_MAX, math.F32_MAX },
{ 3, -F32_MAX, -F32_MAX }, { 3, -math.F32_MAX, -math.F32_MAX },
{ 4, F32_MIN, 0.0 }, { 4, math.F32_MIN, 0.0 },
{ 5, -F32_MIN, -0.0 }, { 5, -math.F32_MIN, -0.0 },
{ 6, 0.0, 0.0 }, { 6, 0.0, 0.0 },
{ 7, -0.0, -0.0 }, { 7, -0.0, -0.0 },
{ 8, 1, 1 }, { 8, 1, 1 },
{ 9, -1, -1 }, { 9, -1, -1 },
{ 10, INF_F32, INF_F32 }, { 10, math.INF_F32, math.INF_F32 },
{ 11, NEG_INF_F32, NEG_INF_F32 }, { 11, math.NEG_INF_F32, math.NEG_INF_F32 },
/* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */ /* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */
{ 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1) { 12, 0h3F80_0001, 1 }, // 0x1.000002p+0 (smallest > 1)
@@ -250,24 +232,21 @@ test_trunc_f32 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = trunc_f32(d.v) r = math.trunc_f32(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
} }
v = SNAN_F32 v = math.SNAN_F32
r = trunc_f32(v) r = math.trunc_f32(v)
tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
v = QNAN_F32 v = math.QNAN_F32
r = trunc_f32(v) r = math.trunc_f32(v)
tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
} }
@test @test
test_trunc_f64 :: proc(t: ^testing.T) { test_trunc_f64 :: proc(t: ^testing.T) {
using math
r, v: f64 r, v: f64
Datum :: struct { Datum :: struct {
@@ -279,16 +258,16 @@ test_trunc_f64 :: proc(t: ^testing.T) {
{ 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken { 0, 10.5, 10 }, // Issue #1574 fract in linalg/glm is broken
{ 1, -10.5, -10 }, { 1, -10.5, -10 },
{ 2, F64_MAX, F64_MAX }, { 2, math.F64_MAX, math.F64_MAX },
{ 3, -F64_MAX, -F64_MAX }, { 3, -math.F64_MAX, -math.F64_MAX },
{ 4, F64_MIN, 0.0 }, { 4, math.F64_MIN, 0.0 },
{ 5, -F64_MIN, -0.0 }, { 5, -math.F64_MIN, -0.0 },
{ 6, 0.0, 0.0 }, { 6, 0.0, 0.0 },
{ 7, -0.0, -0.0 }, { 7, -0.0, -0.0 },
{ 8, 1, 1 }, { 8, 1, 1 },
{ 9, -1, -1 }, { 9, -1, -1 },
{ 10, INF_F64, INF_F64 }, { 10, math.INF_F64, math.INF_F64 },
{ 11, NEG_INF_F64, NEG_INF_F64 }, { 11, math.NEG_INF_F64, math.NEG_INF_F64 },
/* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */ /* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */
{ 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1) { 12, 0h3FF0_0000_0000_0001, 1 }, // 0x1.0000000000001p+0 (smallest > 1)
@@ -315,17 +294,17 @@ test_trunc_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r = trunc_f64(d.v) r = math.trunc_f64(d.v)
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(%h) -> %h != %h", i, #procedure, d.v, r, d.e))
} }
v = SNAN_F64 v = math.SNAN_F64
r = trunc_f64(v) r = math.trunc_f64(v)
tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
v = QNAN_F64 v = math.QNAN_F64
r = trunc_f64(v) r = math.trunc_f64(v)
tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r)) tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
} }
@@ -22,9 +22,6 @@ main :: proc() {
@test @test
test_split_list_windows :: proc(t: ^testing.T) { test_split_list_windows :: proc(t: ^testing.T) {
using filepath
Datum :: struct { Datum :: struct {
i: int, i: int,
v: string, v: string,
@@ -43,7 +40,7 @@ test_split_list_windows :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i)) assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
r := split_list(d.v) r := filepath.split_list(d.v)
defer delete(r) defer delete(r)
tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
i, #procedure, d.v, len(r), len(d.e))) i, #procedure, d.v, len(r), len(d.e)))
@@ -57,12 +54,12 @@ test_split_list_windows :: proc(t: ^testing.T) {
{ {
v := "" v := ""
r := split_list(v) r := filepath.split_list(v)
tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r)) tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
} }
{ {
v := "a" v := "a"
r := split_list(v) r := filepath.split_list(v)
defer delete(r) defer delete(r)
tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
if len(r) == 1 { if len(r) == 1 {
@@ -73,9 +70,6 @@ test_split_list_windows :: proc(t: ^testing.T) {
@test @test
test_split_list_unix :: proc(t: ^testing.T) { test_split_list_unix :: proc(t: ^testing.T) {
using filepath
Datum :: struct { Datum :: struct {
i: int, i: int,
v: string, v: string,
@@ -94,7 +88,7 @@ test_split_list_unix :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i)) assert(i == d.i, fmt.tprintf("wrong data index: i %d != d.i %d\n", i, d.i))
r := split_list(d.v) r := filepath.split_list(d.v)
defer delete(r) defer delete(r)
tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d", tc.expect(t, len(r) == len(d.e), fmt.tprintf("i:%d %s(%s) len(r) %d != len(d.e) %d",
i, #procedure, d.v, len(r), len(d.e))) i, #procedure, d.v, len(r), len(d.e)))
@@ -108,12 +102,12 @@ test_split_list_unix :: proc(t: ^testing.T) {
{ {
v := "" v := ""
r := split_list(v) r := filepath.split_list(v)
tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r)) tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
} }
{ {
v := "a" v := "a"
r := split_list(v) r := filepath.split_list(v)
defer delete(r) defer delete(r)
tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r))) tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
if len(r) == 1 { if len(r) == 1 {
+16 -20
View File
@@ -19,8 +19,6 @@ main :: proc() {
@test @test
test_as_u64 :: proc(t: ^testing.T) { test_as_u64 :: proc(t: ^testing.T) {
using reflect
{ {
/* i8 */ /* i8 */
Datum :: struct { i: int, v: i8, e: u64 } Datum :: struct { i: int, v: i8, e: u64 }
@@ -32,7 +30,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v (0x%X) != %v (0x%X)\n",
i, #procedure, d.v, r, r, d.e, d.e)) i, #procedure, d.v, r, r, d.e, d.e))
@@ -49,7 +47,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v (0x%X) != %v (0x%X)\n",
i, #procedure, d.v, r, r, d.e, d.e)) i, #procedure, d.v, r, r, d.e, d.e))
@@ -66,7 +64,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v (0x%X) != %v (0x%X)\n",
i, #procedure, d.v, r, r, d.e, d.e)) i, #procedure, d.v, r, r, d.e, d.e))
@@ -83,7 +81,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v (0x%X) != %v (0x%X)\n",
i, #procedure, d.v, r, r, d.e, d.e)) i, #procedure, d.v, r, r, d.e, d.e))
@@ -103,7 +101,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (0x%X) != %v (0x%X)\n",
i, #procedure, d.v, r, r, d.e, d.e)) i, #procedure, d.v, r, r, d.e, d.e))
@@ -119,7 +117,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -133,7 +131,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -147,7 +145,7 @@ test_as_u64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_u64(d.v) r, valid := reflect.as_u64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -156,8 +154,6 @@ test_as_u64 :: proc(t: ^testing.T) {
@test @test
test_as_f64 :: proc(t: ^testing.T) { test_as_f64 :: proc(t: ^testing.T) {
using reflect
{ {
/* i8 */ /* i8 */
Datum :: struct { i: int, v: i8, e: f64 } Datum :: struct { i: int, v: i8, e: f64 }
@@ -169,7 +165,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i8 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i8 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -185,7 +181,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i16 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i16 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -201,7 +197,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i32 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i32 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -217,7 +213,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i64 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -234,7 +230,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(i128 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(i128 %v) -> %v (%H) != %v (%H)\n",
i, #procedure, d.v, r, r, d.e, d.e)) i, #procedure, d.v, r, r, d.e, d.e))
@@ -250,7 +246,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(f16 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f16 %v (%H)) -> %v (%H) != %v (%H)\n",
i, #procedure, d.v, d.v, r, r, d.e, d.e)) i, #procedure, d.v, d.v, r, r, d.e, d.e))
@@ -265,7 +261,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(f32 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n", tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f32 %v (%H)) -> %v (%H) != %v (%H)\n",
i, #procedure, d.v, d.v, r, r, d.e, d.e)) i, #procedure, d.v, d.v, r, r, d.e, d.e))
@@ -280,7 +276,7 @@ test_as_f64 :: proc(t: ^testing.T) {
for d, i in data { for d, i in data {
assert(i == d.i) assert(i == d.i)
r, valid := as_f64(d.v) r, valid := reflect.as_f64(d.v)
tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v)) tc.expect(t, valid, fmt.tprintf("i:%d %s(f64 %v) !valid\n", i, #procedure, d.v))
tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e)) tc.expect(t, r == d.e, fmt.tprintf("i:%d %s(f64 %v) -> %v != %v\n", i, #procedure, d.v, r, d.e))
} }
@@ -118,8 +118,6 @@ TESTS := []Test_Suite{
@test @test
tests :: proc(t: ^testing.T) { tests :: proc(t: ^testing.T) {
using fmt
cat: ^i18n.Translation cat: ^i18n.Translation
err: i18n.Error err: i18n.Error
@@ -142,8 +140,6 @@ tests :: proc(t: ^testing.T) {
} }
main :: proc() { main :: proc() {
using fmt
track: mem.Tracking_Allocator track: mem.Tracking_Allocator
mem.tracking_allocator_init(&track, context.allocator) mem.tracking_allocator_init(&track, context.allocator)
context.allocator = mem.tracking_allocator(&track) context.allocator = mem.tracking_allocator(&track)
@@ -157,9 +153,9 @@ main :: proc() {
} }
if len(track.allocation_map) > 0 { if len(track.allocation_map) > 0 {
println() fmt.println()
for _, v in track.allocation_map { for _, v in track.allocation_map {
printf("%v Leaked %v bytes.\n", v.location, v.size) fmt.printf("%v Leaked %v bytes.\n", v.location, v.size)
} }
} }
} }
+1
View File
@@ -15,6 +15,7 @@ set COMMON=-collection:tests=..\..
..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_2466.odin %COMMON% -file || exit /b
..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_2615.odin %COMMON% -file || exit /b
..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b ..\..\..\odin test ..\test_issue_2637.odin %COMMON% -file || exit /b
..\..\..\odin test ..\test_issue_2666.odin %COMMON% -file || exit /b
@echo off @echo off
+1
View File
@@ -18,6 +18,7 @@ $ODIN build ../test_issue_2113.odin $COMMON -file -debug
$ODIN test ../test_issue_2466.odin $COMMON -file $ODIN test ../test_issue_2466.odin $COMMON -file
$ODIN test ../test_issue_2615.odin $COMMON -file $ODIN test ../test_issue_2615.odin $COMMON -file
$ODIN test ../test_issue_2637.odin $COMMON -file $ODIN test ../test_issue_2637.odin $COMMON -file
$ODIN test ../test_issue_2666.odin $COMMON -file
if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then if [[ $($ODIN build ../test_issue_2395.odin $COMMON -file 2>&1 >/dev/null | grep -c "$NO_NIL_ERR") -eq 2 ]] ; then
echo "SUCCESSFUL 1/1" echo "SUCCESSFUL 1/1"
else else
+26
View File
@@ -0,0 +1,26 @@
// Tests issue https://github.com/odin-lang/Odin/issues/2666
// @(disabled=<boolean>) does not work with polymorphic procs
package test_issues
import "core:testing"
@(test)
test_disabled_parapoly :: proc(t: ^testing.T) {
disabled_parapoly(t, 1)
disabled_parapoly_constant(t, 1)
}
@(private="file")
@(disabled = true)
disabled_parapoly :: proc(t: ^testing.T, num: $T) {
testing.error(t, "disabled_parapoly should be disabled")
}
@(private="file")
DISABLE :: true
@(disabled = DISABLE)
@(private = "file")
disabled_parapoly_constant :: proc(t: ^testing.T, num: $T) {
testing.error(t, "disabled_parapoly_constant should be disabled")
}
+2 -2
View File
@@ -5,7 +5,9 @@ package vendor_gl
import "core:os" import "core:os"
import "core:fmt" import "core:fmt"
import "core:strings" import "core:strings"
import "core:runtime"
_ :: fmt _ :: fmt
_ :: runtime
Shader_Type :: enum i32 { Shader_Type :: enum i32 {
NONE = 0x0000, NONE = 0x0000,
@@ -47,8 +49,6 @@ get_last_error_message :: proc() -> (compile_message: string, compile_type: Shad
// except for calling differently named GL functions // except for calling differently named GL functions
// it's a bit ugly looking, but meh // it's a bit ugly looking, but meh
when GL_DEBUG { when GL_DEBUG {
import "core:runtime"
@private @private
check_error :: proc( check_error :: proc(
id: u32, type: Shader_Type, status: u32, id: u32, type: Shader_Type, status: u32,
+5 -3
View File
@@ -2,6 +2,11 @@ package vendor_gl
#assert(size_of(bool) == size_of(u8)) #assert(size_of(bool) == size_of(u8))
import "core:runtime"
import "core:fmt"
_ :: runtime
_ :: fmt
when !GL_DEBUG { when !GL_DEBUG {
// VERSION_1_0 // VERSION_1_0
CullFace :: proc "c" (mode: u32) { impl_CullFace(mode) } CullFace :: proc "c" (mode: u32) { impl_CullFace(mode) }
@@ -756,9 +761,6 @@ when !GL_DEBUG {
MultiDrawElementsIndirectCount :: proc "c" (mode: i32, type: i32, indirect: [^]DrawElementsIndirectCommand, drawcount: i32, maxdrawcount, stride: i32) { impl_MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride) } MultiDrawElementsIndirectCount :: proc "c" (mode: i32, type: i32, indirect: [^]DrawElementsIndirectCommand, drawcount: i32, maxdrawcount, stride: i32) { impl_MultiDrawElementsIndirectCount(mode, type, indirect, drawcount, maxdrawcount, stride) }
PolygonOffsetClamp :: proc "c" (factor, units, clamp: f32) { impl_PolygonOffsetClamp(factor, units, clamp) } PolygonOffsetClamp :: proc "c" (factor, units, clamp: f32) { impl_PolygonOffsetClamp(factor, units, clamp) }
} else { } else {
import "core:runtime"
import "core:fmt"
debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, args: ..any, loc := #caller_location) { debug_helper :: proc"c"(from_loc: runtime.Source_Code_Location, num_ret: int, args: ..any, loc := #caller_location) {
context = runtime.default_context() context = runtime.default_context()
+2 -3
View File
@@ -1,9 +1,8 @@
package objc_Foundation package objc_Foundation
import "core:c"
_ :: c
when ODIN_OS == .Darwin { when ODIN_OS == .Darwin {
import "core:c"
_ :: c
#assert(size_of(c.long) == size_of(int)) #assert(size_of(c.long) == size_of(int))
#assert(size_of(c.ulong) == size_of(uint)) #assert(size_of(c.ulong) == size_of(uint))
} }
+14
View File
@@ -3551,6 +3551,8 @@ PARAMETER_DESC :: struct {
FirstOutComponent: u32, FirstOutComponent: u32,
} }
ID3D11ShaderReflectionType_UUID_STRING :: "6E6FFA6A-9BAE-4613-A51E-91652D508C21"
ID3D11ShaderReflectionType_UUID := &IID{0x6E6FFA6A, 0x9BAE, 0x4613, {0xA5, 0x1E, 0x91, 0x65, 0x2D, 0x50, 0x8C, 0x21}}
IShaderReflectionType :: struct { IShaderReflectionType :: struct {
using vtable: ^IShaderReflectionType_VTable, using vtable: ^IShaderReflectionType_VTable,
} }
@@ -3568,6 +3570,8 @@ IShaderReflectionType_VTable :: struct {
ImplementsInterface: proc "stdcall" (this: ^IShaderReflectionType, pBase: ^IShaderReflectionType) -> HRESULT, ImplementsInterface: proc "stdcall" (this: ^IShaderReflectionType, pBase: ^IShaderReflectionType) -> HRESULT,
} }
ID3D11ShaderReflectionVariable_UUID_STRING :: "51F23923-F3E5-4BD1-91CB-606177D8DB4C"
ID3D11ShaderReflectionVariable_UUID := &IID{0x51F23923, 0xF3E5, 0x4BD1, {0x91, 0xCB, 0x60, 0x61, 0x77, 0xD8, 0xDB, 0x4C}}
IShaderReflectionVariable :: struct { IShaderReflectionVariable :: struct {
using vtable: ^IShaderReflectionVariable_VTable, using vtable: ^IShaderReflectionVariable_VTable,
} }
@@ -3578,6 +3582,8 @@ IShaderReflectionVariable_VTable :: struct {
GetInterfaceSlot: proc "stdcall" (this: ^IShaderReflectionVariable, uArrayIndex: u32) -> u32, GetInterfaceSlot: proc "stdcall" (this: ^IShaderReflectionVariable, uArrayIndex: u32) -> u32,
} }
ID3D11ShaderReflectionConstantBuffer_UUID_STRING :: "EB62D63D-93DD-4318-8AE8-C6F83AD371B8"
ID3D11ShaderReflectionConstantBuffer_UUID := &IID{0xEB62D63D, 0x93DD, 0x4318, {0x8A, 0xE8, 0xC6, 0xF8, 0x3A, 0xD3, 0x71, 0xB8}}
IShaderReflectionConstantBuffer :: struct { IShaderReflectionConstantBuffer :: struct {
using vtable: ^IShaderReflectionConstantBuffer_VTable, using vtable: ^IShaderReflectionConstantBuffer_VTable,
} }
@@ -3588,6 +3594,8 @@ IShaderReflectionConstantBuffer_VTable :: struct {
} }
ID3D11ShaderReflection_UUID_STRING :: "8D536CA1-0CCA-4956-A837-786963755584"
ID3D11ShaderReflection_UUID := &IID{0x8D536CA1, 0x0CCA, 0x4956, {0xA8, 0x37, 0x78, 0x69, 0x63, 0x75, 0x55, 0x84}}
IShaderReflection :: struct #raw_union { IShaderReflection :: struct #raw_union {
#subtype iunknown: IUnknown, #subtype iunknown: IUnknown,
using id3d11shaderreflection_vtable: ^IShaderReflection_VTable, using id3d11shaderreflection_vtable: ^IShaderReflection_VTable,
@@ -3616,6 +3624,8 @@ IShaderReflection_VTable :: struct {
} }
ID3D11LibraryReflection_UUID_STRING :: "54384F1B-5B3E-4BB7-AE01-60BA3097CBB6"
ID3D11LibraryReflection_UUID := &IID{0x54384F1B, 0x5B3E, 0x4BB7, {0xAE, 0x1, 0x60, 0xBA, 0x30, 0x97, 0xCD, 0xB6}}
ILibraryReflection :: struct #raw_union { ILibraryReflection :: struct #raw_union {
#subtype iunknown: IUnknown, #subtype iunknown: IUnknown,
using id3d11libraryreflection_vtable: ^ILibraryReflection_VTable, using id3d11libraryreflection_vtable: ^ILibraryReflection_VTable,
@@ -3626,6 +3636,8 @@ ILibraryReflection_VTable :: struct {
GetFunctionByIndex: proc "stdcall" (this: ^ILibraryReflection, FunctionIndex: i32) -> ^IFunctionReflection, GetFunctionByIndex: proc "stdcall" (this: ^ILibraryReflection, FunctionIndex: i32) -> ^IFunctionReflection,
} }
ID3D11FunctionReflection_UUID_STRING :: "207BCECB-D683-4A06-A8A3-9B149B9F73A4"
ID3D11FunctionReflection_UUID := &IID{0x207BCECB, 0xD683, 0x4A06, {0xA8, 0xA3, 0x9B, 0x14, 0x9B, 0x9F, 0x73, 0xA4}}
IFunctionReflection :: struct { IFunctionReflection :: struct {
using vtable: ^IFunctionReflection_VTable, using vtable: ^IFunctionReflection_VTable,
} }
@@ -3639,6 +3651,8 @@ IFunctionReflection_VTable :: struct {
GetFunctionParameter: proc "stdcall" (this: ^IFunctionReflection, ParameterIndex: i32) -> ^IFunctionParameterReflection, GetFunctionParameter: proc "stdcall" (this: ^IFunctionReflection, ParameterIndex: i32) -> ^IFunctionParameterReflection,
} }
ID3D11FunctionParameterReflection_UUID_STRING :: "42757488-334F-47FE-982E-1A65D08CC462"
ID3D11FunctionParameterReflection_UUID := &IID{0x42757488, 0x334f, 0x47FE, {0x98, 0x2E, 0x1A, 0x65, 0xD0, 0x8C, 0xC4, 0x62}}
IFunctionParameterReflection :: struct { IFunctionParameterReflection :: struct {
using vtable: ^IFunctionParameterReflection_VTable, using vtable: ^IFunctionParameterReflection_VTable,
} }
+3
View File
@@ -149,6 +149,9 @@ ID3DInclude_VTable :: struct {
Close: proc "stdcall" (this: ^ID3DInclude, pData: rawptr) -> HRESULT, Close: proc "stdcall" (this: ^ID3DInclude, pData: rawptr) -> HRESULT,
} }
// Default file includer
D3DCOMPILE_STANDARD_FILE_INCLUDE :: (^ID3DInclude)(uintptr(1))
ID3D11Module :: struct #raw_union { ID3D11Module :: struct #raw_union {
#subtype iunknown: IUnknown, #subtype iunknown: IUnknown,

Some files were not shown because too many files have changed in this diff Show More