mirror of
https://github.com/odin-lang/Odin.git
synced 2026-08-23 13:31:35 +00:00
Merge branch 'master' into windows-llvm-11.1.0
This commit is contained in:
@@ -17,25 +17,54 @@ Marshal_Error :: union #shared_nil {
|
||||
io.Error,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
// careful with MJSON maps & non quotes usage as keys without whitespace will lead to bad results
|
||||
Marshal_Options :: struct {
|
||||
// output based on spec
|
||||
spec: Specification,
|
||||
|
||||
// use line breaks & tab|spaces
|
||||
pretty: bool,
|
||||
|
||||
// spacing
|
||||
use_spaces: bool,
|
||||
spaces: int,
|
||||
|
||||
// state
|
||||
indentation: int,
|
||||
|
||||
// option to output uint in JSON5 & MJSON
|
||||
write_uint_as_hex: bool,
|
||||
|
||||
// mjson output options
|
||||
mjson_keys_use_quotes: bool,
|
||||
mjson_keys_use_equal_sign: bool,
|
||||
|
||||
// mjson state
|
||||
mjson_skipped_first_braces_start: bool,
|
||||
mjson_skipped_first_braces_end: bool,
|
||||
}
|
||||
|
||||
marshal :: proc(v: any, opt: Marshal_Options = {}, allocator := context.allocator) -> (data: []byte, err: Marshal_Error) {
|
||||
b := strings.builder_make(allocator)
|
||||
defer if err != nil {
|
||||
strings.builder_destroy(&b)
|
||||
}
|
||||
|
||||
marshal_to_builder(&b, v) or_return
|
||||
opt := opt
|
||||
marshal_to_builder(&b, v, &opt) or_return
|
||||
|
||||
if len(b.buf) != 0 {
|
||||
data = b.buf[:]
|
||||
}
|
||||
|
||||
return data, nil
|
||||
}
|
||||
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v)
|
||||
marshal_to_builder :: proc(b: ^strings.Builder, v: any, opt: ^Marshal_Options) -> Marshal_Error {
|
||||
return marshal_to_writer(strings.to_writer(b), v, opt)
|
||||
}
|
||||
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
marshal_to_writer :: proc(w: io.Writer, v: any, opt: ^Marshal_Options) -> (err: Marshal_Error) {
|
||||
if v == nil {
|
||||
io.write_string(w, "null") or_return
|
||||
return
|
||||
@@ -82,7 +111,21 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
case u128be: u = u128(i)
|
||||
}
|
||||
|
||||
s := strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
s: string
|
||||
|
||||
// allow uints to be printed as hex
|
||||
if opt.write_uint_as_hex && (opt.spec == .JSON5 || opt.spec == .MJSON) {
|
||||
switch i in a {
|
||||
case u8, u16, u32, u64, u128:
|
||||
s = strconv.append_bits_128(buf[:], u, 16, info.signed, 8*ti.size, "0123456789abcdef", { .Prefix })
|
||||
|
||||
case:
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
} else {
|
||||
s = strconv.append_bits_128(buf[:], u, 10, info.signed, 8*ti.size, "0123456789", nil)
|
||||
}
|
||||
|
||||
io.write_string(w, s) or_return
|
||||
|
||||
|
||||
@@ -169,52 +212,48 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
return .Unsupported_Type
|
||||
|
||||
case runtime.Type_Info_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Enumerated_Array:
|
||||
index := runtime.type_info_base(info.index).variant.(runtime.Type_Info_Enum)
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
for i in 0..<info.count {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(v.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Dynamic_Array:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
array := cast(^mem.Raw_Dynamic_Array)v.data
|
||||
for i in 0..<array.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(array.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Slice:
|
||||
io.write_byte(w, '[') or_return
|
||||
opt_write_start(w, opt, '[') or_return
|
||||
slice := cast(^mem.Raw_Slice)v.data
|
||||
for i in 0..<slice.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
data := uintptr(slice.data) + uintptr(i*info.elem_size)
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}) or_return
|
||||
marshal_to_writer(w, any{rawptr(data), info.elem.id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, ']') or_return
|
||||
opt_write_end(w, opt, ']') or_return
|
||||
|
||||
case runtime.Type_Info_Map:
|
||||
m := (^mem.Raw_Map)(v.data)
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
io.write_byte(w, '{') or_return
|
||||
if m != nil {
|
||||
if info.generated_struct == nil {
|
||||
return .Unsupported_Type
|
||||
@@ -226,31 +265,50 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
entry_size := ed.elem_size
|
||||
|
||||
for i in 0..<entries.len {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
|
||||
data := uintptr(entries.data) + uintptr(i*entry_size)
|
||||
key := rawptr(data + entry_type.offsets[2])
|
||||
value := rawptr(data + entry_type.offsets[3])
|
||||
|
||||
marshal_to_writer(w, any{key, info.key.id}) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
marshal_to_writer(w, any{value, info.value.id}) or_return
|
||||
// check for string type
|
||||
{
|
||||
v := any{key, info.key.id}
|
||||
ti := runtime.type_info_base(type_info_of(v.id))
|
||||
a := any{v.data, ti.id}
|
||||
name: string
|
||||
|
||||
#partial switch info in ti.variant {
|
||||
case runtime.Type_Info_String:
|
||||
switch s in a {
|
||||
case string: name = s
|
||||
case cstring: name = string(s)
|
||||
}
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
case: return .Unsupported_Type
|
||||
}
|
||||
}
|
||||
|
||||
marshal_to_writer(w, any{value, info.value.id}, opt) or_return
|
||||
}
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Struct:
|
||||
io.write_byte(w, '{') or_return
|
||||
opt_write_start(w, opt, '{') or_return
|
||||
|
||||
for name, i in info.names {
|
||||
if i > 0 { io.write_string(w, ", ") or_return }
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
opt_write_iteration(w, opt, i) or_return
|
||||
opt_write_key(w, opt, name) or_return
|
||||
|
||||
id := info.types[i].id
|
||||
data := rawptr(uintptr(v.data) + info.offsets[i])
|
||||
marshal_to_writer(w, any{data, id}) or_return
|
||||
marshal_to_writer(w, any{data, id}, opt) or_return
|
||||
}
|
||||
io.write_byte(w, '}') or_return
|
||||
|
||||
opt_write_end(w, opt, '}') or_return
|
||||
|
||||
case runtime.Type_Info_Union:
|
||||
tag_ptr := uintptr(v.data) + info.tag_offset
|
||||
@@ -273,11 +331,11 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
io.write_string(w, "null") or_return
|
||||
} else {
|
||||
id := info.variants[tag-1].id
|
||||
return marshal_to_writer(w, any{v.data, id})
|
||||
return marshal_to_writer(w, any{v.data, id}, opt)
|
||||
}
|
||||
|
||||
case runtime.Type_Info_Enum:
|
||||
return marshal_to_writer(w, any{v.data, info.base.id})
|
||||
return marshal_to_writer(w, any{v.data, info.base.id}, opt)
|
||||
|
||||
case runtime.Type_Info_Bit_Set:
|
||||
is_bit_set_different_endian_to_platform :: proc(ti: ^runtime.Type_Info) -> bool {
|
||||
@@ -333,3 +391,116 @@ marshal_to_writer :: proc(w: io.Writer, v: any) -> (err: Marshal_Error) {
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// write key as quoted string or with optional quotes in mjson
|
||||
opt_write_key :: proc(w: io.Writer, opt: ^Marshal_Options, name: string) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
io.write_quoted_string(w, name) or_return
|
||||
io.write_string(w, ": ") or_return
|
||||
|
||||
case .MJSON:
|
||||
if opt.mjson_keys_use_quotes {
|
||||
io.write_quoted_string(w, name) or_return
|
||||
} else {
|
||||
io.write_string(w, name) or_return
|
||||
}
|
||||
|
||||
if opt.mjson_keys_use_equal_sign {
|
||||
io.write_string(w, " = ") or_return
|
||||
} else {
|
||||
io.write_string(w, ": ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert start byte and increase indentation on pretty
|
||||
opt_write_start :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
// skip mjson starting braces
|
||||
if opt.spec == .MJSON && !opt.mjson_skipped_first_braces_start {
|
||||
opt.mjson_skipped_first_braces_start = true
|
||||
return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
opt.indentation += 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// insert comma seperation and write indentations
|
||||
opt_write_iteration :: proc(w: io.Writer, opt: ^Marshal_Options, iteration: int) -> (err: io.Error) {
|
||||
switch opt.spec {
|
||||
case .JSON, .JSON5:
|
||||
if iteration > 0 {
|
||||
io.write_string(w, ", ") or_return
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
|
||||
case .MJSON:
|
||||
if iteration > 0 {
|
||||
// on pretty no commas necessary
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
} else {
|
||||
// comma seperation necessary for non pretty output!
|
||||
io.write_string(w, ", ") or_return
|
||||
}
|
||||
}
|
||||
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
// decrease indent, write spacing and insert end byte
|
||||
opt_write_end :: proc(w: io.Writer, opt: ^Marshal_Options, c: byte) -> (err: io.Error) {
|
||||
if opt.spec == .MJSON && opt.mjson_skipped_first_braces_start && !opt.mjson_skipped_first_braces_end {
|
||||
if opt.indentation == 0 {
|
||||
opt.mjson_skipped_first_braces_end = true
|
||||
return
|
||||
}
|
||||
}
|
||||
|
||||
opt.indentation -= 1
|
||||
|
||||
if opt.pretty {
|
||||
io.write_byte(w, '\n') or_return
|
||||
opt_write_indentation(w, opt) or_return
|
||||
}
|
||||
|
||||
io.write_byte(w, c) or_return
|
||||
return
|
||||
}
|
||||
|
||||
// writes current indentation level based on options
|
||||
opt_write_indentation :: proc(w: io.Writer, opt: ^Marshal_Options) -> (err: io.Error) {
|
||||
if !opt.pretty {
|
||||
return
|
||||
}
|
||||
|
||||
if opt.use_spaces {
|
||||
spaces := opt.spaces == 0 ? 4 : opt.spaces
|
||||
for _ in 0..<opt.indentation * spaces {
|
||||
io.write_byte(w, ' ') or_return
|
||||
}
|
||||
} else {
|
||||
for _ in 0..<opt.indentation {
|
||||
io.write_byte(w, '\t') or_return
|
||||
}
|
||||
}
|
||||
|
||||
return
|
||||
}
|
||||
|
||||
@@ -219,7 +219,7 @@ write_image_as_ppm :: proc(filename: string, image: ^image.Image) -> (success: b
|
||||
defer close(fd)
|
||||
|
||||
write_string(fd,
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, (1 << uint(depth) - 1)),
|
||||
fmt.tprintf("P6\n%v %v\n%v\n", width, height, uint(1 << uint(depth) - 1)),
|
||||
)
|
||||
|
||||
if channels == 3 {
|
||||
|
||||
@@ -95,7 +95,7 @@ file_console_logger_proc :: proc(logger_data: rawptr, level: Level, text: string
|
||||
fmt.sbprintf(&buf, "[%s] ", data.ident)
|
||||
}
|
||||
//TODO(Hoej): When we have better atomics and such, make this thread-safe
|
||||
fmt.fprintf(h, "%s %s\n", strings.to_string(buf), text)
|
||||
fmt.fprintf(h, "%s%s\n", strings.to_string(buf), text)
|
||||
}
|
||||
|
||||
do_level_header :: proc(opts: Options, level: Level, str: ^strings.Builder) {
|
||||
|
||||
@@ -102,9 +102,9 @@ growing_arena_allocator :: proc(arena: ^Growing_Arena) -> mem.Allocator {
|
||||
}
|
||||
|
||||
growing_arena_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
size, alignment: int,
|
||||
old_memory: rawptr, old_size: int,
|
||||
location := #caller_location) -> (data: []byte, err: Allocator_Error) {
|
||||
arena := (^Growing_Arena)(allocator_data)
|
||||
|
||||
switch mode {
|
||||
|
||||
@@ -245,12 +245,7 @@ peek_token :: proc(p: ^Parser, lookahead := 0) -> (tok: tokenizer.Token) {
|
||||
return
|
||||
}
|
||||
skip_possible_newline :: proc(p: ^Parser) -> bool {
|
||||
if .Optional_Semicolons not_in p.flags {
|
||||
return false
|
||||
}
|
||||
|
||||
prev := p.curr_tok
|
||||
if tokenizer.is_newline(prev) {
|
||||
if tokenizer.is_newline(p.curr_tok) {
|
||||
advance_token(p)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -42,6 +42,7 @@ foreign user32 {
|
||||
GetTopWindow :: proc(hWnd: HWND) -> HWND ---
|
||||
SetForegroundWindow :: proc(hWnd: HWND) -> BOOL ---
|
||||
GetForegroundWindow :: proc() -> HWND ---
|
||||
UpdateWindow :: proc(hWnd: HWND) -> BOOL ---
|
||||
SetActiveWindow :: proc(hWnd: HWND) -> HWND ---
|
||||
GetActiveWindow :: proc() -> HWND ---
|
||||
|
||||
@@ -95,6 +96,7 @@ foreign user32 {
|
||||
GetSystemMetrics :: proc(nIndex: c_int) -> c_int ---
|
||||
AdjustWindowRect :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL) -> BOOL ---
|
||||
AdjustWindowRectEx :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD) -> BOOL ---
|
||||
AdjustWindowRectExForDpi :: proc(lpRect: LPRECT, dwStyle: DWORD, bMenu: BOOL, dwExStyle: DWORD, dpi: UINT) -> BOOL ---
|
||||
|
||||
SystemParametersInfoW :: proc(uiAction, uiParam: UINT, pvParam: PVOID, fWinIni: UINT) -> BOOL ---
|
||||
|
||||
@@ -137,7 +139,19 @@ foreign user32 {
|
||||
SetCursor :: proc(hCursor: HCURSOR) -> HCURSOR ---
|
||||
|
||||
EnumDisplaySettingsW :: proc(lpszDeviceName: LPCWSTR, iModeNum: DWORD, lpDevMode: ^DEVMODEW) -> BOOL ---
|
||||
|
||||
|
||||
MonitorFromPoint :: proc(pt: POINT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
|
||||
MonitorFromRect :: proc(lprc: LPRECT, dwFlags: Monitor_From_Flags) -> HMONITOR ---
|
||||
MonitorFromWindow :: proc(hwnd: HWND, dwFlags: Monitor_From_Flags) -> HMONITOR ---
|
||||
EnumDisplayMonitors :: proc(hdc: HDC, lprcClip: LPRECT, lpfnEnum: Monitor_Enum_Proc, dwData: LPARAM) -> BOOL ---
|
||||
|
||||
SetThreadDpiAwarenessContext :: proc(dpiContext: DPI_AWARENESS_CONTEXT) -> DPI_AWARENESS_CONTEXT ---
|
||||
GetThreadDpiAwarenessContext :: proc() -> DPI_AWARENESS_CONTEXT ---
|
||||
GetWindowDpiAwarenessContext :: proc(hwnd: HWND) -> DPI_AWARENESS_CONTEXT ---
|
||||
GetDpiFromDpiAwarenessContext :: proc(value: DPI_AWARENESS_CONTEXT) -> UINT ---
|
||||
GetDpiForWindow :: proc(hwnd: HWND) -> UINT ---
|
||||
SetProcessDpiAwarenessContext :: proc(value: DPI_AWARENESS_CONTEXT) -> BOOL ---
|
||||
|
||||
BroadcastSystemMessageW :: proc(
|
||||
flags: DWORD,
|
||||
lpInfo: LPDWORD,
|
||||
@@ -221,7 +235,7 @@ when ODIN_ARCH == .amd64 {
|
||||
SetClassLongPtrW :: SetClassLongW
|
||||
|
||||
GetWindowLongPtrW :: GetWindowLongW
|
||||
SetWindowLongPtrW :: GetWindowLongW
|
||||
SetWindowLongPtrW :: SetWindowLongW
|
||||
}
|
||||
|
||||
GET_SC_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> c_int {
|
||||
@@ -247,3 +261,19 @@ GET_XBUTTON_WPARAM :: #force_inline proc "contextless" (wParam: WPARAM) -> WORD
|
||||
MAKEINTRESOURCEW :: #force_inline proc "contextless" (#any_int i: int) -> LPWSTR {
|
||||
return cast(LPWSTR)uintptr(WORD(i))
|
||||
}
|
||||
|
||||
Monitor_From_Flags :: enum DWORD {
|
||||
MONITOR_DEFAULTTONULL = 0x00000000, // Returns NULL
|
||||
MONITOR_DEFAULTTOPRIMARY = 0x00000001, // Returns a handle to the primary display monitor
|
||||
MONITOR_DEFAULTTONEAREST = 0x00000002, // Returns a handle to the display monitor that is nearest to the window
|
||||
}
|
||||
|
||||
Monitor_Enum_Proc :: #type proc "stdcall" (HMONITOR, HDC, LPRECT, LPARAM) -> BOOL
|
||||
|
||||
USER_DEFAULT_SCREEN_DPI :: 96
|
||||
DPI_AWARENESS_CONTEXT :: distinct HANDLE
|
||||
DPI_AWARENESS_CONTEXT_UNAWARE :: DPI_AWARENESS_CONTEXT(~uintptr(0)) // -1
|
||||
DPI_AWARENESS_CONTEXT_SYSTEM_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(1)) // -2
|
||||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE :: DPI_AWARENESS_CONTEXT(~uintptr(2)) // -3
|
||||
DPI_AWARENESS_CONTEXT_PER_MONITOR_AWARE_V2 :: DPI_AWARENESS_CONTEXT(~uintptr(3)) // -4
|
||||
DPI_AWARENESS_CONTEXT_UNAWARE_GDISCALED :: DPI_AWARENESS_CONTEXT(~uintptr(4)) // -5
|
||||
|
||||
@@ -62,7 +62,7 @@ GetExtensionsStringARBType :: #type proc "c" (HDC) -> cstring
|
||||
// Procedures
|
||||
wglCreateContextAttribsARB: CreateContextAttribsARBType
|
||||
wglChoosePixelFormatARB: ChoosePixelFormatARBType
|
||||
wglSwapIntervalExt: SwapIntervalEXTType
|
||||
wglSwapIntervalEXT: SwapIntervalEXTType
|
||||
wglGetExtensionsStringARB: GetExtensionsStringARBType
|
||||
|
||||
|
||||
|
||||
147
core/sys/windows/wglext.odin
Normal file
147
core/sys/windows/wglext.odin
Normal file
@@ -0,0 +1,147 @@
|
||||
// +build windows
|
||||
package sys_windows
|
||||
|
||||
// WGL_ARB_buffer_region
|
||||
WGL_FRONT_COLOR_BUFFER_BIT_ARB :: 0x00000001
|
||||
WGL_BACK_COLOR_BUFFER_BIT_ARB :: 0x00000002
|
||||
WGL_DEPTH_BUFFER_BIT_ARB :: 0x00000004
|
||||
WGL_STENCIL_BUFFER_BIT_ARB :: 0x00000008
|
||||
|
||||
wglCreateBufferRegionARBType :: #type proc "c" (hDC: HDC, iLayerPlane: c_int, uType: UINT) -> HANDLE
|
||||
wglDeleteBufferRegionARBType :: #type proc "c" (hRegion: HANDLE)
|
||||
wglSaveBufferRegionARBType :: #type proc "c" (hRegion: HANDLE, x: c_int, y: c_int, width: c_int, height: c_int) -> BOOL
|
||||
wglRestoreBufferRegionARBType :: #type proc "c" (hRegion: HANDLE, x: c_int, y: c_int, width: c_int, height: c_int, xSrc: c_int, ySrc: c_int) -> BOOL
|
||||
|
||||
// wglCreateBufferRegionARB: wglCreateBufferRegionARBType
|
||||
// wglDeleteBufferRegionARB: wglDeleteBufferRegionARBType
|
||||
// wglSaveBufferRegionARB: wglSaveBufferRegionARBType
|
||||
// wglRestoreBufferRegionARB: wglRestoreBufferRegionARBType
|
||||
|
||||
// WGL_ARB_context_flush_control
|
||||
WGL_CONTEXT_RELEASE_BEHAVIOR_ARB :: 0x2097
|
||||
WGL_CONTEXT_RELEASE_BEHAVIOR_NONE_ARB :: 0
|
||||
WGL_CONTEXT_RELEASE_BEHAVIOR_FLUSH_ARB :: 0x2098
|
||||
|
||||
// WGL_ARB_create_context
|
||||
WGL_CONTEXT_DEBUG_BIT_ARB :: 0x0001
|
||||
WGL_CONTEXT_FORWARD_COMPATIBLE_BIT_ARB :: 0x0002
|
||||
WGL_CONTEXT_MAJOR_VERSION_ARB :: 0x2091
|
||||
WGL_CONTEXT_MINOR_VERSION_ARB :: 0x2092
|
||||
WGL_CONTEXT_LAYER_PLANE_ARB :: 0x2093
|
||||
WGL_CONTEXT_FLAGS_ARB :: 0x2094
|
||||
ERROR_INVALID_VERSION_ARB :: 0x2095
|
||||
|
||||
// WGL_ARB_create_context_no_error
|
||||
WGL_CONTEXT_OPENGL_NO_ERROR_ARB :: 0x31B3
|
||||
|
||||
// WGL_ARB_create_context_profile
|
||||
WGL_CONTEXT_PROFILE_MASK_ARB :: 0x9126
|
||||
WGL_CONTEXT_CORE_PROFILE_BIT_ARB :: 0x0001
|
||||
WGL_CONTEXT_COMPATIBILITY_PROFILE_BIT_ARB :: 0x0002
|
||||
ERROR_INVALID_PROFILE_ARB :: 0x2096
|
||||
|
||||
// WGL_ARB_create_context_robustness
|
||||
WGL_CONTEXT_ROBUST_ACCESS_BIT_ARB :: 0x00000004
|
||||
WGL_LOSE_CONTEXT_ON_RESET_ARB :: 0x8252
|
||||
WGL_CONTEXT_RESET_NOTIFICATION_STRATEGY_ARB :: 0x8256
|
||||
WGL_NO_RESET_NOTIFICATION_ARB :: 0x8261
|
||||
|
||||
// WGL_ARB_framebuffer_sRGB
|
||||
WGL_FRAMEBUFFER_SRGB_CAPABLE_ARB :: 0x20A9
|
||||
|
||||
// WGL_ARB_make_current_read
|
||||
ERROR_INVALID_PIXEL_TYPE_ARB :: 0x2043
|
||||
ERROR_INCOMPATIBLE_DEVICE_CONTEXTS_ARB :: 0x2054
|
||||
|
||||
wglMakeContextCurrentARBType :: #type proc "c" (hDrawDC: HDC, hReadDC:HDC, hglrc: HGLRC) -> BOOL
|
||||
wglGetCurrentReadDCARBType :: #type proc "c" () -> HDC
|
||||
|
||||
// wglMakeContextCurrentARB: wglMakeContextCurrentARBType
|
||||
// wglGetCurrentReadDCARB: wglGetCurrentReadDCARBType
|
||||
|
||||
// WGL_ARB_multisample
|
||||
WGL_SAMPLE_BUFFERS_ARB :: 0x2041
|
||||
WGL_SAMPLES_ARB :: 0x2042
|
||||
|
||||
// WGL_ARB_pbuffer
|
||||
HPBUFFERARB :: distinct rawptr
|
||||
WGL_DRAW_TO_PBUFFER_ARB :: 0x202D
|
||||
WGL_MAX_PBUFFER_PIXELS_ARB :: 0x202E
|
||||
WGL_MAX_PBUFFER_WIDTH_ARB :: 0x202F
|
||||
WGL_MAX_PBUFFER_HEIGHT_ARB :: 0x2030
|
||||
WGL_PBUFFER_LARGEST_ARB :: 0x2033
|
||||
WGL_PBUFFER_WIDTH_ARB :: 0x2034
|
||||
WGL_PBUFFER_HEIGHT_ARB :: 0x2035
|
||||
WGL_PBUFFER_LOST_ARB :: 0x2036
|
||||
|
||||
wglCreatePbufferARBType :: #type proc "c" (hDC: HDC, iPixelFormat, iWidth, iHeight: c_int, piAttribList: [^]c_int) -> HPBUFFERARB
|
||||
wglGetPbufferDCARBType :: #type proc "c" (hPbuffer: HPBUFFERARB) -> HDC
|
||||
wglReleasePbufferDCARBType :: #type proc "c" (hPbuffer: HPBUFFERARB, hDC: HDC) -> c_int
|
||||
wglDestroyPbufferARBType :: #type proc "c" (hPbuffer: HPBUFFERARB) -> BOOL
|
||||
wglQueryPbufferARBType :: #type proc "c" (hPbuffer: HPBUFFERARB, iAttribute: c_int, piValue: ^c_int) -> BOOL
|
||||
|
||||
// wglCreatePbufferARB: wglCreatePbufferARBType
|
||||
// wglGetPbufferDCARB: wglGetPbufferDCARBType
|
||||
// wglReleasePbufferDCARB: wglReleasePbufferDCARBType
|
||||
// wglDestroyPbufferARB: wglDestroyPbufferARBType
|
||||
// wglQueryPbufferARB: wglQueryPbufferARBType
|
||||
|
||||
// WGL_ARB_pixel_format
|
||||
WGL_NUMBER_PIXEL_FORMATS_ARB :: 0x2000
|
||||
WGL_DRAW_TO_WINDOW_ARB :: 0x2001
|
||||
WGL_DRAW_TO_BITMAP_ARB :: 0x2002
|
||||
WGL_ACCELERATION_ARB :: 0x2003
|
||||
WGL_NEED_PALETTE_ARB :: 0x2004
|
||||
WGL_NEED_SYSTEM_PALETTE_ARB :: 0x2005
|
||||
WGL_SWAP_LAYER_BUFFERS_ARB :: 0x2006
|
||||
WGL_SWAP_METHOD_ARB :: 0x2007
|
||||
WGL_NUMBER_OVERLAYS_ARB :: 0x2008
|
||||
WGL_NUMBER_UNDERLAYS_ARB :: 0x2009
|
||||
WGL_TRANSPARENT_ARB :: 0x200A
|
||||
WGL_TRANSPARENT_RED_VALUE_ARB :: 0x2037
|
||||
WGL_TRANSPARENT_GREEN_VALUE_ARB :: 0x2038
|
||||
WGL_TRANSPARENT_BLUE_VALUE_ARB :: 0x2039
|
||||
WGL_TRANSPARENT_ALPHA_VALUE_ARB :: 0x203A
|
||||
WGL_TRANSPARENT_INDEX_VALUE_ARB :: 0x203B
|
||||
WGL_SHARE_DEPTH_ARB :: 0x200C
|
||||
WGL_SHARE_STENCIL_ARB :: 0x200D
|
||||
WGL_SHARE_ACCUM_ARB :: 0x200E
|
||||
WGL_SUPPORT_GDI_ARB :: 0x200F
|
||||
WGL_SUPPORT_OPENGL_ARB :: 0x2010
|
||||
WGL_DOUBLE_BUFFER_ARB :: 0x2011
|
||||
WGL_STEREO_ARB :: 0x2012
|
||||
WGL_PIXEL_TYPE_ARB :: 0x2013
|
||||
WGL_COLOR_BITS_ARB :: 0x2014
|
||||
WGL_RED_BITS_ARB :: 0x2015
|
||||
WGL_RED_SHIFT_ARB :: 0x2016
|
||||
WGL_GREEN_BITS_ARB :: 0x2017
|
||||
WGL_GREEN_SHIFT_ARB :: 0x2018
|
||||
WGL_BLUE_BITS_ARB :: 0x2019
|
||||
WGL_BLUE_SHIFT_ARB :: 0x201A
|
||||
WGL_ALPHA_BITS_ARB :: 0x201B
|
||||
WGL_ALPHA_SHIFT_ARB :: 0x201C
|
||||
WGL_ACCUM_BITS_ARB :: 0x201D
|
||||
WGL_ACCUM_RED_BITS_ARB :: 0x201E
|
||||
WGL_ACCUM_GREEN_BITS_ARB :: 0x201F
|
||||
WGL_ACCUM_BLUE_BITS_ARB :: 0x2020
|
||||
WGL_ACCUM_ALPHA_BITS_ARB :: 0x2021
|
||||
WGL_DEPTH_BITS_ARB :: 0x2022
|
||||
WGL_STENCIL_BITS_ARB :: 0x2023
|
||||
WGL_AUX_BUFFERS_ARB :: 0x2024
|
||||
WGL_NO_ACCELERATION_ARB :: 0x2025
|
||||
WGL_GENERIC_ACCELERATION_ARB :: 0x2026
|
||||
WGL_FULL_ACCELERATION_ARB :: 0x2027
|
||||
WGL_SWAP_EXCHANGE_ARB :: 0x2028
|
||||
WGL_SWAP_COPY_ARB :: 0x2029
|
||||
WGL_SWAP_UNDEFINED_ARB :: 0x202A
|
||||
WGL_TYPE_RGBA_ARB :: 0x202B
|
||||
WGL_TYPE_COLORINDEX_ARB :: 0x202C
|
||||
|
||||
wglGetPixelFormatAttribivARBType :: #type proc "c" (hdc: HDC, iPixelFormat, iLayerPlane: c_int, nAttributes: UINT, piAttributes: [^]c_int, piValues: [^]c_int) -> BOOL
|
||||
wglGetPixelFormatAttribfvARBType :: #type proc "c" (hdc: HDC, iPixelFormat, iLayerPlane: c_int, nAttributes: UINT, piAttributes: [^]c_int, pfValues: [^]f32) -> BOOL
|
||||
|
||||
// wglGetPixelFormatAttribivARB: wglGetPixelFormatAttribivARBType
|
||||
// wglGetPixelFormatAttribfvARB: wglGetPixelFormatAttribfvARBType
|
||||
|
||||
// WGL_ARB_pixel_format_float
|
||||
WGL_TYPE_RGBA_FLOAT_ARB :: 0x21A0
|
||||
@@ -153,6 +153,7 @@ BM_CLICK :: 0x00f5
|
||||
BM_GETIMAGE :: 0x00f6
|
||||
BM_SETIMAGE :: 0x00f7
|
||||
BM_SETDONTCLICK :: 0x00f8
|
||||
WM_INPUT_DEVICE_CHANGE :: 0x00fe
|
||||
WM_INPUT :: 0x00ff
|
||||
WM_KEYDOWN :: 0x0100
|
||||
WM_KEYFIRST :: 0x0100
|
||||
@@ -165,6 +166,7 @@ WM_SYSCHAR :: 0x0106
|
||||
WM_SYSDEADCHAR :: 0x0107
|
||||
WM_UNICHAR :: 0x0109
|
||||
WM_KEYLAST :: 0x0109
|
||||
UNICODE_NOCHAR :: 0xFFFF
|
||||
WM_WNT_CONVERTREQUESTEX :: 0x0109
|
||||
WM_CONVERTREQUEST :: 0x010a
|
||||
WM_CONVERTRESULT :: 0x010b
|
||||
@@ -279,6 +281,27 @@ WM_ENTERSIZEMOVE :: 0x0231
|
||||
WM_EXITSIZEMOVE :: 0x0232
|
||||
WM_DROPFILES :: 0x0233
|
||||
WM_MDIREFRESHMENU :: 0x0234
|
||||
WM_POINTERDEVICECHANGE :: 0x0238
|
||||
WM_POINTERDEVICEINRANGE :: 0x0239
|
||||
WM_POINTERDEVICEOUTOFRANGE :: 0x023a
|
||||
WM_TOUCH :: 0x0240
|
||||
WM_NCPOINTERUPDATE :: 0x0241
|
||||
WM_NCPOINTERDOWN :: 0x0242
|
||||
WM_NCPOINTERUP :: 0x0243
|
||||
WM_POINTERUPDATE :: 0x0245
|
||||
WM_POINTERDOWN :: 0x0246
|
||||
WM_POINTERUP :: 0x0247
|
||||
WM_POINTERENTER :: 0x0249
|
||||
WM_POINTERLEAVE :: 0x024a
|
||||
WM_POINTERACTIVATE :: 0x024b
|
||||
WM_POINTERCAPTURECHANGED :: 0x024c
|
||||
WM_TOUCHHITTESTING :: 0x024d
|
||||
WM_POINTERWHEEL :: 0x024e
|
||||
WM_POINTERHWHEEL :: 0x024f
|
||||
DM_POINTERHITTEST :: 0x0250
|
||||
WM_POINTERROUTEDTO :: 0x0251
|
||||
WM_POINTERROUTEDAWAY :: 0x0252
|
||||
WM_POINTERROUTEDRELEASED :: 0x0253
|
||||
WM_IME_REPORT :: 0x0280
|
||||
WM_IME_SETCONTEXT :: 0x0281
|
||||
WM_IME_NOTIFY :: 0x0282
|
||||
@@ -295,6 +318,13 @@ WM_NCMOUSEHOVER :: 0x02a0
|
||||
WM_MOUSEHOVER :: 0x02a1
|
||||
WM_NCMOUSELEAVE :: 0x02a2
|
||||
WM_MOUSELEAVE :: 0x02a3
|
||||
WM_WTSSESSION_CHANGE :: 0x02b1
|
||||
WM_TABLET_FIRST :: 0x02c0
|
||||
WM_TABLET_LAST :: 0x02df
|
||||
WM_DPICHANGED :: 0x02e0
|
||||
WM_DPICHANGED_BEFOREPARENT :: 0x02e2
|
||||
WM_DPICHANGED_AFTERPARENT :: 0x02e3
|
||||
WM_GETDPISCALEDSIZE :: 0x02e4
|
||||
WM_CUT :: 0x0300
|
||||
WM_COPY :: 0x0301
|
||||
WM_PASTE :: 0x0302
|
||||
@@ -317,6 +347,15 @@ WM_HOTKEY :: 0x0312
|
||||
WM_PRINT :: 0x0317
|
||||
WM_PRINTCLIENT :: 0x0318
|
||||
WM_APPCOMMAND :: 0x0319
|
||||
WM_THEMECHANGED :: 0x031A
|
||||
WM_CLIPBOARDUPDATE :: 0x031D
|
||||
WM_DWMCOMPOSITIONCHANGED :: 0x031E
|
||||
WM_DWMNCRENDERINGCHANGED :: 0x031F
|
||||
WM_DWMCOLORIZATIONCOLORCHANGED:: 0x0320
|
||||
WM_DWMWINDOWMAXIMIZEDCHANGE :: 0x0321
|
||||
WM_DWMSENDICONICTHUMBNAIL :: 0x0323
|
||||
WM_DWMSENDICONICLIVEPREVIEWBITMAP :: 0x0326
|
||||
WM_GETTITLEBARINFOEX :: 0x033F
|
||||
WM_HANDHELDFIRST :: 0x0358
|
||||
WM_HANDHELDLAST :: 0x035f
|
||||
WM_AFXFIRST :: 0x0360
|
||||
|
||||
@@ -1074,6 +1074,505 @@ bool check_builtin_simd_operation(CheckerContext *c, Operand *operand, Ast *call
|
||||
return false;
|
||||
}
|
||||
|
||||
bool cache_load_file_directive(CheckerContext *c, Ast *call, String const &original_string, bool err_on_not_found, LoadFileCache **cache_) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String builtin_name = bd->name.string;
|
||||
|
||||
String base_dir = dir_from_path(get_file_path_string(call->file_id));
|
||||
|
||||
BlockingMutex *ignore_mutex = nullptr;
|
||||
String path = {};
|
||||
bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
|
||||
gb_unused(ok);
|
||||
|
||||
|
||||
MUTEX_GUARD(&c->info->load_file_mutex);
|
||||
|
||||
gbFileError file_error = gbFileError_None;
|
||||
String data = {};
|
||||
|
||||
LoadFileCache **cache_ptr = string_map_get(&c->info->load_file_cache, path);
|
||||
LoadFileCache *cache = cache_ptr ? *cache_ptr : nullptr;
|
||||
if (cache) {
|
||||
file_error = cache->file_error;
|
||||
data = cache->data;
|
||||
}
|
||||
defer ({
|
||||
if (cache == nullptr) {
|
||||
LoadFileCache *new_cache = gb_alloc_item(permanent_allocator(), LoadFileCache);
|
||||
new_cache->path = path;
|
||||
new_cache->data = data;
|
||||
new_cache->file_error = file_error;
|
||||
string_map_init(&new_cache->hashes, heap_allocator(), 32);
|
||||
string_map_set(&c->info->load_file_cache, path, new_cache);
|
||||
if (cache_) *cache_ = new_cache;
|
||||
} else {
|
||||
cache->data = data;
|
||||
cache->file_error = file_error;
|
||||
if (cache_) *cache_ = cache;
|
||||
}
|
||||
});
|
||||
|
||||
char *c_str = alloc_cstring(heap_allocator(), path);
|
||||
defer (gb_free(heap_allocator(), c_str));
|
||||
|
||||
gbFile f = {};
|
||||
if (cache == nullptr) {
|
||||
file_error = gb_file_open(&f, c_str);
|
||||
}
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_error) {
|
||||
default:
|
||||
case gbFileError_Invalid:
|
||||
if (err_on_not_found) {
|
||||
error(ce->proc, "Failed to `#%.*s` file: %s; invalid file or cannot be found", LIT(builtin_name), c_str);
|
||||
}
|
||||
call->state_flags |= StateFlag_DirectiveWasFalse;
|
||||
return false;
|
||||
case gbFileError_NotExists:
|
||||
if (err_on_not_found) {
|
||||
error(ce->proc, "Failed to `#%.*s` file: %s; file cannot be found", LIT(builtin_name), c_str);
|
||||
}
|
||||
call->state_flags |= StateFlag_DirectiveWasFalse;
|
||||
return false;
|
||||
case gbFileError_Permission:
|
||||
if (err_on_not_found) {
|
||||
error(ce->proc, "Failed to `#%.*s` file: %s; file permissions problem", LIT(builtin_name), c_str);
|
||||
}
|
||||
call->state_flags |= StateFlag_DirectiveWasFalse;
|
||||
return false;
|
||||
case gbFileError_None:
|
||||
// Okay
|
||||
break;
|
||||
}
|
||||
|
||||
if (cache == nullptr) {
|
||||
isize file_size = cast(isize)gb_file_size(&f);
|
||||
if (file_size > 0) {
|
||||
u8 *ptr = cast(u8 *)gb_alloc(permanent_allocator(), file_size+1);
|
||||
gb_file_read_at(&f, ptr, file_size, 0);
|
||||
ptr[file_size] = '\0';
|
||||
data.text = ptr;
|
||||
data.len = file_size;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
bool is_valid_type_for_load(Type *type) {
|
||||
if (type == t_invalid) {
|
||||
return false;
|
||||
} else if (is_type_string(type)) {
|
||||
return true;
|
||||
} else if (is_type_slice(type) /*|| is_type_array(type) || is_type_enumerated_array(type)*/) {
|
||||
Type *elem = nullptr;
|
||||
Type *bt = base_type(type);
|
||||
if (bt->kind == Type_Slice) {
|
||||
elem = bt->Slice.elem;
|
||||
} else if (bt->kind == Type_Array) {
|
||||
elem = bt->Array.elem;
|
||||
} else if (bt->kind == Type_EnumeratedArray) {
|
||||
elem = bt->EnumeratedArray.elem;
|
||||
}
|
||||
GB_ASSERT(elem != nullptr);
|
||||
return is_type_load_safe(elem);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String name = bd->name.string;
|
||||
GB_ASSERT(name == "load");
|
||||
|
||||
if (ce->args.count != 1 && ce->args.count != 2) {
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#%.*s' expects 1 or 2 arguments, got 0", LIT(name));
|
||||
} else {
|
||||
error(ce->args[0], "'#%.*s' expects 1 or 2 arguments, got %td", LIT(name), ce->args.count);
|
||||
}
|
||||
|
||||
return LoadDirective_Error;
|
||||
}
|
||||
|
||||
Ast *arg = ce->args[0];
|
||||
Operand o = {};
|
||||
check_expr(c, &o, arg);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
error(arg, "'#%.*s' expected a constant string argument", LIT(name));
|
||||
return LoadDirective_Error;
|
||||
}
|
||||
|
||||
if (!is_type_string(o.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg, "'#%.*s' expected a constant string, got %s", LIT(name), str);
|
||||
gb_string_free(str);
|
||||
return LoadDirective_Error;
|
||||
}
|
||||
|
||||
GB_ASSERT(o.value.kind == ExactValue_String);
|
||||
|
||||
operand->type = t_u8_slice;
|
||||
if (ce->args.count == 1) {
|
||||
if (type_hint && is_valid_type_for_load(type_hint)) {
|
||||
operand->type = type_hint;
|
||||
}
|
||||
} else if (ce->args.count == 2) {
|
||||
Ast *arg_type = ce->args[1];
|
||||
Type *type = check_type(c, arg_type);
|
||||
if (type != nullptr) {
|
||||
if (is_valid_type_for_load(type)) {
|
||||
operand->type = type;
|
||||
} else {
|
||||
gbString type_str = type_to_string(type);
|
||||
error(arg_type, "'#%.*s' invalid type, expected a string, or slice of simple types, got %s", LIT(name), type_str);
|
||||
gb_string_free(type_str);
|
||||
}
|
||||
}
|
||||
} else {
|
||||
GB_PANIC("unreachable");
|
||||
}
|
||||
operand->mode = Addressing_Constant;
|
||||
|
||||
LoadFileCache *cache = nullptr;
|
||||
if (cache_load_file_directive(c, call, o.value.value_string, err_on_not_found, &cache)) {
|
||||
operand->value = exact_value_string(cache->data);
|
||||
return LoadDirective_Success;
|
||||
}
|
||||
return LoadDirective_NotFound;
|
||||
|
||||
}
|
||||
|
||||
|
||||
bool check_builtin_procedure_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String name = bd->name.string;
|
||||
if (name == "location") {
|
||||
if (ce->args.count > 1) {
|
||||
error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count);
|
||||
}
|
||||
if (ce->args.count > 0) {
|
||||
Ast *arg = ce->args[0];
|
||||
Entity *e = nullptr;
|
||||
Operand o = {};
|
||||
if (arg->kind == Ast_Ident) {
|
||||
e = check_ident(c, &o, arg, nullptr, nullptr, true);
|
||||
} else if (arg->kind == Ast_SelectorExpr) {
|
||||
e = check_selector(c, &o, arg, nullptr);
|
||||
}
|
||||
if (e == nullptr) {
|
||||
error(ce->args[0], "'#location' expected a valid entity name");
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = t_source_code_location;
|
||||
operand->mode = Addressing_Value;
|
||||
} else if (name == "load") {
|
||||
return check_load_directive(c, operand, call, type_hint, true) == LoadDirective_Success;
|
||||
} else if (name == "load_hash") {
|
||||
if (ce->args.count != 2) {
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load_hash' expects 2 argument, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load_hash' expects 2 argument, got %td", ce->args.count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *arg0 = ce->args[0];
|
||||
Ast *arg1 = ce->args[1];
|
||||
Operand o = {};
|
||||
check_expr(c, &o, arg0);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
error(arg0, "'#load_hash' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg0, "'#load_hash' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand o_hash = {};
|
||||
check_expr(c, &o_hash, arg1);
|
||||
if (o_hash.mode != Addressing_Constant) {
|
||||
error(arg1, "'#load_hash' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o_hash.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg1, "'#load_hash' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
GB_ASSERT(o.value.kind == ExactValue_String);
|
||||
GB_ASSERT(o_hash.value.kind == ExactValue_String);
|
||||
|
||||
String original_string = o.value.value_string;
|
||||
String hash_kind = o_hash.value.value_string;
|
||||
|
||||
String supported_hashes[] = {
|
||||
str_lit("adler32"),
|
||||
str_lit("crc32"),
|
||||
str_lit("crc64"),
|
||||
str_lit("fnv32"),
|
||||
str_lit("fnv64"),
|
||||
str_lit("fnv32a"),
|
||||
str_lit("fnv64a"),
|
||||
str_lit("murmur32"),
|
||||
str_lit("murmur64"),
|
||||
};
|
||||
|
||||
bool hash_found = false;
|
||||
for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
|
||||
if (supported_hashes[i] == hash_kind) {
|
||||
hash_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hash_found) {
|
||||
ERROR_BLOCK();
|
||||
error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind));
|
||||
error_line("\tAvailable hash kinds:\n");
|
||||
for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
|
||||
error_line("\t%.*s\n", LIT(supported_hashes[i]));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
LoadFileCache *cache = nullptr;
|
||||
if (cache_load_file_directive(c, call, original_string, true, &cache)) {
|
||||
MUTEX_GUARD(&c->info->load_file_mutex);
|
||||
// TODO(bill): make these procedures fast :P
|
||||
u64 hash_value = 0;
|
||||
u64 *hash_value_ptr = string_map_get(&cache->hashes, hash_kind);
|
||||
if (hash_value_ptr) {
|
||||
hash_value = *hash_value_ptr;
|
||||
} else {
|
||||
u8 *data = cache->data.text;
|
||||
isize file_size = cache->data.len;
|
||||
if (hash_kind == "adler32") {
|
||||
hash_value = gb_adler32(data, file_size);
|
||||
} else if (hash_kind == "crc32") {
|
||||
hash_value = gb_crc32(data, file_size);
|
||||
} else if (hash_kind == "crc64") {
|
||||
hash_value = gb_crc64(data, file_size);
|
||||
} else if (hash_kind == "fnv32") {
|
||||
hash_value = gb_fnv32(data, file_size);
|
||||
} else if (hash_kind == "fnv64") {
|
||||
hash_value = gb_fnv64(data, file_size);
|
||||
} else if (hash_kind == "fnv32a") {
|
||||
hash_value = fnv32a(data, file_size);
|
||||
} else if (hash_kind == "fnv64a") {
|
||||
hash_value = fnv64a(data, file_size);
|
||||
} else if (hash_kind == "murmur32") {
|
||||
hash_value = gb_murmur32(data, file_size);
|
||||
} else if (hash_kind == "murmur64") {
|
||||
hash_value = gb_murmur64(data, file_size);
|
||||
} else {
|
||||
compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));
|
||||
}
|
||||
string_map_set(&cache->hashes, hash_kind, hash_value);
|
||||
}
|
||||
|
||||
operand->type = t_untyped_integer;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_u64(hash_value);
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
} else if (name == "load_or") {
|
||||
warning(call, "'#load_or' is deprecated in favour of '#load(path) or_else default'");
|
||||
|
||||
if (ce->args.count != 2) {
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load_or' expects 2 arguments, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *arg = ce->args[0];
|
||||
Operand o = {};
|
||||
check_expr(c, &o, arg);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
error(arg, "'#load_or' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg, "'#load_or' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *default_arg = ce->args[1];
|
||||
Operand default_op = {};
|
||||
check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice);
|
||||
if (default_op.mode != Addressing_Constant) {
|
||||
error(arg, "'#load_or' expected a constant '[]byte' argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!are_types_identical(base_type(default_op.type), t_u8_slice)) {
|
||||
gbString str = type_to_string(default_op.type);
|
||||
error(arg, "'#load_or' expected a constant '[]byte', got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
GB_ASSERT(o.value.kind == ExactValue_String);
|
||||
String original_string = o.value.value_string;
|
||||
|
||||
operand->type = t_u8_slice;
|
||||
operand->mode = Addressing_Constant;
|
||||
LoadFileCache *cache = nullptr;
|
||||
if (cache_load_file_directive(c, call, original_string, false, &cache)) {
|
||||
operand->value = exact_value_string(cache->data);
|
||||
} else {
|
||||
operand->value = default_op.value;
|
||||
}
|
||||
} else if (name == "assert") {
|
||||
if (ce->args.count != 1 && ce->args.count != 2) {
|
||||
error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
|
||||
gbString str = expr_to_string(ce->args[0]);
|
||||
error(call, "'%s' is not a constant boolean", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
if (ce->args.count == 2) {
|
||||
Ast *arg = unparen_expr(ce->args[1]);
|
||||
if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
|
||||
gbString str = expr_to_string(arg);
|
||||
error(call, "'%s' is not a constant string", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!operand->value.value_bool) {
|
||||
gbString arg1 = expr_to_string(ce->args[0]);
|
||||
gbString arg2 = {};
|
||||
|
||||
if (ce->args.count == 1) {
|
||||
error(call, "Compile time assertion: %s", arg1);
|
||||
} else {
|
||||
arg2 = expr_to_string(ce->args[1]);
|
||||
error(call, "Compile time assertion: %s (%s)", arg1, arg2);
|
||||
}
|
||||
|
||||
if (c->proc_name != "") {
|
||||
gbString str = type_to_string(c->curr_proc_sig);
|
||||
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
|
||||
gb_string_free(arg1);
|
||||
if (ce->args.count == 2) {
|
||||
gb_string_free(arg2);
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = t_untyped_bool;
|
||||
operand->mode = Addressing_Constant;
|
||||
} else if (name == "panic") {
|
||||
if (ce->args.count != 1) {
|
||||
error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) {
|
||||
gbString str = expr_to_string(ce->args[0]);
|
||||
error(call, "'%s' is not a constant string", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
error(call, "Compile time panic: %.*s", LIT(operand->value.value_string));
|
||||
if (c->proc_name != "") {
|
||||
gbString str = type_to_string(c->curr_proc_sig);
|
||||
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
operand->type = t_invalid;
|
||||
operand->mode = Addressing_NoValue;
|
||||
} else if (name == "defined") {
|
||||
if (ce->args.count != 1) {
|
||||
error(call, "'#defined' expects 1 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
Ast *arg = unparen_expr(ce->args[0]);
|
||||
if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) {
|
||||
error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind]));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c->curr_proc_decl == nullptr) {
|
||||
error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_defined = check_identifier_exists(c->scope, arg);
|
||||
gb_unused(is_defined);
|
||||
operand->type = t_untyped_bool;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_bool(false);
|
||||
|
||||
} else if (name == "config") {
|
||||
if (ce->args.count != 2) {
|
||||
error(call, "'#config' expects 2 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
Ast *arg = unparen_expr(ce->args[0]);
|
||||
if (arg == nullptr || arg->kind != Ast_Ident) {
|
||||
error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind]));
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *def_arg = unparen_expr(ce->args[1]);
|
||||
|
||||
Operand def = {};
|
||||
check_expr(c, &def, def_arg);
|
||||
if (def.mode != Addressing_Constant) {
|
||||
error(def_arg, "'#config' default value must be a constant");
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = arg->Ident.token.string;
|
||||
|
||||
|
||||
operand->type = def.type;
|
||||
operand->mode = def.mode;
|
||||
operand->value = def.value;
|
||||
|
||||
Entity *found = scope_lookup_current(config_pkg->scope, name);
|
||||
if (found != nullptr) {
|
||||
if (found->kind != Entity_Constant) {
|
||||
error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name));
|
||||
} else {
|
||||
operand->type = found->type;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = found->Constant.value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(call, "Unknown directive call: #%.*s", LIT(name));
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32 id, Type *type_hint) {
|
||||
ast_node(ce, CallExpr, call);
|
||||
@@ -1186,458 +1685,8 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
|
||||
mpmc_enqueue(&c->info->intrinsics_entry_point_usage, call);
|
||||
break;
|
||||
|
||||
case BuiltinProc_DIRECTIVE: {
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String name = bd->name.string;
|
||||
if (name == "location") {
|
||||
if (ce->args.count > 1) {
|
||||
error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count);
|
||||
}
|
||||
if (ce->args.count > 0) {
|
||||
Ast *arg = ce->args[0];
|
||||
Entity *e = nullptr;
|
||||
Operand o = {};
|
||||
if (arg->kind == Ast_Ident) {
|
||||
e = check_ident(c, &o, arg, nullptr, nullptr, true);
|
||||
} else if (arg->kind == Ast_SelectorExpr) {
|
||||
e = check_selector(c, &o, arg, nullptr);
|
||||
}
|
||||
if (e == nullptr) {
|
||||
error(ce->args[0], "'#location' expected a valid entity name");
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = t_source_code_location;
|
||||
operand->mode = Addressing_Value;
|
||||
} else if (name == "load") {
|
||||
if (ce->args.count != 1) {
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load' expects 1 argument, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load' expects 1 argument, got %td", ce->args.count);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *arg = ce->args[0];
|
||||
Operand o = {};
|
||||
check_expr(c, &o, arg);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
error(arg, "'#load' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg, "'#load' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
GB_ASSERT(o.value.kind == ExactValue_String);
|
||||
String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
|
||||
String original_string = o.value.value_string;
|
||||
|
||||
|
||||
BlockingMutex *ignore_mutex = nullptr;
|
||||
String path = {};
|
||||
bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
|
||||
gb_unused(ok);
|
||||
|
||||
char *c_str = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_str));
|
||||
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
default:
|
||||
case gbFileError_Invalid:
|
||||
error(ce->proc, "Failed to `#load` file: %s; invalid file or cannot be found", c_str);
|
||||
return false;
|
||||
case gbFileError_NotExists:
|
||||
error(ce->proc, "Failed to `#load` file: %s; file cannot be found", c_str);
|
||||
return false;
|
||||
case gbFileError_Permission:
|
||||
error(ce->proc, "Failed to `#load` file: %s; file permissions problem", c_str);
|
||||
return false;
|
||||
case gbFileError_None:
|
||||
// Okay
|
||||
break;
|
||||
}
|
||||
|
||||
String result = {};
|
||||
isize file_size = cast(isize)gb_file_size(&f);
|
||||
if (file_size > 0) {
|
||||
u8 *data = cast(u8 *)gb_alloc(a, file_size+1);
|
||||
gb_file_read_at(&f, data, file_size, 0);
|
||||
data[file_size] = '\0';
|
||||
result.text = data;
|
||||
result.len = file_size;
|
||||
}
|
||||
|
||||
operand->type = t_u8_slice;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_string(result);
|
||||
|
||||
} else if (name == "load_hash") {
|
||||
if (ce->args.count != 2) {
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load_hash' expects 2 argument, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load_hash' expects 2 argument, got %td", ce->args.count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *arg0 = ce->args[0];
|
||||
Ast *arg1 = ce->args[1];
|
||||
Operand o = {};
|
||||
check_expr(c, &o, arg0);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
error(arg0, "'#load_hash' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg0, "'#load_hash' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
Operand o_hash = {};
|
||||
check_expr(c, &o_hash, arg1);
|
||||
if (o_hash.mode != Addressing_Constant) {
|
||||
error(arg1, "'#load_hash' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o_hash.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg1, "'#load_hash' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
GB_ASSERT(o.value.kind == ExactValue_String);
|
||||
GB_ASSERT(o_hash.value.kind == ExactValue_String);
|
||||
|
||||
String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
|
||||
String original_string = o.value.value_string;
|
||||
String hash_kind = o_hash.value.value_string;
|
||||
|
||||
String supported_hashes[] = {
|
||||
str_lit("adler32"),
|
||||
str_lit("crc32"),
|
||||
str_lit("crc64"),
|
||||
str_lit("fnv32"),
|
||||
str_lit("fnv64"),
|
||||
str_lit("fnv32a"),
|
||||
str_lit("fnv64a"),
|
||||
str_lit("murmur32"),
|
||||
str_lit("murmur64"),
|
||||
};
|
||||
|
||||
bool hash_found = false;
|
||||
for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
|
||||
if (supported_hashes[i] == hash_kind) {
|
||||
hash_found = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (!hash_found) {
|
||||
ERROR_BLOCK();
|
||||
error(ce->proc, "Invalid hash kind passed to `#load_hash`, got: %.*s", LIT(hash_kind));
|
||||
error_line("\tAvailable hash kinds:\n");
|
||||
for (isize i = 0; i < gb_count_of(supported_hashes); i++) {
|
||||
error_line("\t%.*s\n", LIT(supported_hashes[i]));
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
|
||||
BlockingMutex *ignore_mutex = nullptr;
|
||||
String path = {};
|
||||
bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
|
||||
gb_unused(ok);
|
||||
|
||||
char *c_str = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_str));
|
||||
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
switch (file_err) {
|
||||
default:
|
||||
case gbFileError_Invalid:
|
||||
error(ce->proc, "Failed to `#load_hash` file: %s; invalid file or cannot be found", c_str);
|
||||
return false;
|
||||
case gbFileError_NotExists:
|
||||
error(ce->proc, "Failed to `#load_hash` file: %s; file cannot be found", c_str);
|
||||
return false;
|
||||
case gbFileError_Permission:
|
||||
error(ce->proc, "Failed to `#load_hash` file: %s; file permissions problem", c_str);
|
||||
return false;
|
||||
case gbFileError_None:
|
||||
// Okay
|
||||
break;
|
||||
}
|
||||
|
||||
// TODO(bill): make these procedures fast :P
|
||||
|
||||
u64 hash_value = 0;
|
||||
String result = {};
|
||||
isize file_size = cast(isize)gb_file_size(&f);
|
||||
if (file_size > 0) {
|
||||
u8 *data = cast(u8 *)gb_alloc(a, file_size);
|
||||
gb_file_read_at(&f, data, file_size, 0);
|
||||
if (hash_kind == "adler32") {
|
||||
hash_value = gb_adler32(data, file_size);
|
||||
} else if (hash_kind == "crc32") {
|
||||
hash_value = gb_crc32(data, file_size);
|
||||
} else if (hash_kind == "crc64") {
|
||||
hash_value = gb_crc64(data, file_size);
|
||||
} else if (hash_kind == "fnv32") {
|
||||
hash_value = gb_fnv32(data, file_size);
|
||||
} else if (hash_kind == "fnv64") {
|
||||
hash_value = gb_fnv64(data, file_size);
|
||||
} else if (hash_kind == "fnv32a") {
|
||||
hash_value = fnv32a(data, file_size);
|
||||
} else if (hash_kind == "fnv64a") {
|
||||
hash_value = fnv64a(data, file_size);
|
||||
} else if (hash_kind == "murmur32") {
|
||||
hash_value = gb_murmur32(data, file_size);
|
||||
} else if (hash_kind == "murmur64") {
|
||||
hash_value = gb_murmur64(data, file_size);
|
||||
} else {
|
||||
compiler_error("unhandled hash kind: %.*s", LIT(hash_kind));
|
||||
}
|
||||
gb_free(a, data);
|
||||
}
|
||||
|
||||
operand->type = t_untyped_integer;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_u64(hash_value);
|
||||
|
||||
} else if (name == "load_or") {
|
||||
if (ce->args.count != 2) {
|
||||
if (ce->args.count == 0) {
|
||||
error(ce->close, "'#load_or' expects 2 arguments, got 0");
|
||||
} else {
|
||||
error(ce->args[0], "'#load_or' expects 2 arguments, got %td", ce->args.count);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *arg = ce->args[0];
|
||||
Operand o = {};
|
||||
check_expr(c, &o, arg);
|
||||
if (o.mode != Addressing_Constant) {
|
||||
error(arg, "'#load_or' expected a constant string argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!is_type_string(o.type)) {
|
||||
gbString str = type_to_string(o.type);
|
||||
error(arg, "'#load_or' expected a constant string, got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *default_arg = ce->args[1];
|
||||
Operand default_op = {};
|
||||
check_expr_with_type_hint(c, &default_op, default_arg, t_u8_slice);
|
||||
if (default_op.mode != Addressing_Constant) {
|
||||
error(arg, "'#load_or' expected a constant '[]byte' argument");
|
||||
return false;
|
||||
}
|
||||
|
||||
if (!are_types_identical(base_type(default_op.type), t_u8_slice)) {
|
||||
gbString str = type_to_string(default_op.type);
|
||||
error(arg, "'#load_or' expected a constant '[]byte', got %s", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
|
||||
gbAllocator a = heap_allocator();
|
||||
|
||||
GB_ASSERT(o.value.kind == ExactValue_String);
|
||||
String base_dir = dir_from_path(get_file_path_string(bd->token.pos.file_id));
|
||||
String original_string = o.value.value_string;
|
||||
|
||||
|
||||
BlockingMutex *ignore_mutex = nullptr;
|
||||
String path = {};
|
||||
bool ok = determine_path_from_string(ignore_mutex, call, base_dir, original_string, &path);
|
||||
gb_unused(ok);
|
||||
|
||||
char *c_str = alloc_cstring(a, path);
|
||||
defer (gb_free(a, c_str));
|
||||
|
||||
|
||||
gbFile f = {};
|
||||
gbFileError file_err = gb_file_open(&f, c_str);
|
||||
defer (gb_file_close(&f));
|
||||
|
||||
operand->type = t_u8_slice;
|
||||
operand->mode = Addressing_Constant;
|
||||
if (file_err == gbFileError_None) {
|
||||
String result = {};
|
||||
isize file_size = cast(isize)gb_file_size(&f);
|
||||
if (file_size > 0) {
|
||||
u8 *data = cast(u8 *)gb_alloc(a, file_size+1);
|
||||
gb_file_read_at(&f, data, file_size, 0);
|
||||
data[file_size] = '\0';
|
||||
result.text = data;
|
||||
result.len = file_size;
|
||||
}
|
||||
|
||||
operand->value = exact_value_string(result);
|
||||
} else {
|
||||
operand->value = default_op.value;
|
||||
}
|
||||
|
||||
} else if (name == "assert") {
|
||||
if (ce->args.count != 1 && ce->args.count != 2) {
|
||||
error(call, "'#assert' expects either 1 or 2 arguments, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_boolean(operand->type) || operand->mode != Addressing_Constant) {
|
||||
gbString str = expr_to_string(ce->args[0]);
|
||||
error(call, "'%s' is not a constant boolean", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
if (ce->args.count == 2) {
|
||||
Ast *arg = unparen_expr(ce->args[1]);
|
||||
if (arg == nullptr || arg->kind != Ast_BasicLit || arg->BasicLit.token.kind != Token_String) {
|
||||
gbString str = expr_to_string(arg);
|
||||
error(call, "'%s' is not a constant string", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (!operand->value.value_bool) {
|
||||
gbString arg1 = expr_to_string(ce->args[0]);
|
||||
gbString arg2 = {};
|
||||
|
||||
if (ce->args.count == 1) {
|
||||
error(call, "Compile time assertion: %s", arg1);
|
||||
} else {
|
||||
arg2 = expr_to_string(ce->args[1]);
|
||||
error(call, "Compile time assertion: %s (%s)", arg1, arg2);
|
||||
}
|
||||
|
||||
if (c->proc_name != "") {
|
||||
gbString str = type_to_string(c->curr_proc_sig);
|
||||
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
|
||||
gb_string_free(arg1);
|
||||
if (ce->args.count == 2) {
|
||||
gb_string_free(arg2);
|
||||
}
|
||||
}
|
||||
|
||||
operand->type = t_untyped_bool;
|
||||
operand->mode = Addressing_Constant;
|
||||
} else if (name == "panic") {
|
||||
if (ce->args.count != 1) {
|
||||
error(call, "'#panic' expects 1 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
if (!is_type_string(operand->type) && operand->mode != Addressing_Constant) {
|
||||
gbString str = expr_to_string(ce->args[0]);
|
||||
error(call, "'%s' is not a constant string", str);
|
||||
gb_string_free(str);
|
||||
return false;
|
||||
}
|
||||
error(call, "Compile time panic: %.*s", LIT(operand->value.value_string));
|
||||
if (c->proc_name != "") {
|
||||
gbString str = type_to_string(c->curr_proc_sig);
|
||||
error_line("\tCalled within '%.*s' :: %s\n", LIT(c->proc_name), str);
|
||||
gb_string_free(str);
|
||||
}
|
||||
operand->type = t_invalid;
|
||||
operand->mode = Addressing_NoValue;
|
||||
} else if (name == "defined") {
|
||||
if (ce->args.count != 1) {
|
||||
error(call, "'#defined' expects 1 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
Ast *arg = unparen_expr(ce->args[0]);
|
||||
if (arg == nullptr || (arg->kind != Ast_Ident && arg->kind != Ast_SelectorExpr)) {
|
||||
error(call, "'#defined' expects an identifier or selector expression, got %.*s", LIT(ast_strings[arg->kind]));
|
||||
return false;
|
||||
}
|
||||
|
||||
if (c->curr_proc_decl == nullptr) {
|
||||
error(call, "'#defined' is only allowed within a procedure, prefer the replacement '#config(NAME, default_value)'");
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_defined = check_identifier_exists(c->scope, arg);
|
||||
gb_unused(is_defined);
|
||||
operand->type = t_untyped_bool;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = exact_value_bool(false);
|
||||
|
||||
} else if (name == "config") {
|
||||
if (ce->args.count != 2) {
|
||||
error(call, "'#config' expects 2 argument, got %td", ce->args.count);
|
||||
return false;
|
||||
}
|
||||
Ast *arg = unparen_expr(ce->args[0]);
|
||||
if (arg == nullptr || arg->kind != Ast_Ident) {
|
||||
error(call, "'#config' expects an identifier, got %.*s", LIT(ast_strings[arg->kind]));
|
||||
return false;
|
||||
}
|
||||
|
||||
Ast *def_arg = unparen_expr(ce->args[1]);
|
||||
|
||||
Operand def = {};
|
||||
check_expr(c, &def, def_arg);
|
||||
if (def.mode != Addressing_Constant) {
|
||||
error(def_arg, "'#config' default value must be a constant");
|
||||
return false;
|
||||
}
|
||||
|
||||
String name = arg->Ident.token.string;
|
||||
|
||||
|
||||
operand->type = def.type;
|
||||
operand->mode = def.mode;
|
||||
operand->value = def.value;
|
||||
|
||||
Entity *found = scope_lookup_current(config_pkg->scope, name);
|
||||
if (found != nullptr) {
|
||||
if (found->kind != Entity_Constant) {
|
||||
error(arg, "'#config' entity '%.*s' found but expected a constant", LIT(name));
|
||||
} else {
|
||||
operand->type = found->type;
|
||||
operand->mode = Addressing_Constant;
|
||||
operand->value = found->Constant.value;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
error(call, "Unknown directive call: #%.*s", LIT(name));
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
case BuiltinProc_DIRECTIVE:
|
||||
return check_builtin_procedure_directive(c, operand, call, type_hint);
|
||||
|
||||
case BuiltinProc_len:
|
||||
check_expr_or_type(c, operand, ce->args[0]);
|
||||
|
||||
@@ -121,6 +121,28 @@ void check_or_return_split_types(CheckerContext *c, Operand *x, String const &na
|
||||
|
||||
bool is_diverging_expr(Ast *expr);
|
||||
|
||||
|
||||
enum LoadDirectiveResult {
|
||||
LoadDirective_Success = 0,
|
||||
LoadDirective_Error = 1,
|
||||
LoadDirective_NotFound = 2,
|
||||
};
|
||||
|
||||
bool is_load_directive_call(Ast *call) {
|
||||
call = unparen_expr(call);
|
||||
if (call->kind != Ast_CallExpr) {
|
||||
return false;
|
||||
}
|
||||
ast_node(ce, CallExpr, call);
|
||||
if (ce->proc->kind != Ast_BasicDirective) {
|
||||
return false;
|
||||
}
|
||||
ast_node(bd, BasicDirective, ce->proc);
|
||||
String name = bd->name.string;
|
||||
return name == "load";
|
||||
}
|
||||
LoadDirectiveResult check_load_directive(CheckerContext *c, Operand *operand, Ast *call, Type *type_hint, bool err_on_not_found);
|
||||
|
||||
void check_did_you_mean_print(DidYouMeanAnswers *d, char const *prefix = "") {
|
||||
auto results = did_you_mean_results(d);
|
||||
if (results.count != 0) {
|
||||
@@ -2505,8 +2527,17 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
|
||||
x->expr->tav.is_lhs = true;
|
||||
}
|
||||
x->mode = Addressing_Value;
|
||||
if (type_hint && is_type_integer(type_hint)) {
|
||||
x->type = type_hint;
|
||||
if (type_hint) {
|
||||
if (is_type_integer(type_hint)) {
|
||||
x->type = type_hint;
|
||||
} else {
|
||||
gbString x_str = expr_to_string(x->expr);
|
||||
gbString to_type = type_to_string(type_hint);
|
||||
error(node, "Conversion of shifted operand '%s' to '%s' is not allowed", x_str, to_type);
|
||||
gb_string_free(x_str);
|
||||
gb_string_free(to_type);
|
||||
x->mode = Addressing_Invalid;
|
||||
}
|
||||
}
|
||||
// x->value = x_val;
|
||||
return;
|
||||
@@ -2522,7 +2553,7 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
|
||||
// TODO(bill): Should we support shifts for fixed arrays and #simd vectors?
|
||||
|
||||
if (!is_type_integer(x->type)) {
|
||||
gbString err_str = expr_to_string(y->expr);
|
||||
gbString err_str = expr_to_string(x->expr);
|
||||
error(node, "Shift operand '%s' must be an integer", err_str);
|
||||
gb_string_free(err_str);
|
||||
x->mode = Addressing_Invalid;
|
||||
@@ -7398,9 +7429,59 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
|
||||
String name = oe->token.string;
|
||||
Ast *arg = oe->x;
|
||||
Ast *default_value = oe->y;
|
||||
|
||||
Operand x = {};
|
||||
Operand y = {};
|
||||
|
||||
// NOTE(bill, 2022-08-11): edge case to handle #load(path) or_else default
|
||||
if (is_load_directive_call(arg)) {
|
||||
LoadDirectiveResult res = check_load_directive(c, &x, arg, type_hint, false);
|
||||
|
||||
// Allow for chaining of '#load(path) or_else #load(path)'
|
||||
if (!(is_load_directive_call(default_value) && res == LoadDirective_Success)) {
|
||||
bool y_is_diverging = false;
|
||||
check_expr_base(c, &y, default_value, x.type);
|
||||
switch (y.mode) {
|
||||
case Addressing_NoValue:
|
||||
if (is_diverging_expr(y.expr)) {
|
||||
// Allow
|
||||
y.mode = Addressing_Value;
|
||||
y_is_diverging = true;
|
||||
} else {
|
||||
error_operand_no_value(&y);
|
||||
y.mode = Addressing_Invalid;
|
||||
}
|
||||
break;
|
||||
case Addressing_Type:
|
||||
error_operand_not_expression(&y);
|
||||
y.mode = Addressing_Invalid;
|
||||
break;
|
||||
}
|
||||
|
||||
if (y.mode == Addressing_Invalid) {
|
||||
o->mode = Addressing_Value;
|
||||
o->type = t_invalid;
|
||||
o->expr = node;
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
if (!y_is_diverging) {
|
||||
check_assignment(c, &y, x.type, name);
|
||||
if (y.mode != Addressing_Constant) {
|
||||
error(y.expr, "expected a constant expression on the right-hand side of 'or_else' in conjuction with '#load'");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (res == LoadDirective_Success) {
|
||||
*o = x;
|
||||
} else {
|
||||
*o = y;
|
||||
}
|
||||
o->expr = node;
|
||||
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
check_multi_expr_with_type_hint(c, &x, arg, type_hint);
|
||||
if (x.mode == Addressing_Invalid) {
|
||||
o->mode = Addressing_Value;
|
||||
@@ -7408,7 +7489,6 @@ ExprKind check_or_else_expr(CheckerContext *c, Operand *o, Ast *node, Type *type
|
||||
o->expr = node;
|
||||
return Expr_Expr;
|
||||
}
|
||||
|
||||
bool y_is_diverging = false;
|
||||
check_expr_base(c, &y, default_value, x.type);
|
||||
switch (y.mode) {
|
||||
|
||||
@@ -1170,6 +1170,8 @@ void init_checker_info(CheckerInfo *i) {
|
||||
|
||||
mutex_init(&i->objc_types_mutex);
|
||||
map_init(&i->objc_msgSend_types, a);
|
||||
mutex_init(&i->load_file_mutex);
|
||||
string_map_init(&i->load_file_cache, a);
|
||||
}
|
||||
|
||||
void destroy_checker_info(CheckerInfo *i) {
|
||||
@@ -1205,6 +1207,8 @@ void destroy_checker_info(CheckerInfo *i) {
|
||||
|
||||
mutex_destroy(&i->objc_types_mutex);
|
||||
map_destroy(&i->objc_msgSend_types);
|
||||
mutex_init(&i->load_file_mutex);
|
||||
string_map_destroy(&i->load_file_cache);
|
||||
}
|
||||
|
||||
CheckerContext make_checker_context(Checker *c) {
|
||||
|
||||
@@ -287,6 +287,12 @@ struct ObjcMsgData {
|
||||
ObjcMsgKind kind;
|
||||
Type *proc_type;
|
||||
};
|
||||
struct LoadFileCache {
|
||||
String path;
|
||||
gbFileError file_error;
|
||||
String data;
|
||||
StringMap<u64> hashes;
|
||||
};
|
||||
|
||||
// CheckerInfo stores all the symbol information for a type-checked program
|
||||
struct CheckerInfo {
|
||||
@@ -363,6 +369,9 @@ struct CheckerInfo {
|
||||
|
||||
BlockingMutex objc_types_mutex;
|
||||
PtrMap<Ast *, ObjcMsgData> objc_msgSend_types;
|
||||
|
||||
BlockingMutex load_file_mutex;
|
||||
StringMap<LoadFileCache *> load_file_cache;
|
||||
};
|
||||
|
||||
struct CheckerContext {
|
||||
|
||||
@@ -223,7 +223,7 @@ i64 lb_sizeof(LLVMTypeRef type) {
|
||||
break;
|
||||
case LLVMArrayTypeKind:
|
||||
{
|
||||
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||
LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
|
||||
i64 elem_size = lb_sizeof(elem);
|
||||
i64 count = LLVMGetArrayLength(type);
|
||||
i64 size = count * elem_size;
|
||||
@@ -235,7 +235,7 @@ i64 lb_sizeof(LLVMTypeRef type) {
|
||||
return 8;
|
||||
case LLVMVectorTypeKind:
|
||||
{
|
||||
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
|
||||
i64 elem_size = lb_sizeof(elem);
|
||||
i64 count = LLVMGetVectorSize(type);
|
||||
i64 size = count * elem_size;
|
||||
@@ -283,14 +283,14 @@ i64 lb_alignof(LLVMTypeRef type) {
|
||||
}
|
||||
break;
|
||||
case LLVMArrayTypeKind:
|
||||
return lb_alignof(LLVMGetElementType(type));
|
||||
return lb_alignof(OdinLLVMGetArrayElementType(type));
|
||||
|
||||
case LLVMX86_MMXTypeKind:
|
||||
return 8;
|
||||
case LLVMVectorTypeKind:
|
||||
{
|
||||
// TODO(bill): This appears to be correct but LLVM isn't necessarily "great" with regards to documentation
|
||||
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
|
||||
i64 elem_size = lb_sizeof(elem);
|
||||
i64 count = LLVMGetVectorSize(type);
|
||||
i64 size = count * elem_size;
|
||||
@@ -793,7 +793,7 @@ namespace lbAbiAmd64SysV {
|
||||
case LLVMArrayTypeKind:
|
||||
{
|
||||
i64 len = LLVMGetArrayLength(t);
|
||||
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||
LLVMTypeRef elem = OdinLLVMGetArrayElementType(t);
|
||||
i64 elem_sz = lb_sizeof(elem);
|
||||
for (i64 i = 0; i < len; i++) {
|
||||
classify_with(elem, cls, ix, off + i*elem_sz);
|
||||
@@ -803,7 +803,7 @@ namespace lbAbiAmd64SysV {
|
||||
case LLVMVectorTypeKind:
|
||||
{
|
||||
i64 len = LLVMGetVectorSize(t);
|
||||
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(t);
|
||||
i64 elem_sz = lb_sizeof(elem);
|
||||
LLVMTypeKind elem_kind = LLVMGetTypeKind(elem);
|
||||
RegClass reg = RegClass_NoClass;
|
||||
@@ -913,7 +913,7 @@ namespace lbAbiArm64 {
|
||||
if (len == 0) {
|
||||
return false;
|
||||
}
|
||||
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||
LLVMTypeRef elem = OdinLLVMGetArrayElementType(type);
|
||||
LLVMTypeRef base_type = nullptr;
|
||||
unsigned member_count = 0;
|
||||
if (is_homogenous_aggregate(c, elem, &base_type, &member_count)) {
|
||||
@@ -1129,7 +1129,7 @@ namespace lbAbiWasm {
|
||||
}
|
||||
if (sz <= MAX_DIRECT_STRUCT_SIZE) {
|
||||
if (kind == LLVMArrayTypeKind) {
|
||||
if (is_basic_register_type(LLVMGetElementType(type))) {
|
||||
if (is_basic_register_type(OdinLLVMGetArrayElementType(type))) {
|
||||
return true;
|
||||
}
|
||||
} else if (kind == LLVMStructTypeKind) {
|
||||
|
||||
@@ -739,11 +739,11 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
|
||||
lb_begin_procedure_body(p);
|
||||
|
||||
if (startup_type_info) {
|
||||
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, startup_type_info->type), startup_type_info->value, nullptr, 0, "");
|
||||
OdinLLVMBuildCall(p, {startup_type_info->value, startup_type_info->type}, nullptr, 0);
|
||||
}
|
||||
|
||||
if (objc_names) {
|
||||
LLVMBuildCall2(p->builder, lb_type_internal_for_procedures_raw(main_module, objc_names->type), objc_names->value, nullptr, 0, "");
|
||||
OdinLLVMBuildCall(p, {objc_names->value, objc_names->type}, nullptr, 0);
|
||||
}
|
||||
|
||||
for_array(i, global_variables) {
|
||||
@@ -780,6 +780,10 @@ lbProcedure *lb_create_startup_runtime(lbModule *main_module, lbProcedure *start
|
||||
var->init = init;
|
||||
} else if (lb_is_const_or_global(init)) {
|
||||
if (!var->is_initialized) {
|
||||
if (is_type_proc(init.type)) {
|
||||
LLVMTypeRef global_type = llvm_addr_type(p->module, var->var);
|
||||
init.value = LLVMConstPointerCast(init.value, global_type);
|
||||
}
|
||||
LLVMSetInitializer(var->var.value, init.value);
|
||||
var->is_initialized = true;
|
||||
continue;
|
||||
@@ -1649,6 +1653,10 @@ void lb_generate_code(lbGenerator *gen) {
|
||||
if (tav.value.kind != ExactValue_Invalid) {
|
||||
ExactValue v = tav.value;
|
||||
lbValue init = lb_const_value(m, tav.type, v);
|
||||
if (is_type_proc(init.type)) {
|
||||
LLVMTypeRef global_type = llvm_addr_type(m, var.var);
|
||||
init.value = LLVMConstPointerCast(init.value, global_type);
|
||||
}
|
||||
LLVMSetInitializer(g.value, init.value);
|
||||
var.is_initialized = true;
|
||||
}
|
||||
|
||||
@@ -357,8 +357,9 @@ lbValue lb_build_expr(lbProcedure *p, Ast *expr);
|
||||
lbAddr lb_build_addr(lbProcedure *p, Ast *expr);
|
||||
void lb_build_stmt_list(lbProcedure *p, Array<Ast *> const &stmts);
|
||||
|
||||
lbValue lb_build_gep(lbProcedure *p, lbValue const &value, i32 index) ;
|
||||
|
||||
lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index);
|
||||
lbValue lb_emit_epi(lbModule *m, lbValue const &value, isize index);
|
||||
lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index);
|
||||
lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index);
|
||||
lbValue lb_emit_struct_ev(lbProcedure *p, lbValue s, i32 index);
|
||||
lbValue lb_emit_array_epi(lbProcedure *p, lbValue value, isize index);
|
||||
@@ -507,6 +508,9 @@ i64 lb_max_zero_init_size(void) {
|
||||
return cast(i64)(4*build_context.word_size);
|
||||
}
|
||||
|
||||
LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type);
|
||||
LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type);
|
||||
|
||||
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
|
||||
#define LB_STARTUP_TYPE_INFO_PROC_NAME "__$startup_type_info"
|
||||
#define LB_TYPE_INFO_DATA_NAME "__$type_info_data"
|
||||
|
||||
@@ -10,11 +10,12 @@ bool lb_is_const(lbValue value) {
|
||||
return false;
|
||||
}
|
||||
|
||||
// TODO remove use of LLVMGetElementType
|
||||
bool lb_is_const_or_global(lbValue value) {
|
||||
if (lb_is_const(value)) {
|
||||
return true;
|
||||
}
|
||||
// TODO remove use of LLVMGetElementType
|
||||
#if 0
|
||||
if (LLVMGetValueKind(value.value) == LLVMGlobalVariableValueKind) {
|
||||
LLVMTypeRef t = LLVMGetElementType(LLVMTypeOf(value.value));
|
||||
if (!lb_is_type_kind(t, LLVMPointerTypeKind)) {
|
||||
@@ -23,6 +24,7 @@ bool lb_is_const_or_global(lbValue value) {
|
||||
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||
return lb_is_type_kind(elem, LLVMFunctionTypeKind);
|
||||
}
|
||||
#endif
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -389,8 +391,8 @@ lbValue lb_const_value(lbModule *m, Type *type, ExactValue value, bool allow_loc
|
||||
|
||||
if (is_type_slice(type)) {
|
||||
if (value.kind == ExactValue_String) {
|
||||
GB_ASSERT(is_type_u8_slice(type));
|
||||
res.value = lb_find_or_add_entity_string_byte_slice(m, value.value_string).value;
|
||||
GB_ASSERT(is_type_slice(type));
|
||||
res.value = lb_find_or_add_entity_string_byte_slice_with_type(m, value.value_string, original_type).value;
|
||||
return res;
|
||||
} else {
|
||||
ast_node(cl, CompoundLit, value.value_compound);
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -175,7 +175,8 @@ struct lbLoopData {
|
||||
struct lbCompoundLitElemTempData {
|
||||
Ast * expr;
|
||||
lbValue value;
|
||||
i32 elem_index;
|
||||
i64 elem_index;
|
||||
i64 elem_length;
|
||||
lbValue gep;
|
||||
};
|
||||
|
||||
@@ -212,6 +213,45 @@ void lb_loop_end(lbProcedure *p, lbLoopData const &data) {
|
||||
}
|
||||
|
||||
|
||||
// This emits a GEP at 0, index
|
||||
lbValue lb_emit_epi(lbProcedure *p, lbValue const &value, isize index) {
|
||||
GB_ASSERT(is_type_pointer(value.type));
|
||||
Type *type = type_deref(value.type);
|
||||
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(lb_type(p->module, t_int), 0, false),
|
||||
LLVMConstInt(lb_type(p->module, t_int), cast(unsigned long long)index, false),
|
||||
};
|
||||
LLVMTypeRef llvm_type = lb_type(p->module, type);
|
||||
lbValue res = {};
|
||||
Type *ptr = base_array_type(type);
|
||||
res.type = alloc_type_pointer(ptr);
|
||||
if (LLVMIsConstant(value.value)) {
|
||||
res.value = LLVMConstGEP2(llvm_type, value.value, indices, gb_count_of(indices));
|
||||
} else {
|
||||
res.value = LLVMBuildGEP2(p->builder, llvm_type, value.value, indices, gb_count_of(indices), "");
|
||||
}
|
||||
return res;
|
||||
}
|
||||
// This emits a GEP at 0, index
|
||||
lbValue lb_emit_epi(lbModule *m, lbValue const &value, isize index) {
|
||||
GB_ASSERT(is_type_pointer(value.type));
|
||||
GB_ASSERT(LLVMIsConstant(value.value));
|
||||
Type *type = type_deref(value.type);
|
||||
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(lb_type(m, t_int), 0, false),
|
||||
LLVMConstInt(lb_type(m, t_int), cast(unsigned long long)index, false),
|
||||
};
|
||||
lbValue res = {};
|
||||
Type *ptr = base_array_type(type);
|
||||
res.type = alloc_type_pointer(ptr);
|
||||
res.value = LLVMConstGEP2(lb_type(m, type), value.value, indices, gb_count_of(indices));
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
LLVMValueRef llvm_zero(lbModule *m) {
|
||||
return LLVMConstInt(lb_type(m, t_int), 0, false);
|
||||
}
|
||||
@@ -528,6 +568,13 @@ void lb_emit_slice_bounds_check(lbProcedure *p, Token token, lbValue low, lbValu
|
||||
}
|
||||
}
|
||||
|
||||
unsigned lb_try_get_alignment(LLVMValueRef addr_ptr, unsigned default_alignment) {
|
||||
if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) {
|
||||
return LLVMGetAlignment(addr_ptr);
|
||||
}
|
||||
return default_alignment;
|
||||
}
|
||||
|
||||
bool lb_try_update_alignment(LLVMValueRef addr_ptr, unsigned alignment) {
|
||||
if (LLVMIsAGlobalValue(addr_ptr) || LLVMIsAAllocaInst(addr_ptr) || LLVMIsALoadInst(addr_ptr)) {
|
||||
if (LLVMGetAlignment(addr_ptr) < alignment) {
|
||||
@@ -807,46 +854,6 @@ void lb_addr_store(lbProcedure *p, lbAddr addr, lbValue value) {
|
||||
lb_emit_store(p, addr.addr, value);
|
||||
}
|
||||
|
||||
void lb_const_store(lbValue ptr, lbValue value) {
|
||||
GB_ASSERT(lb_is_const(ptr));
|
||||
GB_ASSERT(lb_is_const(value));
|
||||
GB_ASSERT(is_type_pointer(ptr.type));
|
||||
LLVMSetInitializer(ptr.value, value.value);
|
||||
}
|
||||
|
||||
|
||||
bool lb_is_type_proc_recursive(Type *t) {
|
||||
for (;;) {
|
||||
if (t == nullptr) {
|
||||
return false;
|
||||
}
|
||||
switch (t->kind) {
|
||||
case Type_Named:
|
||||
t = t->Named.base;
|
||||
break;
|
||||
case Type_Pointer:
|
||||
t = t->Pointer.elem;
|
||||
break;
|
||||
case Type_Array:
|
||||
t = t->Array.elem;
|
||||
break;
|
||||
case Type_EnumeratedArray:
|
||||
t = t->EnumeratedArray.elem;
|
||||
break;
|
||||
case Type_Slice:
|
||||
t = t->Slice.elem;
|
||||
break;
|
||||
case Type_DynamicArray:
|
||||
t = t->DynamicArray.elem;
|
||||
break;
|
||||
case Type_Proc:
|
||||
return true;
|
||||
default:
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
|
||||
GB_ASSERT(value.value != nullptr);
|
||||
Type *a = type_deref(ptr.type);
|
||||
@@ -854,7 +861,9 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
|
||||
if (LLVMIsNull(value.value)) {
|
||||
LLVMTypeRef src_t = llvm_addr_type(p->module, ptr);
|
||||
if (is_type_proc(a)) {
|
||||
LLVMBuildStore(p->builder, LLVMConstNull(llvm_get_element_type(LLVMTypeOf(ptr.value))), ptr.value);
|
||||
LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
|
||||
LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
|
||||
LLVMBuildStore(p->builder, LLVMConstNull(rawptr_type), LLVMBuildBitCast(p->builder, ptr.value, rawptr_ptr_type, ""));
|
||||
} else if (lb_sizeof(src_t) <= lb_max_zero_init_size()) {
|
||||
LLVMBuildStore(p->builder, LLVMConstNull(src_t), ptr.value);
|
||||
} else {
|
||||
@@ -873,25 +882,46 @@ void lb_emit_store(lbProcedure *p, lbValue ptr, lbValue value) {
|
||||
|
||||
enum {MAX_STORE_SIZE = 64};
|
||||
|
||||
if (LLVMIsALoadInst(value.value) && lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) {
|
||||
LLVMValueRef dst_ptr = ptr.value;
|
||||
LLVMValueRef src_ptr = LLVMGetOperand(value.value, 0);
|
||||
src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
|
||||
if (lb_sizeof(LLVMTypeOf(value.value)) > MAX_STORE_SIZE) {
|
||||
if (LLVMIsALoadInst(value.value)) {
|
||||
LLVMValueRef dst_ptr = ptr.value;
|
||||
LLVMValueRef src_ptr_original = LLVMGetOperand(value.value, 0);
|
||||
LLVMValueRef src_ptr = LLVMBuildPointerCast(p->builder, src_ptr_original, LLVMTypeOf(dst_ptr), "");
|
||||
|
||||
LLVMBuildMemMove(p->builder,
|
||||
dst_ptr, 1,
|
||||
src_ptr, 1,
|
||||
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
|
||||
return;
|
||||
LLVMBuildMemMove(p->builder,
|
||||
dst_ptr, lb_try_get_alignment(dst_ptr, 1),
|
||||
src_ptr, lb_try_get_alignment(src_ptr_original, 1),
|
||||
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
|
||||
return;
|
||||
} else if (LLVMIsConstant(value.value)) {
|
||||
lbAddr addr = lb_add_global_generated(p->module, value.type, value, nullptr);
|
||||
LLVMValueRef global_data = addr.addr.value;
|
||||
// make it truly private data
|
||||
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
|
||||
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetGlobalConstant(global_data, true);
|
||||
|
||||
LLVMValueRef dst_ptr = ptr.value;
|
||||
LLVMValueRef src_ptr = global_data;
|
||||
src_ptr = LLVMBuildPointerCast(p->builder, src_ptr, LLVMTypeOf(dst_ptr), "");
|
||||
|
||||
LLVMBuildMemMove(p->builder,
|
||||
dst_ptr, lb_try_get_alignment(dst_ptr, 1),
|
||||
src_ptr, lb_try_get_alignment(global_data, 1),
|
||||
LLVMConstInt(LLVMInt64TypeInContext(p->module->ctx), lb_sizeof(LLVMTypeOf(value.value)), false));
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
if (lb_is_type_proc_recursive(a)) {
|
||||
if (is_type_proc(a)) {
|
||||
// NOTE(bill, 2020-11-11): Because of certain LLVM rules, a procedure value may be
|
||||
// stored as regular pointer with no procedure information
|
||||
|
||||
LLVMTypeRef src_t = LLVMGetElementType(LLVMTypeOf(ptr.value));
|
||||
LLVMValueRef v = LLVMBuildPointerCast(p->builder, value.value, src_t, "");
|
||||
LLVMBuildStore(p->builder, v, ptr.value);
|
||||
LLVMTypeRef rawptr_type = lb_type(p->module, t_rawptr);
|
||||
LLVMTypeRef rawptr_ptr_type = LLVMPointerType(rawptr_type, 0);
|
||||
LLVMBuildStore(p->builder,
|
||||
LLVMBuildPointerCast(p->builder, value.value, rawptr_type, ""),
|
||||
LLVMBuildPointerCast(p->builder, ptr.value, rawptr_ptr_type, ""));
|
||||
} else {
|
||||
Type *ca = core_type(a);
|
||||
if (ca->kind == Type_Basic || ca->kind == Type_Proc) {
|
||||
@@ -1481,7 +1511,7 @@ LLVMTypeRef lb_type_internal_for_procedures_raw(lbModule *m, Type *type) {
|
||||
if (e->flags & EntityFlag_ByPtr) {
|
||||
param_type = lb_type(m, alloc_type_pointer(e_type));
|
||||
} else if (is_type_boolean(e_type) &&
|
||||
type_size_of(e_type) <= 1) {
|
||||
type_size_of(e_type) <= 1) {
|
||||
param_type = LLVMInt1TypeInContext(m->ctx);
|
||||
} else {
|
||||
if (is_type_proc(e_type)) {
|
||||
@@ -2028,15 +2058,13 @@ LLVMTypeRef lb_type_internal(lbModule *m, Type *type) {
|
||||
}
|
||||
|
||||
case Type_Proc:
|
||||
// if (m->internal_type_level > 256) { // TODO HACK(bill): is this really enough?
|
||||
if (m->internal_type_level > 1) { // TODO HACK(bill): is this really enough?
|
||||
return LLVMPointerType(LLVMIntTypeInContext(m->ctx, 8), 0);
|
||||
} else {
|
||||
{
|
||||
// NOTE(bill): we do an explicit cast to the correct procedure type when calling the procedure
|
||||
LLVMTypeRef proc_raw_type = lb_type_internal_for_procedures_raw(m, type);
|
||||
return LLVMPointerType(proc_raw_type, 0);
|
||||
gb_unused(proc_raw_type);
|
||||
return lb_type(m, t_rawptr);
|
||||
}
|
||||
|
||||
break;
|
||||
case Type_BitSet:
|
||||
{
|
||||
Type *ut = bit_set_to_int(type);
|
||||
@@ -2289,6 +2317,17 @@ void lb_emit_if(lbProcedure *p, lbValue cond, lbBlock *true_block, lbBlock *fals
|
||||
}
|
||||
|
||||
|
||||
gb_inline LLVMTypeRef OdinLLVMGetInternalElementType(LLVMTypeRef type) {
|
||||
return LLVMGetElementType(type);
|
||||
}
|
||||
LLVMTypeRef OdinLLVMGetArrayElementType(LLVMTypeRef type) {
|
||||
GB_ASSERT(lb_is_type_kind(type, LLVMArrayTypeKind));
|
||||
return OdinLLVMGetInternalElementType(type);
|
||||
}
|
||||
LLVMTypeRef OdinLLVMGetVectorElementType(LLVMTypeRef type) {
|
||||
GB_ASSERT(lb_is_type_kind(type, LLVMVectorTypeKind));
|
||||
return OdinLLVMGetInternalElementType(type);
|
||||
}
|
||||
|
||||
|
||||
LLVMValueRef OdinLLVMBuildTransmute(lbProcedure *p, LLVMValueRef val, LLVMTypeRef dst_type) {
|
||||
@@ -2469,8 +2508,56 @@ lbValue lb_find_or_add_entity_string_byte_slice(lbModule *m, String const &str)
|
||||
res.type = t_u8_slice;
|
||||
return res;
|
||||
}
|
||||
lbValue lb_find_or_add_entity_string_byte_slice_with_type(lbModule *m, String const &str, Type *slice_type) {
|
||||
GB_ASSERT(is_type_slice(slice_type));
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), llvm_zero(m)};
|
||||
LLVMValueRef data = LLVMConstStringInContext(m->ctx,
|
||||
cast(char const *)str.text,
|
||||
cast(unsigned)str.len,
|
||||
false);
|
||||
|
||||
|
||||
char *name = nullptr;
|
||||
{
|
||||
isize max_len = 7+8+1;
|
||||
name = gb_alloc_array(permanent_allocator(), char, max_len);
|
||||
u32 id = m->gen->global_array_index.fetch_add(1);
|
||||
isize len = gb_snprintf(name, max_len, "csbs$%x", id);
|
||||
len -= 1;
|
||||
}
|
||||
LLVMTypeRef type = LLVMTypeOf(data);
|
||||
LLVMValueRef global_data = LLVMAddGlobal(m->mod, type, name);
|
||||
LLVMSetInitializer(global_data, data);
|
||||
LLVMSetLinkage(global_data, LLVMPrivateLinkage);
|
||||
LLVMSetUnnamedAddress(global_data, LLVMGlobalUnnamedAddr);
|
||||
LLVMSetAlignment(global_data, 1);
|
||||
LLVMSetGlobalConstant(global_data, true);
|
||||
|
||||
i64 data_len = str.len;
|
||||
LLVMValueRef ptr = nullptr;
|
||||
if (data_len != 0) {
|
||||
ptr = LLVMConstInBoundsGEP2(type, global_data, indices, 2);
|
||||
} else {
|
||||
ptr = LLVMConstNull(lb_type(m, t_u8_ptr));
|
||||
}
|
||||
if (!is_type_u8_slice(slice_type)) {
|
||||
Type *bt = base_type(slice_type);
|
||||
Type *elem = bt->Slice.elem;
|
||||
i64 sz = type_size_of(elem);
|
||||
GB_ASSERT(sz > 0);
|
||||
ptr = LLVMConstPointerCast(ptr, lb_type(m, alloc_type_pointer(elem)));
|
||||
data_len /= sz;
|
||||
}
|
||||
|
||||
LLVMValueRef len = LLVMConstInt(lb_type(m, t_int), data_len, true);
|
||||
LLVMValueRef values[2] = {ptr, len};
|
||||
|
||||
lbValue res = {};
|
||||
res.value = llvm_const_named_struct(m, slice_type, values, 2);
|
||||
res.type = slice_type;
|
||||
return res;
|
||||
}
|
||||
|
||||
|
||||
|
||||
lbValue lb_find_ident(lbProcedure *p, lbModule *m, Entity *e, Ast *expr) {
|
||||
|
||||
@@ -1,6 +1,11 @@
|
||||
LLVMValueRef OdinLLVMBuildCall(lbProcedure *p, lbValue const &value, LLVMValueRef *args, unsigned arg_count) {
|
||||
GB_ASSERT(is_type_proc(value.type));
|
||||
LLVMTypeRef type = lb_type_internal_for_procedures_raw(p->module, value.type);
|
||||
LLVMValueRef func = LLVMBuildPointerCast(p->builder, value.value, LLVMPointerType(type, 0), "");
|
||||
return LLVMBuildCall2(p->builder, type, func, args, arg_count, "");
|
||||
}
|
||||
|
||||
LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count)
|
||||
{
|
||||
LLVMValueRef lb_call_intrinsic(lbProcedure *p, const char *name, LLVMValueRef* args, unsigned arg_count, LLVMTypeRef* types, unsigned type_count) {
|
||||
unsigned id = LLVMLookupIntrinsicID(name, gb_strlen(name));
|
||||
GB_ASSERT_MSG(id != 0, "Unable to find %s", name);
|
||||
LLVMValueRef ip = LLVMGetIntrinsicDeclaration(p->module->mod, id, types, type_count);
|
||||
@@ -740,6 +745,11 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
|
||||
}
|
||||
for_array(i, processed_args) {
|
||||
lbValue arg = processed_args[i];
|
||||
if (is_type_proc(arg.type)) {
|
||||
// NOTE(bill): all procedure types (function pointers) are typed as if they are `rawptr`
|
||||
// and then the correct type is set at the call site
|
||||
arg.value = LLVMBuildPointerCast(p->builder, arg.value, lb_type(p->module, arg.type), "");
|
||||
}
|
||||
args[arg_index++] = arg.value;
|
||||
}
|
||||
if (context_ptr.addr.value != nullptr) {
|
||||
@@ -752,11 +762,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
|
||||
|
||||
{
|
||||
LLVMTypeRef fnp = lb_type_internal_for_procedures_raw(p->module, value.type);
|
||||
LLVMTypeRef ftp = LLVMPointerType(fnp, 0);
|
||||
LLVMValueRef fn = value.value;
|
||||
if (!lb_is_type_kind(LLVMTypeOf(value.value), LLVMFunctionTypeKind)) {
|
||||
fn = LLVMBuildPointerCast(p->builder, fn, ftp, "");
|
||||
}
|
||||
|
||||
GB_ASSERT_MSG(lb_is_type_kind(fnp, LLVMFunctionTypeKind), "%s", LLVMPrintTypeToString(fnp));
|
||||
|
||||
{
|
||||
@@ -780,7 +786,7 @@ lbValue lb_emit_call_internal(lbProcedure *p, lbValue value, lbValue return_ptr,
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef ret = LLVMBuildCall2(p->builder, fnp, fn, args, arg_count, "");
|
||||
LLVMValueRef ret = OdinLLVMBuildCall(p, value, args, arg_count);
|
||||
|
||||
if (return_ptr.value != nullptr) {
|
||||
LLVMAddCallSiteAttribute(ret, 1, lb_create_enum_attribute_with_type(p->module->ctx, "sret", LLVMTypeOf(args[0])));
|
||||
@@ -2071,15 +2077,7 @@ lbValue lb_build_builtin_proc(lbProcedure *p, Ast *expr, TypeAndValue const &tv,
|
||||
lbValue ptr = lb_build_expr(p, ce->args[0]);
|
||||
lbValue len = lb_build_expr(p, ce->args[1]);
|
||||
len = lb_emit_conv(p, len, t_int);
|
||||
|
||||
LLVMValueRef indices[1] = {
|
||||
len.value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = tv.type;
|
||||
res.value = LLVMBuildGEP2(p->builder, lb_type(p->module, type_deref(tv.type)), ptr.value, indices, gb_count_of(indices), "");
|
||||
return res;
|
||||
return lb_emit_ptr_offset(p, ptr, len);
|
||||
}
|
||||
case BuiltinProc_ptr_sub:
|
||||
{
|
||||
|
||||
@@ -98,41 +98,8 @@ lbValue lb_type_info(lbModule *m, Type *type) {
|
||||
isize index = lb_type_info_index(m->info, type);
|
||||
GB_ASSERT(index >= 0);
|
||||
|
||||
LLVMTypeRef it = lb_type(m, t_int);
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(it, 0, false),
|
||||
LLVMConstInt(it, index, true),
|
||||
};
|
||||
|
||||
lbValue value = {};
|
||||
lbValue data = lb_global_type_info_data_ptr(m);
|
||||
value.value = LLVMConstGEP2(lb_type(m, type_deref(data.type)), data.value, indices, gb_count_of(indices));
|
||||
value.type = t_type_info_ptr;
|
||||
return value;
|
||||
}
|
||||
|
||||
lbValue lb_get_type_info_ptr(lbModule *m, Type *type) {
|
||||
GB_ASSERT(!build_context.disallow_rtti);
|
||||
|
||||
i32 index = cast(i32)lb_type_info_index(m->info, type);
|
||||
GB_ASSERT(index >= 0);
|
||||
// gb_printf_err("%d %s\n", index, type_to_string(type));
|
||||
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(lb_type(m, t_int), 0, false),
|
||||
LLVMConstInt(lb_type(m, t_int), index, false),
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
res.type = t_type_info_ptr;
|
||||
lbValue data = lb_global_type_info_data_ptr(m);
|
||||
res.value = LLVMConstGEP2(lb_type(m, type_deref(data.type)), data.value, indices, cast(unsigned)gb_count_of(indices));
|
||||
return res;
|
||||
}
|
||||
|
||||
// NOTE: The use of this method needs to be eliminated for pointers.
|
||||
LLVMTypeRef llvm_get_element_type(LLVMTypeRef type) {
|
||||
return LLVMGetElementType(type);
|
||||
return lb_emit_array_epi(m, data, index);
|
||||
}
|
||||
|
||||
LLVMTypeRef lb_get_procedure_raw_type(lbModule *m, Type *type) {
|
||||
@@ -272,7 +239,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
lb_const_string(p->module, t->Named.type_name->token.string).value,
|
||||
lb_get_type_info_ptr(m, t->Named.base).value,
|
||||
lb_type_info(m, t->Named.base).value,
|
||||
pkg_name,
|
||||
loc.value
|
||||
};
|
||||
@@ -431,7 +398,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
|
||||
case Type_Pointer: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_pointer_ptr);
|
||||
lbValue gep = lb_get_type_info_ptr(m, t->Pointer.elem);
|
||||
lbValue gep = lb_type_info(m, t->Pointer.elem);
|
||||
|
||||
LLVMValueRef vals[1] = {
|
||||
gep.value,
|
||||
@@ -445,7 +412,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
}
|
||||
case Type_MultiPointer: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_multi_pointer_ptr);
|
||||
lbValue gep = lb_get_type_info_ptr(m, t->MultiPointer.elem);
|
||||
lbValue gep = lb_type_info(m, t->MultiPointer.elem);
|
||||
|
||||
LLVMValueRef vals[1] = {
|
||||
gep.value,
|
||||
@@ -459,7 +426,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
}
|
||||
case Type_SoaPointer: {
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_soa_pointer_ptr);
|
||||
lbValue gep = lb_get_type_info_ptr(m, t->SoaPointer.elem);
|
||||
lbValue gep = lb_type_info(m, t->SoaPointer.elem);
|
||||
|
||||
LLVMValueRef vals[1] = {
|
||||
gep.value,
|
||||
@@ -476,7 +443,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
i64 ez = type_size_of(t->Array.elem);
|
||||
|
||||
LLVMValueRef vals[3] = {
|
||||
lb_get_type_info_ptr(m, t->Array.elem).value,
|
||||
lb_type_info(m, t->Array.elem).value,
|
||||
lb_const_int(m, t_int, ez).value,
|
||||
lb_const_int(m, t_int, t->Array.count).value,
|
||||
};
|
||||
@@ -491,8 +458,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_enumerated_array_ptr);
|
||||
|
||||
LLVMValueRef vals[7] = {
|
||||
lb_get_type_info_ptr(m, t->EnumeratedArray.elem).value,
|
||||
lb_get_type_info_ptr(m, t->EnumeratedArray.index).value,
|
||||
lb_type_info(m, t->EnumeratedArray.elem).value,
|
||||
lb_type_info(m, t->EnumeratedArray.index).value,
|
||||
lb_const_int(m, t_int, type_size_of(t->EnumeratedArray.elem)).value,
|
||||
lb_const_int(m, t_int, t->EnumeratedArray.count).value,
|
||||
|
||||
@@ -523,7 +490,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_dynamic_array_ptr);
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->DynamicArray.elem).value,
|
||||
lb_type_info(m, t->DynamicArray.elem).value,
|
||||
lb_const_int(m, t_int, type_size_of(t->DynamicArray.elem)).value,
|
||||
};
|
||||
|
||||
@@ -537,7 +504,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_slice_ptr);
|
||||
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->Slice.elem).value,
|
||||
lb_type_info(m, t->Slice.elem).value,
|
||||
lb_const_int(m, t_int, type_size_of(t->Slice.elem)).value,
|
||||
};
|
||||
|
||||
@@ -553,10 +520,10 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
LLVMValueRef params = LLVMConstNull(lb_type(m, t_type_info_ptr));
|
||||
LLVMValueRef results = LLVMConstNull(lb_type(m, t_type_info_ptr));
|
||||
if (t->Proc.params != nullptr) {
|
||||
params = lb_get_type_info_ptr(m, t->Proc.params).value;
|
||||
params = lb_type_info(m, t->Proc.params).value;
|
||||
}
|
||||
if (t->Proc.results != nullptr) {
|
||||
results = lb_get_type_info_ptr(m, t->Proc.results).value;
|
||||
results = lb_type_info(m, t->Proc.results).value;
|
||||
}
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
@@ -587,7 +554,6 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
lbValue index = lb_const_int(m, t_int, i);
|
||||
lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
|
||||
|
||||
// TODO(bill): Make this constant if possible, 'lb_const_store' does not work
|
||||
lb_emit_store(p, type_info, lb_type_info(m, f->type));
|
||||
if (f->token.string.len > 0) {
|
||||
lbValue name = lb_emit_ptr_offset(p, memory_names, index);
|
||||
@@ -675,7 +641,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
// NOTE(bill): Zeroth is nil so ignore it
|
||||
for (isize variant_index = 0; variant_index < variant_count; variant_index++) {
|
||||
Type *vt = t->Union.variants[variant_index];
|
||||
lbValue tip = lb_get_type_info_ptr(m, vt);
|
||||
lbValue tip = lb_type_info(m, vt);
|
||||
|
||||
lbValue index = lb_const_int(m, t_int, variant_index);
|
||||
lbValue type_info = lb_emit_ptr_offset(p, memory_types, index);
|
||||
@@ -763,7 +729,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
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_get_type_info_ptr(m, f->type);
|
||||
lbValue tip = lb_type_info(m, f->type);
|
||||
i64 foffset = 0;
|
||||
if (!t->Struct.is_raw_union) {
|
||||
GB_ASSERT(t->Struct.offsets != nullptr);
|
||||
@@ -820,11 +786,11 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_map_ptr);
|
||||
init_map_internal_types(t);
|
||||
|
||||
lbValue gst = lb_get_type_info_ptr(m, t->Map.generated_struct_type);
|
||||
lbValue gst = lb_type_info(m, t->Map.generated_struct_type);
|
||||
|
||||
LLVMValueRef vals[5] = {
|
||||
lb_get_type_info_ptr(m, t->Map.key).value,
|
||||
lb_get_type_info_ptr(m, t->Map.value).value,
|
||||
lb_type_info(m, t->Map.key).value,
|
||||
lb_type_info(m, t->Map.value).value,
|
||||
gst.value,
|
||||
lb_get_equal_proc_for_type(m, t->Map.key).value,
|
||||
lb_get_hasher_proc_for_type(m, t->Map.key).value
|
||||
@@ -845,13 +811,13 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
|
||||
|
||||
LLVMValueRef vals[4] = {
|
||||
lb_get_type_info_ptr(m, t->BitSet.elem).value,
|
||||
lb_type_info(m, t->BitSet.elem).value,
|
||||
LLVMConstNull(lb_type(m, t_type_info_ptr)),
|
||||
lb_const_int(m, t_i64, t->BitSet.lower).value,
|
||||
lb_const_int(m, t_i64, t->BitSet.upper).value,
|
||||
};
|
||||
if (t->BitSet.underlying != nullptr) {
|
||||
vals[1] =lb_get_type_info_ptr(m, t->BitSet.underlying).value;
|
||||
vals[1] =lb_type_info(m, t->BitSet.underlying).value;
|
||||
}
|
||||
|
||||
lbValue res = {};
|
||||
@@ -867,7 +833,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
|
||||
LLVMValueRef vals[3] = {};
|
||||
|
||||
vals[0] = lb_get_type_info_ptr(m, t->SimdVector.elem).value;
|
||||
vals[0] = lb_type_info(m, t->SimdVector.elem).value;
|
||||
vals[1] = lb_const_int(m, t_int, type_size_of(t->SimdVector.elem)).value;
|
||||
vals[2] = lb_const_int(m, t_int, t->SimdVector.count).value;
|
||||
|
||||
@@ -882,8 +848,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_pointer_ptr);
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->RelativePointer.pointer_type).value,
|
||||
lb_get_type_info_ptr(m, t->RelativePointer.base_integer).value,
|
||||
lb_type_info(m, t->RelativePointer.pointer_type).value,
|
||||
lb_type_info(m, t->RelativePointer.base_integer).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
@@ -896,8 +862,8 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
{
|
||||
tag = lb_const_ptr_cast(m, variant_ptr, t_type_info_relative_slice_ptr);
|
||||
LLVMValueRef vals[2] = {
|
||||
lb_get_type_info_ptr(m, t->RelativeSlice.slice_type).value,
|
||||
lb_get_type_info_ptr(m, t->RelativeSlice.base_integer).value,
|
||||
lb_type_info(m, t->RelativeSlice.slice_type).value,
|
||||
lb_type_info(m, t->RelativeSlice.base_integer).value,
|
||||
};
|
||||
|
||||
lbValue res = {};
|
||||
@@ -912,7 +878,7 @@ void lb_setup_type_info_data(lbProcedure *p) { // NOTE(bill): Setup type_info da
|
||||
i64 ez = type_size_of(t->Matrix.elem);
|
||||
|
||||
LLVMValueRef vals[5] = {
|
||||
lb_get_type_info_ptr(m, t->Matrix.elem).value,
|
||||
lb_type_info(m, t->Matrix.elem).value,
|
||||
lb_const_int(m, t_int, ez).value,
|
||||
lb_const_int(m, t_int, matrix_type_stride_in_elems(t)).value,
|
||||
lb_const_int(m, t_int, t->Matrix.row_count).value,
|
||||
|
||||
@@ -351,6 +351,10 @@ lbValue lb_emit_try_has_value(lbProcedure *p, lbValue rhs) {
|
||||
|
||||
|
||||
lbValue lb_emit_or_else(lbProcedure *p, Ast *arg, Ast *else_expr, TypeAndValue const &tv) {
|
||||
if (arg->state_flags & StateFlag_DirectiveWasFalse) {
|
||||
return lb_build_expr(p, else_expr);
|
||||
}
|
||||
|
||||
lbValue lhs = {};
|
||||
lbValue rhs = {};
|
||||
lb_emit_try_lhs_rhs(p, arg, tv, &lhs, &rhs);
|
||||
@@ -1002,6 +1006,7 @@ lbValue lb_emit_struct_ep(lbProcedure *p, lbValue s, i32 index) {
|
||||
index = lb_convert_struct_index(p->module, t, index);
|
||||
|
||||
if (lb_is_const(s)) {
|
||||
// NOTE(bill): this cannot be replaced with lb_emit_epi
|
||||
lbModule *m = p->module;
|
||||
lbValue res = {};
|
||||
LLVMValueRef indices[2] = {llvm_zero(m), LLVMConstInt(lb_type(m, t_i32), index, false)};
|
||||
@@ -1252,27 +1257,13 @@ lbValue lb_emit_array_ep(lbProcedure *p, lbValue s, lbValue index) {
|
||||
|
||||
Type *ptr = base_array_type(st);
|
||||
lbValue res = {};
|
||||
res.value = LLVMBuildGEP2(p->builder, lb_type(p->module, st), s.value, indices, 2, "");
|
||||
res.type = alloc_type_pointer(ptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
// This emits a GEP at 0, index
|
||||
static inline lbValue lb_emit_gep(lbProcedure *p, Type *type, LLVMValueRef value, isize index)
|
||||
{
|
||||
LLVMValueRef indices[2] = {
|
||||
LLVMConstInt(lb_type(p->module, t_int), 0, false),
|
||||
LLVMConstInt(lb_type(p->module, t_int), cast(unsigned)index, false),
|
||||
};
|
||||
LLVMTypeRef llvm_type = lb_type(p->module, type);
|
||||
lbValue res = {};
|
||||
Type *ptr = base_array_type(type);
|
||||
res.type = alloc_type_pointer(ptr);
|
||||
if (LLVMIsConstant(value)) {
|
||||
res.value = LLVMConstGEP2(llvm_type, value, indices, gb_count_of(indices));
|
||||
if (LLVMIsConstant(s.value) && LLVMIsConstant(index.value)) {
|
||||
res.value = LLVMConstGEP2(lb_type(p->module, st), s.value, indices, gb_count_of(indices));
|
||||
} else {
|
||||
res.value = LLVMBuildGEP2(p->builder, llvm_type, value, indices, gb_count_of(indices), "");
|
||||
res.value = LLVMBuildGEP2(p->builder, lb_type(p->module, st), s.value, indices, gb_count_of(indices), "");
|
||||
}
|
||||
res.type = alloc_type_pointer(ptr);
|
||||
return res;
|
||||
}
|
||||
|
||||
@@ -1282,7 +1273,15 @@ lbValue lb_emit_array_epi(lbProcedure *p, lbValue s, isize index) {
|
||||
Type *st = base_type(type_deref(t));
|
||||
GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st));
|
||||
GB_ASSERT(0 <= index);
|
||||
return lb_emit_gep(p, st, s.value, index);
|
||||
return lb_emit_epi(p, s, index);
|
||||
}
|
||||
lbValue lb_emit_array_epi(lbModule *m, lbValue s, isize index) {
|
||||
Type *t = s.type;
|
||||
GB_ASSERT(is_type_pointer(t));
|
||||
Type *st = base_type(type_deref(t));
|
||||
GB_ASSERT_MSG(is_type_array(st) || is_type_enumerated_array(st) || is_type_matrix(st), "%s", type_to_string(st));
|
||||
GB_ASSERT(0 <= index);
|
||||
return lb_emit_epi(m, s, index);
|
||||
}
|
||||
|
||||
lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
|
||||
@@ -1290,7 +1289,7 @@ lbValue lb_emit_ptr_offset(lbProcedure *p, lbValue ptr, lbValue index) {
|
||||
LLVMValueRef indices[1] = {index.value};
|
||||
lbValue res = {};
|
||||
res.type = ptr.type;
|
||||
LLVMTypeRef type = lb_type(p->module, type_deref(ptr.type));
|
||||
LLVMTypeRef type = lb_type(p->module, type_deref(res.type, true));
|
||||
|
||||
if (lb_is_const(ptr) && lb_is_const(index)) {
|
||||
res.value = LLVMConstGEP2(type, ptr.value, indices, 1);
|
||||
@@ -1306,16 +1305,16 @@ lbValue lb_emit_matrix_epi(lbProcedure *p, lbValue s, isize row, isize column) {
|
||||
Type *mt = base_type(type_deref(t));
|
||||
if (column == 0) {
|
||||
GB_ASSERT_MSG(is_type_matrix(mt) || is_type_array_like(mt), "%s", type_to_string(mt));
|
||||
return lb_emit_gep(p, mt, s.value, row);
|
||||
return lb_emit_epi(p, s, row);
|
||||
} else if (row == 0 && is_type_array_like(mt)) {
|
||||
return lb_emit_gep(p, mt, s.value, column);
|
||||
return lb_emit_epi(p, s, column);
|
||||
}
|
||||
|
||||
|
||||
GB_ASSERT_MSG(is_type_matrix(mt), "%s", type_to_string(mt));
|
||||
|
||||
isize offset = matrix_indices_to_offset(mt, row, column);
|
||||
return lb_emit_gep(p, mt, s.value, offset);
|
||||
return lb_emit_epi(p, s, offset);
|
||||
}
|
||||
|
||||
lbValue lb_emit_matrix_ep(lbProcedure *p, lbValue s, lbValue row, lbValue column) {
|
||||
@@ -1651,7 +1650,7 @@ LLVMValueRef llvm_vector_expand_to_power_of_two(lbProcedure *p, LLVMValueRef val
|
||||
LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
|
||||
LLVMTypeRef type = LLVMTypeOf(value);
|
||||
GB_ASSERT(LLVMGetTypeKind(type) == LLVMVectorTypeKind);
|
||||
LLVMTypeRef elem = LLVMGetElementType(type);
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(type);
|
||||
unsigned len = LLVMGetVectorSize(type);
|
||||
if (len == 0) {
|
||||
return LLVMConstNull(type);
|
||||
@@ -1727,7 +1726,7 @@ LLVMValueRef llvm_vector_reduce_add(lbProcedure *p, LLVMValueRef value) {
|
||||
LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
|
||||
GB_ASSERT(LLVMTypeOf(a) == LLVMTypeOf(b));
|
||||
|
||||
LLVMTypeRef elem = LLVMGetElementType(LLVMTypeOf(a));
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(LLVMTypeOf(a));
|
||||
|
||||
if (LLVMGetTypeKind(elem) == LLVMIntegerTypeKind) {
|
||||
return LLVMBuildAdd(p->builder, a, b, "");
|
||||
@@ -1738,7 +1737,7 @@ LLVMValueRef llvm_vector_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
|
||||
LLVMValueRef llvm_vector_mul(lbProcedure *p, LLVMValueRef a, LLVMValueRef b) {
|
||||
GB_ASSERT(LLVMTypeOf(a) == LLVMTypeOf(b));
|
||||
|
||||
LLVMTypeRef elem = LLVMGetElementType(LLVMTypeOf(a));
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(LLVMTypeOf(a));
|
||||
|
||||
if (LLVMGetTypeKind(elem) == LLVMIntegerTypeKind) {
|
||||
return LLVMBuildMul(p->builder, a, b, "");
|
||||
@@ -1758,7 +1757,7 @@ LLVMValueRef llvm_vector_mul_add(lbProcedure *p, LLVMValueRef a, LLVMValueRef b,
|
||||
GB_ASSERT(t == LLVMTypeOf(c));
|
||||
GB_ASSERT(LLVMGetTypeKind(t) == LLVMVectorTypeKind);
|
||||
|
||||
LLVMTypeRef elem = LLVMGetElementType(t);
|
||||
LLVMTypeRef elem = OdinLLVMGetVectorElementType(t);
|
||||
|
||||
bool is_possible = false;
|
||||
|
||||
|
||||
@@ -1562,7 +1562,7 @@ bool parse_build_flags(Array<String> args) {
|
||||
bad_flags = true;
|
||||
break;
|
||||
}
|
||||
build_context.resource_filepath = substring(path, 0, string_extension_position(path));
|
||||
build_context.resource_filepath = path;
|
||||
build_context.has_resource = true;
|
||||
} else {
|
||||
gb_printf_err("Invalid -resource path, got %.*s\n", LIT(path));
|
||||
|
||||
@@ -282,7 +282,8 @@ enum StateFlag : u8 {
|
||||
StateFlag_type_assert = 1<<2,
|
||||
StateFlag_no_type_assert = 1<<3,
|
||||
|
||||
StateFlag_SelectorCallExpr = 1<<6,
|
||||
StateFlag_SelectorCallExpr = 1<<5,
|
||||
StateFlag_DirectiveWasFalse = 1<<6,
|
||||
|
||||
StateFlag_BeenHandled = 1<<7,
|
||||
};
|
||||
|
||||
@@ -1115,7 +1115,7 @@ Type *alloc_type_simd_vector(i64 count, Type *elem, Type *generic_count=nullptr)
|
||||
////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
Type *type_deref(Type *t) {
|
||||
Type *type_deref(Type *t, bool allow_multi_pointer=false) {
|
||||
if (t != nullptr) {
|
||||
Type *bt = base_type(t);
|
||||
if (bt == nullptr) {
|
||||
@@ -1132,6 +1132,11 @@ Type *type_deref(Type *t) {
|
||||
GB_ASSERT(elem->kind == Type_Struct && elem->Struct.soa_kind != StructSoa_None);
|
||||
return elem->Struct.soa_elem;
|
||||
}
|
||||
case Type_MultiPointer:
|
||||
if (allow_multi_pointer) {
|
||||
return bt->MultiPointer.elem;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
return t;
|
||||
@@ -2398,6 +2403,57 @@ bool is_type_simple_compare(Type *t) {
|
||||
return false;
|
||||
}
|
||||
|
||||
bool is_type_load_safe(Type *type) {
|
||||
GB_ASSERT(type != nullptr);
|
||||
type = core_type(core_array_type(type));
|
||||
switch (type->kind) {
|
||||
case Type_Basic:
|
||||
return (type->Basic.flags & (BasicFlag_Boolean|BasicFlag_Numeric|BasicFlag_Rune)) != 0;
|
||||
|
||||
case Type_BitSet:
|
||||
if (type->BitSet.underlying) {
|
||||
return is_type_load_safe(type->BitSet.underlying);
|
||||
}
|
||||
return true;
|
||||
|
||||
case Type_RelativePointer:
|
||||
case Type_RelativeSlice:
|
||||
return true;
|
||||
|
||||
case Type_Pointer:
|
||||
case Type_MultiPointer:
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
case Type_Proc:
|
||||
case Type_SoaPointer:
|
||||
return false;
|
||||
|
||||
case Type_Enum:
|
||||
case Type_EnumeratedArray:
|
||||
case Type_Array:
|
||||
case Type_SimdVector:
|
||||
case Type_Matrix:
|
||||
GB_PANIC("should never be hit");
|
||||
return false;
|
||||
|
||||
case Type_Struct:
|
||||
for_array(i, type->Struct.fields) {
|
||||
if (!is_type_load_safe(type->Struct.fields[i]->type)) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return type_size_of(type) > 0;
|
||||
case Type_Union:
|
||||
for_array(i, type->Union.variants) {
|
||||
if (!is_type_load_safe(type->Union.variants[i])) {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
return type_size_of(type) > 0;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
String lookup_subtype_polymorphic_field(Type *dst, Type *src) {
|
||||
Type *prev_src = src;
|
||||
// Type *prev_dst = dst;
|
||||
|
||||
33
vendor/directx/d3d11/d3d11.odin
vendored
33
vendor/directx/d3d11/d3d11.odin
vendored
@@ -3625,3 +3625,36 @@ IFunctionLinkingGraph_VTable :: struct {
|
||||
GetLastError: proc "stdcall" (this: ^IFunctionLinkingGraph, ppErrorBuffer: ^^IBlob) -> HRESULT,
|
||||
GenerateHlsl: proc "stdcall" (this: ^IFunctionLinkingGraph, uFlags: u32, ppBuffer: ^^IBlob) -> HRESULT,
|
||||
}
|
||||
|
||||
IDebug_UUID_STRING :: "79CF2233-7536-4948-9D36-1E4692DC5760"
|
||||
IDebug_UUID := &IID{0x79CF2233, 0x7536, 0x4948, {0x9D, 0x36, 0x1E, 0x46, 0x92, 0xDC, 0x57, 0x60}}
|
||||
|
||||
IDebug :: struct #raw_union {
|
||||
#subtype iunknown: IUnknown,
|
||||
using id3d11debug_vtable: ^IDebug_VTable,
|
||||
}
|
||||
|
||||
RLDO_FLAGS :: enum u32 { // TODO: make bit_set
|
||||
SUMMARY = 0x1,
|
||||
DETAIL = 0x2,
|
||||
IGNORE_INTERNAL = 0x4,
|
||||
}
|
||||
|
||||
DEBUG_FEATURE :: enum u32 { // TODO: make bit_set
|
||||
FLUSH_PER_RENDER_OP = 0x1,
|
||||
FINISH_PER_RENDER_OP = 0x2,
|
||||
FEATURE_PRESENT_PER_RENDER_OP = 0x4,
|
||||
}
|
||||
|
||||
IDebug_VTable :: struct {
|
||||
using iunkown_vtable: IUnknown_VTable,
|
||||
SetFeatureMask: proc "stdcall" (this: ^IDebug, mask: DEBUG_FEATURE) -> HRESULT,
|
||||
GetFeatureMask: proc "stdcall" (this: ^IDebug) -> DEBUG_FEATURE,
|
||||
SetPresentPerRenderOpDelay: proc "stdcall" (this: ^IDebug, Milliseconds: u32) -> HRESULT,
|
||||
GetPresentPerRenderOpDelay: proc "stdcall" (this: ^IDebug) -> u32,
|
||||
SetSwapChain: proc "stdcall" (this: ^IDebug, pSwapChain: ^dxgi.ISwapChain) -> HRESULT,
|
||||
GetSwapChain: proc "stdcall" (this: ^IDebug, ppSwapChain: ^^dxgi.ISwapChain) -> HRESULT,
|
||||
ValidateContext: proc "stdcall" (this: ^IDebug, pContext: ^IDeviceContext) -> HRESULT,
|
||||
ReportLiveDeviceObjects: proc "stdcall" (this: ^IDebug, Flags: RLDO_FLAGS) -> HRESULT,
|
||||
ValidateContextForDispatch: proc "stdcall" (this: ^IDebug, pContext: ^IDeviceContext) -> HRESULT,
|
||||
}
|
||||
|
||||
2
vendor/glfw/native.odin
vendored
2
vendor/glfw/native.odin
vendored
@@ -3,7 +3,7 @@ package glfw
|
||||
when ODIN_OS == .Windows {
|
||||
import win32 "core:sys/windows"
|
||||
|
||||
foreign import glfw { "lib/glfw3.lib", "system:user32.lib", "system:gdi32.lib", "system:shell32.lib" }
|
||||
foreign import glfw { "lib/glfw3_mt.lib", "system:user32.lib", "system:gdi32.lib", "system:shell32.lib" }
|
||||
|
||||
@(default_calling_convention="c", link_prefix="glfw")
|
||||
foreign glfw {
|
||||
|
||||
10
vendor/raylib/raylib.odin
vendored
10
vendor/raylib/raylib.odin
vendored
@@ -625,9 +625,13 @@ KeyboardKey :: enum c.int {
|
||||
|
||||
// Mouse buttons
|
||||
MouseButton :: enum c.int {
|
||||
LEFT = 0,
|
||||
RIGHT = 1,
|
||||
MIDDLE = 2,
|
||||
LEFT = 0, // Mouse button left
|
||||
RIGHT = 1, // Mouse button right
|
||||
MIDDLE = 2, // Mouse button middle (pressed wheel)
|
||||
SIDE = 3, // Mouse button side (advanced mouse device)
|
||||
EXTRA = 4, // Mouse button extra (advanced mouse device)
|
||||
FORWARD = 5, // Mouse button fordward (advanced mouse device)
|
||||
BACK = 6, // Mouse button back (advanced mouse device)
|
||||
}
|
||||
|
||||
// Mouse cursor
|
||||
|
||||
Reference in New Issue
Block a user