diff --git a/build.bat b/build.bat index 2fe7a1cbd..d6f360640 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 run ..\examples/demo.odin + && odin ..\examples/demo.odin rem odin run ..\examples/demo.odin diff --git a/examples/demo.odin b/examples/demo.odin index a795e75ae..37b83d4bb 100644 --- a/examples/demo.odin +++ b/examples/demo.odin @@ -1,444 +1,740 @@ -// Demo 001 +// Demo 002 #load "basic.odin" #load "game.odin" +#thread_local tls_int: int + main :: proc() { - Entity :: type union { - Frog: struct { + // Forenotes + + // 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() +} + +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 } - Helicopter: struct { - weight: f32 - blade_code: int - } - } - using Entity - f: Entity = Frog{137} - h: Entity = Helicopter{123, 4} + f: Frog + f.entity.position = Vector3{1, 2, 3} - match type ^f -> e { - case Frog: - print_string("Frog!\n") - print_f32(e.jump_height); nl() - e.jump_height = 69 - print_f32(e.jump_height); nl() - case Helicopter: - print_string("Helicopter!\n") - e.weight = 1337 - default: - print_string("Unknown!\n") - } -} + using f.entity + position = Vector3{1, 2, 3} -nl :: proc() { print_nl() } - -/* -// Demo 001 -#load "basic.odin" -#load "game.odin" - -main :: proc() { - // _ = hellope() - // procedures() - // variables() - // constants() - // types() - // data_control() - // using_fields() - - run_game() -} - - -hellope :: proc() -> int { - print_string("Hellope, 世界\n") - return 1 -} - - -// Line comment -/* - Block Comment -*/ -/* - Nested /* - Block /* - Comment - */ - */ -*/ - -apple, banana, carrot: bool -box, carboard: bool = true, false -// hellope_value: int = hellope() // The procedure is ran just before `main` - -variables :: proc() { - i: int // initialized with zero value - j: int = 1 - x, y: int = 1, 2 - - // Type inference - apple, banana, 世界 := true, 123, "world" - - - // Basic Types of the Language - // - // bool - // - // i8 i16 i32 i64 i128 - // u8 u16 u32 u64 u128 - // - // f32 f64 - // - // int uint (size_of(int) == size_of(uint) == size_of(rawptr)) - // - // rawptr (equivalent to void * in C/C++) - // - // string - // - // byte - alias for u8 - // rune - alias for i32 // Unicode Codepoint - // - // "untyped" types can implicitly convert to any of the "typed" types - // Default Type - // untyped bool - bool - // untyped integer - int - // untyped float - f64 - // untyped pointer - rawptr - // untyped string - string - // untyped rune - rune/i32 - - - // Zero values - zero_numeric := 0 - zero_boolean := false - zero_pointer := null - zero_string1 := "" // Escaped string - zero_string2 := `` // Raw string - // Compound types have a different kind of zero value - - // Unary operators - // +a - // -a - // ~a - // !a - - // Binary operators - // a + b add - // a - b sub - // a ~ b xor - // a | b or - - // a * b mul - // a / b quo - // a % b mod - // a & b and - // a &~ b bitclear == a & (~b) - // a << b shl - // a >> b shr - - // a as Type // Type cast - // a transmute Type // Bit cast - - // a == b eq - // a != b ne - // a < b lt - // a > b gt - // a <= b le - // a >= b ge - -} - -procedures :: proc() { - add :: proc(x: int, y: int) -> int { - return x + y - } - print_int(add(3, 4)) // 7 - print_nl() - - add_v2 :: proc(x, y: int) -> int { - return x + y - } - - fibonacci :: proc(n: int) -> int { - if n < 2 { - return n - } - return fibonacci(n-1) + fibonacci(n-2) - } - print_int(fibonacci(12)); nl() - - - swap_strings :: proc(x, y: string) -> (string, string) { - return y, x - } - a, b := swap_strings("Hellope\n", "World\n") - print_string(a) - print_string(b) - - a, b = b, a // Quirk of grammar the of multiple assignments - // Swap variables - print_string(a) - print_string(b) - - // Not a hint like C/C++, it's mandatory (unless it cannot do it but it will warn) - proc1 :: proc(a, b: int) #inline { - print_int(a + b) - } - proc2 :: proc(a, b: int) #no_inline { - print_int(a + b) - } - - print_int(3 ''add 4) // Infix style - print_nl() - print_int(12 'fibonacci) // Postfix style - print_nl() -} - - -TAU :: 6.28318530718 - -constants :: proc() { - TAU :: 6.28318530718 // untyped float - WORLD_JAPANESE :: "世界" // untyped string - - TAU_32 : f32 : 6.28318530718 - TAU_AS_32 :: 6.28318530718 as f32 - - PI :: TAU / 2 - - CLOSE_TO_PI :: 3 - - DIFF :: (PI - CLOSE_TO_PI) / PI // Evaluated at compile time - - a := TAU // the constant's value becomes typed as f32 - b := CLOSE_TO_PI // the constant's value becomes typed as int - c := DIFF -} - -nl :: proc() { print_nl() } - -types :: proc() { - - x: int = 123 - y := x // y: int = x - // z: f32 = x // invalid - z: f32 = x as f32 - - - ptr_z := ^z // Pascal notation - ptr_z^ = 123 // Derefence Notation - w: f32 = ptr_z^ // 123 - print_f32(z); nl() - - // ^z - pointer to z - // z^ - z from pointer - - // Implicit conversion to and from rawptr - r_ptr: rawptr = ptr_z - ptr_z = r_ptr - - - - - f32_array: [12]f32 // Array of 12 f32 - f32_array[0] = 2 - f32_array[1] = 3 - // f32_array[-1] = 2 // Error - compile time check - // f32_array[13] = 2 // Error - compile time check - f32_array_len := len(f32_array) // builtin procedure - f32_array_cap := cap(f32_array) // == len(f32_array) - - - mda: [2][3][4]int // Column-major - // mda[x][y][z] - - - - api: [2]^f32 - papi: ^[2]^f32 - - - - - f32_slice: []f32 // Slice / Array reference - f32_slice = f32_array[0:5] - f32_slice = f32_array[:5] - f32_slice = f32_array[:] // f32_array[0:len(f32_array)-1] - - f32_slice = f32_array[1:5:7] // low:1, high:5, max:7 - // len: 5-1 == 4 - // cap: 7-1 == 6 - - - - append_success := append(^f32_slice, 1) - _ = append(^f32_slice, 2) - - _ = copy(f32_array[0:2], f32_array[2:4]) // You can use memcpy/memmove if you want - - - - - - - s := "Hellope World" - sub_string: string = s[5:10] - - - - - - v0: {4}f32 // Vector of 4 f32 - v0[0] = 1 - v0[1] = 3 - v0[2] = 6 - v0[3] = 10 - - v1 := v0 + v0 // Simd Arithmetic - v1 = v1 - v0 - v1 *= v0 // i.e. hadamard product - v1 /= v0 - - // builtin procedure - v2 := swizzle(v0, 3, 2, 1, 0) // {10, 6, 3, 1} - - v3: {4}bool = v0 == v2 - // LLVM rant? - - - - - - - - Vec4 :: type {4}f32 - Array3Int :: type [3]int - - Vec3 :: type struct { - x, y, z: f32 - } - - BinaryNode :: type struct { - left, right: ^BinaryNode // same format as procedure argument - data: rawptr - } - - AddProc :: type proc(a, b: int) -> int - - Packed :: type struct #packed { - a: u8 - b: u16 - c: u32 - } - assert(size_of(Packed) == 7) // builtin procedure - { - a, b: ^BinaryNode - a = alloc(size_of(BinaryNode)) as ^BinaryNode - b = alloc(size_of(BinaryNode)) as ^BinaryNode - - c := BinaryNode{a, b, null} - c.left^.data = null - c.left.data = null // No need to deference - - dealloc(a) - dealloc(b) } { - MyInt :: type int - x: int = 1 - y: MyInt = 2 - // z := x + y // Failure - types cannot implicit convert* - z := x as MyInt + y // Type cast using `as` + // 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) + } { - // From: Quake III Arena - Q_rsqrt :: proc(number: f32) -> f32 { - i: i32 - x2, y: f32 - THREE_HALFS :: 1.5 + // More efficient subtyping - x2 = number * 0.5 - y = number - i = (^y as ^i32)^ // evil floating point bit level hacking - i = 0x5f3759df - i>>1 // what the fuck? - y = (^i as ^f32)^ - y = y * (THREE_HALFS - (x2 * y *y)) // 1st iteration - // y = y * (THREE_HALFS - (x2 * y *y)) // 2nd iteration, this can be removed - return y + Entity :: type struct { + position: Vector3 } - Q_rsqrt_v2 :: proc(number: f32) -> f32 { - THREE_HALFS :: 1.5 - - x2 := number * 0.5 - y := number - i := y transmute i32 // evil floating point bit level hacking - i = 0x5f3759df - i>>1 // what the fuck? - y = i transmute f32 - y = y * (THREE_HALFS - (x2 * y *y)) // 1st iteration - // y = y * (THREE_HALFS - (x2 * y *y)) // 2nd iteration, this can be removed - return y + Frog :: type struct { + jump_height: f32 + using entity: ^Entity } - // NOTE(bill): transmute only works if the size of the types are equal + MAX_ENTITES :: 64 + entities: [MAX_ENTITES]Entity + entity_count := 0 - /* - // in C - union { - i32 i - f32 y + 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 } - { // Enumeration - Thing :: type enum { - APPLE, - FROG, - TREE, - TOMB, + { + 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 + } } - a := Thing.APPLE - Sized :: type enum u64 { - APPLE, - FROG, - TREE, - TOMB, - } - assert(size_of(Sized) == size_of(u64)) + using Entity + f1: Frog = Frog{12, 0xff9900} + f2: Entity = Frog{12, 0xff9900} // Implicit cast + f3 := Frog{12, 0xff9900} as Entity // Explicit cast - Certain :: type enum { - APPLE = 3, - FROG, - TREE = 7, - TOMB, + // 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") } - assert(Certain.TOMB == 8) + + 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. } - { // Untagged union - BitHack :: type raw_union { - i: i32 - f: f32 - } - b: BitHack - b.f = 123 - print_int(b.i as int); print_nl() - - - // Manually tagged union + { + // 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, @@ -446,316 +742,133 @@ types :: proc() { } Entity :: type struct { + Guid :: type i64 + kind: EntityKind - guid: u64 + guid: Guid - // Other data + scope: ^Scope + token: Token + type_: ^Type - /*using*/ - data: union { - constant: struct{} - variable: struct{ - visited, is_field, used, anonymous: bool + 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 } - procedure: struct { used: bool } - buitlin: struct { id: i32 } } } - - // NOTE(bill): Tagged unions are not added yet but are planned - } - - - - { // Compound Literals - a := [3]int{1, 2, 3} - b := [3]int{} - c := [..]int{1, 2, 3} - - d := []int{1, 2, 3} // slice - - e := {4}f32{1, 2, 3, 4} - f := {4}f32{1} // broadcasts to all - // g := {4}f32{1, 2} // require either 1 or 4 elements - - Vec2 :: type {2}f32 - - h := Vec2{1, 2} - - i := Vec2{5} * h // For strong type safety - // FORENOTE: 5 * h was originally allowed but it was an edge case in the - // compiler I didn't think it was enough to justify have it it. - - print_f32(i[0]); print_rune(#rune ",") - print_f32(i[1]); print_nl() - } - - - - { // First class procedures - - do_thing :: proc(p: proc(a, b: int) -> int) { - print_int(p(3, 4)); nl() - } - - add :: proc(a, b: int) -> int { - return a + b - } - - - add_lambda := proc(a, b: int) -> int { - return a - b - } // note semicolon - - do_thing(add) - do_thing(add_lambda) - do_thing(proc(a, b: int) -> int { // Anonymous - return a * b - }) - } - - - - { // strings and runes - escaped := "Hellope World\n" - raw := `Hellope World\n` - print_string(escaped) - print_string(raw); nl() - - // Crap shader example - shader_string := -`#version 410 - -layout (location = 0) in vec3 a_position -layout (location = 1) in vec3 a_normal; -layout (location = 2) in vec2 a_tex_coord; - -out vec3 v_position; -out vec3 v_normal; -out vec2 v_tex_coord; - -uniform mat4 u_model_view; -uniform mat3 u_normal; -uniform mat4 u_proj; -uniform mat4 u_mvp; - -void main() { - v_tex_coord = a_tex_coord; - v_normal = normalize(u_normal * a_normal); - v_position = vec3(u_model_view * vec4(a_position, 1.0)); - - gl_Position = u_mvp * vec4(a_position, 1.0); -}`; - - - hearts1 := #rune "💕"; - hearts2 := #rune "\U0001f495"; // 32 bit - hearts3 := #rune "\xf0\x9f\x92\x95"; - - 㐒 := #rune "㐒"; - 㐒16 := #rune "\u4db5"; // 16 bit but will be `rune` - // String ideas "nicked" from Go, so far. I think I might change how some of it works later. - } - - - { // size, align, offset - Thing :: type struct { - a: u8; - b: u16; - c, d, e: u32; - } - - s := size_of(Thing); - a := align_of(Thing); - o := offset_of(Thing, b); - - t: Thing; - - sv := size_of_val(t); - av := align_of_val(t); - ov := offset_of_val(t.b); - } -} - -data_control :: proc() { - sum := 0 - for i := 0; i < 12; i++ { - sum += 1 - } - print_string("sum = "); print_int(sum); nl() - - sum = 1 - for ; sum < 1000000; { - sum += sum - } - print_string("sum = "); print_int(sum); nl() - - sum = 1 - for sum < 1000000 { - sum += sum - } - print_string("sum = "); print_int(sum); nl() - - // loop - // for { } == for true {} - - // Question: Should I separate all these concepts and rename it? - // - // range - iterable - // for - c style - // while - // loop - while true - - // Notes: - // conditions _must_ a boolean expression - // i++ and i-- are statements, not expressions - - - x := 2 - if x < 3 { - print_string("x < 2\n") - } - - // Unified initializer syntax - same as for statements - if x := 2; x < 3 { - print_string("x < 2\n") - } - - if x := 4; x < 3 { - print_string("Never called\n") - } else { - print_string("This is called\n") - } - - { // String comparison - a := "Hellope" - b := "World" - if a < b { - print_string("a < b\n") - } - if a != b { - print_string("a != b\n") - } - - } - - - - - { // Defer statement - defer print_string("日本語\n") - print_string("Japanese\n") + // Plus all the constructing procedures that go along with them!!!! + // It's a nightmare } { - defer print_string("1\n") - defer print_string("2\n") - defer print_string("3\n") + 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? } + { - prev_allocator := context.allocator - context.allocator = __default_allocator() - defer context.allocator = prev_allocator + // `Raw` unions still have uses, especially for mathematic types - File :: type struct { filename: string } - FileError :: type int; - open_file :: proc(filename: string) -> (File, FileError) { - return File{}, 0 - } - close_file :: proc(f: ^File) {} - f, err := open_file("Test") - if err != 0 { - // handle error - } - defer close_file(^f) - } - - for i := 0; i < 100; i++ { - blah := new_slice(int, 100) - defer { - defer print_string("!") - defer print_string("dealloc") - delete(blah) + Vector2 :: type raw_union { + using xy_: struct { x, y: f32 } + e: [2]f32 + v: {2}f32 } - if i == 3 { - // defers called - continue + Vector3 :: type raw_union { + using xyz_: struct { x, y, z: f32 } + xy: Vector2 + e: [3]f32 + v: {3}f32 } - if i == 5 { - // defers called - return // End of procedure - } + v2: Vector2 + v2.x = 1 + v2.e[0] = 1 + v2.v[0] = 1 - if i == 8 { - // defers called - break // never happens - } - } + v3: Vector3 + v3.x = 1 + v3.e[0] = 1 + v3.v[0] = 1 + v3.xy.x = 1 - defer print_string("It'll never happen, mate 1") - print_string("It'll never happen, mate 2") - print_string("It'll never happen, mate 3") -} - - -using_fields :: proc() { - { // Everyday stuff - Vec3 :: type struct { x, y, z: f32; } - - Entity :: type struct { - name: string; - using pos: Vec3; - vel: Vec3; - } - t: Entity; - t.y = 456; - print_f32(t.y); print_nl(); - print_f32(t.pos.y); print_nl(); - print_f32(t.vel.y); print_nl(); - - - Frog :: type struct { // Subtype (kind of) - using entity: Entity; - colour: u32; - jump_height: f32; - } - - f: Frog; - f.y = 1337; - print_f32(f.y); print_nl(); - print_f32(f.pos.y); print_nl(); - print_f32(f.vel.y); print_nl(); - - - Buffalo :: type struct { - using entity: Entity; - speed: f32; - noise_level: f32; - } - } - - - { // Crazy Shit - Vec2 :: type raw_union { - using _xy: struct {x, y: f32}; - e: [2]f32; - v: {2}f32; - } - - Entity :: type struct { - using pos: ^Vec2; - name: string; - } - t: Entity; - t.pos = alloc(size_of(Vec2)) as ^Vec2; // TODO(bill): make an alloc type? i.e. new(Type)? - t.x = 123; - print_f32(t._xy.x); print_nl(); - print_f32(t.pos.x); print_nl(); - print_f32(t.pos._xy.x); print_nl(); + // 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/game.odin b/examples/game.odin index 1568ebabe..050db29fe 100644 --- a/examples/game.odin +++ b/examples/game.odin @@ -6,9 +6,7 @@ TWO_HEARTS :: #rune "💕" win32_perf_count_freq := GetQueryPerformanceFrequency() time_now :: proc() -> f64 { - if win32_perf_count_freq == 0 { - debug_trap() - } + assert(win32_perf_count_freq != 0) counter: i64 _ = QueryPerformanceCounter(^counter) @@ -25,10 +23,10 @@ win32_print_last_error :: proc() { } // Yuk! -to_c_string :: proc(s: string) -> ^u8 { - c_str: ^u8 = alloc(len(s)+1) - memory_copy(c_str, ^s[0], len(s)) - ptr_offset(c_str, len(s))^ = 0 +to_c_string :: proc(s: string) -> []u8 { + c_str := new_slice(u8, len(s)+1) + _ = copy(c_str, s as []byte) + c_str[len(s)] = 0 return c_str } @@ -39,7 +37,7 @@ Window :: type struct { dc: HDC hwnd: HWND opengl_context, rc: HGLRC - c_title: ^u8 + c_title: []u8 } make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (Window, bool) { @@ -65,7 +63,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W } w.hwnd = CreateWindowExA(0, - c_class_name, w.c_title, + c_class_name, ^w.c_title[0], WS_VISIBLE | WS_OVERLAPPED | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX, CW_USEDEFAULT, CW_USEDEFAULT, w.width as i32, w.height as i32, @@ -114,7 +112,7 @@ make_window :: proc(title: string, msg, height: int, window_proc: WNDPROC) -> (W } destroy_window :: proc(w: ^Window) { - heap_free(w.c_title) + delete(w.c_title) } display_window :: proc(w: ^Window) { diff --git a/examples/runtime.odin b/examples/runtime.odin index 7a76a3bff..44ae3d64a 100644 --- a/examples/runtime.odin +++ b/examples/runtime.odin @@ -1,15 +1,25 @@ #load "win32.odin" -debug_trap :: proc() #foreign "llvm.debugtrap" +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" + +bit_reverse16 :: proc(b: u16) -> u16 #foreign "llvm.bitreverse.i16" +bit_reverse32 :: proc(b: u32) -> u32 #foreign "llvm.bitreverse.i32" +bit_reverse64 :: proc(b: u64) -> u64 #foreign "llvm.bitreverse.i64" + +byte_swap16 :: proc(b: u16) -> u16 #foreign "llvm.bswap.i16" +byte_swap32 :: proc(b: u32) -> u32 #foreign "llvm.bswap.i32" +byte_swap64 :: proc(b: u64) -> u64 #foreign "llvm.bswap.i64" + +fmuladd_f32 :: proc(a, b, c: f32) -> f32 #foreign "llvm.fmuladd.f32" +fmuladd_f64 :: proc(a, b, c: f64) -> f64 #foreign "llvm.fmuladd.f64" // TODO(bill): make custom heap procedures -heap_alloc :: proc(len: int) -> rawptr { - return HeapAlloc(GetProcessHeap(), HEAP_ZERO_MEMORY, len) -} -heap_free :: proc(ptr: rawptr) { - _ = HeapFree(GetProcessHeap(), 0, ptr) -} - +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) @@ -288,10 +298,10 @@ Allocator :: type struct { Context :: type struct { - thread_id: int + thread_ptr: rawptr - user_index: int user_data: rawptr + user_index: int allocator: Allocator } @@ -305,6 +315,10 @@ __check_context :: proc() { if context.allocator.procedure == null { context.allocator = __default_allocator() } + if context.thread_ptr == null { + // TODO(bill): + // context.thread_ptr = current_thread_pointer() + } } @@ -368,13 +382,11 @@ __default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocation_Mode, using Allocation_Mode match mode { case ALLOC: - data := heap_alloc(size) - memory_zero(data, size) - return data + return heap_alloc(size) case RESIZE: return default_resize_align(old_memory, old_size, size, alignment) case DEALLOC: - heap_free(old_memory) + heap_dealloc(old_memory) case DEALLOC_ALL: // NOTE(bill): Does nothing } @@ -394,5 +406,7 @@ __default_allocator :: proc() -> Allocator { __assert :: proc(msg: string) { file_write(file_get_standard(File_Standard.ERROR), msg as []byte) - debug_trap() + // TODO(bill): Which is better? + // __trap() + __debug_trap() } diff --git a/src/codegen/codegen.cpp b/src/codegen/codegen.cpp index a583af2c8..7ad1d56e2 100644 --- a/src/codegen/codegen.cpp +++ b/src/codegen/codegen.cpp @@ -118,6 +118,7 @@ void ssa_gen_tree(ssaGen *s) { } if (are_strings_equal(name, original_name)) { + #if 0 Scope *scope = *map_get(&info->scopes, hash_pointer(pd->type)); isize count = multi_map_count(&scope->elements, hash_string(original_name)); if (count > 1) { @@ -127,6 +128,7 @@ void ssa_gen_tree(ssaGen *s) { name_len = gb_snprintf(cast(char *)name_text, name_len, "%.*s$%d", LIT(name), e->guid); name = make_string(name_text, name_len-1); } + #endif } ssaValue *p = ssa_make_value_procedure(a, m, e->type, decl->type_expr, body, name); @@ -208,5 +210,9 @@ void ssa_gen_tree(ssaGen *s) { void ssa_gen_ir(ssaGen *s) { - ssa_print_llvm_ir(&s->output_file, &s->module); + ssaFileBuffer buf = {}; + ssa_file_buffer_init(&buf, &s->output_file); + defer (ssa_file_buffer_destroy(&buf)); + + ssa_print_llvm_ir(&buf, &s->module); } diff --git a/src/codegen/print_llvm.cpp b/src/codegen/print_llvm.cpp index e4e804aa1..fffdee805 100644 --- a/src/codegen/print_llvm.cpp +++ b/src/codegen/print_llvm.cpp @@ -1,20 +1,53 @@ -#define SSA_PRINT_TO_STDOUT 0 +struct ssaFileBuffer { + gbVirtualMemory vm; + isize offset; + gbFile *output; +}; -void ssa_fprintf(gbFile *f, char *fmt, ...) { +void ssa_file_buffer_init(ssaFileBuffer *f, gbFile *output) { + isize size = 8*gb_virtual_memory_page_size(NULL); + f->vm = gb_vm_alloc(NULL, size); + f->offset = 0; + f->output = output; +} + +void ssa_file_buffer_destroy(ssaFileBuffer *f) { + if (f->offset > 0) { + // NOTE(bill): finish writing buffered data + gb_file_write(f->output, f->vm.data, f->offset); + } + + gb_vm_free(f->vm); +} + +void ssa_file_buffer_write(ssaFileBuffer *f, void *data, isize len) { + if (len > f->vm.size) { + gb_file_write(f->output, data, len); + return; + } + + if ((f->vm.size - f->offset) < len) { + gb_file_write(f->output, f->vm.data, f->offset); + f->offset = 0; + } + u8 *cursor = cast(u8 *)f->vm.data + f->offset; + gb_memcopy(cursor, data, len); + f->offset += len; +} + + +void ssa_fprintf(ssaFileBuffer *f, char *fmt, ...) { va_list va; va_start(va, fmt); - gb_fprintf_va(f, fmt, va); -#if SSA_PRINT_TO_STDOUT - gb_printf_va(fmt, va); -#endif + char buf[4096] = {}; + isize len = gb_snprintf_va(buf, gb_size_of(buf), fmt, va); + ssa_file_buffer_write(f, buf, len-1); va_end(va); } -void ssa_file_write(gbFile *f, void *data, isize len) { - gb_file_write(f, data, len); -#if SSA_PRINT_TO_STDOUT - gb_file_write(gb_file_get_standard(gbFileStandard_Output), data, len); -#endif + +void ssa_file_write(ssaFileBuffer *f, void *data, isize len) { + ssa_file_buffer_write(f, data, len); } b32 ssa_valid_char(u8 c) { @@ -35,7 +68,7 @@ b32 ssa_valid_char(u8 c) { return false; } -void ssa_print_escape_string(gbFile *f, String name, b32 print_quotes) { +void ssa_print_escape_string(ssaFileBuffer *f, String name, b32 print_quotes) { isize extra = 0; for (isize i = 0; i < name.len; i++) { u8 c = name.text[i]; @@ -81,18 +114,21 @@ void ssa_print_escape_string(gbFile *f, String name, b32 print_quotes) { -void ssa_print_encoded_local(gbFile *f, String name) { +void ssa_print_encoded_local(ssaFileBuffer *f, String name) { ssa_fprintf(f, "%%"); ssa_print_escape_string(f, name, true); } -void ssa_print_encoded_global(gbFile *f, String name) { +void ssa_print_encoded_global(ssaFileBuffer *f, String name, b32 global_scope = false) { ssa_fprintf(f, "@"); + if (!global_scope) { + ssa_fprintf(f, "."); + } ssa_print_escape_string(f, name, true); } -void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { +void ssa_print_type(ssaFileBuffer *f, BaseTypeSizes s, Type *t) { i64 word_bits = 8*s.word_size; GB_ASSERT_NOT_NULL(t); t = default_type(t); @@ -113,8 +149,8 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { case Basic_u128: ssa_fprintf(f, "u128"); break; case Basic_f32: ssa_fprintf(f, "float"); break; case Basic_f64: ssa_fprintf(f, "double"); break; - case Basic_rawptr: ssa_fprintf(f, "%%.rawptr"); break; - case Basic_string: ssa_fprintf(f, "%%.string"); break; + case Basic_rawptr: ssa_fprintf(f, "%%..rawptr"); break; + case Basic_string: ssa_fprintf(f, "%%..string"); break; case Basic_uint: ssa_fprintf(f, "i%lld", word_bits); break; case Basic_int: ssa_fprintf(f, "i%lld", word_bits); break; } @@ -213,7 +249,7 @@ void ssa_print_type(gbFile *f, BaseTypeSizes s, Type *t) { } } -void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type) { +void ssa_print_exact_value(ssaFileBuffer *f, ssaModule *m, ExactValue value, Type *type) { type = get_base_type(type); if (is_type_float(type)) { value = exact_value_to_float(value); @@ -276,12 +312,12 @@ void ssa_print_exact_value(gbFile *f, ssaModule *m, ExactValue value, Type *type } } -void ssa_print_block_name(gbFile *f, ssaBlock *b) { +void ssa_print_block_name(ssaFileBuffer *f, ssaBlock *b) { ssa_print_escape_string(f, b->label, false); ssa_fprintf(f, "..%d", b->id); } -void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint) { +void ssa_print_value(ssaFileBuffer *f, ssaModule *m, ssaValue *value, Type *type_hint) { if (value == NULL) { ssa_fprintf(f, "!!!NULL_VALUE"); return; @@ -300,7 +336,7 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint) ssa_print_encoded_local(f, value->Param.entity->token.string); break; case ssaValue_Proc: - ssa_print_encoded_global(f, value->Proc.name); + ssa_print_encoded_global(f, value->Proc.name, (value->Proc.tags & ProcTag_foreign) != 0); break; case ssaValue_Instr: ssa_fprintf(f, "%%%d", value->id); @@ -308,7 +344,7 @@ void ssa_print_value(gbFile *f, ssaModule *m, ssaValue *value, Type *type_hint) } } -void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { +void ssa_print_instr(ssaFileBuffer *f, ssaModule *m, ssaValue *value) { GB_ASSERT(value->kind == ssaValue_Instr); ssaInstr *instr = &value->Instr; @@ -316,7 +352,7 @@ void ssa_print_instr(gbFile *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_STARTUP_RUNTIME_PROC_NAME "()\n"); } break; case ssaInstr_Comment: @@ -471,7 +507,9 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { case Token_GtEq: runtime_proc = "__string_gt"; break; } - ssa_fprintf(f, " @%s(", runtime_proc); + ssa_fprintf(f, " "); + ssa_print_encoded_global(f, make_string(runtime_proc), false); + ssa_fprintf(f, "("); ssa_print_type(f, m->sizes, type); ssa_fprintf(f, " "); ssa_print_value(f, m, bo->left, type); @@ -605,7 +643,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { } break; case ssaInstr_MemCopy: { - ssa_fprintf(f, "call void @memory_move"); + 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* "); @@ -689,7 +727,7 @@ void ssa_print_instr(gbFile *f, ssaModule *m, ssaValue *value) { } } -void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { +void ssa_print_proc(ssaFileBuffer *f, ssaModule *m, ssaProcedure *proc) { if (proc->body == NULL) { ssa_fprintf(f, "\ndeclare "); } else { @@ -705,8 +743,11 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { } ssa_fprintf(f, " "); - - ssa_print_encoded_global(f, proc->name); + if (are_strings_equal(proc->name, make_string("main"))) { + ssa_print_encoded_global(f, proc->name, true); + } else { + ssa_print_encoded_global(f, proc->name, (proc->tags & ProcTag_foreign) != 0); + } ssa_fprintf(f, "("); if (proc_type->param_count > 0) { @@ -760,7 +801,7 @@ void ssa_print_proc(gbFile *f, ssaModule *m, ssaProcedure *proc) { } } -void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) { +void ssa_print_type_name(ssaFileBuffer *f, ssaModule *m, ssaValue *v) { GB_ASSERT(v->kind == ssaValue_TypeName); Type *base_type = get_base_type(ssa_type(v)); if (!is_type_struct(base_type) && !is_type_union(base_type)) { @@ -772,17 +813,17 @@ void ssa_print_type_name(gbFile *f, ssaModule *m, ssaValue *v) { ssa_fprintf(f, "\n"); } -void ssa_print_llvm_ir(gbFile *f, ssaModule *m) { +void ssa_print_llvm_ir(ssaFileBuffer *f, ssaModule *m) { if (m->layout.len > 0) { ssa_fprintf(f, "target datalayout = \"%.*s\"\n", LIT(m->layout)); } - ssa_print_encoded_local(f, make_string(".string")); + ssa_print_encoded_local(f, make_string("..string")); ssa_fprintf(f, " = type {i8*, "); ssa_print_type(f, m->sizes, t_int); ssa_fprintf(f, "} ; Basic_string\n"); - ssa_print_encoded_local(f, make_string(".rawptr")); + ssa_print_encoded_local(f, make_string("..rawptr")); ssa_fprintf(f, " = type i8* ; Basic_rawptr\n\n"); gb_for_array(member_index, m->members.entries) { diff --git a/src/codegen/ssa.cpp b/src/codegen/ssa.cpp index 50d33c366..e7d4aaf4b 100644 --- a/src/codegen/ssa.cpp +++ b/src/codegen/ssa.cpp @@ -742,9 +742,10 @@ ssaValue *ssa_emit_call(ssaProcedure *p, ssaValue *value, ssaValue **args, isize return ssa_emit(p, ssa_make_instr_call(p, value, args, arg_count, results)); } -ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name, ssaValue **args, isize arg_count) { - ssaValue **found = map_get(&proc->module->members, hash_string(make_string(name))); - GB_ASSERT_MSG(found != NULL, "%s", name); +ssaValue *ssa_emit_global_call(ssaProcedure *proc, char *name_, ssaValue **args, isize arg_count) { + String name = make_string(name_); + ssaValue **found = map_get(&proc->module->members, hash_string(name)); + GB_ASSERT_MSG(found != NULL, "%.*s", LIT(name)); ssaValue *gp = *found; return ssa_emit_call(proc, gp, args, arg_count); } @@ -1229,7 +1230,7 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) { isize max_len = 4+8+1; u8 *str = cast(u8 *)gb_alloc_array(a, u8, max_len); - isize len = gb_snprintf(cast(char *)str, max_len, ".str%x", proc->module->global_string_index); + isize len = gb_snprintf(cast(char *)str, max_len, "__str$%x", proc->module->global_string_index); proc->module->global_string_index++; String name = make_string(str, len-1); @@ -1238,7 +1239,8 @@ ssaValue *ssa_add_global_string_array(ssaProcedure *proc, ExactValue value) { Type *type = make_type_array(a, t_u8, value.value_string.len); Entity *entity = make_entity_constant(a, NULL, token, type, value); ssaValue *g = ssa_make_value_global(a, entity, ssa_make_value_constant(a, type, value)); - g->Global.is_private = true; + g->Global.is_private = true; + g->Global.is_constant = true; map_set(&proc->module->values, hash_pointer(entity), g); map_set(&proc->module->members, hash_string(name), g); @@ -2780,6 +2782,9 @@ void ssa_build_stmt(ssaProcedure *proc, AstNode *node) { Entity *f = *map_get(&info->foreign_procs, hash_string(name)); ssaValue *value = ssa_make_value_procedure(proc->module->allocator, proc->module, e->type, pd->type, pd->body, name); + + value->Proc.tags = pd->tags; + ssa_module_add_value(proc->module, e, value); ssa_build_proc(value, proc); if (e == f) { diff --git a/src/main.cpp b/src/main.cpp index 4dc85bb15..0b4c955d1 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -40,14 +40,29 @@ i32 win32_exec_command_line_app(char *fmt, ...) { } } + +#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; \ + end_time = gb_utc_time_now(); \ + diff = end_time - start_time; \ + total_time += diff; \ + gb_printf_err("%s: %lld ms\n", section, diff/1000); \ + start_time = gb_utc_time_now(); \ +} while (0) + +#define PRINT_ACCUMULATION() do { \ + gb_printf_err("Total compilation time: %lld ms\n", total_time/1000); \ +} while (0) + + int main(int argc, char **argv) { if (argc < 2) { gb_printf_err("Please specify a .odin file\n"); return 1; } - u64 start_time, end_time; - start_time = gb_utc_time_now(); + INIT_TIMER(); init_universal_scope(); @@ -67,9 +82,7 @@ int main(int argc, char **argv) { if (parse_files(&parser, init_filename) != ParseFile_None) return 1; - end_time = gb_utc_time_now(); - gb_printf_err("Parser: %lld ms\n", (end_time - start_time)/1000); - start_time = gb_utc_time_now(); + PRINT_TIMER("Syntax Parser"); // print_ast(parser.files[0].decls, 0); @@ -81,9 +94,7 @@ int main(int argc, char **argv) { check_parsed_files(&checker); - // end_time = gb_utc_time_now(); - // gb_printf_err("Checker: %lld ms\n", (end_time - start_time)/1000); - // start_time = gb_utc_time_now(); + PRINT_TIMER("Semantic Checker"); #if 1 ssaGen ssa = {}; @@ -93,17 +104,12 @@ int main(int argc, char **argv) { ssa_gen_tree(&ssa); - // end_time = gb_utc_time_now(); - // gb_printf_err("ssa tree: %lld ms\n", (end_time - start_time)/1000); - // start_time = gb_utc_time_now(); + PRINT_TIMER("SSA Tree"); // TODO(bill): Speedup writing to file for IR code ssa_gen_ir(&ssa); - // end_time = gb_utc_time_now(); - // gb_printf_err("ssa ir: %lld ms\n", (end_time - start_time)/1000); - // start_time = gb_utc_time_now(); - + PRINT_TIMER("SSA IR"); char const *output_name = ssa.output_file.filename; isize base_name_len = gb_path_extension(output_name)-1 - output_name; @@ -112,16 +118,23 @@ int main(int argc, char **argv) { i32 exit_code = 0; exit_code = win32_exec_command_line_app( - "../misc/llvm-bin/opt -mem2reg %s -o %.*s.bc", + "../misc/llvm-bin/opt %s -o %.*s.bc " + "-memcpyopt " + "-mem2reg " + "-die -dse " + "-dce " + // "-S " + // "-debug-pass=Arguments " + "", output_name, cast(int)base_name_len, output_name); if (exit_code != 0) return exit_code; - // end_time = gb_utc_time_now(); - // gb_printf_err("llvm-opt: %lld ms\n", (end_time - start_time)/1000); - // start_time = gb_utc_time_now(); + PRINT_TIMER("llvm-opt"); +#if 1 gbString lib_str = gb_string_make(gb_heap_allocator(), "-lKernel32.lib"); + defer (gb_string_free(lib_str)); char lib_str_buf[1024] = {}; gb_for_array(i, parser.system_libraries) { String lib = parser.system_libraries[i]; @@ -130,26 +143,26 @@ int main(int argc, char **argv) { lib_str = gb_string_appendc(lib_str, lib_str_buf); } - exit_code = win32_exec_command_line_app( - "clang -o %.*s.exe %.*s.bc " + "clang %.*s.bc -o %.*s.o " + "-O0 " "-Wno-override-module " - // "-nostartfiles " - "%s " - , + "%s", cast(int)base_name_len, output_name, cast(int)base_name_len, output_name, lib_str); - gb_string_free(lib_str); + if (exit_code != 0) return exit_code; - // end_time = gb_utc_time_now(); - // gb_printf_err("clang: %lld ms\n\n\n", (end_time - start_time)/1000); + PRINT_TIMER("clang-compiler"); + + PRINT_ACCUMULATION(); if (run_output) { win32_exec_command_line_app("%.*s.exe", cast(int)base_name_len, output_name); } +#endif #endif return 0; diff --git a/src/parser.cpp b/src/parser.cpp index eb7d05952..afe52ae6b 100644 --- a/src/parser.cpp +++ b/src/parser.cpp @@ -33,6 +33,7 @@ struct AstFile { ErrorCollector error_collector; + // TODO(bill): Error recovery // NOTE(bill): Error recovery #define PARSER_MAX_FIX_COUNT 6 isize fix_count;