mirror of
https://github.com/Ed94/Odin.git
synced 2026-06-15 02:12:22 -07:00
Merge branch 'master' into stdlib-parser-fixes
This commit is contained in:
@@ -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%\"
|
||||
|
||||
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%\"
|
||||
:skip_git_hash
|
||||
|
||||
@@ -77,6 +80,16 @@ set libs= ^
|
||||
Synchronization.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
|
||||
|
||||
if %release_mode% EQU 0 ( rem Debug
|
||||
|
||||
+7
-4
@@ -8,17 +8,20 @@ set -eu
|
||||
: ${ODIN_VERSION=dev-$(date +"%Y-%m")}
|
||||
: ${GIT_SHA=}
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
|
||||
CXXFLAGS="$CXXFLAGS -std=c++14"
|
||||
LDFLAGS="$LDFLAGS -pthread -lm -lstdc++"
|
||||
|
||||
if [ -d ".git" ]; then
|
||||
GIT_SHA=$(git rev-parse --short HEAD || :)
|
||||
if [ "$GIT_SHA" ]; then
|
||||
if [ -d ".git" ] && [ $(which git) ]; then
|
||||
versionTag=( $(git show --pretty='%cd %h' --date=format:%Y-%m --no-patch --no-notes HEAD) )
|
||||
if [ $? -eq 0 ]; then
|
||||
ODIN_VERSION="dev-${versionTag[0]}"
|
||||
GIT_SHA="${versionTag[1]}"
|
||||
CPPFLAGS="$CPPFLAGS -DGIT_SHA=\"$GIT_SHA\""
|
||||
fi
|
||||
fi
|
||||
|
||||
CPPFLAGS="$CPPFLAGS -DODIN_VERSION_RAW=\"$ODIN_VERSION\""
|
||||
|
||||
DISABLED_WARNINGS="-Wno-switch -Wno-macro-redefined -Wno-unused-value"
|
||||
OS=$(uname)
|
||||
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//+vet !using-param
|
||||
package zlib
|
||||
|
||||
/*
|
||||
|
||||
@@ -184,28 +184,26 @@ decode_xml :: proc(input: string, options := XML_Decode_Options{}, allocator :=
|
||||
|
||||
advance :: proc(t: ^Tokenizer) -> (err: Error) {
|
||||
if t == nil { return .Tokenizer_Is_Nil }
|
||||
using t
|
||||
|
||||
#no_bounds_check {
|
||||
if read_offset < len(src) {
|
||||
offset = read_offset
|
||||
r, w = rune(src[read_offset]), 1
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset
|
||||
t.r, t.w = rune(t.src[t.read_offset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
case t.r == 0:
|
||||
return .Illegal_NUL_Character
|
||||
case r >= utf8.RUNE_SELF:
|
||||
r, w = utf8.decode_rune_in_string(src[read_offset:])
|
||||
if r == utf8.RUNE_ERROR && w == 1 {
|
||||
case t.r >= utf8.RUNE_SELF:
|
||||
t.r, t.w = utf8.decode_rune_in_string(t.src[t.read_offset:])
|
||||
if t.r == utf8.RUNE_ERROR && t.w == 1 {
|
||||
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
|
||||
}
|
||||
}
|
||||
read_offset += w
|
||||
t.read_offset += t.w
|
||||
return .None
|
||||
} else {
|
||||
offset = len(src)
|
||||
r = -1
|
||||
t.offset = len(t.src)
|
||||
t.r = -1
|
||||
return
|
||||
}
|
||||
}
|
||||
@@ -273,26 +271,25 @@ _extract_xml_entity :: proc(t: ^Tokenizer) -> (entity: string, err: Error) {
|
||||
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.
|
||||
*/
|
||||
using t
|
||||
|
||||
length := len(t.src)
|
||||
found := false
|
||||
|
||||
#no_bounds_check {
|
||||
for read_offset < length {
|
||||
if src[read_offset] == ';' {
|
||||
for t.read_offset < length {
|
||||
if t.src[t.read_offset] == ';' {
|
||||
t.read_offset += 1
|
||||
found = true
|
||||
read_offset += 1
|
||||
break
|
||||
}
|
||||
read_offset += 1
|
||||
t.read_offset += 1
|
||||
}
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
/*
|
||||
|
||||
@@ -207,7 +207,7 @@ marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err:
|
||||
case runtime.Type_Info_Relative_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Relative_Slice:
|
||||
case runtime.Type_Info_Relative_Multi_Pointer:
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
|
||||
@@ -19,43 +19,39 @@ import "core:fmt"
|
||||
*/
|
||||
print :: proc(writer: io.Writer, doc: ^Document) -> (written: int, err: io.Error) {
|
||||
if doc == nil { return }
|
||||
using fmt
|
||||
|
||||
written += wprintf(writer, "[XML Prolog]\n")
|
||||
written += fmt.wprintf(writer, "[XML Prolog]\n")
|
||||
|
||||
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 {
|
||||
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 {
|
||||
wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
wprintln(writer, " --- ")
|
||||
fmt.wprintln(writer, " --- ")
|
||||
print_element(writer, doc, 0)
|
||||
wprintln(writer, " --- ")
|
||||
fmt.wprintln(writer, " --- ")
|
||||
}
|
||||
|
||||
return written, .None
|
||||
}
|
||||
|
||||
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) {
|
||||
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]
|
||||
|
||||
if element.kind == .Element {
|
||||
wprintf(writer, "<%v>\n", element.ident)
|
||||
if len(element.value) > 0 {
|
||||
tab(writer, indent + 1)
|
||||
wprintf(writer, "[Value] %v\n", element.value)
|
||||
fmt.wprintf(writer, "<%v>\n", element.ident)
|
||||
|
||||
for value in 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 {
|
||||
tab(writer, indent + 1)
|
||||
wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
|
||||
}
|
||||
|
||||
for child in element.children {
|
||||
print_element(writer, doc, child, indent + 1)
|
||||
fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
|
||||
}
|
||||
} else if element.kind == .Comment {
|
||||
wprintf(writer, "[COMMENT] %v\n", element.value)
|
||||
fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
|
||||
}
|
||||
|
||||
return written, .None
|
||||
|
||||
@@ -72,10 +72,10 @@ example :: proc() {
|
||||
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])
|
||||
printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0xcaa042b9 else "🤬", crc32)
|
||||
crc32 := doc_hash(docs[0], false)
|
||||
printf("[%v] CRC32: 0x%08x\n", "🎉" if crc32 == 0x420dbac5 else "🤬", crc32)
|
||||
|
||||
for round in 0..<N {
|
||||
defer xml.destroy(docs[round])
|
||||
|
||||
@@ -13,20 +13,25 @@ find_child_by_ident :: proc(doc: ^Document, parent_id: Element_ID, ident: string
|
||||
tag := doc.elements[parent_id]
|
||||
|
||||
count := 0
|
||||
for child_id in tag.children {
|
||||
child := doc.elements[child_id]
|
||||
/*
|
||||
Skip commments. They have no name.
|
||||
*/
|
||||
if child.kind != .Element { continue }
|
||||
for v in tag.value {
|
||||
switch child_id in v {
|
||||
case string: continue
|
||||
case Element_ID:
|
||||
child := doc.elements[child_id]
|
||||
/*
|
||||
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 child.ident == ident {
|
||||
if count == nth { return child_id, true }
|
||||
count += 1
|
||||
/*
|
||||
If the ident matches and it's the nth such child, return it.
|
||||
*/
|
||||
if child.ident == ident {
|
||||
if count == nth { return child_id, true }
|
||||
count += 1
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
return 0, false
|
||||
}
|
||||
|
||||
@@ -125,38 +125,38 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
}
|
||||
|
||||
@(optimization_mode="speed")
|
||||
advance_rune :: proc(using t: ^Tokenizer) {
|
||||
advance_rune :: proc(t: ^Tokenizer) {
|
||||
#no_bounds_check {
|
||||
/*
|
||||
Already bounds-checked here.
|
||||
*/
|
||||
if read_offset < len(src) {
|
||||
offset = read_offset
|
||||
if ch == '\n' {
|
||||
line_offset = offset
|
||||
line_count += 1
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset
|
||||
if t.ch == '\n' {
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
r, w := rune(src[read_offset]), 1
|
||||
r, w := rune(t.src[t.read_offset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
error(t, t.offset, "illegal character NUL")
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
}
|
||||
read_offset += w
|
||||
ch = r
|
||||
t.read_offset += w
|
||||
t.ch = r
|
||||
} else {
|
||||
offset = len(src)
|
||||
if ch == '\n' {
|
||||
line_offset = offset
|
||||
line_count += 1
|
||||
t.offset = len(t.src)
|
||||
if t.ch == '\n' {
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
ch = -1
|
||||
t.ch = -1
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -125,16 +125,19 @@ Document :: struct {
|
||||
|
||||
Element :: struct {
|
||||
ident: string,
|
||||
value: string,
|
||||
value: [dynamic]Value,
|
||||
attribs: Attributes,
|
||||
|
||||
kind: enum {
|
||||
Element = 0,
|
||||
Comment,
|
||||
},
|
||||
|
||||
parent: Element_ID,
|
||||
children: [dynamic]Element_ID,
|
||||
}
|
||||
|
||||
Value :: union {
|
||||
string,
|
||||
Element_ID,
|
||||
}
|
||||
|
||||
Attribute :: struct {
|
||||
@@ -247,9 +250,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
|
||||
err = .Unexpected_Token
|
||||
element, parent: Element_ID
|
||||
|
||||
tag_is_open := false
|
||||
first_element := true
|
||||
open: Token
|
||||
|
||||
/*
|
||||
@@ -275,16 +275,10 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
e.g. <odin - Start of new element.
|
||||
*/
|
||||
element = new_element(doc)
|
||||
tag_is_open = true
|
||||
|
||||
if first_element {
|
||||
/*
|
||||
First element.
|
||||
*/
|
||||
parent = element
|
||||
first_element = false
|
||||
if element == 0 { // First Element
|
||||
parent = element
|
||||
} else {
|
||||
append(&doc.elements[parent].children, element)
|
||||
append(&doc.elements[parent].value, element)
|
||||
}
|
||||
|
||||
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
|
||||
parent = doc.elements[element].parent
|
||||
element = parent
|
||||
tag_is_open = false
|
||||
|
||||
case:
|
||||
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
|
||||
element = parent
|
||||
tag_is_open = false
|
||||
|
||||
} else if open.kind == .Exclaim {
|
||||
/*
|
||||
@@ -392,8 +384,8 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
el := new_element(doc)
|
||||
doc.elements[el].parent = element
|
||||
doc.elements[el].kind = .Comment
|
||||
doc.elements[el].value = comment
|
||||
append(&doc.elements[element].children, el)
|
||||
append(&doc.elements[el].value, comment)
|
||||
append(&doc.elements[element].value, el)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -436,9 +428,6 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
/*
|
||||
End of file.
|
||||
*/
|
||||
if tag_is_open {
|
||||
return doc, .Premature_EOF
|
||||
}
|
||||
break loop
|
||||
|
||||
case:
|
||||
@@ -450,7 +439,7 @@ parse_bytes :: proc(data: []u8, options := DEFAULT_OPTIONS, path := "", error_ha
|
||||
needs_processing |= .Decode_SGML_Entities in opts.flags
|
||||
|
||||
if !needs_processing {
|
||||
doc.elements[element].value = body_text
|
||||
append(&doc.elements[element].value, body_text)
|
||||
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)
|
||||
if decode_err == .None {
|
||||
doc.elements[element].value = decoded
|
||||
append(&doc.elements[element].value, decoded)
|
||||
append(&doc.strings_to_free, decoded)
|
||||
} 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 {
|
||||
delete(el.attribs)
|
||||
delete(el.children)
|
||||
delete(el.value)
|
||||
}
|
||||
delete(doc.elements)
|
||||
|
||||
@@ -710,6 +699,5 @@ new_element :: proc(doc: ^Document) -> (id: Element_ID) {
|
||||
|
||||
cur := doc.element_count
|
||||
doc.element_count += 1
|
||||
|
||||
return cur
|
||||
}
|
||||
+14
-35
@@ -835,22 +835,22 @@ int_from_arg :: proc(args: []any, arg_index: int) -> (int, int, bool) {
|
||||
// - fi: A pointer to an Info structure
|
||||
// - 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
|
||||
defer fi.in_bad = prev_in_bad
|
||||
fi.in_bad = true
|
||||
|
||||
io.write_string(writer, "%!", &fi.n)
|
||||
io.write_rune(writer, verb, &fi.n)
|
||||
io.write_byte(writer, '(', &fi.n)
|
||||
if arg.id != nil {
|
||||
reflect.write_typeid(writer, arg.id, &fi.n)
|
||||
io.write_byte(writer, '=', &fi.n)
|
||||
fmt_value(fi, arg, 'v')
|
||||
io.write_string(fi.writer, "%!", &fi.n)
|
||||
io.write_rune(fi.writer, verb, &fi.n)
|
||||
io.write_byte(fi.writer, '(', &fi.n)
|
||||
if fi.arg.id != nil {
|
||||
reflect.write_typeid(fi.writer, fi.arg.id, &fi.n)
|
||||
io.write_byte(fi.writer, '=', &fi.n)
|
||||
fmt_value(fi, fi.arg, 'v')
|
||||
} 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
|
||||
//
|
||||
@@ -859,7 +859,7 @@ fmt_bad_verb :: proc(using fi: ^Info, verb: rune) {
|
||||
// - b: The boolean value to format
|
||||
// - 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 {
|
||||
case 't', 'v':
|
||||
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)
|
||||
|
||||
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)
|
||||
absolute_ptr := any{ptr, info.pointer.id}
|
||||
|
||||
if verb == 'p' {
|
||||
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)
|
||||
}
|
||||
}
|
||||
fmt_value(fi, absolute_ptr, verb)
|
||||
|
||||
case runtime.Type_Info_Matrix:
|
||||
fmt_matrix(fi, v, verb, info)
|
||||
|
||||
@@ -4,13 +4,14 @@ import "core:bytes"
|
||||
import "core:image"
|
||||
|
||||
destroy :: proc(img: ^image.Image) -> bool {
|
||||
if img == nil do return false
|
||||
if img == nil {
|
||||
return false
|
||||
}
|
||||
|
||||
defer free(img)
|
||||
bytes.buffer_destroy(&img.pixels)
|
||||
|
||||
info, ok := img.metadata.(^image.Netpbm_Info)
|
||||
if !ok do return false
|
||||
info := img.metadata.(^image.Netpbm_Info) or_return
|
||||
|
||||
header_destroy(&info.header)
|
||||
free(info)
|
||||
@@ -19,9 +20,9 @@ destroy :: proc(img: ^image.Image) -> bool {
|
||||
return true
|
||||
}
|
||||
|
||||
header_destroy :: proc(using header: ^Header) {
|
||||
if format == .P7 && tupltype != "" {
|
||||
delete(tupltype)
|
||||
tupltype = ""
|
||||
header_destroy :: proc(header: ^Header) {
|
||||
if header.format == .P7 && header.tupltype != "" {
|
||||
delete(header.tupltype)
|
||||
header.tupltype = ""
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
//+vet !using-stmt
|
||||
package netpbm
|
||||
|
||||
import "core:bytes"
|
||||
|
||||
@@ -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) {
|
||||
if png_time, png_ok := time(c); png_ok {
|
||||
using png_time
|
||||
if t, png_ok := time(c); png_ok {
|
||||
return coretime.datetime_to_time(
|
||||
int(year), int(month), int(day),
|
||||
int(hour), int(minute), int(second),
|
||||
int(t.year), int(t.month), int(t.day),
|
||||
int(t.hour), int(t.minute), int(t.second),
|
||||
)
|
||||
} else {
|
||||
return {}, false
|
||||
|
||||
@@ -11,6 +11,7 @@
|
||||
// package png implements a PNG image reader
|
||||
//
|
||||
// The PNG specification is at https://www.w3.org/TR/PNG/.
|
||||
//+vet !using-stmt
|
||||
package png
|
||||
|
||||
import "core:compress"
|
||||
@@ -444,15 +445,14 @@ load_from_context :: proc(ctx: ^$C, options := Options{}, allocator := context.a
|
||||
img.width = int(header.width)
|
||||
img.height = int(header.height)
|
||||
|
||||
using header
|
||||
h := image.PNG_IHDR{
|
||||
width = width,
|
||||
height = height,
|
||||
bit_depth = bit_depth,
|
||||
color_type = color_type,
|
||||
compression_method = compression_method,
|
||||
filter_method = filter_method,
|
||||
interlace_method = interlace_method,
|
||||
width = header.width,
|
||||
height = header.height,
|
||||
bit_depth = header.bit_depth,
|
||||
color_type = header.color_type,
|
||||
compression_method = header.compression_method,
|
||||
filter_method = header.filter_method,
|
||||
interlace_method = header.interlace_method,
|
||||
}
|
||||
info.header = h
|
||||
|
||||
|
||||
@@ -29,13 +29,13 @@ Fixed52_12 :: distinct Fixed(i64, 12)
|
||||
|
||||
|
||||
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 &= 1<<Fraction_Width - 1
|
||||
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) {
|
||||
i, f := math.modf(val)
|
||||
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 {
|
||||
res := f64(x.i >> Fraction_Width)
|
||||
res += f64(x.i & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
|
||||
return res
|
||||
sign := -1.0 if x.i < 0 else 1.0
|
||||
num := math.abs(x.i)
|
||||
res := f64(num >> Fraction_Width)
|
||||
res += f64(num & (1<<Fraction_Width-1)) / f64(1<<Fraction_Width)
|
||||
return res * sign
|
||||
}
|
||||
|
||||
|
||||
|
||||
+12
-12
@@ -2286,20 +2286,20 @@ F64_MASK :: 0x7ff
|
||||
F64_SHIFT :: 64 - 12
|
||||
F64_BIAS :: 0x3ff
|
||||
|
||||
INF_F16 :f16: 0h7C00
|
||||
NEG_INF_F16 :f16: 0hFC00
|
||||
INF_F16 :: f16(0h7C00)
|
||||
NEG_INF_F16 :: f16(0hFC00)
|
||||
|
||||
SNAN_F16 :f16: 0h7C01
|
||||
QNAN_F16 :f16: 0h7E01
|
||||
SNAN_F16 :: f16(0h7C01)
|
||||
QNAN_F16 :: f16(0h7E01)
|
||||
|
||||
INF_F32 :f32: 0h7F80_0000
|
||||
NEG_INF_F32 :f32: 0hFF80_0000
|
||||
INF_F32 :: f32(0h7F80_0000)
|
||||
NEG_INF_F32 :: f32(0hFF80_0000)
|
||||
|
||||
SNAN_F32 :f32: 0hFF80_0001
|
||||
QNAN_F32 :f32: 0hFFC0_0001
|
||||
SNAN_F32 :: f32(0hFF80_0001)
|
||||
QNAN_F32 :: f32(0hFFC0_0001)
|
||||
|
||||
INF_F64 :f64: 0h7FF0_0000_0000_0000
|
||||
NEG_INF_F64 :f64: 0hFFF0_0000_0000_0000
|
||||
INF_F64 :: f64(0h7FF0_0000_0000_0000)
|
||||
NEG_INF_F64 :: f64(0hFFF0_0000_0000_0000)
|
||||
|
||||
SNAN_F64 :f64: 0h7FF0_0000_0000_0001
|
||||
QNAN_F64 :f64: 0h7FF8_0000_0000_0001
|
||||
SNAN_F64 :: f64(0h7FF0_0000_0000_0001)
|
||||
QNAN_F64 :: f64(0h7FF8_0000_0000_0001)
|
||||
|
||||
+52
-52
@@ -111,11 +111,11 @@ begin_arena_temp_memory :: proc(a: ^Arena) -> Arena_Temp_Memory {
|
||||
return tmp
|
||||
}
|
||||
|
||||
end_arena_temp_memory :: proc(using tmp: Arena_Temp_Memory) {
|
||||
assert(arena.offset >= prev_offset)
|
||||
assert(arena.temp_count > 0)
|
||||
arena.offset = prev_offset
|
||||
arena.temp_count -= 1
|
||||
end_arena_temp_memory :: proc(tmp: Arena_Temp_Memory) {
|
||||
assert(tmp.arena.offset >= tmp.prev_offset)
|
||||
assert(tmp.arena.temp_count > 0)
|
||||
tmp.arena.offset = tmp.prev_offset
|
||||
tmp.arena.temp_count -= 1
|
||||
}
|
||||
|
||||
|
||||
@@ -702,11 +702,11 @@ dynamic_pool_init :: proc(pool: ^Dynamic_Pool,
|
||||
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)
|
||||
delete(unused_blocks)
|
||||
delete(used_blocks)
|
||||
delete(out_band_allocations)
|
||||
delete(pool.unused_blocks)
|
||||
delete(pool.used_blocks)
|
||||
delete(pool.out_band_allocations)
|
||||
|
||||
zero(pool, size_of(pool^))
|
||||
}
|
||||
@@ -719,90 +719,90 @@ dynamic_pool_alloc :: proc(pool: ^Dynamic_Pool, bytes: int) -> (rawptr, Allocato
|
||||
}
|
||||
|
||||
@(require_results)
|
||||
dynamic_pool_alloc_bytes :: proc(using pool: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
|
||||
cycle_new_block :: proc(using pool: ^Dynamic_Pool) -> (err: Allocator_Error) {
|
||||
if block_allocator.procedure == nil {
|
||||
dynamic_pool_alloc_bytes :: proc(p: ^Dynamic_Pool, bytes: int) -> ([]byte, Allocator_Error) {
|
||||
cycle_new_block :: proc(p: ^Dynamic_Pool) -> (err: Allocator_Error) {
|
||||
if p.block_allocator.procedure == nil {
|
||||
panic("You must call pool_init on a Pool before using it")
|
||||
}
|
||||
|
||||
if current_block != nil {
|
||||
append(&used_blocks, current_block)
|
||||
if p.current_block != nil {
|
||||
append(&p.used_blocks, p.current_block)
|
||||
}
|
||||
|
||||
new_block: rawptr
|
||||
if len(unused_blocks) > 0 {
|
||||
new_block = pop(&unused_blocks)
|
||||
if len(p.unused_blocks) > 0 {
|
||||
new_block = pop(&p.unused_blocks)
|
||||
} else {
|
||||
data: []byte
|
||||
data, err = block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0)
|
||||
data, err = p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
|
||||
p.block_size, p.alignment,
|
||||
nil, 0)
|
||||
new_block = raw_data(data)
|
||||
}
|
||||
|
||||
bytes_left = block_size
|
||||
current_pos = new_block
|
||||
current_block = new_block
|
||||
p.bytes_left = p.block_size
|
||||
p.current_pos = new_block
|
||||
p.current_block = new_block
|
||||
return
|
||||
}
|
||||
|
||||
n := bytes
|
||||
extra := alignment - (n % alignment)
|
||||
extra := p.alignment - (n % p.alignment)
|
||||
n += extra
|
||||
if n >= out_band_size {
|
||||
assert(block_allocator.procedure != nil)
|
||||
memory, err := block_allocator.procedure(block_allocator.data, Allocator_Mode.Alloc,
|
||||
block_size, alignment,
|
||||
nil, 0)
|
||||
if n >= p.out_band_size {
|
||||
assert(p.block_allocator.procedure != nil)
|
||||
memory, err := p.block_allocator.procedure(p.block_allocator.data, Allocator_Mode.Alloc,
|
||||
p.block_size, p.alignment,
|
||||
nil, 0)
|
||||
if memory != nil {
|
||||
append(&out_band_allocations, raw_data(memory))
|
||||
append(&p.out_band_allocations, raw_data(memory))
|
||||
}
|
||||
return memory, err
|
||||
}
|
||||
|
||||
if bytes_left < n {
|
||||
err := cycle_new_block(pool)
|
||||
if p.bytes_left < n {
|
||||
err := cycle_new_block(p)
|
||||
if err != nil {
|
||||
return nil, err
|
||||
}
|
||||
if current_block == nil {
|
||||
if p.current_block == nil {
|
||||
return nil, .Out_Of_Memory
|
||||
}
|
||||
}
|
||||
|
||||
memory := current_pos
|
||||
current_pos = ptr_offset((^byte)(current_pos), n)
|
||||
bytes_left -= n
|
||||
return byte_slice(memory, bytes), nil
|
||||
memory := p.current_pos
|
||||
p.current_pos = ([^]byte)(p.current_pos)[n:]
|
||||
p.bytes_left -= n
|
||||
return ([^]byte)(memory)[:bytes], nil
|
||||
}
|
||||
|
||||
|
||||
dynamic_pool_reset :: proc(using pool: ^Dynamic_Pool) {
|
||||
if current_block != nil {
|
||||
append(&unused_blocks, current_block)
|
||||
current_block = nil
|
||||
dynamic_pool_reset :: proc(p: ^Dynamic_Pool) {
|
||||
if p.current_block != nil {
|
||||
append(&p.unused_blocks, p.current_block)
|
||||
p.current_block = nil
|
||||
}
|
||||
|
||||
for block in used_blocks {
|
||||
append(&unused_blocks, block)
|
||||
for block in p.used_blocks {
|
||||
append(&p.unused_blocks, block)
|
||||
}
|
||||
clear(&used_blocks)
|
||||
clear(&p.used_blocks)
|
||||
|
||||
for a in out_band_allocations {
|
||||
free(a, block_allocator)
|
||||
for a in p.out_band_allocations {
|
||||
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_reset(pool)
|
||||
dynamic_pool_free_all :: proc(p: ^Dynamic_Pool) {
|
||||
dynamic_pool_reset(p)
|
||||
|
||||
for block in unused_blocks {
|
||||
free(block, block_allocator)
|
||||
for block in p.unused_blocks {
|
||||
free(block, p.block_allocator)
|
||||
}
|
||||
clear(&unused_blocks)
|
||||
clear(&p.unused_blocks)
|
||||
}
|
||||
|
||||
|
||||
|
||||
+35
-35
@@ -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 {
|
||||
using strings
|
||||
b := strings.builder_make(allocator)
|
||||
strings.builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
|
||||
|
||||
b := builder_make(allocator)
|
||||
builder_grow(&b, len(scheme) + 3 + len(host) + 1 + len(path))
|
||||
|
||||
write_string(&b, scheme)
|
||||
write_string(&b, "://")
|
||||
write_string(&b, trim_space(host))
|
||||
strings.write_string(&b, scheme)
|
||||
strings.write_string(&b, "://")
|
||||
strings.write_string(&b, strings.trim_space(host))
|
||||
|
||||
if path != "" {
|
||||
if path[0] != '/' do write_string(&b, "/")
|
||||
write_string(&b, trim_space(path))
|
||||
if path[0] != '/' {
|
||||
strings.write_string(&b, "/")
|
||||
}
|
||||
strings.write_string(&b, strings.trim_space(path))
|
||||
}
|
||||
|
||||
|
||||
query_length := len(queries)
|
||||
if query_length > 0 do write_string(&b, "?")
|
||||
if query_length > 0 {
|
||||
strings.write_string(&b, "?")
|
||||
}
|
||||
i := 0
|
||||
for query_name, query_value in queries {
|
||||
write_string(&b, query_name)
|
||||
strings.write_string(&b, query_name)
|
||||
if query_value != "" {
|
||||
write_string(&b, "=")
|
||||
write_string(&b, query_value)
|
||||
strings.write_string(&b, "=")
|
||||
strings.write_string(&b, query_value)
|
||||
}
|
||||
if i < query_length - 1 {
|
||||
write_string(&b, "&")
|
||||
strings.write_string(&b, "&")
|
||||
}
|
||||
i += 1
|
||||
}
|
||||
|
||||
return to_string(b)
|
||||
return strings.to_string(b)
|
||||
}
|
||||
|
||||
percent_encode :: proc(s: string, allocator := context.allocator) -> string {
|
||||
using strings
|
||||
|
||||
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.
|
||||
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.
|
||||
|
||||
for ch in s {
|
||||
switch ch {
|
||||
case 'A'..='Z', 'a'..='z', '0'..='9', '-', '_', '.', '~':
|
||||
write_rune(&b, ch)
|
||||
strings.write_rune(&b, ch)
|
||||
case:
|
||||
bytes, n := utf8.encode_rune(ch)
|
||||
for byte in bytes[:n] {
|
||||
buf: [2]u8 = ---
|
||||
t := strconv.append_int(buf[:], i64(byte), 16)
|
||||
write_rune(&b, '%')
|
||||
write_string(&b, t)
|
||||
strings.write_rune(&b, '%')
|
||||
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) {
|
||||
using strings
|
||||
|
||||
b := builder_make(allocator)
|
||||
builder_grow(&b, len(encoded_string))
|
||||
defer if !ok do builder_destroy(&b)
|
||||
b := strings.builder_make(allocator)
|
||||
strings.builder_grow(&b, len(encoded_string))
|
||||
defer if !ok do strings.builder_destroy(&b)
|
||||
|
||||
s := encoded_string
|
||||
|
||||
for len(s) > 0 {
|
||||
i := index_byte(s, '%')
|
||||
i := strings.index_byte(s, '%')
|
||||
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
|
||||
}
|
||||
|
||||
write_string(&b, s[:i])
|
||||
strings.write_string(&b, s[:i])
|
||||
s = s[i:]
|
||||
|
||||
if len(s) == 0 do return // percent without anything after it
|
||||
s = s[1:]
|
||||
|
||||
if s[0] == '%' {
|
||||
write_byte(&b, '%')
|
||||
strings.write_byte(&b, '%')
|
||||
s = s[1:]
|
||||
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
|
||||
write_byte(&b, val)
|
||||
strings.write_byte(&b, val)
|
||||
s = s[2:]
|
||||
}
|
||||
|
||||
ok = true
|
||||
decoded_string = to_string(b)
|
||||
decoded_string = strings.to_string(b)
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -162,31 +162,31 @@ Attribute :: struct {
|
||||
}
|
||||
|
||||
Type_Kind :: enum u32le {
|
||||
Invalid = 0,
|
||||
Basic = 1,
|
||||
Named = 2,
|
||||
Generic = 3,
|
||||
Pointer = 4,
|
||||
Array = 5,
|
||||
Enumerated_Array = 6,
|
||||
Slice = 7,
|
||||
Dynamic_Array = 8,
|
||||
Map = 9,
|
||||
Struct = 10,
|
||||
Union = 11,
|
||||
Enum = 12,
|
||||
Tuple = 13,
|
||||
Proc = 14,
|
||||
Bit_Set = 15,
|
||||
Simd_Vector = 16,
|
||||
SOA_Struct_Fixed = 17,
|
||||
SOA_Struct_Slice = 18,
|
||||
SOA_Struct_Dynamic = 19,
|
||||
Relative_Pointer = 20,
|
||||
Relative_Slice = 21,
|
||||
Multi_Pointer = 22,
|
||||
Matrix = 23,
|
||||
Soa_Pointer = 24,
|
||||
Invalid = 0,
|
||||
Basic = 1,
|
||||
Named = 2,
|
||||
Generic = 3,
|
||||
Pointer = 4,
|
||||
Array = 5,
|
||||
Enumerated_Array = 6,
|
||||
Slice = 7,
|
||||
Dynamic_Array = 8,
|
||||
Map = 9,
|
||||
Struct = 10,
|
||||
Union = 11,
|
||||
Enum = 12,
|
||||
Tuple = 13,
|
||||
Proc = 14,
|
||||
Bit_Set = 15,
|
||||
Simd_Vector = 16,
|
||||
SOA_Struct_Fixed = 17,
|
||||
SOA_Struct_Slice = 18,
|
||||
SOA_Struct_Dynamic = 19,
|
||||
Relative_Pointer = 20,
|
||||
Relative_Multi_Pointer = 21,
|
||||
Multi_Pointer = 22,
|
||||
Matrix = 23,
|
||||
Soa_Pointer = 24,
|
||||
}
|
||||
|
||||
Type_Elems_Cap :: 4
|
||||
|
||||
@@ -2943,7 +2943,7 @@ parse_call_expr :: proc(p: ^Parser, operand: ^ast.Expr) -> ^ast.Expr {
|
||||
|
||||
seen_ellipsis := false
|
||||
for p.curr_tok.kind != .Close_Paren &&
|
||||
p.curr_tok.kind != .EOF {
|
||||
p.curr_tok.kind != .EOF {
|
||||
|
||||
if p.curr_tok.kind == .Comma {
|
||||
error(p, p.curr_tok.pos, "expected an expression not ,")
|
||||
|
||||
@@ -336,22 +336,20 @@ hint_current_line :: proc(p: ^Printer, hint: Line_Type) {
|
||||
|
||||
@(private)
|
||||
visit_decl :: proc(p: ^Printer, decl: ^ast.Decl, called_in_stmt := false) {
|
||||
using ast
|
||||
|
||||
if decl == nil {
|
||||
return
|
||||
}
|
||||
|
||||
#partial switch v in decl.derived_stmt {
|
||||
case ^Expr_Stmt:
|
||||
case ^ast.Expr_Stmt:
|
||||
move_line(p, decl.pos)
|
||||
visit_expr(p, v.expr)
|
||||
if p.config.semicolons {
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
}
|
||||
case ^When_Stmt:
|
||||
visit_stmt(p, cast(^Stmt)decl)
|
||||
case ^Foreign_Import_Decl:
|
||||
case ^ast.When_Stmt:
|
||||
visit_stmt(p, cast(^ast.Stmt)decl)
|
||||
case ^ast.Foreign_Import_Decl:
|
||||
if len(v.attributes) > 0 {
|
||||
sort.sort(sort_attribute(&v.attributes))
|
||||
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 {
|
||||
push_ident_token(p, path, 0)
|
||||
}
|
||||
case ^Foreign_Block_Decl:
|
||||
case ^ast.Foreign_Block_Decl:
|
||||
if len(v.attributes) > 0 {
|
||||
sort.sort(sort_attribute(&v.attributes))
|
||||
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_stmt(p, v.body)
|
||||
case ^Import_Decl:
|
||||
case ^ast.Import_Decl:
|
||||
move_line(p, decl.pos)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
case ^Value_Decl:
|
||||
case ^ast.Value_Decl:
|
||||
if len(v.attributes) > 0 {
|
||||
sort.sort(sort_attribute(&v.attributes))
|
||||
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 {
|
||||
#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
|
||||
case ^Proc_Lit:
|
||||
case ^ast.Proc_Lit:
|
||||
add_semicolon = false
|
||||
}
|
||||
}
|
||||
@@ -510,40 +508,38 @@ visit_attributes :: proc(p: ^Printer, attributes: [dynamic]^ast.Attribute) {
|
||||
|
||||
@(private)
|
||||
visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Generic, empty_block := false, block_stmt := false) {
|
||||
using ast
|
||||
|
||||
if stmt == nil {
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
switch v in stmt.derived_stmt {
|
||||
case ^Bad_Stmt:
|
||||
case ^Bad_Decl:
|
||||
case ^Package_Decl:
|
||||
case ^ast.Bad_Stmt:
|
||||
case ^ast.Bad_Decl:
|
||||
case ^ast.Package_Decl:
|
||||
|
||||
case ^Empty_Stmt:
|
||||
case ^ast.Empty_Stmt:
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
case ^Tag_Stmt:
|
||||
case ^ast.Tag_Stmt:
|
||||
push_generic_token(p, .Hash, 1)
|
||||
push_generic_token(p, v.op.kind, 1, v.op.text)
|
||||
visit_stmt(p, v.stmt)
|
||||
|
||||
|
||||
case ^Import_Decl:
|
||||
visit_decl(p, cast(^Decl)stmt, true)
|
||||
case ^ast.Import_Decl:
|
||||
visit_decl(p, cast(^ast.Decl)stmt, true)
|
||||
return
|
||||
case ^Value_Decl:
|
||||
visit_decl(p, cast(^Decl)stmt, true)
|
||||
case ^ast.Value_Decl:
|
||||
visit_decl(p, cast(^ast.Decl)stmt, true)
|
||||
return
|
||||
case ^Foreign_Import_Decl:
|
||||
visit_decl(p, cast(^Decl)stmt, true)
|
||||
case ^ast.Foreign_Import_Decl:
|
||||
visit_decl(p, cast(^ast.Decl)stmt, true)
|
||||
return
|
||||
case ^Foreign_Block_Decl:
|
||||
visit_decl(p, cast(^Decl)stmt, true)
|
||||
case ^ast.Foreign_Block_Decl:
|
||||
visit_decl(p, cast(^ast.Decl)stmt, true)
|
||||
return
|
||||
|
||||
case ^Using_Stmt:
|
||||
case ^ast.Using_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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 {
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
}
|
||||
case ^Block_Stmt:
|
||||
case ^ast.Block_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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)
|
||||
}
|
||||
}
|
||||
case ^If_Stmt:
|
||||
case ^ast.If_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
if v.label != nil {
|
||||
@@ -606,7 +602,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
||||
|
||||
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
|
||||
}
|
||||
|
||||
@@ -637,7 +633,7 @@ visit_stmt :: proc(p: ^Printer, stmt: ^ast.Stmt, block_type: Block_Type = .Gener
|
||||
|
||||
visit_stmt(p, v.else_stmt)
|
||||
}
|
||||
case ^Switch_Stmt:
|
||||
case ^ast.Switch_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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_stmt(p, v.body)
|
||||
case ^Case_Clause:
|
||||
case ^ast.Case_Clause:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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 {
|
||||
indent(p)
|
||||
}
|
||||
case ^Type_Switch_Stmt:
|
||||
case ^ast.Type_Switch_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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.body)
|
||||
case ^Assign_Stmt:
|
||||
case ^ast.Assign_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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 {
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
}
|
||||
case ^Expr_Stmt:
|
||||
case ^ast.Expr_Stmt:
|
||||
move_line(p, v.pos)
|
||||
visit_expr(p, v.expr)
|
||||
if block_stmt && p.config.semicolons {
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
}
|
||||
case ^For_Stmt:
|
||||
case ^ast.For_Stmt:
|
||||
// this should be simplified
|
||||
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)
|
||||
|
||||
case ^Inline_Range_Stmt:
|
||||
case ^ast.Inline_Range_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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_stmt(p, v.body)
|
||||
|
||||
case ^Range_Stmt:
|
||||
case ^ast.Range_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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_stmt(p, v.body)
|
||||
case ^Return_Stmt:
|
||||
case ^ast.Return_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
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 {
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
}
|
||||
case ^Defer_Stmt:
|
||||
case ^ast.Defer_Stmt:
|
||||
move_line(p, v.pos)
|
||||
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 {
|
||||
push_generic_token(p, .Semicolon, 0)
|
||||
}
|
||||
case ^When_Stmt:
|
||||
case ^ast.When_Stmt:
|
||||
move_line(p, v.pos)
|
||||
push_generic_token(p, .When, 1)
|
||||
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)
|
||||
}
|
||||
|
||||
case ^Branch_Stmt:
|
||||
case ^ast.Branch_Stmt:
|
||||
move_line(p, v.pos)
|
||||
|
||||
push_generic_token(p, v.tok.kind, 0)
|
||||
@@ -921,8 +917,6 @@ push_poly_params :: proc(p: ^Printer, poly_params: ^ast.Field_List) {
|
||||
|
||||
@(private)
|
||||
visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
||||
using ast
|
||||
|
||||
if expr == nil {
|
||||
return
|
||||
}
|
||||
@@ -930,14 +924,14 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
||||
set_source_position(p, expr.pos)
|
||||
|
||||
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, v.op.kind, 1, v.op.text)
|
||||
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, .Open_Paren, 1)
|
||||
@@ -954,42 +948,42 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
||||
push_generic_token(p, .Comma, 0)
|
||||
visit_expr(p, v.constraints_string)
|
||||
push_generic_token(p, .Close_Brace, 0)
|
||||
case ^Undef:
|
||||
case ^ast.Undef:
|
||||
push_generic_token(p, .Undef, 1)
|
||||
case ^Auto_Cast:
|
||||
case ^ast.Auto_Cast:
|
||||
push_generic_token(p, v.op.kind, 1)
|
||||
visit_expr(p, v.expr)
|
||||
case ^Ternary_If_Expr:
|
||||
case ^ast.Ternary_If_Expr:
|
||||
visit_expr(p, v.x)
|
||||
push_generic_token(p, v.op1.kind, 1)
|
||||
visit_expr(p, v.cond)
|
||||
push_generic_token(p, v.op2.kind, 1)
|
||||
visit_expr(p, v.y)
|
||||
case ^Ternary_When_Expr:
|
||||
case ^ast.Ternary_When_Expr:
|
||||
visit_expr(p, v.x)
|
||||
push_generic_token(p, v.op1.kind, 1)
|
||||
visit_expr(p, v.cond)
|
||||
push_generic_token(p, v.op2.kind, 1)
|
||||
visit_expr(p, v.y)
|
||||
case ^Or_Else_Expr:
|
||||
case ^ast.Or_Else_Expr:
|
||||
visit_expr(p, v.x)
|
||||
push_generic_token(p, v.token.kind, 1)
|
||||
visit_expr(p, v.y)
|
||||
case ^Or_Return_Expr:
|
||||
case ^ast.Or_Return_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
push_generic_token(p, v.token.kind, 1)
|
||||
case ^Selector_Call_Expr:
|
||||
case ^ast.Selector_Call_Expr:
|
||||
visit_expr(p, v.call.expr)
|
||||
push_generic_token(p, .Open_Paren, 1)
|
||||
visit_exprs(p, v.call.args, {.Add_Comma})
|
||||
push_generic_token(p, .Close_Paren, 0)
|
||||
case ^Ellipsis:
|
||||
case ^ast.Ellipsis:
|
||||
push_generic_token(p, .Ellipsis, 1)
|
||||
visit_expr(p, v.expr)
|
||||
case ^Relative_Type:
|
||||
case ^ast.Relative_Type:
|
||||
visit_expr(p, v.tag)
|
||||
visit_expr(p, v.type)
|
||||
case ^Slice_Expr:
|
||||
case ^ast.Slice_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
push_generic_token(p, .Open_Bracket, 0)
|
||||
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)
|
||||
}
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
case ^Ident:
|
||||
case ^ast.Ident:
|
||||
if .Enforce_Poly_Names in options {
|
||||
push_generic_token(p, .Dollar, 1)
|
||||
push_ident_token(p, v.name, 0)
|
||||
} else {
|
||||
push_ident_token(p, v.name, 1)
|
||||
}
|
||||
case ^Deref_Expr:
|
||||
case ^ast.Deref_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
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, .Open_Paren, 0)
|
||||
visit_expr(p, v.type)
|
||||
push_generic_token(p, .Close_Paren, 0)
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.expr)
|
||||
case ^Basic_Directive:
|
||||
case ^ast.Basic_Directive:
|
||||
push_generic_token(p, v.tok.kind, 1)
|
||||
push_ident_token(p, v.name, 0)
|
||||
case ^Distinct_Type:
|
||||
case ^ast.Distinct_Type:
|
||||
push_generic_token(p, .Distinct, 1)
|
||||
visit_expr(p, v.type)
|
||||
case ^Dynamic_Array_Type:
|
||||
case ^ast.Dynamic_Array_Type:
|
||||
visit_expr(p, v.tag)
|
||||
push_generic_token(p, .Open_Bracket, 1)
|
||||
push_generic_token(p, .Dynamic, 0)
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
merge_next_token(p)
|
||||
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, .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)
|
||||
case ^Union_Type:
|
||||
case ^ast.Union_Type:
|
||||
push_generic_token(p, .Union, 1)
|
||||
|
||||
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_end_brace(p, v.end)
|
||||
}
|
||||
case ^Enum_Type:
|
||||
case ^ast.Enum_Type:
|
||||
push_generic_token(p, .Enum, 1)
|
||||
|
||||
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)
|
||||
case ^Struct_Type:
|
||||
case ^ast.Struct_Type:
|
||||
push_generic_token(p, .Struct, 1)
|
||||
|
||||
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)
|
||||
case ^Proc_Lit:
|
||||
case ^ast.Proc_Lit:
|
||||
switch v.inlining {
|
||||
case .None:
|
||||
case .Inline:
|
||||
@@ -1143,16 +1137,16 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
||||
} else {
|
||||
push_generic_token(p, .Undef, 1)
|
||||
}
|
||||
case ^Proc_Type:
|
||||
case ^ast.Proc_Type:
|
||||
visit_proc_type(p, v)
|
||||
case ^Basic_Lit:
|
||||
case ^ast.Basic_Lit:
|
||||
push_generic_token(p, v.tok.kind, 1, v.tok.text)
|
||||
case ^Binary_Expr:
|
||||
case ^ast.Binary_Expr:
|
||||
visit_binary_expr(p, v)
|
||||
case ^Implicit_Selector_Expr:
|
||||
case ^ast.Implicit_Selector_Expr:
|
||||
push_generic_token(p, .Period, 1)
|
||||
push_ident_token(p, v.field.name, 0)
|
||||
case ^Call_Expr:
|
||||
case ^ast.Call_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
|
||||
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)
|
||||
push_generic_token(p, .Close_Paren, 0)
|
||||
case ^Typeid_Type:
|
||||
case ^ast.Typeid_Type:
|
||||
push_generic_token(p, .Typeid, 1)
|
||||
|
||||
if v.specialization != nil {
|
||||
push_generic_token(p, .Quo, 0)
|
||||
visit_expr(p, v.specialization)
|
||||
}
|
||||
case ^Selector_Expr:
|
||||
case ^ast.Selector_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
push_generic_token(p, v.op.kind, 0)
|
||||
visit_expr(p, v.field)
|
||||
case ^Paren_Expr:
|
||||
case ^ast.Paren_Expr:
|
||||
push_generic_token(p, .Open_Paren, 1)
|
||||
visit_expr(p, v.expr)
|
||||
push_generic_token(p, .Close_Paren, 0)
|
||||
case ^Index_Expr:
|
||||
case ^ast.Index_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
push_generic_token(p, .Open_Bracket, 0)
|
||||
visit_expr(p, v.index)
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
case ^Matrix_Index_Expr:
|
||||
case ^ast.Matrix_Index_Expr:
|
||||
visit_expr(p, v.expr)
|
||||
push_generic_token(p, .Open_Bracket, 0)
|
||||
visit_expr(p, v.row_index)
|
||||
push_generic_token(p, .Comma, 0)
|
||||
visit_expr(p, v.column_index)
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
case ^Proc_Group:
|
||||
case ^ast.Proc_Group:
|
||||
push_generic_token(p, v.tok.kind, 1)
|
||||
|
||||
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)
|
||||
}
|
||||
|
||||
case ^Comp_Lit:
|
||||
case ^ast.Comp_Lit:
|
||||
if v.type != nil {
|
||||
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)
|
||||
}
|
||||
|
||||
case ^Unary_Expr:
|
||||
case ^ast.Unary_Expr:
|
||||
push_generic_token(p, v.op.kind, 1)
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.expr)
|
||||
case ^Field_Value:
|
||||
case ^ast.Field_Value:
|
||||
visit_expr(p, v.field)
|
||||
push_generic_token(p, .Eq, 1)
|
||||
visit_expr(p, v.value)
|
||||
case ^Type_Assertion:
|
||||
case ^ast.Type_Assertion:
|
||||
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)
|
||||
visit_expr(p, v.type)
|
||||
} else {
|
||||
@@ -1247,13 +1241,13 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
||||
push_generic_token(p, .Close_Paren, 0)
|
||||
}
|
||||
|
||||
case ^Pointer_Type:
|
||||
case ^ast.Pointer_Type:
|
||||
push_generic_token(p, .Pointer, 1)
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.elem)
|
||||
case ^Implicit:
|
||||
case ^ast.Implicit:
|
||||
push_generic_token(p, v.tok.kind, 1)
|
||||
case ^Poly_Type:
|
||||
case ^ast.Poly_Type:
|
||||
push_generic_token(p, .Dollar, 1)
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.type)
|
||||
@@ -1263,28 +1257,28 @@ visit_expr :: proc(p: ^Printer, expr: ^ast.Expr, options := List_Options{}) {
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.specialization)
|
||||
}
|
||||
case ^Array_Type:
|
||||
case ^ast.Array_Type:
|
||||
visit_expr(p, v.tag)
|
||||
push_generic_token(p, .Open_Bracket, 1)
|
||||
visit_expr(p, v.len)
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.elem)
|
||||
case ^Map_Type:
|
||||
case ^ast.Map_Type:
|
||||
push_generic_token(p, .Map, 1)
|
||||
push_generic_token(p, .Open_Bracket, 0)
|
||||
visit_expr(p, v.key)
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
merge_next_token(p)
|
||||
visit_expr(p, v.value)
|
||||
case ^Helper_Type:
|
||||
case ^ast.Helper_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, .Pointer, 0)
|
||||
push_generic_token(p, .Close_Bracket, 0)
|
||||
visit_expr(p, v.elem)
|
||||
case ^Matrix_Type:
|
||||
case ^ast.Matrix_Type:
|
||||
push_generic_token(p, .Matrix, 1)
|
||||
push_generic_token(p, .Open_Bracket, 0)
|
||||
visit_expr(p, v.row_count)
|
||||
|
||||
@@ -75,34 +75,34 @@ error :: proc(t: ^Tokenizer, offset: int, msg: string, args: ..any) {
|
||||
t.error_count += 1
|
||||
}
|
||||
|
||||
advance_rune :: proc(using t: ^Tokenizer) {
|
||||
if read_offset < len(src) {
|
||||
offset = read_offset
|
||||
if ch == '\n' {
|
||||
line_offset = offset
|
||||
line_count += 1
|
||||
advance_rune :: proc(t: ^Tokenizer) {
|
||||
if t.read_offset < len(t.src) {
|
||||
t.offset = t.read_offset
|
||||
if t.ch == '\n' {
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
r, w := rune(src[read_offset]), 1
|
||||
r, w := rune(t.src[t.read_offset]), 1
|
||||
switch {
|
||||
case r == 0:
|
||||
error(t, t.offset, "illegal character NUL")
|
||||
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 {
|
||||
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")
|
||||
}
|
||||
}
|
||||
read_offset += w
|
||||
ch = r
|
||||
t.read_offset += w
|
||||
t.ch = r
|
||||
} else {
|
||||
offset = len(src)
|
||||
if ch == '\n' {
|
||||
line_offset = offset
|
||||
line_count += 1
|
||||
t.offset = len(t.src)
|
||||
if t.ch == '\n' {
|
||||
t.line_offset = t.offset
|
||||
t.line_count += 1
|
||||
}
|
||||
ch = -1
|
||||
t.ch = -1
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+59
-60
@@ -8,35 +8,35 @@ _ :: intrinsics
|
||||
|
||||
Type_Info :: runtime.Type_Info
|
||||
|
||||
Type_Info_Named :: runtime.Type_Info_Named
|
||||
Type_Info_Integer :: runtime.Type_Info_Integer
|
||||
Type_Info_Rune :: runtime.Type_Info_Rune
|
||||
Type_Info_Float :: runtime.Type_Info_Float
|
||||
Type_Info_Complex :: runtime.Type_Info_Complex
|
||||
Type_Info_Quaternion :: runtime.Type_Info_Quaternion
|
||||
Type_Info_String :: runtime.Type_Info_String
|
||||
Type_Info_Boolean :: runtime.Type_Info_Boolean
|
||||
Type_Info_Any :: runtime.Type_Info_Any
|
||||
Type_Info_Type_Id :: runtime.Type_Info_Type_Id
|
||||
Type_Info_Pointer :: runtime.Type_Info_Pointer
|
||||
Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer
|
||||
Type_Info_Procedure :: runtime.Type_Info_Procedure
|
||||
Type_Info_Array :: runtime.Type_Info_Array
|
||||
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
|
||||
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array
|
||||
Type_Info_Slice :: runtime.Type_Info_Slice
|
||||
Type_Info_Parameters :: runtime.Type_Info_Parameters
|
||||
Type_Info_Tuple :: runtime.Type_Info_Parameters
|
||||
Type_Info_Struct :: runtime.Type_Info_Struct
|
||||
Type_Info_Union :: runtime.Type_Info_Union
|
||||
Type_Info_Enum :: runtime.Type_Info_Enum
|
||||
Type_Info_Map :: runtime.Type_Info_Map
|
||||
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
|
||||
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
|
||||
Type_Info_Relative_Slice :: runtime.Type_Info_Relative_Slice
|
||||
Type_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
|
||||
Type_Info_Named :: runtime.Type_Info_Named
|
||||
Type_Info_Integer :: runtime.Type_Info_Integer
|
||||
Type_Info_Rune :: runtime.Type_Info_Rune
|
||||
Type_Info_Float :: runtime.Type_Info_Float
|
||||
Type_Info_Complex :: runtime.Type_Info_Complex
|
||||
Type_Info_Quaternion :: runtime.Type_Info_Quaternion
|
||||
Type_Info_String :: runtime.Type_Info_String
|
||||
Type_Info_Boolean :: runtime.Type_Info_Boolean
|
||||
Type_Info_Any :: runtime.Type_Info_Any
|
||||
Type_Info_Type_Id :: runtime.Type_Info_Type_Id
|
||||
Type_Info_Pointer :: runtime.Type_Info_Pointer
|
||||
Type_Info_Multi_Pointer :: runtime.Type_Info_Multi_Pointer
|
||||
Type_Info_Procedure :: runtime.Type_Info_Procedure
|
||||
Type_Info_Array :: runtime.Type_Info_Array
|
||||
Type_Info_Enumerated_Array :: runtime.Type_Info_Enumerated_Array
|
||||
Type_Info_Dynamic_Array :: runtime.Type_Info_Dynamic_Array
|
||||
Type_Info_Slice :: runtime.Type_Info_Slice
|
||||
Type_Info_Parameters :: runtime.Type_Info_Parameters
|
||||
Type_Info_Tuple :: runtime.Type_Info_Parameters
|
||||
Type_Info_Struct :: runtime.Type_Info_Struct
|
||||
Type_Info_Union :: runtime.Type_Info_Union
|
||||
Type_Info_Enum :: runtime.Type_Info_Enum
|
||||
Type_Info_Map :: runtime.Type_Info_Map
|
||||
Type_Info_Bit_Set :: runtime.Type_Info_Bit_Set
|
||||
Type_Info_Simd_Vector :: runtime.Type_Info_Simd_Vector
|
||||
Type_Info_Relative_Pointer :: runtime.Type_Info_Relative_Pointer
|
||||
Type_Info_Relative_Multi_Pointer :: runtime.Type_Info_Relative_Multi_Pointer
|
||||
Type_Info_Matrix :: runtime.Type_Info_Matrix
|
||||
Type_Info_Soa_Pointer :: runtime.Type_Info_Soa_Pointer
|
||||
|
||||
Type_Info_Enum_Value :: runtime.Type_Info_Enum_Value
|
||||
|
||||
@@ -69,7 +69,7 @@ Type_Kind :: enum {
|
||||
Bit_Set,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
Soa_Pointer,
|
||||
}
|
||||
@@ -80,34 +80,34 @@ type_kind :: proc(T: typeid) -> Type_Kind {
|
||||
ti := type_info_of(T)
|
||||
if ti != nil {
|
||||
switch _ in ti.variant {
|
||||
case Type_Info_Named: return .Named
|
||||
case Type_Info_Integer: return .Integer
|
||||
case Type_Info_Rune: return .Rune
|
||||
case Type_Info_Float: return .Float
|
||||
case Type_Info_Complex: return .Complex
|
||||
case Type_Info_Quaternion: return .Quaternion
|
||||
case Type_Info_String: return .String
|
||||
case Type_Info_Boolean: return .Boolean
|
||||
case Type_Info_Any: return .Any
|
||||
case Type_Info_Type_Id: return .Type_Id
|
||||
case Type_Info_Pointer: return .Pointer
|
||||
case Type_Info_Multi_Pointer: return .Multi_Pointer
|
||||
case Type_Info_Procedure: return .Procedure
|
||||
case Type_Info_Array: return .Array
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array
|
||||
case Type_Info_Slice: return .Slice
|
||||
case Type_Info_Parameters: return .Tuple
|
||||
case Type_Info_Struct: return .Struct
|
||||
case Type_Info_Union: return .Union
|
||||
case Type_Info_Enum: return .Enum
|
||||
case Type_Info_Map: return .Map
|
||||
case Type_Info_Bit_Set: return .Bit_Set
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Slice: return .Relative_Slice
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
case Type_Info_Soa_Pointer: return .Soa_Pointer
|
||||
case Type_Info_Named: return .Named
|
||||
case Type_Info_Integer: return .Integer
|
||||
case Type_Info_Rune: return .Rune
|
||||
case Type_Info_Float: return .Float
|
||||
case Type_Info_Complex: return .Complex
|
||||
case Type_Info_Quaternion: return .Quaternion
|
||||
case Type_Info_String: return .String
|
||||
case Type_Info_Boolean: return .Boolean
|
||||
case Type_Info_Any: return .Any
|
||||
case Type_Info_Type_Id: return .Type_Id
|
||||
case Type_Info_Pointer: return .Pointer
|
||||
case Type_Info_Multi_Pointer: return .Multi_Pointer
|
||||
case Type_Info_Procedure: return .Procedure
|
||||
case Type_Info_Array: return .Array
|
||||
case Type_Info_Enumerated_Array: return .Enumerated_Array
|
||||
case Type_Info_Dynamic_Array: return .Dynamic_Array
|
||||
case Type_Info_Slice: return .Slice
|
||||
case Type_Info_Parameters: return .Tuple
|
||||
case Type_Info_Struct: return .Struct
|
||||
case Type_Info_Union: return .Union
|
||||
case Type_Info_Enum: return .Enum
|
||||
case Type_Info_Map: return .Map
|
||||
case Type_Info_Bit_Set: return .Bit_Set
|
||||
case Type_Info_Simd_Vector: return .Simd_Vector
|
||||
case Type_Info_Relative_Pointer: return .Relative_Pointer
|
||||
case Type_Info_Relative_Multi_Pointer: return .Relative_Multi_Pointer
|
||||
case Type_Info_Matrix: return .Matrix
|
||||
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)
|
||||
case Type_Info_Map:
|
||||
return false
|
||||
case Type_Info_Relative_Slice:
|
||||
return false
|
||||
case
|
||||
Type_Info_Boolean,
|
||||
Type_Info_Integer,
|
||||
@@ -1474,6 +1472,7 @@ equal :: proc(a, b: any, including_indirect_array_recursion := false, recursion_
|
||||
Type_Info_Enum,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Soa_Pointer,
|
||||
Type_Info_Matrix:
|
||||
return runtime.memory_compare(a.data, b.data, t.size) == 0
|
||||
|
||||
@@ -165,9 +165,9 @@ are_types_identical :: proc(a, b: ^Type_Info) -> bool {
|
||||
y := b.variant.(Type_Info_Relative_Pointer) or_return
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
y := b.variant.(Type_Info_Relative_Slice) or_return
|
||||
return x.base_integer == y.base_integer && x.slice == y.slice
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
y := b.variant.(Type_Info_Relative_Multi_Pointer) or_return
|
||||
return x.base_integer == y.base_integer && x.pointer == y.pointer
|
||||
|
||||
case Type_Info_Matrix:
|
||||
y := b.variant.(Type_Info_Matrix) or_return
|
||||
@@ -383,9 +383,9 @@ is_relative_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
return ok
|
||||
}
|
||||
@(require_results)
|
||||
is_relative_slice :: proc(info: ^Type_Info) -> bool {
|
||||
is_relative_multi_pointer :: proc(info: ^Type_Info) -> bool {
|
||||
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
|
||||
}
|
||||
|
||||
@@ -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) {
|
||||
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
|
||||
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
|
||||
write_type(w, info.base_integer, &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:
|
||||
io.write_string(w, "matrix[", &n) or_return
|
||||
|
||||
@@ -162,11 +162,11 @@ Type_Info_Simd_Vector :: struct {
|
||||
count: int,
|
||||
}
|
||||
Type_Info_Relative_Pointer :: struct {
|
||||
pointer: ^Type_Info,
|
||||
pointer: ^Type_Info, // ^T
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Relative_Slice :: struct {
|
||||
slice: ^Type_Info,
|
||||
Type_Info_Relative_Multi_Pointer :: struct {
|
||||
pointer: ^Type_Info, // [^]T
|
||||
base_integer: ^Type_Info,
|
||||
}
|
||||
Type_Info_Matrix :: struct {
|
||||
@@ -219,7 +219,7 @@ Type_Info :: struct {
|
||||
Type_Info_Bit_Set,
|
||||
Type_Info_Simd_Vector,
|
||||
Type_Info_Relative_Pointer,
|
||||
Type_Info_Relative_Slice,
|
||||
Type_Info_Relative_Multi_Pointer,
|
||||
Type_Info_Matrix,
|
||||
Type_Info_Soa_Pointer,
|
||||
},
|
||||
@@ -252,7 +252,7 @@ Typeid_Kind :: enum u8 {
|
||||
Bit_Set,
|
||||
Simd_Vector,
|
||||
Relative_Pointer,
|
||||
Relative_Slice,
|
||||
Relative_Multi_Pointer,
|
||||
Matrix,
|
||||
}
|
||||
#assert(len(Typeid_Kind) < 32)
|
||||
|
||||
@@ -4,13 +4,16 @@
|
||||
//+build !js
|
||||
package runtime
|
||||
|
||||
// TODO(bill): reimplement these procedures in the os_specific stuff
|
||||
import "core:os"
|
||||
|
||||
when ODIN_DEFAULT_TO_NIL_ALLOCATOR {
|
||||
_ :: os
|
||||
|
||||
// mem.nil_allocator reimplementation
|
||||
default_allocator_proc :: nil_allocator_proc
|
||||
default_allocator :: nil_allocator
|
||||
} else {
|
||||
// TODO(bill): reimplement these procedures in the os_specific stuff
|
||||
import "core:os"
|
||||
|
||||
default_allocator_proc :: os.heap_allocator_proc
|
||||
|
||||
|
||||
@@ -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)
|
||||
tv := map_cell_index_dynamic(sv, info.vs, 1)
|
||||
|
||||
for {
|
||||
hp := &hs[pos]
|
||||
element_hash := hp^
|
||||
swap_loop: for {
|
||||
element_hash := hs[pos]
|
||||
|
||||
if map_hash_is_empty(element_hash) {
|
||||
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)
|
||||
hp^ = h
|
||||
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), 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 vp
|
||||
return result if result != 0 else v_dst
|
||||
}
|
||||
|
||||
if map_hash_is_deleted(element_hash) {
|
||||
next_pos := (pos + 1) & mask
|
||||
|
||||
// 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
|
||||
break swap_loop
|
||||
}
|
||||
|
||||
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)
|
||||
|
||||
th := h
|
||||
h = hp^
|
||||
hp^ = th
|
||||
h = hs[pos]
|
||||
hs[pos] = th
|
||||
|
||||
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
|
||||
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)
|
||||
@@ -696,49 +746,19 @@ map_erase_dynamic :: #force_inline proc "contextless" (#no_alias m: ^Raw_Map, #n
|
||||
m.len -= 1
|
||||
ok = true
|
||||
|
||||
{ // coalesce tombstones
|
||||
// HACK NOTE(bill): This is an ugly bodge but it is coalescing the tombstone slots
|
||||
mask := (uintptr(1)<<map_log2_cap(m^)) - 1
|
||||
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
|
||||
}
|
||||
mask := (uintptr(1)<<map_log2_cap(m^)) - 1
|
||||
curr_index := uintptr(index)
|
||||
next_index := (curr_index + 1) & mask
|
||||
|
||||
// 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
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
|
||||
@@ -235,7 +235,7 @@ make_slice_error_loc :: #force_inline proc "contextless" (loc := #caller_locatio
|
||||
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 {
|
||||
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(file_path, line, column, index, count)
|
||||
bounds_check_error_loc :: #force_inline proc "contextless" (loc := #caller_location, index, count: int) {
|
||||
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(file_path, line, column, hi, len)
|
||||
slice_expr_error_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, hi: int, len: int) {
|
||||
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(file_path, line, column, lo, hi, len)
|
||||
slice_expr_error_lo_hi_loc :: #force_inline proc "contextless" (loc := #caller_location, lo, hi: int, len: int) {
|
||||
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(file_path, line, column, low, high, max)
|
||||
dynamic_array_expr_error_loc :: #force_inline proc "contextless" (loc := #caller_location, low, high, max: int) {
|
||||
dynamic_array_expr_error(loc.file_path, loc.line, loc.column, low, high, max)
|
||||
}
|
||||
|
||||
@@ -11,7 +11,7 @@ RUNTIME_LINKAGE :: "strong" when (
|
||||
ODIN_BUILD_MODE == .Dynamic ||
|
||||
!ODIN_NO_CRT) &&
|
||||
!IS_WASM) else "internal"
|
||||
RUNTIME_REQUIRE :: true
|
||||
RUNTIME_REQUIRE :: !ODIN_TILDE
|
||||
|
||||
|
||||
@(private)
|
||||
@@ -218,10 +218,18 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
case n == 0: return true
|
||||
case x == y: return true
|
||||
}
|
||||
|
||||
a, b := ([^]byte)(x), ([^]byte)(y)
|
||||
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 {
|
||||
if word_length := length >> 3; word_length != 0 {
|
||||
for _ in 0..<word_length {
|
||||
@@ -276,6 +284,7 @@ memory_equal :: proc "contextless" (x, y: rawptr, n: int) -> bool {
|
||||
|
||||
return true
|
||||
}
|
||||
*/
|
||||
|
||||
}
|
||||
memory_compare :: proc "contextless" (a, b: rawptr, n: int) -> int #no_bounds_check {
|
||||
|
||||
@@ -1,9 +1,6 @@
|
||||
//+private
|
||||
//+build windows
|
||||
package runtime
|
||||
|
||||
import "core:intrinsics"
|
||||
|
||||
foreign import kernel32 "system:Kernel32.lib"
|
||||
|
||||
@(private="file")
|
||||
@@ -102,12 +99,12 @@ _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, ol
|
||||
|
||||
allocated_mem: rawptr
|
||||
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))
|
||||
} else {
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
aligned_mem = rawptr(aligned_ptr)
|
||||
intrinsics.ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem
|
||||
aligned_mem = ([^]byte)(aligned_ptr)
|
||||
([^]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) {
|
||||
@@ -129,7 +126,7 @@ _windows_default_alloc :: proc "contextless" (size, alignment: int, zero_memory
|
||||
|
||||
_windows_default_free :: proc "contextless" (ptr: rawptr) {
|
||||
if ptr != nil {
|
||||
heap_free(intrinsics.ptr_offset((^rawptr)(ptr), -1)^)
|
||||
heap_free(([^]rawptr)(ptr)[-1])
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+17
-9
@@ -8,6 +8,11 @@ _INTEGER_DIGITS_VAR := _INTEGER_DIGITS
|
||||
when !ODIN_NO_RTTI {
|
||||
print_any_single :: proc "contextless" (arg: any) {
|
||||
x := arg
|
||||
if x.data == nil {
|
||||
print_string("nil")
|
||||
return
|
||||
}
|
||||
|
||||
if loc, ok := x.(Source_Code_Location); ok {
|
||||
print_caller_location(loc)
|
||||
return
|
||||
@@ -48,6 +53,7 @@ when !ODIN_NO_RTTI {
|
||||
case int: print_int(v)
|
||||
case uint: print_uint(v)
|
||||
case uintptr: print_uintptr(v)
|
||||
case rawptr: print_uintptr(uintptr(v))
|
||||
|
||||
case bool: 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:
|
||||
ti := type_info_of(x.id)
|
||||
#partial switch v in ti.variant {
|
||||
case Type_Info_Pointer:
|
||||
case Type_Info_Pointer, Type_Info_Multi_Pointer:
|
||||
print_uintptr((^uintptr)(x.data)^)
|
||||
return
|
||||
}
|
||||
@@ -67,7 +73,9 @@ when !ODIN_NO_RTTI {
|
||||
}
|
||||
}
|
||||
println_any :: proc "contextless" (args: ..any) {
|
||||
context = default_context()
|
||||
loop: for arg, i in args {
|
||||
assert(arg.id != nil)
|
||||
if i != 0 {
|
||||
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_int :: proc "contextless" (x: int) { print_i64(i64(x)) }
|
||||
|
||||
print_caller_location :: proc "contextless" (using loc: Source_Code_Location) {
|
||||
print_string(file_path)
|
||||
print_caller_location :: proc "contextless" (loc: Source_Code_Location) {
|
||||
print_string(loc.file_path)
|
||||
when ODIN_ERROR_POS_STYLE == .Default {
|
||||
print_byte('(')
|
||||
print_u64(u64(line))
|
||||
print_u64(u64(loc.line))
|
||||
print_byte(':')
|
||||
print_u64(u64(column))
|
||||
print_u64(u64(loc.column))
|
||||
print_byte(')')
|
||||
} else when ODIN_ERROR_POS_STYLE == .Unix {
|
||||
print_byte(':')
|
||||
print_u64(u64(line))
|
||||
print_u64(u64(loc.line))
|
||||
print_byte(':')
|
||||
print_u64(u64(column))
|
||||
print_u64(u64(loc.column))
|
||||
print_byte(':')
|
||||
} else {
|
||||
#panic("unhandled ODIN_ERROR_POS_STYLE")
|
||||
@@ -463,11 +471,11 @@ print_type :: proc "contextless" (ti: ^Type_Info) {
|
||||
print_string(") ")
|
||||
print_type(info.pointer)
|
||||
|
||||
case Type_Info_Relative_Slice:
|
||||
case Type_Info_Relative_Multi_Pointer:
|
||||
print_string("#relative(")
|
||||
print_type(info.base_integer)
|
||||
print_string(") ")
|
||||
print_type(info.slice)
|
||||
print_type(info.pointer)
|
||||
|
||||
case Type_Info_Matrix:
|
||||
print_string("matrix[")
|
||||
|
||||
@@ -31,7 +31,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
if ptr != nil && len != 0 {
|
||||
b := byte(val)
|
||||
p := ([^]byte)(ptr)
|
||||
for i in 0..<len {
|
||||
for i := 0; i < len; i += 1 {
|
||||
p[i] = b
|
||||
}
|
||||
}
|
||||
@@ -75,7 +75,7 @@ when ODIN_NO_CRT && ODIN_OS == .Windows {
|
||||
if ptr != nil && len != 0 {
|
||||
b := byte(val)
|
||||
p := ([^]byte)(ptr)
|
||||
for i in 0..<len {
|
||||
for i := 0; i < len; i += 1 {
|
||||
p[i] = b
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1709,6 +1709,12 @@ MONITORINFO :: struct {
|
||||
}
|
||||
LPMONITORINFO :: ^MONITORINFO
|
||||
|
||||
CCHDEVICENAME :: 32
|
||||
MONITORINFOEXW :: struct {
|
||||
using _: MONITORINFO,
|
||||
szDevice: [CCHDEVICENAME]WCHAR,
|
||||
}
|
||||
|
||||
// SetWindowsHook() codes
|
||||
WH_MIN :: -1
|
||||
WH_MSGFILTER :: -1
|
||||
|
||||
@@ -228,6 +228,9 @@ foreign user32 {
|
||||
SetWindowRgn :: proc(hWnd: HWND, hRgn: HRGN, bRedraw: BOOL) -> int ---
|
||||
CreateRectRgnIndirect :: proc(lprect: ^RECT) -> HRGN ---
|
||||
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" (
|
||||
|
||||
@@ -71,6 +71,8 @@ Error :: enum {
|
||||
TS_File_Expected_Source,
|
||||
TS_File_Expected_Translation,
|
||||
TS_File_Expected_NumerusForm,
|
||||
Bad_Str,
|
||||
Bad_Id,
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -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) {
|
||||
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)
|
||||
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
|
||||
}
|
||||
|
||||
@@ -46,10 +62,12 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
|
||||
|
||||
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.
|
||||
child := ts.elements[child_id]
|
||||
if child.ident != "context" {
|
||||
|
||||
if ts.elements[child_id].ident != "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, "")
|
||||
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 {
|
||||
@@ -92,8 +111,14 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
|
||||
return translation, .TS_File_Expected_Translation
|
||||
}
|
||||
|
||||
source, _ := strings.intern_get(&translation.intern, ts.elements[source_id].value)
|
||||
xlat, _ := strings.intern_get(&translation.intern, ts.elements[translation_id].value)
|
||||
source := get_str(ts.elements[source_id].value[0]) or_return
|
||||
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 {
|
||||
return translation, .Duplicate_Key
|
||||
@@ -124,7 +149,8 @@ parse_qt_linguist_from_bytes :: proc(data: []byte, options := DEFAULT_PARSE_OPTI
|
||||
if !numerus_found {
|
||||
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
|
||||
|
||||
num_plurals += 1
|
||||
|
||||
@@ -129,8 +129,8 @@ _destroy :: proc(thread: ^Thread) {
|
||||
free(thread, thread.creation_allocator)
|
||||
}
|
||||
|
||||
_terminate :: proc(using thread : ^Thread, exit_code: int) {
|
||||
win32.TerminateThread(win32_thread, u32(exit_code))
|
||||
_terminate :: proc(thread: ^Thread, exit_code: int) {
|
||||
win32.TerminateThread(thread.win32_thread, u32(exit_code))
|
||||
}
|
||||
|
||||
_yield :: proc() {
|
||||
|
||||
+19
-17
@@ -59,28 +59,30 @@ sleep :: proc "contextless" (d: Duration) {
|
||||
_sleep(d)
|
||||
}
|
||||
|
||||
stopwatch_start :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
if !running {
|
||||
_start_time = tick_now()
|
||||
running = true
|
||||
stopwatch_start :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
if !stopwatch.running {
|
||||
stopwatch._start_time = tick_now()
|
||||
stopwatch.running = true
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch_stop :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
if running {
|
||||
_accumulation += tick_diff(_start_time, tick_now())
|
||||
running = false
|
||||
stopwatch_stop :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
if stopwatch.running {
|
||||
stopwatch._accumulation += tick_diff(stopwatch._start_time, tick_now())
|
||||
stopwatch.running = false
|
||||
}
|
||||
}
|
||||
|
||||
stopwatch_reset :: proc "contextless" (using stopwatch: ^Stopwatch) {
|
||||
_accumulation = {}
|
||||
running = false
|
||||
stopwatch_reset :: proc "contextless" (stopwatch: ^Stopwatch) {
|
||||
stopwatch._accumulation = {}
|
||||
stopwatch.running = false
|
||||
}
|
||||
|
||||
stopwatch_duration :: proc "contextless" (using stopwatch: Stopwatch) -> Duration {
|
||||
if !running { return _accumulation }
|
||||
return _accumulation + tick_diff(_start_time, tick_now())
|
||||
stopwatch_duration :: proc "contextless" (stopwatch: Stopwatch) -> Duration {
|
||||
if !stopwatch.running {
|
||||
return stopwatch._accumulation
|
||||
}
|
||||
return stopwatch._accumulation + tick_diff(stopwatch._start_time, tick_now())
|
||||
}
|
||||
|
||||
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) {
|
||||
abs := _time_abs(t)
|
||||
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
|
||||
return Weekday(int(sec) / SECONDS_PER_DAY)
|
||||
abs := _time_abs(t)
|
||||
sec := (abs + u64(Weekday.Monday) * SECONDS_PER_DAY) % SECONDS_PER_WEEK
|
||||
return Weekday(int(sec) / SECONDS_PER_DAY)
|
||||
}
|
||||
|
||||
clock :: proc { clock_from_time, clock_from_duration, clock_from_stopwatch }
|
||||
|
||||
+28
-6
@@ -1,3 +1,4 @@
|
||||
//+vet !using-stmt !using-param
|
||||
package main
|
||||
|
||||
import "core:fmt"
|
||||
@@ -458,6 +459,27 @@ named_proc_return_parameters :: proc() {
|
||||
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() {
|
||||
fmt.println("\n# explicit procedure overloading")
|
||||
@@ -2023,12 +2045,11 @@ relative_data_types :: proc() {
|
||||
fmt.println(ptr^)
|
||||
|
||||
arr := [3]int{1, 2, 3}
|
||||
s := arr[:]
|
||||
rel_slice: #relative(i16) []int
|
||||
rel_slice = s
|
||||
fmt.println(rel_slice)
|
||||
fmt.println(rel_slice[:])
|
||||
fmt.println(rel_slice[1])
|
||||
multi_ptr: #relative(i16) [^]int
|
||||
multi_ptr = &arr[0]
|
||||
fmt.println(multi_ptr)
|
||||
fmt.println(multi_ptr[:3])
|
||||
fmt.println(multi_ptr[1])
|
||||
}
|
||||
|
||||
or_else_operator :: proc() {
|
||||
@@ -2462,6 +2483,7 @@ main :: proc() {
|
||||
the_basics()
|
||||
control_flow()
|
||||
named_proc_return_parameters()
|
||||
variadic_procedures()
|
||||
explicit_procedure_overloading()
|
||||
struct_type()
|
||||
union_type()
|
||||
|
||||
@@ -168,6 +168,17 @@ gb_internal gb_inline Slice<T> slice(Slice<T> const &array, isize lo, isize hi)
|
||||
}
|
||||
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>
|
||||
|
||||
+41
-3
@@ -216,6 +216,43 @@ enum BuildPath : u8 {
|
||||
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
|
||||
struct BuildContext {
|
||||
// Constants
|
||||
@@ -255,6 +292,8 @@ struct BuildContext {
|
||||
String resource_filepath;
|
||||
String pdb_filepath;
|
||||
|
||||
u64 vet_flags;
|
||||
|
||||
bool has_resource;
|
||||
String link_flags;
|
||||
String extra_linker_flags;
|
||||
@@ -280,15 +319,12 @@ struct BuildContext {
|
||||
bool no_entry_point;
|
||||
bool no_thread_local;
|
||||
bool use_lld;
|
||||
bool vet;
|
||||
bool vet_extra;
|
||||
bool cross_compiling;
|
||||
bool different_os;
|
||||
bool keep_object_files;
|
||||
bool disallow_do;
|
||||
|
||||
bool strict_style;
|
||||
bool strict_style_init_only;
|
||||
|
||||
bool ignore_warnings;
|
||||
bool warnings_as_errors;
|
||||
@@ -318,6 +354,8 @@ struct BuildContext {
|
||||
|
||||
isize max_error_count;
|
||||
|
||||
bool tilde_backend;
|
||||
|
||||
|
||||
u32 cmd_doc_flags;
|
||||
Array<String> extra_packages;
|
||||
|
||||
@@ -1406,7 +1406,7 @@ gb_internal bool check_builtin_procedure_directive(CheckerContext *c, Operand *o
|
||||
}
|
||||
return false;
|
||||
} 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 == 0) {
|
||||
@@ -1748,7 +1748,7 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
mode = Addressing_Constant;
|
||||
value = exact_value_i64(at->EnumeratedArray.count);
|
||||
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;
|
||||
} else if (is_type_dynamic_array(op_type)) {
|
||||
mode = Addressing_Value;
|
||||
@@ -3692,6 +3692,9 @@ gb_internal bool check_builtin_procedure(CheckerContext *c, Operand *operand, As
|
||||
case Type_SimdVector:
|
||||
operand->type = alloc_type_multi_pointer(base_array_type(base));
|
||||
break;
|
||||
case Type_Matrix:
|
||||
operand->type = alloc_type_multi_pointer(base->Matrix.elem);
|
||||
break;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
+77
-90
@@ -7,13 +7,15 @@ gb_internal Type *check_init_variable(CheckerContext *ctx, Entity *e, Operand *o
|
||||
e->type == t_invalid) {
|
||||
|
||||
if (operand->mode == Addressing_Builtin) {
|
||||
ERROR_BLOCK();
|
||||
gbString expr_str = expr_to_string(operand->expr);
|
||||
|
||||
// TODO(bill): is this a good enough error message?
|
||||
error(operand->expr,
|
||||
"Cannot assign built-in procedure '%s' in %.*s",
|
||||
expr_str,
|
||||
LIT(context_name));
|
||||
"Cannot assign built-in procedure '%s' in %.*s",
|
||||
expr_str,
|
||||
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;
|
||||
|
||||
@@ -159,9 +161,8 @@ gb_internal void check_init_constant(CheckerContext *ctx, Entity *e, Operand *op
|
||||
}
|
||||
|
||||
if (operand->mode != Addressing_Constant) {
|
||||
// TODO(bill): better error
|
||||
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);
|
||||
if (e->type == nullptr) {
|
||||
e->type = t_invalid;
|
||||
@@ -354,31 +355,7 @@ gb_internal void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr,
|
||||
|
||||
// using decl
|
||||
if (decl->is_using) {
|
||||
warning(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
|
||||
error(init_expr, "'using' an enum declaration is not allowed, prefer using implicit selector expressions e.g. '.A'");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -757,6 +734,66 @@ gb_internal String handle_link_name(CheckerContext *ctx, Token token, String lin
|
||||
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_ASSERT(e->type == nullptr);
|
||||
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;
|
||||
|
||||
if (ac.objc_name.len || ac.objc_is_class_method || ac.objc_type) {
|
||||
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});
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
check_objc_methods(ctx, e, ac);
|
||||
|
||||
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");
|
||||
@@ -1059,7 +1041,7 @@ gb_internal void check_proc_decl(CheckerContext *ctx, Entity *e, DeclInfo *d) {
|
||||
auto *fp = &ctx->info->foreigns;
|
||||
StringHashKey key = string_hash_string(name);
|
||||
Entity **found = string_map_get(fp, key);
|
||||
if (found) {
|
||||
if (found && e != *found) {
|
||||
Entity *f = *found;
|
||||
TokenPos pos = f->token.pos;
|
||||
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");
|
||||
}
|
||||
|
||||
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);
|
||||
auto *pge = &pg_entity->ProcGroup;
|
||||
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) {
|
||||
@@ -1626,7 +1613,7 @@ gb_internal bool check_proc_body(CheckerContext *ctx_, Token token, DeclInfo *de
|
||||
}
|
||||
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);
|
||||
|
||||
|
||||
+188
-154
@@ -349,6 +349,10 @@ gb_internal bool find_or_generate_polymorphic_procedure(CheckerContext *old_c, E
|
||||
return false;
|
||||
}
|
||||
|
||||
if (base_entity->flags & EntityFlag_Disabled) {
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = base_entity->token.string;
|
||||
|
||||
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;
|
||||
defer (nctx.no_polymorphic_errors = prev_no_polymorphic_errors);
|
||||
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
|
||||
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);
|
||||
success = check_procedure_type(&nctx, final_proc_type, cloned_proc_type_node, &operands);
|
||||
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
|
||||
if (are_types_identical(type, t_rawptr) && is_type_pointer(src)) {
|
||||
return 5;
|
||||
@@ -808,7 +802,6 @@ gb_internal i64 check_distance_between_types(CheckerContext *c, Operand *operand
|
||||
return 4;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
if (is_type_polymorphic(dst) && !is_type_polymorphic(src)) {
|
||||
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) {
|
||||
Type *vt = dst->Union.variants[0];
|
||||
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)) {
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativeSlice.slice_type);
|
||||
if (is_type_relative_multi_pointer(dst)) {
|
||||
i64 score = check_distance_between_types(c, operand, dst->RelativeMultiPointer.pointer_type);
|
||||
if (score >= 0) {
|
||||
return score+2;
|
||||
}
|
||||
@@ -1013,8 +1005,8 @@ gb_internal AstPackage *get_package_of_type(Type *type) {
|
||||
case Type_RelativePointer:
|
||||
type = type->RelativePointer.pointer_type;
|
||||
continue;
|
||||
case Type_RelativeSlice:
|
||||
type = type->RelativeSlice.slice_type;
|
||||
case Type_RelativeMultiPointer:
|
||||
type = type->RelativeMultiPointer.pointer_type;
|
||||
continue;
|
||||
}
|
||||
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?
|
||||
error(operand->expr,
|
||||
"Cannot assign overloaded procedure '%s' to '%s' in %.*s",
|
||||
"Cannot assign overloaded procedure group '%s' to '%s' in %.*s",
|
||||
expr_str,
|
||||
op_type_str,
|
||||
LIT(context_name));
|
||||
@@ -1120,7 +1112,6 @@ gb_internal void check_assignment(CheckerContext *c, Operand *operand, Type *typ
|
||||
|
||||
switch (operand->mode) {
|
||||
case Addressing_Builtin:
|
||||
// TODO(bill): Actually allow built in procedures to be passed around and thus be created on use
|
||||
error(operand->expr,
|
||||
"Cannot assign built-in procedure '%s' in %.*s",
|
||||
expr_str,
|
||||
@@ -1412,9 +1403,6 @@ gb_internal bool is_polymorphic_type_assignable(CheckerContext *c, Type *poly, T
|
||||
return false;
|
||||
case 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 *y = &source->Proc;
|
||||
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;
|
||||
#endif
|
||||
}
|
||||
return false;
|
||||
case Type_Map:
|
||||
@@ -1699,7 +1686,6 @@ gb_internal bool check_unary_op(CheckerContext *c, Operand *o, Token op) {
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
// TODO(bill): Handle errors correctly
|
||||
Type *type = base_type(core_array_type(o->type));
|
||||
gbString str = nullptr;
|
||||
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) {
|
||||
Type *main_type = o->type;
|
||||
|
||||
// TODO(bill): Handle errors correctly
|
||||
Type *type = base_type(core_array_type(main_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) {
|
||||
if (!build_context.strict_style) {
|
||||
if (!(build_context.strict_style || (check_vet_flags(expr) & VetFlag_Style))) {
|
||||
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);
|
||||
}
|
||||
} else {
|
||||
if (build_context.strict_style && ast_node_expect(node, Ast_UnaryExpr)) {
|
||||
if (ast_node_expect(node, Ast_UnaryExpr)) {
|
||||
ast_node(ue, UnaryExpr, node);
|
||||
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);
|
||||
}
|
||||
|
||||
// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
|
||||
|
||||
if (!is_type_integer(x->type)) {
|
||||
gbString err_str = expr_to_string(x->expr);
|
||||
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);
|
||||
}
|
||||
|
||||
if (build_context.vet_extra) {
|
||||
if (check_vet_flags(x->expr) & VetFlag_Extra) {
|
||||
if (are_types_identical(x->type, type)) {
|
||||
gbString str = type_to_string(type);
|
||||
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;
|
||||
}
|
||||
|
||||
if (build_context.vet_extra) {
|
||||
if (check_vet_flags(node) & VetFlag_Extra) {
|
||||
if (are_types_identical(o->type, dst_t)) {
|
||||
gbString str = type_to_string(dst_t);
|
||||
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;
|
||||
|
||||
default:
|
||||
// TODO(bill): Should this be a general fallback?
|
||||
if (success_) *success_ = true;
|
||||
if (finish_) *finish_ = true;
|
||||
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))) {
|
||||
// TODO(bill): Simd_Vector swizzling
|
||||
|
||||
String field_name = selector->Ident.token.string;
|
||||
if (1 < field_name.len && field_name.len <= 4) {
|
||||
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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (o.type && is_type_no_copy(o.type)) {
|
||||
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) {
|
||||
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 is_variadic = (flags & UnpackFlag_IsVariadic) != 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);
|
||||
|
||||
bool named_variadic_param = false;
|
||||
|
||||
if (named_operands.count != 0) {
|
||||
GB_ASSERT(ce->split_args->named.count == named_operands.count);
|
||||
for_array(i, ce->split_args->named) {
|
||||
@@ -5519,6 +5505,9 @@ gb_internal CallArgumentError check_call_arguments_internal(CheckerContext *c, A
|
||||
err = CallArgumentError_ParameterNotFound;
|
||||
continue;
|
||||
}
|
||||
if (pt->variadic && param_index == pt->variadic_index) {
|
||||
named_variadic_param = true;
|
||||
}
|
||||
if (visited[param_index]) {
|
||||
if (show_error) {
|
||||
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;
|
||||
}
|
||||
|
||||
if (param_is_variadic) {
|
||||
continue;
|
||||
}
|
||||
|
||||
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
|
||||
// where the same positional parameter has the same type value (and ellipsis)
|
||||
bool proc_arg_count_all_equal = true;
|
||||
isize proc_arg_count = -1;
|
||||
for (Entity *p : procs) {
|
||||
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) {
|
||||
proc_arg_count = pt->Proc.param_count;
|
||||
} else {
|
||||
if (proc_arg_count != pt->Proc.param_count) {
|
||||
proc_arg_count_all_equal = false;
|
||||
break;
|
||||
}
|
||||
proc_arg_count = gb_min(proc_arg_count, pt->Proc.param_count);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (proc_arg_count >= 0 && proc_arg_count_all_equal) {
|
||||
if (proc_arg_count >= 0) {
|
||||
lhs_count = proc_arg_count;
|
||||
if (lhs_count > 0) {
|
||||
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;
|
||||
|
||||
ValidIndexAndScore item = {};
|
||||
item.score = data.score;
|
||||
|
||||
if (data.gen_entity != nullptr) {
|
||||
array_add(&proc_entities, data.gen_entity);
|
||||
index = proc_entities.count-1;
|
||||
|
||||
// prefer non-polymorphic procedures over polymorphic
|
||||
item.score += assign_score_function(1);
|
||||
}
|
||||
|
||||
ValidIndexAndScore item = {};
|
||||
item.index = index;
|
||||
item.score = data.score;
|
||||
array_add(&valids, item);
|
||||
}
|
||||
}
|
||||
@@ -6328,9 +6312,44 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
print_argument_types();
|
||||
}
|
||||
|
||||
if (procs.count == 0) {
|
||||
procs = proc_group_entities_cloned(c, *operand);
|
||||
}
|
||||
if (procs.count > 0) {
|
||||
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) {
|
||||
TokenPos pos = proc->token.pos;
|
||||
Type *t = base_type(proc->type);
|
||||
@@ -6350,12 +6369,23 @@ gb_internal CallArgumentData check_call_arguments_proc_group(CheckerContext *c,
|
||||
prefix_sep = str_lit(".");
|
||||
}
|
||||
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 = "::";
|
||||
if (proc->kind == Entity_Variable) {
|
||||
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) {
|
||||
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);
|
||||
print_argument_types();
|
||||
|
||||
for (isize i = 0; i < valids.count; i++) {
|
||||
Entity *proc = proc_entities[valids[i].index];
|
||||
for (auto const &valid : valids) {
|
||||
Entity *proc = proc_entities[valid.index];
|
||||
GB_ASSERT(proc != nullptr);
|
||||
TokenPos pos = proc->token.pos;
|
||||
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;
|
||||
Entity *e = entity_of_node(operand->expr);
|
||||
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)) {
|
||||
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;
|
||||
}
|
||||
}
|
||||
add_entity_use(c, operand->expr, initial_entity);
|
||||
}
|
||||
|
||||
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;
|
||||
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
{
|
||||
Type *slice_type = base_type(t->RelativeSlice.slice_type);
|
||||
GB_ASSERT(slice_type->kind == Type_Slice);
|
||||
o->type = slice_type->Slice.elem;
|
||||
Type *pointer_type = base_type(t->RelativeMultiPointer.pointer_type);
|
||||
GB_ASSERT(pointer_type->kind == Type_MultiPointer);
|
||||
o->type = pointer_type->MultiPointer.elem;
|
||||
if (o->mode != Addressing_Constant) {
|
||||
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);
|
||||
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->type = t_invalid;
|
||||
o->expr = node;
|
||||
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);
|
||||
error(se->call, "Selector call expressions expect a procedure type for the call, got '%s'", 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;
|
||||
}
|
||||
|
||||
Type *pt = base_type(x.type);
|
||||
GB_ASSERT(pt->kind == Type_Proc);
|
||||
Type *first_type = nullptr;
|
||||
String first_arg_name = {};
|
||||
if (pt->Proc.param_count > 0) {
|
||||
Entity *f = pt->Proc.params->Tuple.variables[0];
|
||||
first_type = f->type;
|
||||
first_arg_name = f->token.string;
|
||||
}
|
||||
if (first_arg_name.len == 0) {
|
||||
first_arg_name = str_lit("_");
|
||||
}
|
||||
if (e->kind != Entity_ProcGroup) {
|
||||
Type *pt = base_type(x.type);
|
||||
GB_ASSERT_MSG(pt->kind == Type_Proc, "%.*s %.*s %s", LIT(e->token.string), LIT(entity_strings[e->kind]), type_to_string(x.type));
|
||||
Type *first_type = nullptr;
|
||||
String first_arg_name = {};
|
||||
if (pt->Proc.param_count > 0) {
|
||||
Entity *f = pt->Proc.params->Tuple.variables[0];
|
||||
first_type = f->type;
|
||||
first_arg_name = f->token.string;
|
||||
}
|
||||
if (first_arg_name.len == 0) {
|
||||
first_arg_name = str_lit("_");
|
||||
}
|
||||
|
||||
if (first_type == nullptr) {
|
||||
error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
|
||||
o->mode = Addressing_Invalid;
|
||||
o->type = t_invalid;
|
||||
o->expr = node;
|
||||
return Expr_Stmt;
|
||||
}
|
||||
if (first_type == nullptr) {
|
||||
error(se->call, "Selector call expressions expect a procedure type for the call with at least 1 parameter");
|
||||
o->mode = Addressing_Invalid;
|
||||
o->type = t_invalid;
|
||||
o->expr = node;
|
||||
return Expr_Stmt;
|
||||
}
|
||||
|
||||
Operand y = {};
|
||||
y.mode = first_arg->tav.mode;
|
||||
y.type = first_arg->tav.type;
|
||||
y.value = first_arg->tav.value;
|
||||
Operand y = {};
|
||||
y.mode = first_arg->tav.mode;
|
||||
y.type = first_arg->tav.type;
|
||||
y.value = first_arg->tav.value;
|
||||
|
||||
if (check_is_assignable_to(c, &y, first_type)) {
|
||||
// Do nothing, it's valid
|
||||
} else {
|
||||
Operand z = y;
|
||||
z.type = type_deref(y.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)) {
|
||||
if (check_is_assignable_to(c, &y, first_type)) {
|
||||
// Do nothing, it's valid
|
||||
} else {
|
||||
Operand z = y;
|
||||
z.type = type_deref(y.type);
|
||||
if (check_is_assignable_to(c, &z, first_type)) {
|
||||
// NOTE(bill): AST GENERATION HACK!
|
||||
Token op = {Token_And};
|
||||
first_arg = ast_unary_expr(first_arg->file(), op, first_arg);
|
||||
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!
|
||||
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);
|
||||
modified_args[0] = first_arg;
|
||||
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_type_array(t)) {
|
||||
// OKay
|
||||
// Okay
|
||||
} else if (is_type_slice(t)) {
|
||||
// Okay
|
||||
} else if (is_type_enumerated_array(t)) {
|
||||
// Okay
|
||||
} else if (is_type_string(t)) {
|
||||
// Okay
|
||||
} else if (is_type_relative_slice(t)) {
|
||||
} else if (is_type_relative_multi_pointer(t)) {
|
||||
// Okay
|
||||
} else if (is_type_matrix(t)) {
|
||||
// Okay
|
||||
@@ -9616,17 +9647,9 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
valid = true;
|
||||
o->type = t->RelativeSlice.slice_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;
|
||||
}
|
||||
o->type = type_deref(o->type);
|
||||
break;
|
||||
|
||||
case Type_EnumeratedArray:
|
||||
@@ -9705,8 +9728,19 @@ gb_internal ExprKind check_slice_expr(CheckerContext *c, Operand *o, Ast *node,
|
||||
x[i:n] -> []T
|
||||
*/
|
||||
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;
|
||||
|
||||
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);
|
||||
check_cast(c, o, 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");
|
||||
}
|
||||
}
|
||||
|
||||
+11
-6
@@ -384,7 +384,6 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
|
||||
}
|
||||
|
||||
if (e != nullptr) {
|
||||
// HACK TODO(bill): Should the entities be freed as it's technically a leak
|
||||
rhs->mode = Addressing_Value;
|
||||
rhs->type = e->type;
|
||||
rhs->proc_group = nullptr;
|
||||
@@ -394,7 +393,7 @@ gb_internal Type *check_assignment_variable(CheckerContext *ctx, Operand *lhs, O
|
||||
ast_node(i, Ident, node);
|
||||
e = scope_lookup(ctx->scope, i->token.string);
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
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) {
|
||||
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);
|
||||
if (vd->type != nullptr && entity_count > 1) {
|
||||
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++) {
|
||||
@@ -2294,7 +2293,7 @@ gb_internal void check_for_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
|
||||
mod_flags |= Stmt_BreakAllowed | Stmt_ContinueAllowed;
|
||||
|
||||
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) {
|
||||
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");
|
||||
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) {
|
||||
expr = unparen_expr(expr);
|
||||
Entity *e = nullptr;
|
||||
|
||||
+10
-4
@@ -1474,6 +1474,12 @@ gb_internal Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_para
|
||||
Type *specialization = nullptr;
|
||||
|
||||
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) {
|
||||
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 *base_type = check_type(ctx, rt->type);
|
||||
if (!is_type_pointer(base_type) && !is_type_slice(base_type)) {
|
||||
error(rt->type, "#relative types can only be a pointer or slice");
|
||||
if (!is_type_pointer(base_type) && !is_type_multi_pointer(base_type)) {
|
||||
error(rt->type, "#relative types can only be a pointer or multi-pointer");
|
||||
relative_type = base_type;
|
||||
} else if (base_integer == nullptr) {
|
||||
relative_type = base_type;
|
||||
} else {
|
||||
if (is_type_pointer(base_type)) {
|
||||
relative_type = alloc_type_relative_pointer(base_type, base_integer);
|
||||
} else if (is_type_slice(base_type)) {
|
||||
relative_type = alloc_type_relative_slice(base_type, base_integer);
|
||||
} else if (is_type_multi_pointer(base_type)) {
|
||||
relative_type = alloc_type_relative_multi_pointer(base_type, base_integer);
|
||||
}
|
||||
}
|
||||
GB_ASSERT(relative_type != nullptr);
|
||||
|
||||
+123
-44
@@ -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 {
|
||||
VettedEntity_Invalid,
|
||||
|
||||
@@ -655,9 +677,9 @@ gb_internal bool check_vet_unused(Checker *c, Entity *e, VettedEntity *ve) {
|
||||
return false;
|
||||
}
|
||||
|
||||
gb_internal void check_scope_usage(Checker *c, Scope *scope) {
|
||||
bool vet_unused = true;
|
||||
bool vet_shadowing = true;
|
||||
gb_internal void check_scope_usage(Checker *c, Scope *scope, u64 vet_flags) {
|
||||
bool vet_unused = (vet_flags & VetFlag_Unused) != 0;
|
||||
bool vet_shadowing = (vet_flags & (VetFlag_Shadowing|VetFlag_Using)) != 0;
|
||||
|
||||
Array<VettedEntity> vetted_entities = {};
|
||||
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) {
|
||||
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) {
|
||||
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;
|
||||
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);
|
||||
} 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);
|
||||
}
|
||||
break;
|
||||
@@ -726,7 +750,7 @@ gb_internal void check_scope_usage(Checker *c, Scope *scope) {
|
||||
if (child->flags & (ScopeFlag_Proc|ScopeFlag_Type|ScopeFlag_File)) {
|
||||
// Ignore these
|
||||
} 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("false", false);
|
||||
|
||||
// TODO(bill): Set through flags in the compiler
|
||||
add_global_string_constant("ODIN_VENDOR", bc->ODIN_VENDOR);
|
||||
add_global_string_constant("ODIN_VERSION", bc->ODIN_VERSION);
|
||||
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_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()));
|
||||
|
||||
@@ -1114,6 +1138,7 @@ gb_internal void init_universal(void) {
|
||||
|
||||
|
||||
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_i64_ptr = alloc_type_pointer(t_i64);
|
||||
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) {
|
||||
mutex = &ctx->decl->type_and_value_mutex;
|
||||
} else if (ctx->pkg) {
|
||||
// TODO(bill): is a per package mutex is a good idea here?
|
||||
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) {
|
||||
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);
|
||||
entity->flags |= EntityFlag_Used;
|
||||
if (entity_has_deferred_procedure(entity)) {
|
||||
Entity *deferred = entity->Procedure.deferred_procedure.entity;
|
||||
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);
|
||||
break;
|
||||
|
||||
case Type_RelativeSlice:
|
||||
add_type_info_type_internal(c, bt->RelativeSlice.slice_type);
|
||||
add_type_info_type_internal(c, bt->RelativeSlice.base_integer);
|
||||
case Type_RelativeMultiPointer:
|
||||
add_type_info_type_internal(c, bt->RelativeMultiPointer.pointer_type);
|
||||
add_type_info_type_internal(c, bt->RelativeMultiPointer.base_integer);
|
||||
break;
|
||||
|
||||
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);
|
||||
break;
|
||||
|
||||
case Type_RelativeSlice:
|
||||
add_min_dep_type_info(c, bt->RelativeSlice.slice_type);
|
||||
add_min_dep_type_info(c, bt->RelativeSlice.base_integer);
|
||||
case Type_RelativeMultiPointer:
|
||||
add_min_dep_type_info(c, bt->RelativeMultiPointer.pointer_type);
|
||||
add_min_dep_type_info(c, bt->RelativeMultiPointer.base_integer);
|
||||
break;
|
||||
|
||||
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_compare"),
|
||||
str_lit("memory_compare_zero"),
|
||||
);
|
||||
|
||||
FORCE_ADD_RUNTIME_ENTITIES(!build_context.tilde_backend,
|
||||
// Extended data type internal procedures
|
||||
str_lit("umodti3"),
|
||||
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");
|
||||
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_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_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_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_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_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_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;
|
||||
}
|
||||
|
||||
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) {
|
||||
if (name == ATTRIBUTE_USER_TAG_NAME) {
|
||||
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);
|
||||
Entity *e = entity_of_node(o.expr);
|
||||
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) {
|
||||
error(elem, "Previous usage of a 'deferred_*' attribute");
|
||||
}
|
||||
@@ -4506,7 +4581,7 @@ gb_internal DECL_ATTRIBUTE_PROC(foreign_import_decl_attribute) {
|
||||
if (value != nullptr) {
|
||||
error(elem, "Expected no parameter for '%.*s'", LIT(name));
|
||||
} 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;
|
||||
return true;
|
||||
@@ -5904,7 +5979,11 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
TIME_SECTION("check scope usage");
|
||||
for (auto const &entry : c->info.files) {
|
||||
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");
|
||||
@@ -6022,7 +6101,7 @@ gb_internal void check_parsed_files(Checker *c) {
|
||||
while (mpsc_dequeue(&c->info.intrinsics_entry_point_usage, &node)) {
|
||||
if (c->info.entry_point == nullptr && node != nullptr) {
|
||||
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
@@ -387,8 +387,6 @@ struct CheckerInfo {
|
||||
BlockingMutex foreign_mutex; // NOT recursive
|
||||
StringMap<Entity *> foreigns;
|
||||
|
||||
// NOTE(bill): These are actually MPSC queues
|
||||
// TODO(bill): Convert them to be MPSC queues
|
||||
MPSCQueue<Entity *> definition_queue;
|
||||
MPSCQueue<Entity *> entity_queue;
|
||||
MPSCQueue<Entity *> required_global_variable_queue;
|
||||
@@ -449,6 +447,9 @@ struct CheckerContext {
|
||||
Ast *assignment_lhs_hint;
|
||||
};
|
||||
|
||||
gb_internal u64 check_vet_flags(CheckerContext *c);
|
||||
gb_internal u64 check_vet_flags(Ast *node);
|
||||
|
||||
|
||||
struct Checker {
|
||||
Parser * parser;
|
||||
|
||||
+25
-25
@@ -59,31 +59,31 @@ struct OdinDocPosition {
|
||||
};
|
||||
|
||||
enum OdinDocTypeKind : u32 {
|
||||
OdinDocType_Invalid = 0,
|
||||
OdinDocType_Basic = 1,
|
||||
OdinDocType_Named = 2,
|
||||
OdinDocType_Generic = 3,
|
||||
OdinDocType_Pointer = 4,
|
||||
OdinDocType_Array = 5,
|
||||
OdinDocType_EnumeratedArray = 6,
|
||||
OdinDocType_Slice = 7,
|
||||
OdinDocType_DynamicArray = 8,
|
||||
OdinDocType_Map = 9,
|
||||
OdinDocType_Struct = 10,
|
||||
OdinDocType_Union = 11,
|
||||
OdinDocType_Enum = 12,
|
||||
OdinDocType_Tuple = 13,
|
||||
OdinDocType_Proc = 14,
|
||||
OdinDocType_BitSet = 15,
|
||||
OdinDocType_SimdVector = 16,
|
||||
OdinDocType_SOAStructFixed = 17,
|
||||
OdinDocType_SOAStructSlice = 18,
|
||||
OdinDocType_SOAStructDynamic = 19,
|
||||
OdinDocType_RelativePointer = 20,
|
||||
OdinDocType_RelativeSlice = 21,
|
||||
OdinDocType_MultiPointer = 22,
|
||||
OdinDocType_Matrix = 23,
|
||||
OdinDocType_SoaPointer = 24,
|
||||
OdinDocType_Invalid = 0,
|
||||
OdinDocType_Basic = 1,
|
||||
OdinDocType_Named = 2,
|
||||
OdinDocType_Generic = 3,
|
||||
OdinDocType_Pointer = 4,
|
||||
OdinDocType_Array = 5,
|
||||
OdinDocType_EnumeratedArray = 6,
|
||||
OdinDocType_Slice = 7,
|
||||
OdinDocType_DynamicArray = 8,
|
||||
OdinDocType_Map = 9,
|
||||
OdinDocType_Struct = 10,
|
||||
OdinDocType_Union = 11,
|
||||
OdinDocType_Enum = 12,
|
||||
OdinDocType_Tuple = 13,
|
||||
OdinDocType_Proc = 14,
|
||||
OdinDocType_BitSet = 15,
|
||||
OdinDocType_SimdVector = 16,
|
||||
OdinDocType_SOAStructFixed = 17,
|
||||
OdinDocType_SOAStructSlice = 18,
|
||||
OdinDocType_SOAStructDynamic = 19,
|
||||
OdinDocType_RelativePointer = 20,
|
||||
OdinDocType_RelativeMultiPointer = 21,
|
||||
OdinDocType_MultiPointer = 22,
|
||||
OdinDocType_Matrix = 23,
|
||||
OdinDocType_SoaPointer = 24,
|
||||
};
|
||||
|
||||
enum OdinDocTypeFlag_Basic : u32 {
|
||||
|
||||
+4
-4
@@ -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));
|
||||
}
|
||||
break;
|
||||
case Type_RelativeSlice:
|
||||
doc_type.kind = OdinDocType_RelativeSlice;
|
||||
case Type_RelativeMultiPointer:
|
||||
doc_type.kind = OdinDocType_RelativeMultiPointer;
|
||||
{
|
||||
OdinDocTypeIndex types[2] = {};
|
||||
types[0] = odin_doc_type(w, type->RelativeSlice.slice_type);
|
||||
types[1] = odin_doc_type(w, type->RelativeSlice.base_integer);
|
||||
types[0] = odin_doc_type(w, type->RelativeMultiPointer.pointer_type);
|
||||
types[1] = odin_doc_type(w, type->RelativeMultiPointer.base_integer);
|
||||
doc_type.types = odin_write_slice(w, types, gb_count_of(types));
|
||||
}
|
||||
break;
|
||||
|
||||
+11
-8
@@ -2,8 +2,6 @@ struct Scope;
|
||||
struct Checker;
|
||||
struct Type;
|
||||
struct DeclInfo;
|
||||
struct lbModule;
|
||||
struct lbProcedure;
|
||||
|
||||
|
||||
#define ENTITY_KINDS \
|
||||
@@ -183,8 +181,14 @@ struct Entity {
|
||||
|
||||
Entity * aliased_of;
|
||||
|
||||
lbModule * code_gen_module;
|
||||
lbProcedure *code_gen_procedure;
|
||||
union {
|
||||
struct lbModule *code_gen_module;
|
||||
struct cgModule *cg_module;
|
||||
};
|
||||
union {
|
||||
struct lbProcedure *code_gen_procedure;
|
||||
struct cgProcedure *cg_procedure;
|
||||
};
|
||||
|
||||
u64 order_in_src;
|
||||
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) {
|
||||
// TODO(bill): Determine the actual exportation rules for imports of entities
|
||||
GB_ASSERT(e != nullptr);
|
||||
if (!is_entity_kind_exported(e->kind, allow_builtin)) {
|
||||
return false;
|
||||
@@ -401,7 +404,7 @@ gb_internal Entity *alloc_entity_array_elem(Scope *scope, Token token, Type *typ
|
||||
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->Procedure.tags = tags;
|
||||
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.name = name;
|
||||
entity->ImportName.scope = import_scope;
|
||||
entity->state = EntityState_Resolved; // TODO(bill): Is this correct?
|
||||
entity->state = EntityState_Resolved;
|
||||
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->LibraryName.paths = paths;
|
||||
entity->LibraryName.name = name;
|
||||
entity->state = EntityState_Resolved; // TODO(bill): Is this correct?
|
||||
entity->state = EntityState_Resolved;
|
||||
return entity;
|
||||
}
|
||||
|
||||
|
||||
+2
-2
@@ -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) {
|
||||
mutex_lock(&global_error_collector.mutex);
|
||||
global_error_collector.count++;
|
||||
global_error_collector.count.fetch_add(1);
|
||||
// NOTE(bill): Duplicate error, skip it
|
||||
if (pos.line == 0) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
}
|
||||
|
||||
+2
-5
@@ -26,8 +26,8 @@ enum ExactValueKind {
|
||||
ExactValue_Complex = 5,
|
||||
ExactValue_Quaternion = 6,
|
||||
ExactValue_Pointer = 7,
|
||||
ExactValue_Compound = 8, // TODO(bill): Is this good enough?
|
||||
ExactValue_Procedure = 9, // TODO(bill): Is this good enough?
|
||||
ExactValue_Compound = 8,
|
||||
ExactValue_Procedure = 9,
|
||||
ExactValue_Typeid = 10,
|
||||
|
||||
ExactValue_Count,
|
||||
@@ -101,7 +101,6 @@ gb_internal ExactValue exact_value_bool(bool b) {
|
||||
}
|
||||
|
||||
gb_internal ExactValue exact_value_string(String string) {
|
||||
// TODO(bill): Allow for numbers with underscores in them
|
||||
ExactValue result = {ExactValue_String};
|
||||
result.value_string = string;
|
||||
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);
|
||||
}
|
||||
|
||||
// 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) {
|
||||
match_exact_values(&x, &y);
|
||||
|
||||
@@ -943,7 +941,6 @@ gb_internal bool compare_exact_values(TokenKind op, ExactValue x, ExactValue y)
|
||||
case ExactValue_String: {
|
||||
String a = x.value_string;
|
||||
String b = y.value_string;
|
||||
// TODO(bill): gb_memcompare is used because the strings are UTF-8
|
||||
switch (op) {
|
||||
case Token_CmpEq: return a == b;
|
||||
case Token_NotEq: return a != b;
|
||||
|
||||
+460
@@ -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
@@ -69,7 +69,6 @@ enum lbAddrKind {
|
||||
lbAddr_SoaVariable,
|
||||
|
||||
lbAddr_RelativePointer,
|
||||
lbAddr_RelativeSlice,
|
||||
|
||||
lbAddr_Swizzle,
|
||||
lbAddr_SwizzleLarge,
|
||||
@@ -190,13 +189,9 @@ struct lbModule {
|
||||
LLVMPassManagerRef function_pass_managers[lbFunctionPassManager_COUNT];
|
||||
};
|
||||
|
||||
struct lbGenerator {
|
||||
struct lbGenerator : LinkerData {
|
||||
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<LLVMContextRef, lbModule *> modules_through_ctx;
|
||||
lbModule default_module;
|
||||
@@ -204,10 +199,6 @@ struct lbGenerator {
|
||||
RecursiveMutex anonymous_proc_lits_mutex;
|
||||
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_generated_index;
|
||||
|
||||
@@ -346,7 +337,9 @@ struct lbProcedure {
|
||||
};
|
||||
|
||||
|
||||
#ifndef ABI_PKG_NAME_SEPARATOR
|
||||
#define ABI_PKG_NAME_SEPARATOR "."
|
||||
#endif
|
||||
|
||||
|
||||
#if !ODIN_LLVM_MINIMUM_VERSION_14
|
||||
|
||||
+67
-69
@@ -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))) {
|
||||
if (is_type_rune_array(type) && value.kind == ExactValue_String) {
|
||||
if (is_type_rune_array(type)) {
|
||||
i64 count = type->Array.count;
|
||||
Type *elem = type->Array.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);
|
||||
bool *visited = gb_alloc_array(temporary_allocator(), bool, value_count);
|
||||
|
||||
if (cl->elems.count > 0) {
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
isize elem_count = cl->elems.count;
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
ast_node(fv, FieldValue, cl->elems[i]);
|
||||
String name = fv->field->Ident.token.string;
|
||||
if (cl->elems[0]->kind == Ast_FieldValue) {
|
||||
isize elem_count = cl->elems.count;
|
||||
for (isize i = 0; i < elem_count; i++) {
|
||||
ast_node(fv, FieldValue, cl->elems[i]);
|
||||
String name = fv->field->Ident.token.string;
|
||||
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
TypeAndValue tav = fv->value->tav;
|
||||
GB_ASSERT(tav.mode != Addressing_Invalid);
|
||||
|
||||
Selection sel = lookup_field(type, name, false);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
Selection sel = lookup_field(type, name, false);
|
||||
GB_ASSERT(!sel.indirect);
|
||||
|
||||
Entity *f = type->Struct.fields[sel.index[0]];
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
if (sel.index.count == 1) {
|
||||
values[index] = lb_const_value(m, f->type, tav.value, allow_local).value;
|
||||
Entity *f = type->Struct.fields[sel.index[0]];
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
if (sel.index.count == 1) {
|
||||
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;
|
||||
} else {
|
||||
if (!visited[index]) {
|
||||
values[index] = lb_const_value(m, f->type, {}, false).value;
|
||||
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);
|
||||
}
|
||||
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)) {
|
||||
bool is_constant = true;
|
||||
Type *cv_type = f->type;
|
||||
for (isize j = 1; j < sel.index.count; j++) {
|
||||
i32 index = sel.index[j];
|
||||
Type *cvt = base_type(cv_type);
|
||||
if (lb_is_nested_possibly_constant(type, sel, fv->value)) {
|
||||
bool is_constant = true;
|
||||
Type *cv_type = f->type;
|
||||
for (isize j = 1; j < sel.index.count; j++) {
|
||||
i32 index = sel.index[j];
|
||||
Type *cvt = base_type(cv_type);
|
||||
|
||||
if (cvt->kind == Type_Struct) {
|
||||
if (cvt->Struct.is_raw_union) {
|
||||
// sanity check which should have been caught by `lb_is_nested_possibly_constant`
|
||||
is_constant = false;
|
||||
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));
|
||||
if (cvt->kind == Type_Struct) {
|
||||
if (cvt->Struct.is_raw_union) {
|
||||
// sanity check which should have been caught by `lb_is_nested_possibly_constant`
|
||||
is_constant = false;
|
||||
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));
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
|
||||
GB_ASSERT(LLVMIsConstant(elem_value));
|
||||
values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
|
||||
}
|
||||
}
|
||||
if (is_constant) {
|
||||
LLVMValueRef elem_value = lb_const_value(m, tav.type, tav.value, allow_local).value;
|
||||
GB_ASSERT(LLVMIsConstant(elem_value));
|
||||
values[index] = LLVMConstInsertValue(values[index], elem_value, idx_list, idx_list_len);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for_array(i, cl->elems) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
ExactValue val = {};
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
val = tav.value;
|
||||
}
|
||||
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[index] = lb_const_value(m, f->type, val, allow_local).value;
|
||||
visited[index] = true;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for_array(i, cl->elems) {
|
||||
Entity *f = type->Struct.fields[i];
|
||||
TypeAndValue tav = cl->elems[i]->tav;
|
||||
ExactValue val = {};
|
||||
if (tav.mode != Addressing_Invalid) {
|
||||
val = tav.value;
|
||||
}
|
||||
|
||||
i32 index = field_remapping[f->Variable.field_index];
|
||||
if (elem_type_can_be_constant(f->type)) {
|
||||
values[index] = lb_const_value(m, f->type, val, allow_local).value;
|
||||
visited[index] = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -442,19 +442,12 @@ gb_internal LLVMMetadataRef lb_debug_type_internal(lbModule *m, Type *type) {
|
||||
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_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: {
|
||||
LLVMMetadataRef subscripts[1] = {};
|
||||
subscripts[0] = LLVMDIBuilderGetOrCreateSubrange(m->debug_builder,
|
||||
|
||||
+27
-17
@@ -2863,7 +2863,6 @@ gb_internal lbValue lb_build_unary_and(lbProcedure *p, Ast *expr) {
|
||||
ast_node(ue, UnaryExpr, expr);
|
||||
auto tv = type_and_value_of_expr(expr);
|
||||
|
||||
|
||||
Ast *ue_expr = unparen_expr(ue->expr);
|
||||
if (ue_expr->kind == Ast_IndexExpr && tv.mode == Addressing_OptionalOkPtr && is_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 = {};
|
||||
|
||||
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);
|
||||
return lb_addr(v);
|
||||
}
|
||||
|
||||
case Type_RelativeSlice: {
|
||||
lbAddr slice_addr = {};
|
||||
case Type_RelativeMultiPointer: {
|
||||
lbAddr rel_ptr_addr = {};
|
||||
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 {
|
||||
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_emit_conv(p, lb_build_expr(p, ie->index), t_int);
|
||||
lbValue len = lb_slice_len(p, slice);
|
||||
lb_emit_bounds_check(p, ast_token(ie->index), index, len);
|
||||
lbValue v = lb_emit_ptr_offset(p, elem, index);
|
||||
lbValue index = lb_build_expr(p, ie->index);
|
||||
index = lb_emit_conv(p, index, t_int);
|
||||
lbValue v = {};
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
@@ -3925,8 +3931,11 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
return slice;
|
||||
}
|
||||
|
||||
case Type_RelativeSlice:
|
||||
GB_PANIC("TODO(bill): Type_RelativeSlice should be handled above already on the lb_addr_load");
|
||||
case Type_RelativePointer:
|
||||
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;
|
||||
|
||||
case Type_DynamicArray: {
|
||||
@@ -3996,7 +4005,7 @@ gb_internal lbAddr lb_build_addr_slice_expr(lbProcedure *p, Ast *expr) {
|
||||
}
|
||||
|
||||
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);
|
||||
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!!!!
|
||||
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);
|
||||
} else {
|
||||
@@ -4519,8 +4528,9 @@ gb_internal lbAddr lb_build_addr_internal(lbProcedure *p, Ast *expr) {
|
||||
Selection sel = lookup_field(type, selector, false);
|
||||
GB_ASSERT(sel.entity != nullptr);
|
||||
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);
|
||||
GB_ASSERT(e->kind == Entity_Procedure);
|
||||
return lb_addr(lb_find_value_from_entity(p->module, e));
|
||||
}
|
||||
|
||||
|
||||
+87
-155
@@ -103,37 +103,7 @@ gb_internal bool lb_init_generator(lbGenerator *gen, Checker *c) {
|
||||
}
|
||||
|
||||
String init_fullpath = c->parser->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));
|
||||
linker_data_init(gen, &c->info, init_fullpath);
|
||||
|
||||
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->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) {
|
||||
for (auto const &entry : gen->info->packages) {
|
||||
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))) {
|
||||
GB_ASSERT(is_type_pointer(addr.type));
|
||||
v.kind = lbAddr_RelativePointer;
|
||||
} else if (addr.type != nullptr && is_type_relative_slice(type_deref(addr.type))) {
|
||||
GB_ASSERT(is_type_pointer(addr.type));
|
||||
v.kind = lbAddr_RelativeSlice;
|
||||
} else if (addr.type != nullptr && is_type_relative_multi_pointer(type_deref(addr.type))) {
|
||||
GB_ASSERT(is_type_pointer(addr.type) ||
|
||||
is_type_multi_pointer(addr.type));
|
||||
v.kind = lbAddr_RelativePointer;
|
||||
}
|
||||
return v;
|
||||
}
|
||||
@@ -458,6 +425,43 @@ gb_internal Type *lb_addr_type(lbAddr const &addr) {
|
||||
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) {
|
||||
if (addr.addr.value == 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:
|
||||
return lb_internal_dynamic_map_get_ptr(p, addr.addr, addr.map.key);
|
||||
|
||||
case lbAddr_RelativePointer: {
|
||||
Type *rel_ptr = base_type(lb_addr_type(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_RelativePointer:
|
||||
return lb_relative_pointer_to_pointer(p, addr);
|
||||
|
||||
case lbAddr_SoaVariable:
|
||||
// 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) {
|
||||
lbAddr addr = lb_build_addr(p, expr);
|
||||
if (addr.kind == lbAddr_RelativePointer) {
|
||||
return addr.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) {
|
||||
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));
|
||||
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.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, 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,
|
||||
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
|
||||
);
|
||||
LLVMBuildStore(p->builder, offset.value, offset_ptr.value);
|
||||
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) {
|
||||
lb_internal_dynamic_map_set(p, addr.addr, addr.map.type, addr.map.key, value, p->curr_stmt);
|
||||
return;
|
||||
@@ -1054,67 +1018,43 @@ gb_internal lbValue lb_addr_load(lbProcedure *p, lbAddr const &addr) {
|
||||
|
||||
if (addr.kind == lbAddr_RelativePointer) {
|
||||
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 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);
|
||||
|
||||
|
||||
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_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
|
||||
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 = {};
|
||||
final_ptr.type = absolute_ptr.type;
|
||||
final_ptr.value = LLVMBuildSelect(p->builder, cond.value, nil_ptr.value, absolute_ptr.value, "");
|
||||
|
||||
return lb_emit_load(p, 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);
|
||||
if (rel_ptr->kind == Type_RelativeMultiPointer) {
|
||||
return final_ptr;
|
||||
}
|
||||
offset = lb_emit_conv(p, offset, t_uintptr);
|
||||
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);
|
||||
|
||||
return lb_emit_load(p, final_ptr);
|
||||
|
||||
} else if (addr.kind == lbAddr_Map) {
|
||||
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:
|
||||
return lb_type_internal(m, base);
|
||||
|
||||
// TODO(bill): Deal with this correctly. Can this be named?
|
||||
case Type_Proc:
|
||||
// TODO(bill): Deal with this correctly. Can this be named?
|
||||
return lb_type_internal(m, base);
|
||||
|
||||
case Type_Tuple:
|
||||
@@ -2173,17 +2113,10 @@ gb_internal LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
|
||||
case Type_RelativePointer:
|
||||
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:
|
||||
{
|
||||
@@ -2869,7 +2802,6 @@ gb_internal lbValue lb_find_value_from_entity(lbModule *m, Entity *e) {
|
||||
if (USE_SEPARATE_MODULES) {
|
||||
lbModule *other_module = lb_module_of_entity(m->gen, e);
|
||||
|
||||
// TODO(bill): correct this logic
|
||||
bool is_external = other_module != m;
|
||||
if (!is_external) {
|
||||
if (e->code_gen_module != nullptr) {
|
||||
|
||||
@@ -362,7 +362,6 @@ gb_internal lbProcedure *lb_create_dummy_procedure(lbModule *m, String link_name
|
||||
|
||||
Type *pt = p->type;
|
||||
lbCallingConventionKind cc_kind = lbCallingConvention_C;
|
||||
// TODO(bill): Clean up this logic
|
||||
if (!is_arch_wasm()) {
|
||||
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]);
|
||||
Type *t = base_type(v.type);
|
||||
if (is_type_pointer(t)) {
|
||||
// IMPORTANT TODO(bill): Should there be a nil pointer check?
|
||||
v = lb_emit_load(p, v);
|
||||
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);
|
||||
} else if (is_type_array(t)) {
|
||||
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);
|
||||
} else if (is_type_dynamic_array(t)) {
|
||||
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]);
|
||||
Type *t = base_type(v.type);
|
||||
if (is_type_pointer(t)) {
|
||||
// IMPORTANT TODO(bill): Should there be a nil pointer check?
|
||||
v = lb_emit_load(p, v);
|
||||
t = type_deref(t);
|
||||
}
|
||||
@@ -1738,7 +1735,7 @@ gb_internal lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValu
|
||||
GB_PANIC("Unreachable");
|
||||
} else if (is_type_array(t)) {
|
||||
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);
|
||||
} else if (is_type_dynamic_array(t)) {
|
||||
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);
|
||||
|
||||
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(res.type->Tuple.variables.count == 2);
|
||||
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);
|
||||
GB_ASSERT(param_index >= 0);
|
||||
|
||||
lbValue value = lb_build_expr(p, fv->value);
|
||||
GB_ASSERT(!is_type_tuple(value.type));
|
||||
args[param_index] = value;
|
||||
Entity *e = pt->params->Tuple.variables[param_index];
|
||||
if (e->kind == Entity_TypeName) {
|
||||
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;
|
||||
|
||||
@@ -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_debug_local_variable(p, ptr.value, case_entity->type, case_entity->token);
|
||||
} else {
|
||||
// TODO(bill): is the correct expected behaviour?
|
||||
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));
|
||||
|
||||
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");
|
||||
lb_emit_jump(p, init);
|
||||
lb_start_block(p, init);
|
||||
#endif
|
||||
|
||||
lb_build_stmt(p, is->init);
|
||||
}
|
||||
lbBlock *then = lb_create_block(p, "if.then");
|
||||
|
||||
@@ -57,7 +57,7 @@ gb_internal lbValue lb_typeid(lbModule *m, Type *type) {
|
||||
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_RelativeSlice: kind = Typeid_Relative_Slice; break;
|
||||
case Type_RelativeMultiPointer: kind = Typeid_Relative_Multi_Pointer; 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
|
||||
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];
|
||||
lbValue tip = lb_type_info(m, f->type);
|
||||
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);
|
||||
}
|
||||
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] = {
|
||||
lb_type_info(m, t->RelativeSlice.slice_type).value,
|
||||
lb_type_info(m, t->RelativeSlice.base_integer).value,
|
||||
lb_type_info(m, t->RelativeMultiPointer.pointer_type).value,
|
||||
lb_type_info(m, t->RelativeMultiPointer.base_integer).value,
|
||||
};
|
||||
|
||||
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);
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_Matrix:
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_matrix_ptr);
|
||||
|
||||
@@ -1124,11 +1124,6 @@ gb_internal lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
|
||||
}
|
||||
} else if (is_type_array(t)) {
|
||||
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)) {
|
||||
switch (index) {
|
||||
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);
|
||||
}
|
||||
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);
|
||||
}
|
||||
gb_internal lbValue lb_dynamic_array_elem(lbProcedure *p, lbValue da) {
|
||||
|
||||
+139
-460
@@ -1,5 +1,4 @@
|
||||
// #define NO_ARRAY_BOUNDS_CHECK
|
||||
|
||||
#include "common.cpp"
|
||||
#include "timings.cpp"
|
||||
#include "tokenizer.cpp"
|
||||
@@ -71,6 +70,18 @@ gb_global Timings global_timings = {0};
|
||||
#include "checker.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"
|
||||
|
||||
#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;
|
||||
}
|
||||
|
||||
|
||||
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) {
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
@@ -653,8 +248,16 @@ enum BuildFlagKind {
|
||||
BuildFlag_UseSeparateModules,
|
||||
BuildFlag_NoThreadedChecker,
|
||||
BuildFlag_ShowDebugMessages,
|
||||
|
||||
BuildFlag_Vet,
|
||||
BuildFlag_VetShadowing,
|
||||
BuildFlag_VetUnused,
|
||||
BuildFlag_VetUsingStmt,
|
||||
BuildFlag_VetUsingParam,
|
||||
BuildFlag_VetStyle,
|
||||
BuildFlag_VetSemicolon,
|
||||
BuildFlag_VetExtra,
|
||||
|
||||
BuildFlag_IgnoreUnknownAttributes,
|
||||
BuildFlag_ExtraLinkerFlags,
|
||||
BuildFlag_ExtraAssemblerFlags,
|
||||
@@ -671,7 +274,6 @@ enum BuildFlagKind {
|
||||
BuildFlag_DisallowDo,
|
||||
BuildFlag_DefaultToNilAllocator,
|
||||
BuildFlag_StrictStyle,
|
||||
BuildFlag_StrictStyleInitOnly,
|
||||
BuildFlag_ForeignErrorProcedures,
|
||||
BuildFlag_NoRTTI,
|
||||
BuildFlag_DynamicMapCalls,
|
||||
@@ -695,6 +297,8 @@ enum BuildFlagKind {
|
||||
BuildFlag_InternalIgnoreLazy,
|
||||
BuildFlag_InternalIgnoreLLVMBuild,
|
||||
|
||||
BuildFlag_Tilde,
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
BuildFlag_IgnoreVsSearch,
|
||||
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_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_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_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_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_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_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_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_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)
|
||||
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);
|
||||
@@ -1362,13 +977,25 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
build_context.show_debug_messages = true;
|
||||
break;
|
||||
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;
|
||||
case BuildFlag_VetExtra: {
|
||||
build_context.vet = true;
|
||||
build_context.vet_extra = true;
|
||||
|
||||
case BuildFlag_VetUnused: build_context.vet_flags |= VetFlag_Unused; break;
|
||||
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;
|
||||
}
|
||||
|
||||
case BuildFlag_IgnoreUnknownAttributes:
|
||||
build_context.ignore_unknown_attributes = true;
|
||||
break;
|
||||
@@ -1456,20 +1083,9 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_ForeignErrorProcedures:
|
||||
build_context.ODIN_FOREIGN_ERROR_PROCEDURES = true;
|
||||
break;
|
||||
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");
|
||||
}
|
||||
case BuildFlag_StrictStyle:
|
||||
build_context.strict_style = true;
|
||||
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:
|
||||
build_context.cmd_doc_flags |= CmdDocFlag_Short;
|
||||
break;
|
||||
@@ -1536,6 +1152,10 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
case BuildFlag_InternalIgnoreLLVMBuild:
|
||||
build_context.ignore_llvm_build = true;
|
||||
break;
|
||||
case BuildFlag_Tilde:
|
||||
build_context.tilde_backend = true;
|
||||
break;
|
||||
|
||||
#if defined(GB_SYSTEM_WINDOWS)
|
||||
case BuildFlag_IgnoreVsSearch: {
|
||||
GB_ASSERT(value.kind == ExactValue_Invalid);
|
||||
@@ -1572,7 +1192,7 @@ gb_internal bool parse_build_flags(Array<String> args) {
|
||||
if (path_is_directory(path)) {
|
||||
gb_printf_err("Invalid -pdb-name path. %.*s, is a directory.\n", LIT(path));
|
||||
bad_flags = true;
|
||||
break;
|
||||
break;
|
||||
}
|
||||
// #if defined(GB_SYSTEM_WINDOWS)
|
||||
// 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(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(2, "Export timings to one of a few formats. Requires `-show-timings` or `-show-more-timings`");
|
||||
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 defined(GB_SYSTEM_WINDOWS)
|
||||
print_usage_line(1, "-no-threaded-checker");
|
||||
print_usage_line(2, "Disabled multithreading in the semantic checker stage");
|
||||
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(2, "Do extra checks on the code");
|
||||
print_usage_line(2, "Extra checks include:");
|
||||
print_usage_line(3, "Variable shadowing within procedures");
|
||||
print_usage_line(3, "Unused declarations");
|
||||
print_usage_line(2, "-vet-unused");
|
||||
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(1, "-vet-extra");
|
||||
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(0, "");
|
||||
}
|
||||
|
||||
if (check) {
|
||||
print_usage_line(1, "-ignore-unknown-attributes");
|
||||
print_usage_line(2, "Ignores unknown attributes");
|
||||
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(2, "Errs on unneeded tokens, such as unneeded semicolons");
|
||||
print_usage_line(0, "");
|
||||
|
||||
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(2, "Errs on missing trailing commas followed by a newline");
|
||||
print_usage_line(2, "Errs on deprecated syntax");
|
||||
print_usage_line(0, "");
|
||||
|
||||
print_usage_line(1, "-ignore-warnings");
|
||||
@@ -2347,6 +1995,8 @@ gb_internal void print_show_unused(Checker *c) {
|
||||
}
|
||||
|
||||
gb_internal bool check_env(void) {
|
||||
TIME_SECTION("init check env");
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
char const *odin_root = gb_get_env("ODIN_ROOT", a);
|
||||
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) {
|
||||
TIME_SECTION("init terminal");
|
||||
build_context.has_ansi_terminal_colours = false;
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
@@ -2614,11 +2265,13 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
TIME_SECTION("init default library collections");
|
||||
array_init(&library_collections, heap_allocator());
|
||||
// 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("vendor"), get_fullpath_relative(heap_allocator(), odin_root_dir(), str_lit("vendor")));
|
||||
|
||||
TIME_SECTION("init args");
|
||||
map_init(&build_context.defined_values);
|
||||
build_context.extra_packages.allocator = heap_allocator();
|
||||
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) {
|
||||
String build_path = path_to_string(heap_allocator(), build_context.build_paths[i]);
|
||||
debugf("build_paths[%ld]: %.*s\n", i, LIT(build_path));
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
TIME_SECTION("init thread pool");
|
||||
init_global_thread_pool();
|
||||
defer (thread_pool_destroy(&global_thread_pool));
|
||||
|
||||
TIME_SECTION("init universal");
|
||||
init_universal();
|
||||
// TODO(bill): prevent compiling without a linker
|
||||
|
||||
@@ -2882,16 +2537,18 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
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)) {
|
||||
#if ALLOW_TILDE
|
||||
if (build_context.tilde_backend) {
|
||||
LinkerData linker_data = {};
|
||||
MAIN_TIME_SECTION("Tilde Code Gen");
|
||||
if (!cg_generate_code(checker, &linker_data)) {
|
||||
return 1;
|
||||
}
|
||||
|
||||
switch (build_context.build_mode) {
|
||||
case BuildMode_Executable:
|
||||
case BuildMode_DynamicLibrary:
|
||||
i32 result = linker_stage(gen);
|
||||
i32 result = linker_stage(&linker_data);
|
||||
if (result) {
|
||||
if (build_context.show_timings) {
|
||||
show_timings(checker, &global_timings);
|
||||
@@ -2900,9 +2557,31 @@ int main(int arg_count, char const **arg_ptr) {
|
||||
}
|
||||
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) {
|
||||
show_timings(checker, &global_timings);
|
||||
|
||||
+139
-34
@@ -1,7 +1,21 @@
|
||||
#include "parser_pos.cpp"
|
||||
|
||||
// #undef at the bottom of this file
|
||||
#define ALLOW_NEWLINE (!build_context.strict_style)
|
||||
gb_internal u64 ast_file_vet_flags(AstFile *f) {
|
||||
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) {
|
||||
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 *curr_token = &f->tokens[f->curr_token_index];
|
||||
GB_ASSERT(prev_token->kind == Token_Semicolon);
|
||||
if (prev_token->string == ";") {
|
||||
bool ok = false;
|
||||
if (curr_token->pos.line > prev_token->pos.line) {
|
||||
if (prev_token->string != ";") {
|
||||
return;
|
||||
}
|
||||
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;
|
||||
} 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;
|
||||
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;
|
||||
break;
|
||||
}
|
||||
}
|
||||
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) {
|
||||
@@ -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);
|
||||
} else if (name.string == "relative") {
|
||||
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);
|
||||
return ast_relative_type(f, tag, type);
|
||||
} 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;
|
||||
bool prev_allow_newline = f->allow_newline;
|
||||
f->expr_level = 0;
|
||||
f->allow_newline = ALLOW_NEWLINE;
|
||||
f->allow_newline = file_allow_newline(f);
|
||||
|
||||
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) {
|
||||
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());
|
||||
for (;;) {
|
||||
@@ -3472,7 +3490,7 @@ gb_internal Ast *parse_results(AstFile *f, bool *diverging) {
|
||||
Ast *list = nullptr;
|
||||
expect_token(f, Token_OpenParen);
|
||||
list = parse_field_list(f, nullptr, FieldFlag_Results, Token_CloseParen, true, false);
|
||||
if (ALLOW_NEWLINE) {
|
||||
if (file_allow_newline(f)) {
|
||||
skip_possible_newline(f);
|
||||
}
|
||||
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);
|
||||
params = parse_field_list(f, nullptr, FieldFlag_Signature, Token_CloseParen, true, true);
|
||||
if (ALLOW_NEWLINE) {
|
||||
if (file_allow_newline(f)) {
|
||||
skip_possible_newline(f);
|
||||
}
|
||||
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) {
|
||||
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;
|
||||
switch (next) {
|
||||
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) {
|
||||
bool prev_allow_newline = f->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;
|
||||
|
||||
@@ -4169,6 +4187,8 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
|
||||
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)) {
|
||||
body = parse_do_body(f, cond ? ast_token(cond) : token, "then when statement");
|
||||
} else {
|
||||
@@ -4195,6 +4215,7 @@ gb_internal Ast *parse_when_stmt(AstFile *f) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
f->in_when_statement = was_in_when_statement;
|
||||
|
||||
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);
|
||||
}
|
||||
|
||||
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) {
|
||||
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_ASSERT(p != nullptr);
|
||||
// TODO(bill): Fix memory leak
|
||||
for (AstPackage *pkg : p->packages) {
|
||||
for (AstFile *file : pkg->files) {
|
||||
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) {
|
||||
// TODO(bill): Use a better allocator
|
||||
ImportedFile f = {pkg, fi, pos, p->file_to_process_count++};
|
||||
auto wd = gb_alloc_item(permanent_allocator(), ParserWorkerData);
|
||||
wd->parser = p;
|
||||
@@ -5528,6 +5551,88 @@ gb_internal bool parse_build_tag(Token token_for_pos, String s) {
|
||||
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) {
|
||||
String base_dir = path;
|
||||
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)) {
|
||||
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"))) {
|
||||
return false;
|
||||
} 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;
|
||||
}
|
||||
|
||||
|
||||
|
||||
#undef ALLOW_NEWLINE
|
||||
|
||||
@@ -104,6 +104,8 @@ struct AstFile {
|
||||
Token package_token;
|
||||
String package_name;
|
||||
|
||||
u64 vet_flags;
|
||||
bool vet_flags_set;
|
||||
|
||||
// >= 0: In Expression
|
||||
// < 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 in_foreign_block;
|
||||
bool allow_type;
|
||||
bool in_when_statement;
|
||||
|
||||
isize total_file_decl_count;
|
||||
isize delayed_decl_count;
|
||||
|
||||
+813
@@ -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
@@ -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 ¶m_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
File diff suppressed because it is too large
Load Diff
Binary file not shown.
@@ -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);
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
|
||||
@@ -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 */
|
||||
@@ -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
File diff suppressed because it is too large
Load Diff
@@ -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
File diff suppressed because it is too large
Load Diff
+1307
File diff suppressed because it is too large
Load Diff
+2614
File diff suppressed because it is too large
Load Diff
@@ -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
@@ -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 (str_eq(entry->text, token->string)) {
|
||||
token->kind = entry->kind;
|
||||
if (token->kind == Token_not_in && entry->text == "notin") {
|
||||
syntax_warning(*token, "'notin' is deprecated in favour of 'not_in'");
|
||||
if (token->kind == Token_not_in && entry->text.len == 5) {
|
||||
syntax_error(*token, "Did you mean 'not_in'?");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+127
-70
@@ -143,6 +143,7 @@ struct TypeStruct {
|
||||
Type * soa_elem;
|
||||
i32 soa_count;
|
||||
StructSoaKind soa_kind;
|
||||
BlockingMutex mutex; // for settings offsets
|
||||
|
||||
bool is_polymorphic;
|
||||
bool are_offsets_set : 1;
|
||||
@@ -244,6 +245,7 @@ struct TypeProc {
|
||||
TYPE_KIND(Tuple, struct { \
|
||||
Slice<Entity *> variables; /* Entity_Variable */ \
|
||||
i64 * offsets; \
|
||||
BlockingMutex mutex; /* for settings offsets */ \
|
||||
bool are_offsets_being_processed; \
|
||||
bool are_offsets_set; \
|
||||
bool is_packed; \
|
||||
@@ -265,8 +267,8 @@ struct TypeProc {
|
||||
Type *pointer_type; \
|
||||
Type *base_integer; \
|
||||
}) \
|
||||
TYPE_KIND(RelativeSlice, struct { \
|
||||
Type *slice_type; \
|
||||
TYPE_KIND(RelativeMultiPointer, struct { \
|
||||
Type *pointer_type; \
|
||||
Type *base_integer; \
|
||||
}) \
|
||||
TYPE_KIND(Matrix, struct { \
|
||||
@@ -347,7 +349,7 @@ enum Typeid_Kind : u8 {
|
||||
Typeid_Bit_Set,
|
||||
Typeid_Simd_Vector,
|
||||
Typeid_Relative_Pointer,
|
||||
Typeid_Relative_Slice,
|
||||
Typeid_Relative_Multi_Pointer,
|
||||
Typeid_Matrix,
|
||||
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_multi_ptr = nullptr;
|
||||
gb_global Type *t_int_ptr = nullptr;
|
||||
gb_global Type *t_i64_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_simd_vector = 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_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_simd_vector_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_soa_pointer_ptr = nullptr;
|
||||
|
||||
@@ -725,7 +728,7 @@ struct TypePath;
|
||||
|
||||
gb_internal i64 type_size_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, gbAllocator allocator, bool shorthand=true);
|
||||
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 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_proc(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
|
||||
|
||||
gb_internal bool type_ptr_set_update(PtrSet<Type *> *s, Type *t) {
|
||||
if (t == nullptr) {
|
||||
return true;
|
||||
}
|
||||
if (ptr_set_exists(s, t)) {
|
||||
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) {
|
||||
if (t == nullptr) {
|
||||
return true;
|
||||
}
|
||||
|
||||
if (ptr_set_exists(s, t)) {
|
||||
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) {
|
||||
Type *t = alloc_type(Type_Slice);
|
||||
t->Array.elem = elem;
|
||||
t->Slice.elem = elem;
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -1025,12 +1036,12 @@ gb_internal Type *alloc_type_relative_pointer(Type *pointer_type, Type *base_int
|
||||
return t;
|
||||
}
|
||||
|
||||
gb_internal Type *alloc_type_relative_slice(Type *slice_type, Type *base_integer) {
|
||||
GB_ASSERT(is_type_slice(slice_type));
|
||||
gb_internal Type *alloc_type_relative_multi_pointer(Type *pointer_type, Type *base_integer) {
|
||||
GB_ASSERT(is_type_multi_pointer(pointer_type));
|
||||
GB_ASSERT(is_type_integer(base_integer));
|
||||
Type *t = alloc_type(Type_RelativeSlice);
|
||||
t->RelativeSlice.slice_type = slice_type;
|
||||
t->RelativeSlice.base_integer = base_integer;
|
||||
Type *t = alloc_type(Type_RelativeMultiPointer);
|
||||
t->RelativeMultiPointer.pointer_type = pointer_type;
|
||||
t->RelativeMultiPointer.base_integer = base_integer;
|
||||
return t;
|
||||
}
|
||||
|
||||
@@ -1541,9 +1552,9 @@ gb_internal bool is_type_relative_pointer(Type *t) {
|
||||
t = base_type(t);
|
||||
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);
|
||||
return t->kind == Type_RelativeSlice;
|
||||
return t->kind == Type_RelativeMultiPointer;
|
||||
}
|
||||
|
||||
gb_internal bool is_type_u8_slice(Type *t) {
|
||||
@@ -1960,7 +1971,7 @@ gb_internal bool is_type_indexable(Type *t) {
|
||||
return true;
|
||||
case Type_EnumeratedArray:
|
||||
return true;
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
return true;
|
||||
case Type_Matrix:
|
||||
return true;
|
||||
@@ -1979,7 +1990,7 @@ gb_internal bool is_type_sliceable(Type *t) {
|
||||
return true;
|
||||
case Type_EnumeratedArray:
|
||||
return false;
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
return true;
|
||||
case Type_Matrix:
|
||||
return false;
|
||||
@@ -2185,12 +2196,12 @@ gb_internal bool is_type_polymorphic(Type *t, bool or_specialized=false) {
|
||||
}
|
||||
break;
|
||||
|
||||
case Type_RelativeSlice:
|
||||
if (is_type_polymorphic(t->RelativeSlice.slice_type, or_specialized)) {
|
||||
case Type_RelativeMultiPointer:
|
||||
if (is_type_polymorphic(t->RelativeMultiPointer.pointer_type, or_specialized)) {
|
||||
return true;
|
||||
}
|
||||
if (t->RelativeSlice.base_integer != nullptr &&
|
||||
is_type_polymorphic(t->RelativeSlice.base_integer, or_specialized)) {
|
||||
if (t->RelativeMultiPointer.base_integer != nullptr &&
|
||||
is_type_polymorphic(t->RelativeMultiPointer.base_integer, or_specialized)) {
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
@@ -2248,7 +2259,7 @@ gb_internal bool type_has_nil(Type *t) {
|
||||
return false;
|
||||
|
||||
case Type_RelativePointer:
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
@@ -2415,7 +2426,7 @@ gb_internal bool is_type_load_safe(Type *type) {
|
||||
return true;
|
||||
|
||||
case Type_RelativePointer:
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
return true;
|
||||
|
||||
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_count == y->Struct.soa_count &&
|
||||
are_types_identical(x->Struct.soa_elem, y->Struct.soa_elem)) {
|
||||
// TODO(bill); Fix the custom alignment rule
|
||||
for_array(i, x->Struct.fields) {
|
||||
Entity *xf = x->Struct.fields[i];
|
||||
Entity *yf = y->Struct.fields[i];
|
||||
@@ -2807,7 +2817,6 @@ gb_internal i64 union_tag_size(Type *u) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
// TODO(bill): Is this an okay approach?
|
||||
i64 max_align = 1;
|
||||
|
||||
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) {
|
||||
max_align = 4;
|
||||
} 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) {
|
||||
@@ -3081,7 +3090,7 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
|
||||
mutex_lock(md->mutex);
|
||||
defer (mutex_unlock(md->mutex));
|
||||
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) {
|
||||
sel.entity = entry.entity;
|
||||
sel.pseudo_field = true;
|
||||
@@ -3136,8 +3145,6 @@ gb_internal Selection lookup_field_with_selection(Type *type_, String field_name
|
||||
switch (type->Basic.kind) {
|
||||
case Basic_any: {
|
||||
#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 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);
|
||||
@@ -3623,8 +3630,8 @@ gb_internal i64 type_align_of_internal(Type *t, TypePath *path) {
|
||||
|
||||
case Type_RelativePointer:
|
||||
return type_align_of_internal(t->RelativePointer.base_integer, path);
|
||||
case Type_RelativeSlice:
|
||||
return type_align_of_internal(t->RelativeSlice.base_integer, path);
|
||||
case Type_RelativeMultiPointer:
|
||||
return type_align_of_internal(t->RelativeMultiPointer.base_integer, path);
|
||||
|
||||
case Type_SoaPointer:
|
||||
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) {
|
||||
for_array(i, fields) {
|
||||
i64 size = type_size_of(fields[i]->type);
|
||||
offsets[i] = curr_offset;
|
||||
curr_offset += size;
|
||||
if (fields[i]->kind != Entity_Variable) {
|
||||
offsets[i] = -1;
|
||||
} else {
|
||||
i64 size = type_size_of(fields[i]->type);
|
||||
offsets[i] = curr_offset;
|
||||
curr_offset += size;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
for_array(i, fields) {
|
||||
Type *t = fields[i]->type;
|
||||
i64 align = gb_max(type_align_of(t), 1);
|
||||
i64 size = gb_max(type_size_of( t), 0);
|
||||
curr_offset = align_formula(curr_offset, align);
|
||||
offsets[i] = curr_offset;
|
||||
curr_offset += size;
|
||||
if (fields[i]->kind != Entity_Variable) {
|
||||
offsets[i] = -1;
|
||||
} else {
|
||||
Type *t = fields[i]->type;
|
||||
i64 align = gb_max(type_align_of(t), 1);
|
||||
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;
|
||||
}
|
||||
|
||||
gb_internal bool type_set_offsets(Type *t) {
|
||||
MUTEX_GUARD(&g_type_mutex); // TODO(bill): only per struct
|
||||
|
||||
t = base_type(t);
|
||||
if (t->kind == Type_Struct) {
|
||||
MUTEX_GUARD(&t->Struct.mutex);
|
||||
if (!t->Struct.are_offsets_set) {
|
||||
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);
|
||||
@@ -3675,6 +3689,7 @@ gb_internal bool type_set_offsets(Type *t) {
|
||||
return true;
|
||||
}
|
||||
} else if (is_type_tuple(t)) {
|
||||
MUTEX_GUARD(&t->Tuple.mutex);
|
||||
if (!t->Tuple.are_offsets_set) {
|
||||
t->Tuple.are_offsets_being_processed = true;
|
||||
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;
|
||||
}
|
||||
}
|
||||
// TODO(bill): Is this how it should work?
|
||||
return align_formula(max, align);
|
||||
} else {
|
||||
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:
|
||||
return type_size_of_internal(t->RelativePointer.base_integer, path);
|
||||
case Type_RelativeSlice:
|
||||
return 2*type_size_of_internal(t->RelativeSlice.base_integer, path);
|
||||
case Type_RelativeMultiPointer:
|
||||
return type_size_of_internal(t->RelativeMultiPointer.base_integer, path);
|
||||
}
|
||||
|
||||
// Catch all
|
||||
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);
|
||||
if (t->kind == Type_Struct) {
|
||||
switch (t->kind) {
|
||||
case Type_Struct:
|
||||
type_set_offsets(t);
|
||||
if (gb_is_between(index, 0, t->Struct.fields.count-1)) {
|
||||
GB_ASSERT(t->Struct.offsets != nullptr);
|
||||
if (field_type_) *field_type_ = t->Struct.fields[index]->type;
|
||||
return t->Struct.offsets[index];
|
||||
}
|
||||
} else if (t->kind == Type_Tuple) {
|
||||
break;
|
||||
case Type_Tuple:
|
||||
type_set_offsets(t);
|
||||
if (gb_is_between(index, 0, t->Tuple.variables.count-1)) {
|
||||
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) {
|
||||
switch (index) {
|
||||
case 0: return 0; // data
|
||||
case 1: return build_context.int_size; // len
|
||||
case 0:
|
||||
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) {
|
||||
switch (index) {
|
||||
case 0: return 0; // type_info
|
||||
case 1: return build_context.ptr_size; // data
|
||||
case 0:
|
||||
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) {
|
||||
case 0: return 0; // data
|
||||
case 1: return 1*build_context.int_size; // len
|
||||
case 2: return 2*build_context.int_size; // cap
|
||||
case 0:
|
||||
if (field_type_) *field_type_ = alloc_type_multi_pointer(t->Slice.elem);
|
||||
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) {
|
||||
case 0: return 0; // data
|
||||
case 1: return 1*build_context.int_size; // len
|
||||
case 2: return 2*build_context.int_size; // cap
|
||||
case 3: return 3*build_context.int_size; // allocator
|
||||
case 0:
|
||||
if (field_type_) *field_type_ = alloc_type_multi_pointer(t->DynamicArray.elem);
|
||||
return 0; // data
|
||||
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) {
|
||||
/* i64 s = */ type_size_of(t);
|
||||
switch (index) {
|
||||
case -1: return align_formula(t->Union.variant_block_size, build_context.ptr_size); // __type_info
|
||||
break;
|
||||
case Type_Union:
|
||||
if (!is_type_union_maybe_pointer(t)) {
|
||||
/* 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;
|
||||
}
|
||||
|
||||
@@ -3965,8 +4020,10 @@ gb_internal i64 type_offset_of_from_selection(Type *type, Selection sel) {
|
||||
i32 index = sel.index[i];
|
||||
t = base_type(t);
|
||||
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;
|
||||
} else if (t->kind == Type_Array) {
|
||||
t = t->Array.elem;
|
||||
} else {
|
||||
// NOTE(bill): No need to worry about custom types, just need the alignment
|
||||
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 = write_type_to_string(str, type->RelativePointer.pointer_type);
|
||||
break;
|
||||
case Type_RelativeSlice:
|
||||
case Type_RelativeMultiPointer:
|
||||
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 = write_type_to_string(str, type->RelativeSlice.slice_type);
|
||||
str = write_type_to_string(str, type->RelativePointer.pointer_type);
|
||||
break;
|
||||
|
||||
case Type_Matrix:
|
||||
|
||||
@@ -21,16 +21,13 @@ main :: proc() {
|
||||
|
||||
@test
|
||||
test_read :: proc(t: ^testing.T) {
|
||||
|
||||
using hxa
|
||||
|
||||
filename := tc.get_data_path(t, TEAPOT_PATH)
|
||||
defer delete(filename)
|
||||
|
||||
file, err := read_from_file(filename)
|
||||
file, err := hxa.read_from_file(filename)
|
||||
e :: hxa.Read_Error.None
|
||||
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 */
|
||||
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_write :: proc(t: ^testing.T) {
|
||||
|
||||
using hxa
|
||||
|
||||
n1 :Node
|
||||
n1: hxa.Node
|
||||
|
||||
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_l2 := Layer{"l2", 3, []f64le{0.64, 1.64, -2.64}}
|
||||
n1_l1 := hxa.Layer{"l1", 2, []f32le{32.1, -41.3}}
|
||||
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
|
||||
|
||||
w_file :File
|
||||
w_file.nodes = []Node{n1}
|
||||
w_file: hxa.File
|
||||
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)
|
||||
|
||||
n, write_err := write(buf, w_file)
|
||||
n, write_err := hxa.write(buf, w_file)
|
||||
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, 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
|
||||
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)
|
||||
|
||||
|
||||
@@ -47,7 +47,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "恥ずべきフクロウ",
|
||||
},
|
||||
crc32 = 0x30d82264,
|
||||
crc32 = 0xe9b62f03,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -62,7 +62,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "恥ずべきフクロウ",
|
||||
},
|
||||
crc32 = 0xad31d8e8,
|
||||
crc32 = 0x9c2643ed,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -77,7 +77,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "TS",
|
||||
},
|
||||
crc32 = 0x7bce2630,
|
||||
crc32 = 0x859b7443,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -92,7 +92,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "xliff",
|
||||
},
|
||||
crc32 = 0x43f19d61,
|
||||
crc32 = 0x3deaf329,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -107,7 +107,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "xliff",
|
||||
},
|
||||
crc32 = 0x961e7635,
|
||||
crc32 = 0x0c55e287,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -118,7 +118,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "html",
|
||||
},
|
||||
crc32 = 0x573c1033,
|
||||
crc32 = 0x05373317,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -129,7 +129,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "html",
|
||||
},
|
||||
crc32 = 0x82588917,
|
||||
crc32 = 0x3b6d4a90,
|
||||
},
|
||||
|
||||
{
|
||||
@@ -140,7 +140,7 @@ TESTS :: []TEST{
|
||||
},
|
||||
expected_doctype = "html",
|
||||
},
|
||||
crc32 = 0x5e74d8a6,
|
||||
crc32 = 0x5be2ffdc,
|
||||
},
|
||||
|
||||
/*
|
||||
@@ -170,7 +170,7 @@ TESTS :: []TEST{
|
||||
expected_doctype = "",
|
||||
},
|
||||
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) {
|
||||
if doc == nil { return }
|
||||
using fmt
|
||||
|
||||
written += wprintf(writer, "[XML Prolog]\n")
|
||||
written += fmt.wprintf(writer, "[XML Prolog]\n")
|
||||
|
||||
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 {
|
||||
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 {
|
||||
wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||
fmt.wprintf(writer, "\t%v\n", doc.doctype.rest)
|
||||
}
|
||||
}
|
||||
|
||||
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 {
|
||||
wprintln(writer, " --- ")
|
||||
fmt.wprintln(writer, " --- ")
|
||||
print_element(writer, doc, 0)
|
||||
wprintln(writer, " --- ")
|
||||
fmt.wprintln(writer, " --- ")
|
||||
}
|
||||
|
||||
return written, .None
|
||||
}
|
||||
|
||||
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) {
|
||||
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]
|
||||
|
||||
if element.kind == .Element {
|
||||
wprintf(writer, "<%v>\n", element.ident)
|
||||
if len(element.value) > 0 {
|
||||
tab(writer, indent + 1)
|
||||
wprintf(writer, "[Value] %v\n", element.value)
|
||||
fmt.wprintf(writer, "<%v>\n", element.ident)
|
||||
|
||||
for value in 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 {
|
||||
tab(writer, indent + 1)
|
||||
wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
|
||||
}
|
||||
|
||||
for child in element.children {
|
||||
print_element(writer, doc, child, indent + 1)
|
||||
fmt.wprintf(writer, "[Attr] %v: %v\n", attr.key, attr.val)
|
||||
}
|
||||
} else if element.kind == .Comment {
|
||||
wprintf(writer, "[COMMENT] %v\n", element.value)
|
||||
fmt.wprintf(writer, "[COMMENT] %v\n", element.value)
|
||||
}
|
||||
|
||||
return written, .None
|
||||
@@ -289,8 +288,6 @@ doc_to_string :: proc(doc: ^xml.Document) -> (result: string) {
|
||||
|
||||
@test
|
||||
run_tests :: proc(t: ^testing.T) {
|
||||
using fmt
|
||||
|
||||
for test in TESTS {
|
||||
path := test_file_path(test.filename)
|
||||
log(t, fmt.tprintf("Trying to parse %v", path))
|
||||
@@ -305,11 +302,11 @@ run_tests :: proc(t: ^testing.T) {
|
||||
crc32 := hash.crc32(tree_bytes)
|
||||
|
||||
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)
|
||||
|
||||
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)
|
||||
|
||||
if failed {
|
||||
@@ -317,7 +314,7 @@ run_tests :: proc(t: ^testing.T) {
|
||||
Don't fully print big trees.
|
||||
*/
|
||||
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_fract_f32 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
|
||||
r: f32
|
||||
|
||||
Datum :: struct {
|
||||
@@ -35,8 +32,8 @@ test_fract_f32 :: proc(t: ^testing.T) {
|
||||
@static data := []Datum{
|
||||
{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
|
||||
{ 1, -10.5, -0.5 },
|
||||
{ 2, F32_MIN, F32_MIN }, // 0x1p-126
|
||||
{ 3, -F32_MIN, -F32_MIN },
|
||||
{ 2, math.F32_MIN, math.F32_MIN }, // 0x1p-126
|
||||
{ 3, -math.F32_MIN, -math.F32_MIN },
|
||||
{ 4, 0.0, 0.0 },
|
||||
{ 5, -0.0, -0.0 },
|
||||
{ 6, 1, 0.0 },
|
||||
@@ -54,9 +51,6 @@ test_fract_f32 :: proc(t: ^testing.T) {
|
||||
|
||||
@test
|
||||
test_fract_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
|
||||
r: f64
|
||||
|
||||
Datum :: struct {
|
||||
@@ -67,8 +61,8 @@ test_fract_f64 :: proc(t: ^testing.T) {
|
||||
@static data := []Datum{
|
||||
{ 0, 10.5, 0.5 }, // Issue #1574 fract in linalg/glm is broken
|
||||
{ 1, -10.5, -0.5 },
|
||||
{ 2, F64_MIN, F64_MIN }, // 0x1p-1022
|
||||
{ 3, -F64_MIN, -F64_MIN },
|
||||
{ 2, math.F64_MIN, math.F64_MIN }, // 0x1p-1022
|
||||
{ 3, -math.F64_MIN, -math.F64_MIN },
|
||||
{ 4, 0.0, 0.0 },
|
||||
{ 5, -0.0, -0.0 },
|
||||
{ 6, 1, 0.0 },
|
||||
|
||||
@@ -43,11 +43,7 @@ main :: proc() {
|
||||
|
||||
@test
|
||||
test_classify_f16 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
using Float_Class
|
||||
|
||||
r: Float_Class
|
||||
r: math.Float_Class
|
||||
|
||||
Datum :: struct {
|
||||
i: int,
|
||||
@@ -55,38 +51,34 @@ test_classify_f16 :: proc(t: ^testing.T) {
|
||||
e: math.Float_Class,
|
||||
}
|
||||
@static data := []Datum{
|
||||
{ 0, 1.2, Normal },
|
||||
{ 1, 0h0001, Subnormal },
|
||||
{ 2, 0.0, Zero },
|
||||
{ 3, -0.0, Neg_Zero },
|
||||
{ 4, SNAN_F16, NaN },
|
||||
{ 5, QNAN_F16, NaN },
|
||||
{ 6, INF_F16, Inf },
|
||||
{ 7, NEG_INF_F16, Neg_Inf },
|
||||
{ 0, 1.2, .Normal },
|
||||
{ 1, 0h0001, .Subnormal },
|
||||
{ 2, 0.0, .Zero },
|
||||
{ 3, -0.0, .Neg_Zero },
|
||||
{ 4, math.SNAN_F16, .NaN },
|
||||
{ 5, math.QNAN_F16, .NaN },
|
||||
{ 6, math.INF_F16, .Inf },
|
||||
{ 7, math.NEG_INF_F16, .Neg_Inf },
|
||||
}
|
||||
|
||||
for d, i in data {
|
||||
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))
|
||||
}
|
||||
|
||||
/* Check all subnormals (exponent 0, 10-bit significand non-zero) */
|
||||
for i :u16 = 1; i < 0x400; i += 1 {
|
||||
v :f16 = transmute(f16)i
|
||||
r = classify_f16(v)
|
||||
e :Float_Class: Subnormal
|
||||
for i in u16(1)..<0x400 {
|
||||
v := transmute(f16)i
|
||||
r = math.classify_f16(v)
|
||||
e :: math.Float_Class.Subnormal
|
||||
tc.expect(t, r == e, fmt.tprintf("i:%d %s(%h) -> %v != %v", i, #procedure, v, r, e))
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_classify_f32 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
using Float_Class
|
||||
|
||||
r: Float_Class
|
||||
r: math.Float_Class
|
||||
|
||||
Datum :: struct {
|
||||
i: int,
|
||||
@@ -94,30 +86,26 @@ test_classify_f32 :: proc(t: ^testing.T) {
|
||||
e: math.Float_Class,
|
||||
}
|
||||
@static data := []Datum{
|
||||
{ 0, 1.2, Normal },
|
||||
{ 1, 0h0000_0001, Subnormal },
|
||||
{ 2, 0.0, Zero },
|
||||
{ 3, -0.0, Neg_Zero },
|
||||
{ 4, SNAN_F32, NaN },
|
||||
{ 5, QNAN_F32, NaN },
|
||||
{ 6, INF_F32, Inf },
|
||||
{ 7, NEG_INF_F32, Neg_Inf },
|
||||
{ 0, 1.2, .Normal },
|
||||
{ 1, 0h0000_0001, .Subnormal },
|
||||
{ 2, 0.0, .Zero },
|
||||
{ 3, -0.0, .Neg_Zero },
|
||||
{ 4, math.SNAN_F32, .NaN },
|
||||
{ 5, math.QNAN_F32, .NaN },
|
||||
{ 6, math.INF_F32, .Inf },
|
||||
{ 7, math.NEG_INF_F32, .Neg_Inf },
|
||||
}
|
||||
|
||||
for d, i in data {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_classify_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
using Float_Class
|
||||
|
||||
r: Float_Class
|
||||
r: math.Float_Class
|
||||
|
||||
Datum :: struct {
|
||||
i: int,
|
||||
@@ -125,28 +113,25 @@ test_classify_f64 :: proc(t: ^testing.T) {
|
||||
e: math.Float_Class,
|
||||
}
|
||||
@static data := []Datum{
|
||||
{ 0, 1.2, Normal },
|
||||
{ 1, 0h0000_0000_0000_0001, Subnormal },
|
||||
{ 2, 0.0, Zero },
|
||||
{ 3, -0.0, Neg_Zero },
|
||||
{ 4, SNAN_F64, NaN },
|
||||
{ 5, QNAN_F64, NaN },
|
||||
{ 6, INF_F64, Inf },
|
||||
{ 7, NEG_INF_F64, Neg_Inf },
|
||||
{ 0, 1.2, .Normal },
|
||||
{ 1, 0h0000_0000_0000_0001, .Subnormal },
|
||||
{ 2, 0.0, .Zero },
|
||||
{ 3, -0.0, .Neg_Zero },
|
||||
{ 4, math.SNAN_F64, .NaN },
|
||||
{ 5, math.QNAN_F64, .NaN },
|
||||
{ 6, math.INF_F64, .Inf },
|
||||
{ 7, math.NEG_INF_F64, .Neg_Inf },
|
||||
}
|
||||
|
||||
for d, i in data {
|
||||
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))
|
||||
}
|
||||
}
|
||||
|
||||
@test
|
||||
test_trunc_f16 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
|
||||
r, v: f16
|
||||
|
||||
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
|
||||
{ 1, -10.5, -10 },
|
||||
|
||||
{ 2, F16_MAX, F16_MAX },
|
||||
{ 3, -F16_MAX, -F16_MAX },
|
||||
{ 4, F16_MIN, 0.0 },
|
||||
{ 5, -F16_MIN, -0.0 },
|
||||
{ 2, math.F16_MAX, math.F16_MAX },
|
||||
{ 3, -math.F16_MAX, -math.F16_MAX },
|
||||
{ 4, math.F16_MIN, 0.0 },
|
||||
{ 5, -math.F16_MIN, -0.0 },
|
||||
{ 6, 0.0, 0.0 },
|
||||
{ 7, -0.0, -0.0 },
|
||||
{ 8, 1, 1 },
|
||||
{ 9, -1, -1 },
|
||||
{ 10, INF_F16, INF_F16 },
|
||||
{ 11, NEG_INF_F16, NEG_INF_F16 },
|
||||
{ 10, math.INF_F16, math.INF_F16 },
|
||||
{ 11, math.NEG_INF_F16, math.NEG_INF_F16 },
|
||||
|
||||
/* From https://en.wikipedia.org/wiki/Half-precision_floating-point_format */
|
||||
{ 12, 0h3C01, 1 }, // 0x1.004p+0 (smallest > 1)
|
||||
@@ -185,24 +170,21 @@ test_trunc_f16 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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))
|
||||
}
|
||||
|
||||
v = SNAN_F16
|
||||
r = trunc_f16(v)
|
||||
tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
v = math.SNAN_F16
|
||||
r = math.trunc_f16(v)
|
||||
tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
|
||||
v = QNAN_F16
|
||||
r = trunc_f16(v)
|
||||
tc.expect(t, is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
v = math.QNAN_F16
|
||||
r = math.trunc_f16(v)
|
||||
tc.expect(t, math.is_nan_f16(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
}
|
||||
|
||||
@test
|
||||
test_trunc_f32 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
|
||||
r, v: f32
|
||||
|
||||
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
|
||||
{ 1, -10.5, -10 },
|
||||
|
||||
{ 2, F32_MAX, F32_MAX },
|
||||
{ 3, -F32_MAX, -F32_MAX },
|
||||
{ 4, F32_MIN, 0.0 },
|
||||
{ 5, -F32_MIN, -0.0 },
|
||||
{ 2, math.F32_MAX, math.F32_MAX },
|
||||
{ 3, -math.F32_MAX, -math.F32_MAX },
|
||||
{ 4, math.F32_MIN, 0.0 },
|
||||
{ 5, -math.F32_MIN, -0.0 },
|
||||
{ 6, 0.0, 0.0 },
|
||||
{ 7, -0.0, -0.0 },
|
||||
{ 8, 1, 1 },
|
||||
{ 9, -1, -1 },
|
||||
{ 10, INF_F32, INF_F32 },
|
||||
{ 11, NEG_INF_F32, NEG_INF_F32 },
|
||||
{ 10, math.INF_F32, math.INF_F32 },
|
||||
{ 11, math.NEG_INF_F32, math.NEG_INF_F32 },
|
||||
|
||||
/* From https://en.wikipedia.org/wiki/Single-precision_floating-point_format */
|
||||
{ 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 {
|
||||
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))
|
||||
}
|
||||
|
||||
v = SNAN_F32
|
||||
r = trunc_f32(v)
|
||||
tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
v = math.SNAN_F32
|
||||
r = math.trunc_f32(v)
|
||||
tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
|
||||
v = QNAN_F32
|
||||
r = trunc_f32(v)
|
||||
tc.expect(t, is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
v = math.QNAN_F32
|
||||
r = math.trunc_f32(v)
|
||||
tc.expect(t, math.is_nan_f32(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
}
|
||||
|
||||
@test
|
||||
test_trunc_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
using math
|
||||
|
||||
r, v: f64
|
||||
|
||||
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
|
||||
{ 1, -10.5, -10 },
|
||||
|
||||
{ 2, F64_MAX, F64_MAX },
|
||||
{ 3, -F64_MAX, -F64_MAX },
|
||||
{ 4, F64_MIN, 0.0 },
|
||||
{ 5, -F64_MIN, -0.0 },
|
||||
{ 2, math.F64_MAX, math.F64_MAX },
|
||||
{ 3, -math.F64_MAX, -math.F64_MAX },
|
||||
{ 4, math.F64_MIN, 0.0 },
|
||||
{ 5, -math.F64_MIN, -0.0 },
|
||||
{ 6, 0.0, 0.0 },
|
||||
{ 7, -0.0, -0.0 },
|
||||
{ 8, 1, 1 },
|
||||
{ 9, -1, -1 },
|
||||
{ 10, INF_F64, INF_F64 },
|
||||
{ 11, NEG_INF_F64, NEG_INF_F64 },
|
||||
{ 10, math.INF_F64, math.INF_F64 },
|
||||
{ 11, math.NEG_INF_F64, math.NEG_INF_F64 },
|
||||
|
||||
/* From https://en.wikipedia.org/wiki/Double-precision_floating-point_format */
|
||||
{ 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 {
|
||||
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))
|
||||
}
|
||||
|
||||
v = SNAN_F64
|
||||
r = trunc_f64(v)
|
||||
tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
v = math.SNAN_F64
|
||||
r = math.trunc_f64(v)
|
||||
tc.expect(t, math.is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
|
||||
v = QNAN_F64
|
||||
r = trunc_f64(v)
|
||||
tc.expect(t, is_nan_f64(r), fmt.tprintf("%s(%f) -> %f != NaN", #procedure, v, r))
|
||||
v = math.QNAN_F64
|
||||
r = math.trunc_f64(v)
|
||||
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_split_list_windows :: proc(t: ^testing.T) {
|
||||
|
||||
using filepath
|
||||
|
||||
Datum :: struct {
|
||||
i: int,
|
||||
v: string,
|
||||
@@ -43,7 +40,7 @@ test_split_list_windows :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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)
|
||||
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)))
|
||||
@@ -57,12 +54,12 @@ test_split_list_windows :: proc(t: ^testing.T) {
|
||||
|
||||
{
|
||||
v := ""
|
||||
r := split_list(v)
|
||||
r := filepath.split_list(v)
|
||||
tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
|
||||
}
|
||||
{
|
||||
v := "a"
|
||||
r := split_list(v)
|
||||
r := filepath.split_list(v)
|
||||
defer delete(r)
|
||||
tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
|
||||
if len(r) == 1 {
|
||||
@@ -73,9 +70,6 @@ test_split_list_windows :: proc(t: ^testing.T) {
|
||||
|
||||
@test
|
||||
test_split_list_unix :: proc(t: ^testing.T) {
|
||||
|
||||
using filepath
|
||||
|
||||
Datum :: struct {
|
||||
i: int,
|
||||
v: string,
|
||||
@@ -94,7 +88,7 @@ test_split_list_unix :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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)
|
||||
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)))
|
||||
@@ -108,12 +102,12 @@ test_split_list_unix :: proc(t: ^testing.T) {
|
||||
|
||||
{
|
||||
v := ""
|
||||
r := split_list(v)
|
||||
r := filepath.split_list(v)
|
||||
tc.expect(t, r == nil, fmt.tprintf("%s(%s) -> %v != nil", #procedure, v, r))
|
||||
}
|
||||
{
|
||||
v := "a"
|
||||
r := split_list(v)
|
||||
r := filepath.split_list(v)
|
||||
defer delete(r)
|
||||
tc.expect(t, len(r) == 1, fmt.tprintf("%s(%s) len(r) %d != 1", #procedure, v, len(r)))
|
||||
if len(r) == 1 {
|
||||
|
||||
@@ -19,8 +19,6 @@ main :: proc() {
|
||||
|
||||
@test
|
||||
test_as_u64 :: proc(t: ^testing.T) {
|
||||
using reflect
|
||||
|
||||
{
|
||||
/* i8 */
|
||||
Datum :: struct { i: int, v: i8, e: u64 }
|
||||
@@ -32,7 +30,7 @@ test_as_u64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -49,7 +47,7 @@ test_as_u64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -66,7 +64,7 @@ test_as_u64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -83,7 +81,7 @@ test_as_u64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -103,7 +101,7 @@ test_as_u64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -119,7 +117,7 @@ test_as_u64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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 {
|
||||
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, 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 {
|
||||
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, 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_as_f64 :: proc(t: ^testing.T) {
|
||||
using reflect
|
||||
|
||||
{
|
||||
/* i8 */
|
||||
Datum :: struct { i: int, v: i8, e: f64 }
|
||||
@@ -169,7 +165,7 @@ test_as_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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 {
|
||||
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, 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 {
|
||||
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, 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 {
|
||||
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, 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 {
|
||||
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, 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))
|
||||
@@ -250,7 +246,7 @@ test_as_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -265,7 +261,7 @@ test_as_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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))
|
||||
@@ -280,7 +276,7 @@ test_as_f64 :: proc(t: ^testing.T) {
|
||||
|
||||
for d, i in data {
|
||||
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, 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
|
||||
tests :: proc(t: ^testing.T) {
|
||||
using fmt
|
||||
|
||||
cat: ^i18n.Translation
|
||||
err: i18n.Error
|
||||
|
||||
@@ -142,8 +140,6 @@ tests :: proc(t: ^testing.T) {
|
||||
}
|
||||
|
||||
main :: proc() {
|
||||
using fmt
|
||||
|
||||
track: mem.Tracking_Allocator
|
||||
mem.tracking_allocator_init(&track, context.allocator)
|
||||
context.allocator = mem.tracking_allocator(&track)
|
||||
@@ -157,9 +153,9 @@ main :: proc() {
|
||||
}
|
||||
|
||||
if len(track.allocation_map) > 0 {
|
||||
println()
|
||||
fmt.println()
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -15,6 +15,7 @@ set COMMON=-collection:tests=..\..
|
||||
..\..\..\odin test ..\test_issue_2466.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_2666.odin %COMMON% -file || exit /b
|
||||
|
||||
@echo off
|
||||
|
||||
|
||||
@@ -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_2615.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
|
||||
echo "SUCCESSFUL 1/1"
|
||||
else
|
||||
|
||||
@@ -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")
|
||||
}
|
||||
Vendored
+2
-2
@@ -5,7 +5,9 @@ package vendor_gl
|
||||
import "core:os"
|
||||
import "core:fmt"
|
||||
import "core:strings"
|
||||
import "core:runtime"
|
||||
_ :: fmt
|
||||
_ :: runtime
|
||||
|
||||
Shader_Type :: enum i32 {
|
||||
NONE = 0x0000,
|
||||
@@ -47,8 +49,6 @@ get_last_error_message :: proc() -> (compile_message: string, compile_type: Shad
|
||||
// except for calling differently named GL functions
|
||||
// it's a bit ugly looking, but meh
|
||||
when GL_DEBUG {
|
||||
import "core:runtime"
|
||||
|
||||
@private
|
||||
check_error :: proc(
|
||||
id: u32, type: Shader_Type, status: u32,
|
||||
|
||||
Vendored
+5
-3
@@ -2,6 +2,11 @@ package vendor_gl
|
||||
|
||||
#assert(size_of(bool) == size_of(u8))
|
||||
|
||||
import "core:runtime"
|
||||
import "core:fmt"
|
||||
_ :: runtime
|
||||
_ :: fmt
|
||||
|
||||
when !GL_DEBUG {
|
||||
// VERSION_1_0
|
||||
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) }
|
||||
PolygonOffsetClamp :: proc "c" (factor, units, clamp: f32) { impl_PolygonOffsetClamp(factor, units, clamp) }
|
||||
} 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) {
|
||||
context = runtime.default_context()
|
||||
|
||||
|
||||
+2
-3
@@ -1,9 +1,8 @@
|
||||
package objc_Foundation
|
||||
|
||||
import "core:c"
|
||||
_ :: c
|
||||
when ODIN_OS == .Darwin {
|
||||
import "core:c"
|
||||
_ :: c
|
||||
|
||||
#assert(size_of(c.long) == size_of(int))
|
||||
#assert(size_of(c.ulong) == size_of(uint))
|
||||
}
|
||||
|
||||
Vendored
+14
@@ -3551,6 +3551,8 @@ PARAMETER_DESC :: struct {
|
||||
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 {
|
||||
using vtable: ^IShaderReflectionType_VTable,
|
||||
}
|
||||
@@ -3568,6 +3570,8 @@ IShaderReflectionType_VTable :: struct {
|
||||
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 {
|
||||
using vtable: ^IShaderReflectionVariable_VTable,
|
||||
}
|
||||
@@ -3578,6 +3582,8 @@ IShaderReflectionVariable_VTable :: struct {
|
||||
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 {
|
||||
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 {
|
||||
#subtype iunknown: IUnknown,
|
||||
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 {
|
||||
#subtype iunknown: IUnknown,
|
||||
using id3d11libraryreflection_vtable: ^ILibraryReflection_VTable,
|
||||
@@ -3626,6 +3636,8 @@ ILibraryReflection_VTable :: struct {
|
||||
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 {
|
||||
using vtable: ^IFunctionReflection_VTable,
|
||||
}
|
||||
@@ -3639,6 +3651,8 @@ IFunctionReflection_VTable :: struct {
|
||||
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 {
|
||||
using vtable: ^IFunctionParameterReflection_VTable,
|
||||
}
|
||||
|
||||
@@ -149,6 +149,9 @@ ID3DInclude_VTable :: struct {
|
||||
Close: proc "stdcall" (this: ^ID3DInclude, pData: rawptr) -> HRESULT,
|
||||
}
|
||||
|
||||
// Default file includer
|
||||
D3DCOMPILE_STANDARD_FILE_INCLUDE :: (^ID3DInclude)(uintptr(1))
|
||||
|
||||
|
||||
ID3D11Module :: struct #raw_union {
|
||||
#subtype iunknown: IUnknown,
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user