diff --git a/build.bat b/build.bat index d6f360640..2fe7a1cbd 100644 --- a/build.bat +++ b/build.bat @@ -51,7 +51,7 @@ pushd %build_dir% cl %compiler_settings% "..\src\main.cpp" ^ /link %linker_settings% -OUT:%exe_name% ^ - && odin ..\examples/demo.odin + && odin run ..\examples/demo.odin rem odin run ..\examples/demo.odin diff --git a/examples/demo.odin b/examples/demo.odin index 37b83d4bb..53d85d268 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -1,874 +1,36 @@ -// Demo 002 #load "basic.odin" -#load "game.odin" +#load "math.odin" -#thread_local tls_int: int + +print_type_info_kind :: proc(info: ^Type_Info) { + using Type_Info + match type i : info { + case Named: print_string("Named\n") + case Integer: print_string("Integer\n") + case Float: print_string("Float\n") + case String: print_string("String\n") + case Boolean: print_string("Boolean\n") + case Pointer: print_string("Pointer\n") + case Procedure: print_string("Procedure\n") + case Array: print_string("Array\n") + case Slice: print_string("Slice\n") + case Vector: print_string("Vector\n") + case Struct: print_string("Struct\n") + case Union: print_string("Union\n") + case Raw_Union: print_string("RawUnion\n") + case Enum: print_string("Enum\n") + default: print_string("void\n") + } +} main :: proc() { - // Forenotes + i: int + s: struct { + x, y, z: f32 + } + p := ^s - // Semicolons are now optional - // Rule for when a semicolon is expected after a statement - // - If the next token is not on the same line - // - if the next token is a closing brace } - // - Otherwise, a semicolon is needed - // - // Expections: - // for, if, match - // if x := thing(); x < 123 {} - // for i := 0; i < 123; i++ {} - - // Q: Should I use the new rule or go back to the old one without optional semicolons? - - - // #thread_local - see runtime.odin and above at `tls_int` - // #foreign_system_library - see win32.odin - - struct_compound_literals() - enumerations() - variadic_procedures() - new_builtins() - match_statement() - namespacing() - subtyping() - tagged_unions() + print_type_info_kind(type_info(i)) + print_type_info_kind(type_info(s)) + print_type_info_kind(type_info(p)) } - -struct_compound_literals :: proc() { - Thing :: type struct { - id: int - x: f32 - name: string - } - { - t1: Thing - t1.id = 1 - - t3 := Thing{} - t4 := Thing{1, 2, "Fred"} - // t5 := Thing{1, 2} - - t6 := Thing{ - name = "Tom", - x = 23, - } - } -} - -enumerations :: proc() { - { - Fruit :: type enum { - APPLE, // 0 - BANANA, // 1 - PEAR, // 2 - } - - f := Fruit.APPLE - // g: int = Fruit.BANANA - g: int = Fruit.BANANA as int - } - { - Fruit1 :: type enum int { - APPLE, - BANANA, - PEAR, - } - - Fruit2 :: type enum u8 { - APPLE, - BANANA, - PEAR, - } - - Fruit3 :: type enum u8 { - APPLE = 1, - BANANA, // 2 - PEAR = 5, - TOMATO, // 6 - } - } -} - -variadic_procedures :: proc() { - print_ints :: proc(args: ..int) { - for i := 0; i < len(args); i++ { - if i > 0 { - print_string(", ") - } - print_int(args[i]) - } - } - - print_ints(); nl() - print_ints(1); nl() - print_ints(1, 2, 3); nl() - - print_prefix_f32s :: proc(prefix: string, args: ..f32) { - print_string(prefix) - print_string(": ") - for i := 0; i < len(args); i++ { - if i > 0 { - print_string(", ") - } - print_f32(args[i]) - } - } - - print_prefix_f32s("a"); nl() - print_prefix_f32s("b", 1); nl() - print_prefix_f32s("c", 1, 2, 3); nl() - - // Internally, the variadic procedures get allocated to an array on the stack, - // and this array is passed a slice - - // This is first step for a `print` procedure but I do not have an `any` type - // yet as this requires a few other things first - i.e. introspection -} - -new_builtins :: proc() { - { - a := new(int) - b := new_slice(int, 12) - c := new_slice(int, 12, 16) - - defer delete(a) - defer delete(b) - defer delete(c) - - // NOTE(bill): These use the current context's allocator not the default allocator - // see runtime.odin - - // Q: Should this be `free` rather than `delete` and should I overload it for slices too? - } - - { - a: int = 123 - b: type_of_val(a) = 321 - - // NOTE(bill): This matches the current naming scheme - // size_of - // align_of - // offset_of - // - // size_of_val - // align_of_val - // offset_of_val - // type_of_val - } - - { - // Compile time assert - COND :: true - assert(COND) - // assert(!COND) - - // Runtime assert - x := true - assert(x) - // assert(!x) - } - - { - x: ^u32 = null; - y := ptr_offset(x, 100) - z := ptr_sub(y, x) - w := slice_ptr(x, 12) - t := slice_ptr(x, 12, 16) - - // NOTE(bill): These are here because I've removed: - // pointer arithmetic - // pointer indexing - // pointer slicing - - // Reason - - a: [16]int - a[1] = 1; - b := ^a - // Auto pointer deref - // consistent with record members - assert(b[1] == 1) - - // Q: Should I add them back in at the cost of inconsitency? - } - - { - a, b := -1, 2 - print_int(min(a, b)); nl() - print_int(max(a, b)); nl() - print_int(abs(a)); nl() - - // These work at compile time too - A :: -1 - B :: 2 - C :: min(A, B) - D :: max(A, B) - E :: abs(A) - - print_int(C); nl() - print_int(D); nl() - print_int(E); nl() - } -} - - -match_statement :: proc() { - // NOTE(bill): `match` statements are similar to `switch` statements - // in other languages but there are few differences - - { - match x := 2; x { - case 1: // cases must be constant expression - print_string("1!\n") - // break by default - - case 2: - s := "2!\n"; // Each case has its own scope - print_string(s) - break // explicit break - - case 3, 4: // multiple cases - print_string("3 or 4!\n") - - case 5: - print_string("5!\n") - fallthrough // explicit fallthrough - - default: - print_string("default!\n") - } - - - - match x := 1.5; x { - case 1.5: - print_string("1.5!\n") - // break by default - case MATH_TAU: - print_string("τ!\n") - default: - print_string("default!\n") - } - - - - match x := "Hello"; x { - case "Hello": - print_string("greeting\n") - // break by default - case "Goodbye": - print_string("farewell\n") - default: - print_string("???\n") - } - - - - - - - a := 53 - match { - case a == 1: - print_string("one\n") - case a == 2: - print_string("a couple\n") - case a < 7, a == 7: - print_string("a few\n") - case a < 12: // intentional bug - print_string("several\n") - case a >= 12 && a < 100: - print_string("dozens\n") - case a >= 100 && a < 1000: - print_string("hundreds\n") - default: - print_string("a fuck ton\n") - } - - // Identical to this - - b := 53 - if b == 1 { - print_string("one\n") - } else if b == 2 { - print_string("a couple\n") - } else if b < 7 || b == 7 { - print_string("a few\n") - } else if b < 12 { // intentional bug - print_string("several\n") - } else if b >= 12 && b < 100 { - print_string("dozens\n") - } else if b >= 100 && b < 1000 { - print_string("hundreds\n") - } else { - print_string("a fuck ton\n") - } - - // However, match statements allow for `break` and `fallthrough` unlike - // an if statement - } -} - -Vector3 :: type struct { - x, y, z: f32 -} - -print_floats :: proc(args: ..f32) { - for i := 0; i < len(args); i++ { - if i > 0 { - print_string(", ") - } - print_f32(args[i]) - } - print_nl() -} - -namespacing :: proc() { - { - Thing :: type struct { - x: f32 - name: string - } - - a: Thing - a.x = 3 - { - Thing :: type struct { - y: int - test: bool - } - - b: Thing // Uses this scope's Thing - b.test = true - } - } - - { - Entity :: type struct { - Guid :: type int - Nested :: type struct { - MyInt :: type int - i: int - } - - CONSTANT :: 123 - - - guid: Guid - name: string - pos: Vector3 - vel: Vector3 - nested: Nested - } - - guid: Entity.Guid = Entity.CONSTANT - i: Entity.Nested.MyInt - - - - { - using Entity - guid: Guid = CONSTANT - using Nested - i: MyInt - } - - - { - using Entity.Nested - guid: Entity.Guid = Entity.CONSTANT - i: MyInt - } - - - { - e: Entity - using e - guid = 78456 - name = "Thing" - - print_int(e.guid as int); nl() - print_string(e.name); nl() - } - - { - using e: Entity - guid = 78456 - name = "Thing" - - print_int(e.guid as int); nl() - print_string(e.name); nl() - } - } - - { - Entity :: type struct { - Guid :: type int - Nested :: type struct { - MyInt :: type int - i: int - } - - CONSTANT :: 123 - - - guid: Guid - name: string - using pos: Vector3 - vel: Vector3 - using nested: ^Nested - } - - e := Entity{nested = new(Entity.Nested)} - e.x = 123 - e.i = Entity.CONSTANT - } - - - - { - Entity :: type struct { - position: Vector3 - } - - print_pos_1 :: proc(entity: ^Entity) { - print_string("print_pos_1: ") - print_floats(entity.position.x, entity.position.y, entity.position.z) - } - - print_pos_2 :: proc(entity: ^Entity) { - using entity - print_string("print_pos_2: ") - print_floats(position.x, position.y, position.z) - } - - print_pos_3 :: proc(using entity: ^Entity) { - print_string("print_pos_3: ") - print_floats(position.x, position.y, position.z) - } - - print_pos_4 :: proc(using entity: ^Entity) { - using position - print_string("print_pos_4: ") - print_floats(x, y, z) - } - - e := Entity{position = Vector3{1, 2, 3}} - print_pos_1(^e) - print_pos_2(^e) - print_pos_3(^e) - print_pos_4(^e) - - // This is similar to C++'s `this` pointer that is implicit and only available in methods - } -} - -subtyping :: proc() { - { - // C way for subtyping/subclassing - - Entity :: type struct { - position: Vector3 - } - - Frog :: type struct { - entity: Entity - jump_height: f32 - } - - f: Frog - f.entity.position = Vector3{1, 2, 3} - - using f.entity - position = Vector3{1, 2, 3} - - } - - { - // C++ way for subtyping/subclassing - - Entity :: type struct { - position: Vector3 - } - - Frog :: type struct { - using entity: Entity - jump_height: f32 - } - - f: Frog - f.position = Vector3{1, 2, 3} - - - print_pos :: proc(using entity: Entity) { - print_string("print_pos: ") - print_floats(position.x, position.y, position.z) - } - - print_pos(f.entity) - print_pos(f) - - // Subtype Polymorphism - } - - { - // More than C++ way for subtyping/subclassing - - Entity :: type struct { - position: Vector3 - } - - Frog :: type struct { - jump_height: f32 - using entity: ^Entity // Doesn't have to be first member! - } - - f: Frog - f.entity = new(Entity) - f.position = Vector3{1, 2, 3} - - - print_pos :: proc(using entity: ^Entity) { - print_string("print_pos: ") - print_floats(position.x, position.y, position.z) - } - - print_pos(f.entity) - print_pos(^f) - print_pos(f) - } - - { - // More efficient subtyping - - Entity :: type struct { - position: Vector3 - } - - Frog :: type struct { - jump_height: f32 - using entity: ^Entity - } - - MAX_ENTITES :: 64 - entities: [MAX_ENTITES]Entity - entity_count := 0 - - next_entity :: proc(entities: []Entity, entity_count: ^int) -> ^Entity { - e := ^entities[entity_count^] - entity_count^++ - return e - } - - f: Frog - f.entity = next_entity(entities[:], ^entity_count) - f.position = Vector3{3, 4, 6} - - using f.position - print_floats(x, y, z) - } - - { - // Down casting - - Entity :: type struct { - position: Vector3 - } - - Frog :: type struct { - jump_height: f32 - using entity: Entity - } - - f: Frog - f.jump_height = 564 - e := ^f.entity - - frog := e down_cast ^Frog - print_string("down_cast: ") - print_f32(frog.jump_height); nl() - - // NOTE(bill): `down_cast` is unsafe and there are not check are compile time or run time - } - - { - // Multiple "inheritance" - - Entity :: type struct { - position: Vector3 - } - Climber :: type struct { - speed: f32 - } - - Frog :: type struct { - using entity: Entity - using climber: Climber - } - } -} - -tagged_unions :: proc() { - { - EntityKind :: type enum { - INVALID, - FROG, - GIRAFFE, - HELICOPTER, - } - - Entity :: type struct { - kind: EntityKind - using data: raw_union { - frog: struct { - jump_height: f32 - colour: u32 - } - giraffe: struct { - neck_length: f32 - spot_count: int - } - helicopter: struct { - blade_count: int - weight: f32 - pilot_name: string - } - } - } - - e: Entity - e.kind = EntityKind.FROG - e.frog.jump_height = 12 - - f: type_of_val(e.frog); - - // But this is very unsafe and extremely cumbersome to write - // In C++, I use macros to alleviate this but it's not a solution - } - - { - Entity :: type union { - Frog: struct { - jump_height: f32 - colour: u32 - } - Giraffe: struct { - neck_length: f32 - spot_count: int - } - Helicopter: struct { - blade_count: int - weight: f32 - pilot_name: string - } - } - - using Entity - f1: Frog = Frog{12, 0xff9900} - f2: Entity = Frog{12, 0xff9900} // Implicit cast - f3 := Frog{12, 0xff9900} as Entity // Explicit cast - - // f3.Frog.jump_height = 12 // There are "members" of a union - - - - e, f, g, h: Entity - f = Frog{12, 0xff9900} - g = Giraffe{2.1, 23} - h = Helicopter{4, 1000, "Frank"} - - - - - // Requires a pointer to the union - // `x` will be a pointer to type of the case - // Q: Allow for a non pointer version with takes a copy instead? - match type ^f -> x { - case Frog: - print_string("Frog!\n") - print_f32(x.jump_height); nl() - x.jump_height = 3 - print_f32(x.jump_height); nl() - case Giraffe: - print_string("Giraffe!\n") - case Helicopter: - print_string("ROFLCOPTER!\n") - default: - print_string("invalid entity\n") - } - - fp := ^f as ^Frog // Unsafe - print_f32(fp.jump_height); nl() - - - // Internals of a tagged union - /* - struct { - data: [size_of_biggest_tag]u8 - tag_index: int - } - */ - // This is to allow for pointer casting if needed - - - // Advantage over subtyping version - MAX_ENTITES :: 64 - entities: [MAX_ENTITES]Entity - - entities[0] = Frog{} - entities[1] = Helicopter{} - // etc. - } - - - { - // Transliteration of code from this actual compiler - // Some stuff is missing - Type :: type struct {} - Scope :: type struct {} - Token :: type struct {} - AstNode :: type struct {} - ExactValue :: type struct {} - - EntityKind :: type enum { - Invalid, - Constant, - Variable, - UsingVariable, - TypeName, - Procedure, - Builtin, - Count, - } - - Entity :: type struct { - Guid :: type i64 - - kind: EntityKind - guid: Guid - - scope: ^Scope - token: Token - type_: ^Type - - using data: raw_union { - Constant: struct { - value: ExactValue - } - Variable: struct { - visited: bool // Cycle detection - used: bool // Variable is used - is_field: bool // Is struct field - anonymous: bool // Variable is an anonymous - } - UsingVariable: struct { - } - TypeName: struct { - } - Procedure: struct { - used: bool - } - Builtin: struct { - id: int - } - } - } - - // Plus all the constructing procedures that go along with them!!!! - // It's a nightmare - } - - { - Type :: type struct {} - Scope :: type struct {} - Token :: type struct {} - AstNode :: type struct {} - ExactValue :: type struct {} - - - EntityBase :: type struct { - Guid :: type i64 - guid: Guid - - scope: ^Scope - token: Token - type_: ^Type - } - - Entity :: type union { - Constant: struct { - using base: EntityBase - value: ExactValue - } - Variable: struct { - using base: EntityBase - visited: bool // Cycle detection - used: bool // Variable is used - is_field: bool // Is struct field - anonymous: bool // Variable is an anonymous - } - UsingVariable: struct { - using base: EntityBase - } - TypeName: struct { - using base: EntityBase - } - Procedure: struct { - using base: EntityBase - used: bool - } - Builtin: struct { - using base: EntityBase - id: int - } - } - - using Entity - - e: Entity - - e = Variable{ - base = EntityBase{}, - used = true, - anonymous = false, - } - - - - // Q: Allow a "base" type to be added to a union? - } - - - { - // `Raw` unions still have uses, especially for mathematic types - - Vector2 :: type raw_union { - using xy_: struct { x, y: f32 } - e: [2]f32 - v: {2}f32 - } - - Vector3 :: type raw_union { - using xyz_: struct { x, y, z: f32 } - xy: Vector2 - e: [3]f32 - v: {3}f32 - } - - v2: Vector2 - v2.x = 1 - v2.e[0] = 1 - v2.v[0] = 1 - - v3: Vector3 - v3.x = 1 - v3.e[0] = 1 - v3.v[0] = 1 - v3.xy.x = 1 - - // Q: If I applied using to a vector element in a raw_union, - // should that type now act as if it was a vector? - } -} - -nl :: proc() { print_nl() } diff --git a/examples/runtime.odin b/examples/runtime.odin index 44ae3d64a..dd765211e 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -1,10 +1,68 @@ #load "win32.odin" +// IMPORTANT NOTE(bill): Do not change the order of any of this data +// The compiler relies upon this _exact_ order +Type_Info :: union { + Member :: struct { + name: string + type_: ^Type_Info + offset: int + } + Record :: struct { + fields: []Member + } + + + Named: struct { + name: string + base: ^Type_Info + } + Integer: struct { + bits: int + signed: bool + } + Float: struct { + bits: int + } + String: struct {} + Boolean: struct {} + Pointer: struct { + elem: ^Type_Info + } + Procedure: struct{} + Array: struct { + elem: ^Type_Info + count: int + } + Slice: struct { + elem: ^Type_Info + } + Vector: struct { + elem: ^Type_Info + count: int + } + Struct: Record + Union: Record + Raw_Union: Record + Enum: struct { + base: ^Type_Info + } +} + +Any :: struct { + type_info: ^Type_Info + // pointer to the data stored + data: rawptr +} + + + + assume :: proc(cond: bool) #foreign "llvm.assume" -__debug_trap :: proc() #foreign "llvm.debugtrap" -__trap :: proc() #foreign "llvm.trap" -read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter" +__debug_trap :: proc() #foreign "llvm.debugtrap" +__trap :: proc() #foreign "llvm.trap" +read_cycle_counter :: proc() -> u64 #foreign "llvm.readcyclecounter" bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16" bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32" @@ -22,218 +80,29 @@ heap_alloc :: proc(len: int) -> rawptr #foreign "malloc" heap_dealloc :: proc(ptr: rawptr) #foreign "free" memory_zero :: proc(data: rawptr, len: int) { - d := slice_ptr(data as ^byte, len) - for i := 0; i < len; i++ { - d[i] = 0 - } + llvm_memset_64bit :: proc(dst: rawptr, val: byte, len: int, align: i32, is_volatile: bool) #foreign "llvm.memset.p0i8.i64" + llvm_memset_64bit(data, 0, len, 1, false) } memory_compare :: proc(dst, src: rawptr, len: int) -> int { - s1, s2: ^byte = dst, src + // TODO(bill): make a faster `memory_compare` + a, b := slice_ptr(dst as ^byte, len), slice_ptr(src as ^byte, len) for i := 0; i < len; i++ { - a := ptr_offset(s1, i)^ - b := ptr_offset(s2, i)^ - if a != b { - return (a - b) as int + if a[i] != b[i] { + return (a[i] - b[i]) as int } } return 0 } -memory_copy :: proc(dst, src: rawptr, n: int) #inline { - if dst == src { - return - } - - v128b :: type {4}u32 - assert(align_of(v128b) == 16) - - d, s: ^byte = dst, src - - for ; s as uint % 16 != 0 && n != 0; n-- { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - - if d as uint % 16 == 0 { - for ; n >= 16; d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 { - (d as ^v128b)^ = (s as ^v128b)^ - } - - if n&8 != 0 { - (d as ^u64)^ = (s as ^u64)^ - d, s = ptr_offset(d, 8), ptr_offset(s, 8) - } - if n&4 != 0 { - (d as ^u32)^ = (s as ^u32)^; - d, s = ptr_offset(d, 4), ptr_offset(s, 4) - } - if n&2 != 0 { - (d as ^u16)^ = (s as ^u16)^ - d, s = ptr_offset(d, 2), ptr_offset(s, 2) - } - if n&1 != 0 { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - return; - } - - // IMPORTANT NOTE(bill): Little endian only - LS :: proc(a, b: u32) -> u32 #inline { return a << b } - RS :: proc(a, b: u32) -> u32 #inline { return a >> b } - /* NOTE(bill): Big endian version - LS :: proc(a, b: u32) -> u32 #inline { return a >> b; } - RS :: proc(a, b: u32) -> u32 #inline { return a << b; } - */ - - w, x: u32 - - if d as uint % 4 == 1 { - w = (s as ^u32)^ - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - n -= 3 - - for n > 16 { - d32 := d as ^u32 - s32 := ptr_offset(s, 1) as ^u32 - x = s32^; d32^ = LS(w, 24) | RS(x, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 24) | RS(w, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - x = s32^; d32^ = LS(w, 24) | RS(x, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 24) | RS(w, 8) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - - d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 - } - - } else if d as uint % 4 == 2 { - w = (s as ^u32)^ - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - d^ = s^; d = ptr_offset(d, 1); s = ptr_offset(s, 1) - n -= 2 - - for n > 17 { - d32 := d as ^u32 - s32 := ptr_offset(s, 2) as ^u32 - x = s32^; d32^ = LS(w, 16) | RS(x, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 16) | RS(w, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - x = s32^; d32^ = LS(w, 16) | RS(x, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 16) | RS(w, 16) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - - d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 - } - - } else if d as uint % 4 == 3 { - w = (s as ^u32)^ - d^ = s^ - n -= 1 - - for n > 18 { - d32 := d as ^u32 - s32 := ptr_offset(s, 3) as ^u32 - x = s32^; d32^ = LS(w, 8) | RS(x, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 8) | RS(w, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - x = s32^; d32^ = LS(w, 8) | RS(x, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - w = s32^; d32^ = LS(x, 8) | RS(w, 24) - d32, s32 = ptr_offset(d32, 1), ptr_offset(s32, 1) - - d, s, n = ptr_offset(d, 16), ptr_offset(s, 16), n-16 - } - } - - if n&16 != 0 { - (d as ^v128b)^ = (s as ^v128b)^ - d, s = ptr_offset(d, 16), ptr_offset(s, 16) - } - if n&8 != 0 { - (d as ^u64)^ = (s as ^u64)^ - d, s = ptr_offset(d, 8), ptr_offset(s, 8) - } - if n&4 != 0 { - (d as ^u32)^ = (s as ^u32)^; - d, s = ptr_offset(d, 4), ptr_offset(s, 4) - } - if n&2 != 0 { - (d as ^u16)^ = (s as ^u16)^ - d, s = ptr_offset(d, 2), ptr_offset(s, 2) - } - if n&1 != 0 { - d^ = s^ - } +memory_copy :: proc(dst, src: rawptr, len: int) #inline { + llvm_memcpy_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memcpy.p0i8.p0i8.i64" + llvm_memcpy_64bit(dst, src, len, 1, false) } -memory_move :: proc(dst, src: rawptr, n: int) #inline { - d, s: ^byte = dst, src - if d == s { - return - } - if d >= ptr_offset(s, n) || ptr_offset(d, n) <= s { - memory_copy(d, s, n) - return - } - - // TODO(bill): Vectorize the shit out of this - if d < s { - if s as int % size_of(int) == d as int % size_of(int) { - for d as int % size_of(int) != 0 { - if n == 0 { - return - } - n-- - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - di, si := d as ^int, s as ^int - for n >= size_of(int) { - di^ = si^ - di, si = ptr_offset(di, 1), ptr_offset(si, 1) - n -= size_of(int) - } - } - for ; n > 0; n-- { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - } else { - if s as int % size_of(int) == d as int % size_of(int) { - for ptr_offset(d, n) as int % size_of(int) != 0 { - if n == 0 { - return - } - n-- - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - for n >= size_of(int) { - n -= size_of(int) - di := ptr_offset(d, n) as ^int - si := ptr_offset(s, n) as ^int - di^ = si^ - } - for ; n > 0; n-- { - d^ = s^ - d, s = ptr_offset(d, 1), ptr_offset(s, 1) - } - } - for n > 0 { - n-- - dn := ptr_offset(d, n) - sn := ptr_offset(s, n) - dn^ = sn^ - } - } +memory_move :: proc(dst, src: rawptr, len: int) #inline { + llvm_memmove_64bit :: proc(dst, src: rawptr, len: int, align: i32, is_volatile: bool) #foreign "llvm.memmove.p0i8.p0i8.i64" + llvm_memmove_64bit(dst, src, len, 1, false) } __string_eq :: proc(a, b: string) -> bool { @@ -247,25 +116,36 @@ __string_eq :: proc(a, b: string) -> bool { } __string_cmp :: proc(a, b : string) -> int { - min_len := len(a) - if len(b) < min_len { - min_len = len(b) + // Translation of http://mgronhol.github.io/fast-strcmp/ + n := min(len(a), len(b)) + + fast := n/size_of(int) + 1 + offset := (fast-1)*size_of(int) + curr_block := 0 + if n <= size_of(int) { + fast = 0 } - for i := 0; i < min_len; i++ { - x := a[i] - y := b[i] - if x < y { - return -1 - } else if x > y { - return +1 + + la := slice_ptr(^a[0] as ^int, fast) + lb := slice_ptr(^b[0] as ^int, fast) + + for ; curr_block < fast; curr_block++ { + if (la[curr_block] ~ lb[curr_block]) != 0 { + for pos := curr_block*size_of(int); pos < n; pos++ { + if (a[pos] ~ b[pos]) != 0 { + return a[pos] as int - b[pos] as int + } + } + } + + } + + for ; offset < n; offset++ { + if (a[offset] ~ b[offset]) != 0 { + return a[offset] as int - b[offset] as int } } - if len(a) < len(b) { - return -1 - } else if len(a) > len(b) { - return +1 - } return 0 } @@ -278,26 +158,24 @@ __string_ge :: proc(a, b : string) -> bool #inline { return __string_cmp(a, b) > -Allocation_Mode :: type enum { +Allocation_Mode :: enum { ALLOC, DEALLOC, DEALLOC_ALL, RESIZE, } - - Allocator_Proc :: type proc(allocator_data: rawptr, mode: Allocation_Mode, size, alignment: int, old_memory: rawptr, old_size: int, flags: u64) -> rawptr -Allocator :: type struct { +Allocator :: struct { procedure: Allocator_Proc; data: rawptr } -Context :: type struct { +Context :: struct { thread_ptr: rawptr user_data: rawptr diff --git a/examples/win32.odin b/examples/win32.odin index 744754b34..f7ddb003e 100644 --- a/examples/win32.odin +++ b/examples/win32.odin @@ -42,7 +42,7 @@ INVALID_HANDLE_VALUE :: (-1 as int) as HANDLE WNDPROC :: type proc(hwnd: HWND, msg: u32, wparam: WPARAM, lparam: LPARAM) -> LRESULT -WNDCLASSEXA :: type struct { +WNDCLASSEXA :: struct { size, style: u32 wnd_proc: WNDPROC cls_extra, wnd_extra: i32 @@ -54,7 +54,7 @@ WNDCLASSEXA :: type struct { sm: HICON } -MSG :: type struct { +MSG :: struct { hwnd: HWND message: u32 wparam: WPARAM @@ -191,7 +191,7 @@ PROC :: type proc() wglCreateContextAttribsARBType :: type proc(hdc: HDC, hshareContext: rawptr, attribList: ^i32) -> HGLRC -PIXELFORMATDESCRIPTOR :: type struct { +PIXELFORMATDESCRIPTOR :: struct { size, version, flags: u32 diff --git a/misc/llvm-bin/lli.exe b/misc/llvm-bin/lli.exe index 3a4ead99d..a05e7657e 100644 Binary files a/misc/llvm-bin/lli.exe and b/misc/llvm-bin/lli.exe differ diff --git a/misc/llvm-bin/opt.exe b/misc/llvm-bin/opt.exe index d1bc6d7e9..b970d0088 100644 Binary files a/misc/llvm-bin/opt.exe and b/misc/llvm-bin/opt.exe differ diff --git a/src/checker/checker.cpp b/src/checker/checker.cpp index 04b68eb9b..94917d7c7 100644 --- a/src/checker/checker.cpp +++ b/src/checker/checker.cpp @@ -150,6 +150,8 @@ enum BuiltinProcId { BuiltinProc_max, BuiltinProc_abs, + BuiltinProc_type_info, + BuiltinProc_Count, }; struct BuiltinProc { @@ -188,6 +190,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_Count] = { {STR_LIT("min"), 2, false, Expr_Expr}, {STR_LIT("max"), 2, false, Expr_Expr}, {STR_LIT("abs"), 1, false, Expr_Expr}, + + {STR_LIT("type_info"), 1, false, Expr_Expr}, }; struct CheckerContext { @@ -197,13 +201,14 @@ struct CheckerContext { // NOTE(bill): Symbol tables struct CheckerInfo { - Map types; // Key: AstNode * | Expression -> Type (and value) - Map definitions; // Key: AstNode * | Identifier -> Entity - Map uses; // Key: AstNode * | Identifier -> Entity - Map scopes; // Key: AstNode * | Node -> Scope - Map untyped; // Key: AstNode * | Expression -> ExpressionInfo - Map entities; // Key: Entity * - Map foreign_procs; // Key: String + Map types; // Key: AstNode * | Expression -> Type (and value) + Map definitions; // Key: AstNode * | Identifier -> Entity + Map uses; // Key: AstNode * | Identifier -> Entity + Map scopes; // Key: AstNode * | Node -> Scope + Map untyped; // Key: AstNode * | Expression -> ExpressionInfo + Map entities; // Key: Entity * + Map foreign_procs; // Key: String + Map type_info_types; // Key: Type * }; struct Checker { @@ -383,6 +388,26 @@ void add_global_constant(gbAllocator a, String name, Type *type, ExactValue valu add_global_entity(entity); } + +Type *t_type_info = NULL; +Type *t_type_info_ptr = NULL; + +Type *t_type_info_named = NULL; +Type *t_type_info_integer = NULL; +Type *t_type_info_float = NULL; +Type *t_type_info_string = NULL; +Type *t_type_info_boolean = NULL; +Type *t_type_info_pointer = NULL; +Type *t_type_info_procedure = NULL; +Type *t_type_info_array = NULL; +Type *t_type_info_slice = NULL; +Type *t_type_info_vector = NULL; +Type *t_type_info_struct = NULL; +Type *t_type_info_union = NULL; +Type *t_type_info_raw_union = NULL; +Type *t_type_info_enum = NULL; + + void init_universal_scope(void) { // NOTE(bill): No need to free these gbAllocator a = gb_heap_allocator(); @@ -425,13 +450,14 @@ void init_universal_scope(void) { void init_checker_info(CheckerInfo *i) { gbAllocator a = gb_heap_allocator(); - map_init(&i->types, a); - map_init(&i->definitions, a); - map_init(&i->uses, a); - map_init(&i->scopes, a); - map_init(&i->entities, a); - map_init(&i->untyped, a); - map_init(&i->foreign_procs, a); + map_init(&i->types, a); + map_init(&i->definitions, a); + map_init(&i->uses, a); + map_init(&i->scopes, a); + map_init(&i->entities, a); + map_init(&i->untyped, a); + map_init(&i->foreign_procs, a); + map_init(&i->type_info_types, a); } @@ -443,6 +469,7 @@ void destroy_checker_info(CheckerInfo *i) { map_destroy(&i->entities); map_destroy(&i->untyped); map_destroy(&i->foreign_procs); + map_destroy(&i->type_info_types); } diff --git a/src/checker/expr.cpp b/src/checker/expr.cpp index eff3852ac..b8919c0bb 100644 --- a/src/checker/expr.cpp +++ b/src/checker/expr.cpp @@ -1900,6 +1900,42 @@ Entity *check_selector(Checker *c, Operand *operand, AstNode *node) { return NULL; } +void add_type_info_type(Checker *c, Type *t) { + if (t == NULL) { + return; + } + t = default_type(t); + if (map_get(&c->info.type_info_types, hash_pointer(t)) != NULL) { + // Types have already been added + return; + } + + map_set(&c->info.type_info_types, hash_pointer(t), t); + Type *bt = get_base_type(t); + switch (bt->kind) { + case Type_Named: add_type_info_type(c, bt->Named.base); break; + case Type_Array: add_type_info_type(c, bt->Array.elem); break; + case Type_Slice: add_type_info_type(c, bt->Slice.elem); break; + case Type_Vector: add_type_info_type(c, bt->Vector.elem); break; + case Type_Pointer: add_type_info_type(c, bt->Pointer.elem); break; + case Type_Record: { + switch (bt->Record.kind) { + case TypeRecord_Enum: + add_type_info_type(c, bt->Record.enum_base); + break; + default: + for (isize i = 0; i < bt->Record.field_count; i++) { + Entity *f = bt->Record.fields[i]; + add_type_info_type(c, f->type); + } + break; + } + } break; + } + // TODO(bill): Type info for procedures and tuples + // TODO(bill): Remove duplicate identical types efficiently +} + b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) { GB_ASSERT(call->kind == AstNode_CallExpr); ast_node(ce, CallExpr, call); @@ -2652,6 +2688,38 @@ b32 check_builtin_procedure(Checker *c, Operand *operand, AstNode *call, i32 id) operand->type = type; } break; + + case BuiltinProc_type_info: { + if (t_type_info == NULL) { + String type_info_str = make_string("Type_Info"); + Entity **found = map_get(&c->global_scope->elements, hash_string(type_info_str)); + GB_ASSERT_MSG(found != NULL, "Internal Compiler Error: Could not find type declaration for `Type_Info`"); + Entity *e = *found; + t_type_info = e->type; + t_type_info_ptr = make_type_pointer(c->allocator, t_type_info); + + auto *record = &get_base_type(e->type)->Record; + GB_ASSERT(record->field_count == 15); + t_type_info_named = record->fields[ 1]->type; + t_type_info_integer = record->fields[ 2]->type; + t_type_info_float = record->fields[ 3]->type; + t_type_info_string = record->fields[ 4]->type; + t_type_info_boolean = record->fields[ 5]->type; + t_type_info_pointer = record->fields[ 6]->type; + t_type_info_procedure = record->fields[ 7]->type; + t_type_info_array = record->fields[ 8]->type; + t_type_info_slice = record->fields[ 9]->type; + t_type_info_vector = record->fields[10]->type; + t_type_info_struct = record->fields[11]->type; + t_type_info_union = record->fields[12]->type; + t_type_info_raw_union = record->fields[13]->type; + t_type_info_enum = record->fields[14]->type; + } + + add_type_info_type(c, operand->type); + operand->mode = Addressing_Value; + operand->type = t_type_info_ptr; + } break; } return true; diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index 7ad1d56e2..5d8df5060 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -31,27 +31,6 @@ b32 ssa_gen_init(ssaGen *s, Checker *c) { if (err != gbFileError_None) return false; -#if 0 - Map type_map; // Key: Type * - map_init(&type_map, gb_heap_allocator()); - i32 index = 0; - gb_for_array(i, c->info.types.entries) { - TypeAndValue tav = c->info.types.entries[i].value; - Type *type = tav.type; - HashKey key = hash_pointer(type); - auto found = map_get(&type_map, key); - if (!found) { - map_set(&type_map, key, index); - index++; - } - } - gb_for_array(i, type_map.entries) { - auto *e = &type_map.entries[i]; - Type *t = cast(Type *)cast(uintptr)e->key.key; - gb_printf("%s\n", type_to_string(t)); - } -#endif - return true; } @@ -199,6 +178,149 @@ void ssa_gen_tree(ssaGen *s) { } } + { // NOTE(bill): Setup type_info data + ssaValue **found = map_get(&proc->module->members, hash_string(make_string("__type_info_data"))); + GB_ASSERT(found != NULL); + ssaValue *type_info_data = *found; + CheckerInfo *info = proc->module->info; + + + Type *t_int_ptr = make_type_pointer(a, t_int); + Type *t_bool_ptr = make_type_pointer(a, t_bool); + Type *t_string_ptr = make_type_pointer(a, t_string); + Type *t_type_info_ptr_ptr = make_type_pointer(a, t_type_info_ptr); + + auto get_type_info_ptr = [](ssaProcedure *proc, ssaValue *type_info_data, Type *type) -> ssaValue * { + auto *info = proc->module->info; + MapFindResult fr = map__find(&info->type_info_types, hash_pointer(type)); + GB_ASSERT(fr.entry_index >= 0); + return ssa_emit_struct_gep(proc, type_info_data, fr.entry_index, t_type_info_ptr); + }; + + + gb_for_array(entry_index, info->type_info_types.entries) { + auto *entry = &info->type_info_types.entries[entry_index]; + Type *t = entry->value; + + ssaValue *tag = NULL; + + switch (t->kind) { + case Type_Named: { + tag = ssa_add_local_generated(proc, t_type_info_named); + + ssaValue *gsa = ssa_add_global_string_array(proc, make_exact_value_string(t->Named.name)); + ssaValue *elem = ssa_array_elem(proc, gsa); + ssaValue *len = ssa_array_len(proc, ssa_emit_load(proc, gsa)); + ssaValue *name = ssa_emit_string(proc, elem, len); + + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Named.base); + + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero, t_string_ptr), name); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_one32, t_type_info_ptr), gep); + } break; + + case Type_Basic: + switch (t->Basic.kind) { + case Basic_bool: + tag = ssa_add_local_generated(proc, t_type_info_boolean); + break; + case Basic_i8: + case Basic_i16: + case Basic_i32: + case Basic_i64: + case Basic_i128: + case Basic_u8: + case Basic_u16: + case Basic_u32: + case Basic_u64: + case Basic_u128: + case Basic_int: + case Basic_uint: { + tag = ssa_add_local_generated(proc, t_type_info_integer); + b32 is_unsigned = (basic_types[t->Basic.kind].flags & BasicFlag_Unsigned) != 0; + ssaValue *bits = ssa_make_value_constant(a, t_int, make_exact_value_integer(8*type_size_of(m->sizes, a, t))); + ssaValue *is_signed = ssa_make_value_constant(a, t_bool, make_exact_value_bool(!is_unsigned)); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_int_ptr), bits); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_one32, t_bool_ptr), is_signed); + } break; + + case Basic_f32: + case Basic_f64: { + tag = ssa_add_local_generated(proc, t_type_info_float); + ssaValue *bits = ssa_make_value_constant(a, t_int, make_exact_value_integer(8*type_size_of(m->sizes, a, t))); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_int_ptr), bits); + } break; + + case Basic_rawptr: + tag = ssa_add_local_generated(proc, t_type_info_pointer); + break; + + case Basic_string: + tag = ssa_add_local_generated(proc, t_type_info_string); + break; + } + break; + + case Type_Pointer: { + tag = ssa_add_local_generated(proc, t_type_info_pointer); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Pointer.elem); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep); + } break; + case Type_Array: { + tag = ssa_add_local_generated(proc, t_type_info_array); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Array.elem); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep); + } break; + case Type_Slice: { + tag = ssa_add_local_generated(proc, t_type_info_slice); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Slice.elem); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep); + } break; + case Type_Vector: { + tag = ssa_add_local_generated(proc, t_type_info_vector); + ssaValue *gep = get_type_info_ptr(proc, type_info_data, t->Vector.elem); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep); + } break; + case Type_Record: { + switch (t->Record.kind) { + // TODO(bill): Record members for `Type_Info` + case TypeRecord_Struct: + tag = ssa_add_local_generated(proc, t_type_info_struct); + break; + case TypeRecord_Union: + tag = ssa_add_local_generated(proc, t_type_info_union); + break; + case TypeRecord_RawUnion: + tag = ssa_add_local_generated(proc, t_type_info_raw_union); + break; + case TypeRecord_Enum: { + tag = ssa_add_local_generated(proc, t_type_info_enum); + Type *enum_base = t->Record.enum_base; + if (enum_base == NULL) { + enum_base = t_int; + } + ssaValue *gep = get_type_info_ptr(proc, type_info_data, enum_base); + ssa_emit_store(proc, ssa_emit_struct_gep(proc, tag, v_zero32, t_type_info_ptr_ptr), gep); + } break; + } + } break; + + case Type_Tuple: + // TODO(bill): Type_Info for tuples + break; + case Type_Proc: + // TODO(bill): Type_Info for procedures + break; + } + + if (tag != NULL) { + ssaValue *gep = ssa_emit_struct_gep(proc, type_info_data, entry_index, t_type_info_ptr); + ssaValue *val = ssa_emit_conv(proc, ssa_emit_load(proc, tag), t_type_info); + ssa_emit_store(proc, gep, val); + } + } + } + ssa_end_procedure_body(proc); } diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index fffdee805..c8dff6d6b 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -314,7 +314,7 @@ void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Typ void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) { ssa_print_escape_string(f, b->label, false); - ssa_fprintf(f, "..%d", b->id); + ssa_fprintf(f, "-%d", b->id); } void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) { @@ -352,7 +352,9 @@ void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) { switch (instr->kind) { case ssaInstr_StartupRuntime: { - ssa_fprintf(f, "call void @." SSA_STARTUP_RUNTIME_PROC_NAME "()\n"); + ssa_fprintf(f, "call void "); + ssa_print_encoded_global(f, make_string(SSA_STARTUP_RUNTIME_PROC_NAME)); + ssa_fprintf(f, "()\n"); } break; case ssaInstr_Comment: @@ -642,20 +644,6 @@ void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) { ssa_fprintf(f, "\n"); } break; - case ssaInstr_MemCopy: { - ssa_fprintf(f, "call void @.memory_move"); - ssa_fprintf(f, "(i8* "); - ssa_print_value(f, m, instr->CopyMemory.dst, t_rawptr); - ssa_fprintf(f, ", i8* "); - ssa_print_value(f, m, instr->CopyMemory.src, t_rawptr); - ssa_fprintf(f, ", "); - ssa_print_type(f, m->sizes, t_int); - ssa_fprintf(f, " "); - ssa_print_value(f, m, instr->CopyMemory.len, t_int); - ssa_fprintf(f, ")\n"); - } break; - - case ssaInstr_ExtractElement: { Type *vt = ssa_type(instr->ExtractElement.vector); ssa_fprintf(f, "%%%d = extractelement ", value->id); diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index e7d4aaf4b..5bff4a344 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -85,7 +85,6 @@ struct ssaProcedure { SSA_INSTR_KIND(Unreachable), \ SSA_INSTR_KIND(BinaryOp), \ SSA_INSTR_KIND(Call), \ - SSA_INSTR_KIND(MemCopy), \ SSA_INSTR_KIND(NoOp), \ SSA_INSTR_KIND(ExtractElement), \ SSA_INSTR_KIND(InsertElement), \ @@ -198,13 +197,6 @@ struct ssaInstr { ssaValue **args; isize arg_count; } Call; - struct { - ssaValue *dst, *src; - ssaValue *len; - i32 align; - b32 is_volatile; - } CopyMemory; - struct { ssaValue *vector; ssaValue *index; @@ -315,6 +307,25 @@ void ssa_module_init(ssaModule *m, Checker *c) { map_init(&m->values, gb_heap_allocator()); map_init(&m->members, gb_heap_allocator()); + + { + // Add type info data + ssaValue *ssa_make_value_global(gbAllocator a, Entity *e, ssaValue *value); + + + String name = make_string("__type_info_data"); + Token token = {}; + token.kind = Token_Identifier; + token.string = name; + + + isize count = gb_array_count(c->info.type_info_types.entries); + Entity *e = make_entity_variable(m->allocator, NULL, token, make_type_array(m->allocator, t_type_info, count)); + ssaValue *g = ssa_make_value_global(m->allocator, e, NULL); + g->Global.is_private = true; + map_set(&m->values, hash_pointer(e), g); + map_set(&m->members, hash_string(name), g); + } } void ssa_module_destroy(ssaModule *m) { @@ -356,9 +367,6 @@ Type *ssa_type(ssaInstr *instr) { } return NULL; } break; - case ssaInstr_MemCopy: - return t_int; - case ssaInstr_ExtractElement: { Type *vt = ssa_type(instr->ExtractElement.vector); Type *bt = base_vector_type(get_base_type(vt)); @@ -545,16 +553,6 @@ ssaValue *ssa_make_instr_call(ssaProcedure *p, ssaValue *value, ssaValue **args, return v; } -ssaValue *ssa_make_instr_copy_memory(ssaProcedure *p, ssaValue *dst, ssaValue *src, ssaValue *len, i32 align, b32 is_volatile) { - ssaValue *v = ssa_alloc_instr(p, ssaInstr_MemCopy); - v->Instr.CopyMemory.dst = dst; - v->Instr.CopyMemory.src = src; - v->Instr.CopyMemory.len = len; - v->Instr.CopyMemory.align = align; - v->Instr.CopyMemory.is_volatile = is_volatile; - return v; -} - ssaValue *ssa_make_instr_conv(ssaProcedure *p, ssaConvKind kind, ssaValue *value, Type *from, Type *to) { ssaValue *v = ssa_alloc_instr(p, ssaInstr_Conv); v->Instr.Conv.kind = kind; @@ -925,7 +923,6 @@ void ssa_end_procedure_body(ssaProcedure *proc) { case ssaInstr_Br: case ssaInstr_Ret: case ssaInstr_Unreachable: - case ssaInstr_MemCopy: case ssaInstr_StartupRuntime: continue; case ssaInstr_Call: @@ -2008,11 +2005,13 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue make_exact_value_integer(size_of_elem)); ssaValue *byte_count = ssa_emit_arith(proc, mul, len, elem_size, t_int); + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = dst; + args[1] = src; + args[2] = byte_count; - i32 align = cast(i32)type_align_of(proc->module->sizes, proc->module->allocator, elem_type); - b32 is_volatile = false; + ssa_emit_global_call(proc, "memory_move", args, 3); - ssa_emit(proc, ssa_make_instr_copy_memory(proc, dst, src, byte_count, align, is_volatile)); return len; } break; case BuiltinProc_append: { @@ -2056,7 +2055,12 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue item = ssa_emit_ptr_offset(proc, item, v_zero); item = ssa_emit_conv(proc, item, t_rawptr, true); - ssa_emit(proc, ssa_make_instr_copy_memory(proc, offset, item, byte_count, 1, false)); + ssaValue **args = gb_alloc_array(proc->module->allocator, ssaValue *, 3); + args[0] = offset; + args[1] = item; + args[2] = byte_count; + + ssa_emit_global_call(proc, "memory_move", args, 3); // Increment slice length Token add = {Token_Add}; @@ -2172,6 +2176,22 @@ ssaValue *ssa_build_single_expr(ssaProcedure *proc, AstNode *expr, TypeAndValue ssaValue *cond = ssa_emit_comp(proc, lt, x, v_zero); return ssa_emit_select(proc, cond, neg_x, x); } break; + + + case BuiltinProc_type_info: { + ssaValue **found = map_get(&proc->module->members, hash_string(make_string("__type_info_data"))); + GB_ASSERT(found != NULL); + ssaValue *type_info_data = *found; + ssaValue *x = ssa_build_expr(proc, ce->arg_list); + Type *t = default_type(type_of_expr(proc->module->info, ce->arg_list)); + MapFindResult fr = map__find(&proc->module->info->type_info_types, hash_pointer(t)); + GB_ASSERT(fr.entry_index >= 0); + // Zero is null and void + isize offset = fr.entry_index; + + ssaValue *gep = ssa_emit_struct_gep(proc, type_info_data, offset, t_type_info_ptr); + return gep; + } break; } } } @@ -3153,11 +3173,16 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { Type *union_type = type_deref(ssa_type(parent)); GB_ASSERT(is_type_union(union_type)); + ssa_emit_comment(proc, make_string("get union's tag")); ssaValue *tag_index = ssa_emit_struct_gep(proc, parent, v_one32, make_type_pointer(allocator, t_int)); tag_index = ssa_emit_load(proc, tag_index); ssaValue *data = ssa_emit_conv(proc, parent, t_rawptr); + ssaBlock *start_block = ssa_add_block(proc, node, make_string("type-match.case.first")); + ssa_emit_jump(proc, start_block); + proc->curr_block = start_block; + ssaBlock *done = ssa__make_block(proc, node, make_string("type-match.done")); // NOTE(bill): Append later ast_node(body, BlockStmt, ms->body); @@ -3173,30 +3198,22 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { for (AstNode *clause = body->list; clause != NULL; clause = clause->next, i++) { - ssaBlock *body = NULL; - b32 append_body = false; - ast_node(cc, CaseClause, clause); - if (body == NULL) { - append_body = true; - if (cc->list == NULL) { - body = ssa__make_block(proc, clause, make_string("type-match.dflt.body")); - } else { - body = ssa__make_block(proc, clause, make_string("type-match.case.body")); - } - } - - if (cc->list == NULL) { // default case default_stmts = cc->stmts; - default_block = body; + default_block = ssa__make_block(proc, clause, make_string("type-match.dflt.body")); continue; } + + ssaBlock *body = ssa__make_block(proc, clause, make_string("type-match.case.body")); + + Scope *scope = *map_get(&proc->module->info->scopes, hash_pointer(clause)); Entity *tag_var_entity = current_scope_lookup_entity(scope, tag_var_name); + GB_ASSERT(tag_var_entity != NULL); ssaValue *tag_var = ssa_add_local(proc, tag_var_entity); ssaValue *data_ptr = ssa_emit_conv(proc, data, tag_var_entity->type); ssa_emit_store(proc, tag_var, data_ptr); @@ -3205,10 +3222,13 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { Type *base_type = type_deref(tag_var_entity->type); ssaValue *index = NULL; - for (isize i = 0; i < get_base_type(union_type)->Record.field_count; i++) { - Entity *f = get_base_type(union_type)->Record.fields[i]; + Type *ut = get_base_type(union_type); + GB_ASSERT(ut->Record.kind == TypeRecord_Union); + for (isize field_index = 1; field_index < ut->Record.field_count; field_index++) { + Entity *f = get_base_type(union_type)->Record.fields[field_index]; if (are_types_identical(f->type, base_type)) { - index = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(i)); + index = ssa_make_value_constant(allocator, t_int, make_exact_value_integer(field_index)); + break; } } GB_ASSERT(index != NULL); @@ -3220,9 +3240,7 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { gb_array_append(proc->blocks, next_cond); proc->curr_block = next_cond; - if (append_body) { - gb_array_append(proc->blocks, body); - } + gb_array_append(proc->blocks, body); proc->curr_block = body; ssa_push_target_list(proc, done, NULL, NULL); ssa_build_stmt_list(proc, cc->stmts); diff --git a/src/main.cpp b/src/main.cpp index 0b4c955d1..6099e10f7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -41,6 +41,7 @@ i32 win32_exec_command_line_app(char *fmt, ...) { } +#if 0 #define INIT_TIMER() u64 start_time, end_time = 0, total_time = 0; start_time = gb_utc_time_now() #define PRINT_TIMER(section) do { \ u64 diff; \ @@ -54,6 +55,11 @@ i32 win32_exec_command_line_app(char *fmt, ...) { #define PRINT_ACCUMULATION() do { \ gb_printf_err("Total compilation time: %lld ms\n", total_time/1000); \ } while (0) +#else +#define INIT_TIMER() +#define PRINT_TIMER(section) +#define PRINT_ACCUMULATION() +#endif int main(int argc, char **argv) { @@ -117,8 +123,10 @@ int main(int argc, char **argv) { i32 exit_code = 0; + // For more passes arguments: http://llvm.org/docs/Passes.html exit_code = win32_exec_command_line_app( - "../misc/llvm-bin/opt %s -o %.*s.bc " + // "../misc/llvm-bin/opt %s -o %.*s.bc " + "opt %s -o %.*s.bc " "-memcpyopt " "-mem2reg " "-die -dse " @@ -144,7 +152,7 @@ int main(int argc, char **argv) { } exit_code = win32_exec_command_line_app( - "clang %.*s.bc -o %.*s.o " + "clang %.*s.bc -o %.*s.exe " "-O0 " "-Wno-override-module " "%s", diff --git a/src/parser.cpp b/src/parser.cpp index afe52ae6b..3efb0451d 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -2063,8 +2063,15 @@ AstNode *parse_decl(AstFile *f, AstNode *name_list, isize name_count) { declaration_kind = Declaration_Immutable; next_token(f); - if (f->cursor[0].kind == Token_type) { - Token token = expect_token(f, Token_type); + if (f->cursor[0].kind == Token_type || + f->cursor[0].kind == Token_struct || + f->cursor[0].kind == Token_enum || + f->cursor[0].kind == Token_union || + f->cursor[0].kind == Token_raw_union) { + Token token = f->cursor[0]; + if (token.kind == Token_type) { + next_token(f); + } if (name_count != 1) { ast_file_err(f, ast_node_token(name_list), "You can only declare one type at a time"); return make_bad_decl(f, name_list->Ident, token); @@ -2290,9 +2297,9 @@ AstNode *parse_match_stmt(AstFile *f) { Token open, close; if (allow_token(f, Token_type)) { - tag = parse_expr(f, true); - expect_token(f, Token_ArrowRight); AstNode *var = parse_identifier(f); + expect_token(f, Token_Colon); + tag = parse_simple_stmt(f); open = expect_token(f, Token_OpenBrace); AstNode *list = NULL; @@ -2307,6 +2314,8 @@ AstNode *parse_match_stmt(AstFile *f) { close = expect_token(f, Token_CloseBrace); body = make_block_stmt(f, list, list_count, open, close); + + tag = convert_stmt_to_expr(f, tag, make_string("type match expression")); return make_type_match_stmt(f, token, tag, var, body); } else { if (f->cursor[0].kind != Token_OpenBrace) {