This commit is contained in:
jockus
2021-05-27 12:01:28 +01:00
66 changed files with 5644 additions and 2820 deletions
@@ -7,6 +7,8 @@ assignees: ''
--- ---
# PLEASE POST THIS IN THE DISCUSSION TAB UNDER "PROPOSALS" OR "IDEAS/REQUESTS"
**Is your feature request related to a problem? Please describe.** **Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
+1 -1
View File
@@ -1,4 +1,4 @@
Copyright (c) 2016-2020 Ginger Bill. All rights reserved. Copyright (c) 2016-2021 Ginger Bill. All rights reserved.
Redistribution and use in source and binary forms, with or without Redistribution and use in source and binary forms, with or without
modification, are permitted provided that the following conditions are met: modification, are permitted provided that the following conditions are met:
+21 -16
View File
@@ -8,26 +8,31 @@ CC=clang
OS=$(shell uname) OS=$(shell uname)
ifeq ($(OS), Darwin) ifeq ($(OS), Darwin)
LLVM_CONFIG=llvm-config LLVM_CONFIG=llvm-config
ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config
else
$(error "Requirement: llvm-config must be version 11")
endif
LDFLAGS:=$(LDFLAGS) -liconv LDFLAGS:=$(LDFLAGS) -liconv
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) -lLLVM-C LDFLAGS:=$(LDFLAGS) -lLLVM-C
endif endif
ifeq ($(OS), Linux) ifeq ($(OS), Linux)
LLVM_CONFIG=llvm-config-11 LLVM_CONFIG=llvm-config-11
ifneq ($(shell which llvm-config-11 2>/dev/null),) ifneq ($(shell which llvm-config-11 2>/dev/null),)
LLVM_CONFIG=llvm-config-11 LLVM_CONFIG=llvm-config-11
else else
ifneq ($(shell llvm-config --version | grep '^11\.'),) ifneq ($(shell llvm-config --version | grep '^11\.'),)
LLVM_CONFIG=llvm-config LLVM_CONFIG=llvm-config
else else
$(error "Requirement: llvm-config must be version 11") $(error "Requirement: llvm-config must be version 11")
endif endif
endif endif
CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags) CFLAGS:=$(CFLAGS) $(shell $(LLVM_CONFIG) --cxxflags --ldflags)
LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs) LDFLAGS:=$(LDFLAGS) $(shell $(LLVM_CONFIG) --libs core native --system-libs)
endif endif
all: debug demo all: debug demo
+8
View File
@@ -526,6 +526,14 @@ replace :: proc(s, old, new: []byte, n: int, allocator := context.allocator) ->
return; return;
} }
remove :: proc(s, key: []byte, n: int, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
return replace(s, key, {}, n, allocator);
}
remove_all :: proc(s, key: []byte, allocator := context.allocator) -> (output: []byte, was_allocation: bool) {
return remove(s, key, -1, allocator);
}
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1}; @(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+3 -3
View File
@@ -641,9 +641,9 @@ fmt_write_padding :: proc(fi: ^Info, width: int) {
return; return;
} }
pad_byte: byte = '0'; pad_byte: byte = ' ';
if fi.space { if !fi.space {
pad_byte = ' '; pad_byte = '0';
} }
for i := 0; i < width; i += 1 { for i := 0; i < width; i += 1 {
+8
View File
@@ -31,6 +31,13 @@ overflow_add :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_sub :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok --- overflow_mul :: proc(lhs, rhs: $T) -> (T, bool) #optional_ok ---
sqrt :: proc(x: $T) -> T where type_is_float(T) ---
mem_copy :: proc(dst, src: rawptr, len: int) ---
mem_copy_non_overlapping :: proc(dst, src: rawptr, len: int) ---
mem_zero :: proc(ptr: rawptr, len: int) ---
fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- fixed_point_mul :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- fixed_point_div :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
fixed_point_mul_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) --- fixed_point_mul_sat :: proc(lhs, rhs: $T, #const scale: uint) -> T where type_is_integer(T) ---
@@ -159,6 +166,7 @@ type_is_simd_vector :: proc($T: typeid) -> bool ---
type_has_nil :: proc($T: typeid) -> bool --- type_has_nil :: proc($T: typeid) -> bool ---
type_is_specialization_of :: proc($T, $S: typeid) -> bool --- type_is_specialization_of :: proc($T, $S: typeid) -> bool ---
type_is_variant_of :: proc($U, $V: typeid) -> bool where type_is_union(U) ---
type_has_field :: proc($T: typeid, $name: string) -> bool --- type_has_field :: proc($T: typeid, $name: string) -> bool ---
+2 -2
View File
@@ -6,9 +6,9 @@ Rand :: struct {
} }
@(private, static) @(private)
_GLOBAL_SEED_DATA := 1234567890; _GLOBAL_SEED_DATA := 1234567890;
@(private, static) @(private)
global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA))); global_rand := create(u64(uintptr(&_GLOBAL_SEED_DATA)));
set_global_seed :: proc(seed: u64) { set_global_seed :: proc(seed: u64) {
+7 -11
View File
@@ -16,16 +16,12 @@ Proc_Inlining :: enum u32 {
No_Inline = 2, No_Inline = 2,
} }
Proc_Calling_Convention :: enum i32 { Proc_Calling_Convention_Extra :: enum i32 {
Invalid = 0, Foreign_Block_Default,
Odin, }
Contextless, Proc_Calling_Convention :: union {
C_Decl, string,
Std_Call, Proc_Calling_Convention_Extra,
Fast_Call,
None,
Foreign_Block_Default = -1,
} }
Node_State_Flag :: enum { Node_State_Flag :: enum {
@@ -69,7 +65,7 @@ File :: struct {
pkg: ^Package, pkg: ^Package,
fullpath: string, fullpath: string,
src: []byte, src: string,
docs: ^Comment_Group, docs: ^Comment_Group,
+41
View File
@@ -0,0 +1,41 @@
package odin_format
import "core:odin/printer"
import "core:odin/parser"
import "core:odin/ast"
default_style := printer.default_style;
simplify :: proc(file: ^ast.File) {
}
format :: proc(filepath: string, source: string, config: printer.Config, parser_flags := parser.Flags{}, allocator := context.allocator) -> (string, bool) {
config := config;
pkg := ast.Package {
kind = .Normal,
};
file := ast.File {
pkg = &pkg,
src = source,
fullpath = filepath,
};
config.newline_limit = clamp(config.newline_limit, 0, 16);
config.spaces = clamp(config.spaces, 1, 16);
config.align_length_break = clamp(config.align_length_break, 0, 64);
p := parser.default_parser(parser_flags);
ok := parser.parse_file(&p, &file);
if !ok || file.syntax_error_count > 0 {
return {}, false;
}
prnt := printer.make_printer(config, allocator);
return printer.print(&prnt, &file), true;
}
+1 -1
View File
@@ -39,7 +39,7 @@ collect_package :: proc(path: string) -> (pkg: ^ast.Package, success: bool) {
} }
file := ast.new(ast.File, NO_POS, NO_POS); file := ast.new(ast.File, NO_POS, NO_POS);
file.pkg = pkg; file.pkg = pkg;
file.src = src; file.src = string(src);
file.fullpath = fullpath; file.fullpath = fullpath;
pkg.files[fullpath] = file; pkg.files[fullpath] = file;
} }
+79 -50
View File
@@ -8,10 +8,21 @@ import "core:fmt"
Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any); Warning_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any);
Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any); Error_Handler :: #type proc(pos: tokenizer.Pos, fmt: string, args: ..any);
Flag :: enum u32 {
Optional_Semicolons,
}
Flags :: distinct bit_set[Flag; u32];
Parser :: struct { Parser :: struct {
file: ^ast.File, file: ^ast.File,
tok: tokenizer.Tokenizer, tok: tokenizer.Tokenizer,
// If .Optional_Semicolons is true, semicolons are completely as statement terminators
// different to .Insert_Semicolon in tok.flags
flags: Flags,
warn: Warning_Handler, warn: Warning_Handler,
err: Error_Handler, err: Error_Handler,
@@ -100,8 +111,9 @@ end_pos :: proc(tok: tokenizer.Token) -> tokenizer.Pos {
return pos; return pos;
} }
default_parser :: proc() -> Parser { default_parser :: proc(flags := Flags{}) -> Parser {
return Parser { return Parser {
flags = flags,
err = default_error_handler, err = default_error_handler,
warn = default_warning_handler, warn = default_warning_handler,
}; };
@@ -128,6 +140,10 @@ parse_file :: proc(p: ^Parser, file: ^ast.File) -> bool {
p.line_comment = nil; p.line_comment = nil;
} }
if .Optional_Semicolons in p.flags {
p.tok.flags += {.Insert_Semicolon};
}
p.file = file; p.file = file;
tokenizer.init(&p.tok, file.src, file.fullpath, p.err); tokenizer.init(&p.tok, file.src, file.fullpath, p.err);
if p.tok.ch <= 0 { if p.tok.ch <= 0 {
@@ -400,6 +416,11 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if node == nil { if node == nil {
return false; return false;
} }
if .Optional_Semicolons in p.flags {
return true;
}
switch n in node.derived { switch n in node.derived {
case ast.Empty_Stmt, ast.Block_Stmt: case ast.Empty_Stmt, ast.Block_Stmt:
return true; return true;
@@ -439,14 +460,34 @@ is_semicolon_optional_for_node :: proc(p: ^Parser, node: ^ast.Node) -> bool {
return false; return false;
} }
expect_semicolon_newline_error :: proc(p: ^Parser, token: tokenizer.Token, s: ^ast.Node) {
if .Optional_Semicolons not_in p.flags && .Insert_Semicolon in p.tok.flags && token.text == "\n" {
#partial switch token.kind {
case .Close_Brace:
case .Close_Paren:
case .Else:
return;
}
if is_semicolon_optional_for_node(p, s) {
return;
}
tok := token;
tok.pos.column -= 1;
error(p, tok.pos, "expected ';', got newline");
}
}
expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool { expect_semicolon :: proc(p: ^Parser, node: ^ast.Node) -> bool {
if allow_token(p, .Semicolon) { if allow_token(p, .Semicolon) {
expect_semicolon_newline_error(p, p.prev_tok, node);
return true; return true;
} }
prev := p.prev_tok; prev := p.prev_tok;
if prev.kind == .Semicolon { if prev.kind == .Semicolon {
expect_semicolon_newline_error(p, p.prev_tok, node);
return true; return true;
} }
@@ -615,7 +656,7 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
cond = parse_expr(p, false); cond = parse_expr(p, false);
} else { } else {
init = parse_simple_stmt(p, nil); init = parse_simple_stmt(p, nil);
if allow_token(p, .Semicolon) { if parse_control_statement_semicolon_separator(p) {
cond = parse_expr(p, false); cond = parse_expr(p, false);
} else { } else {
cond = convert_stmt_to_expr(p, init, "boolean expression"); cond = convert_stmt_to_expr(p, init, "boolean expression");
@@ -668,6 +709,18 @@ parse_if_stmt :: proc(p: ^Parser) -> ^ast.If_Stmt {
return if_stmt; return if_stmt;
} }
parse_control_statement_semicolon_separator :: proc(p: ^Parser) -> bool {
tok := peek_token(p);
if tok.kind != .Open_Brace {
return allow_token(p, .Semicolon);
}
if tok.text == ";" {
return allow_token(p, .Semicolon);
}
return false;
}
parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt { parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
if p.curr_proc == nil { if p.curr_proc == nil {
error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope"); error(p, p.curr_tok.pos, "you cannot use a for statement in the file scope");
@@ -716,7 +769,7 @@ parse_for_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
} }
} }
if !is_range && allow_token(p, .Semicolon) { if !is_range && parse_control_statement_semicolon_separator(p) {
init = cond; init = cond;
cond = nil; cond = nil;
if p.curr_tok.kind != .Semicolon { if p.curr_tok.kind != .Semicolon {
@@ -820,7 +873,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In}); tag = parse_simple_stmt(p, {Stmt_Allow_Flag.In});
if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In { if as, ok := tag.derived.(ast.Assign_Stmt); ok && as.op.kind == .In {
is_type_switch = true; is_type_switch = true;
} else if allow_token(p, .Semicolon) { } else if parse_control_statement_semicolon_separator(p) {
init = tag; init = tag;
tag = nil; tag = nil;
if p.curr_tok.kind != .Open_Brace { if p.curr_tok.kind != .Open_Brace {
@@ -831,6 +884,7 @@ parse_switch_stmt :: proc(p: ^Parser) -> ^ast.Stmt {
} }
skip_possible_newline(p);
open := expect_token(p, .Open_Brace); open := expect_token(p, .Open_Brace);
for p.curr_tok.kind == .Case { for p.curr_tok.kind == .Case {
@@ -958,6 +1012,7 @@ parse_foreign_block :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Foreign_Bl
defer p.in_foreign_block = prev_in_foreign_block; defer p.in_foreign_block = prev_in_foreign_block;
p.in_foreign_block = true; p.in_foreign_block = true;
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace); open := expect_token(p, .Open_Brace);
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
decl := parse_foreign_block_decl(p); decl := parse_foreign_block_decl(p);
@@ -1287,7 +1342,7 @@ token_precedence :: proc(p: ^Parser, kind: tokenizer.Token_Kind) -> int {
#partial switch kind { #partial switch kind {
case .Question, .If, .When: case .Question, .If, .When:
return 1; return 1;
case .Ellipsis, .Range_Half: case .Ellipsis, .Range_Half, .Range_Full:
if !p.allow_range { if !p.allow_range {
return 0; return 0;
} }
@@ -1884,24 +1939,12 @@ parse_results :: proc(p: ^Parser) -> (list: ^ast.Field_List, diverging: bool) {
string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention { string_to_calling_convention :: proc(s: string) -> ast.Proc_Calling_Convention {
if s[0] != '"' && s[0] != '`' { if s[0] != '"' && s[0] != '`' {
return .Invalid; return nil;
} }
switch s[1:len(s)-1] { if len(s) == 2 {
case "odin": return nil;
return .Odin;
case "contextless":
return .Contextless;
case "cdecl", "c":
return .C_Decl;
case "stdcall", "std":
return .Std_Call;
case "fast", "fastcall":
return .Fast_Call;
case "none":
return .None;
} }
return .Invalid; return s;
} }
parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) { parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
@@ -1926,21 +1969,17 @@ parse_proc_tags :: proc(p: ^Parser) -> (tags: ast.Proc_Tags) {
} }
parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type { parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
cc := ast.Proc_Calling_Convention.Invalid; cc: ast.Proc_Calling_Convention;
if p.curr_tok.kind == .String { if p.curr_tok.kind == .String {
str := expect_token(p, .String); str := expect_token(p, .String);
cc = string_to_calling_convention(str.text); cc = string_to_calling_convention(str.text);
if cc == ast.Proc_Calling_Convention.Invalid { if cc == nil {
error(p, str.pos, "unknown calling convention '%s'", str.text); error(p, str.pos, "unknown calling convention '%s'", str.text);
} }
} }
if cc == ast.Proc_Calling_Convention.Invalid { if cc == nil && p.in_foreign_block {
if p.in_foreign_block { cc = .Foreign_Block_Default;
cc = ast.Proc_Calling_Convention.Foreign_Block_Default;
} else {
cc = ast.Proc_Calling_Convention.Odin;
}
} }
expect_token(p, .Open_Paren); expect_token(p, .Open_Paren);
@@ -1976,23 +2015,6 @@ parse_proc_type :: proc(p: ^Parser, tok: tokenizer.Token) -> ^ast.Proc_Type {
return pt; return pt;
} }
check_poly_params_for_type :: proc(p: ^Parser, poly_params: ^ast.Field_List, tok: tokenizer.Token) {
if poly_params == nil {
return;
}
for field in poly_params.list {
for name in field.names {
if name == nil {
continue;
}
if _, ok := name.derived.(ast.Poly_Type); ok {
error(p, name.pos, "polymorphic names are not needed for %s parameters", tok.text);
return;
}
}
}
}
parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ast.Expr { parse_inlining_operand :: proc(p: ^Parser, lhs: bool, tok: tokenizer.Token) -> ^ast.Expr {
expr := parse_unary_expr(p, lhs); expr := parse_unary_expr(p, lhs);
@@ -2224,6 +2246,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = -1; p.expr_level = -1;
where_clauses = parse_rhs_expr_list(p); where_clauses = parse_rhs_expr_list(p);
p.expr_level = prev_level; p.expr_level = prev_level;
tags = parse_proc_tags(p);
} }
if p.allow_type && p.expr_level < 0 { if p.allow_type && p.expr_level < 0 {
if where_token.kind != .Invalid { if where_token.kind != .Invalid {
@@ -2233,6 +2256,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
} }
body: ^ast.Stmt; body: ^ast.Stmt;
skip_possible_newline_for_literal(p);
if allow_token(p, .Undef) { if allow_token(p, .Undef) {
body = nil; body = nil;
if where_token.kind != .Invalid { if where_token.kind != .Invalid {
@@ -2358,7 +2383,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
poly_params = nil; poly_params = nil;
} }
expect_token_after(p, .Close_Paren, "parameter list"); expect_token_after(p, .Close_Paren, "parameter list");
check_poly_params_for_type(p, poly_params, tok);
} }
prev_level := p.expr_level; prev_level := p.expr_level;
@@ -2405,6 +2429,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
p.expr_level = where_prev_level; p.expr_level = where_prev_level;
} }
skip_possible_newline_for_literal(p);
expect_token(p, .Open_Brace); expect_token(p, .Open_Brace);
fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct); fields, name_count = parse_field_list(p, .Close_Brace, ast.Field_Flags_Struct);
close := expect_token(p, .Close_Brace); close := expect_token(p, .Close_Brace);
@@ -2434,7 +2459,6 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
poly_params = nil; poly_params = nil;
} }
expect_token_after(p, .Close_Paren, "parameter list"); expect_token_after(p, .Close_Paren, "parameter list");
check_poly_params_for_type(p, poly_params, tok);
} }
prev_level := p.expr_level; prev_level := p.expr_level;
@@ -2473,6 +2497,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
variants: [dynamic]^ast.Expr; variants: [dynamic]^ast.Expr;
skip_possible_newline_for_literal(p);
expect_token_after(p, .Open_Brace, "union"); expect_token_after(p, .Open_Brace, "union");
for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF { for p.curr_tok.kind != .Close_Brace && p.curr_tok.kind != .EOF {
@@ -2503,6 +2528,8 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
if p.curr_tok.kind != .Open_Brace { if p.curr_tok.kind != .Open_Brace {
base_type = parse_type(p); base_type = parse_type(p);
} }
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace); open := expect_token(p, .Open_Brace);
fields := parse_elem_list(p); fields := parse_elem_list(p);
close := expect_token(p, .Close_Brace); close := expect_token(p, .Close_Brace);
@@ -2601,6 +2628,7 @@ parse_operand :: proc(p: ^Parser, lhs: bool) -> ^ast.Expr {
} }
} }
skip_possible_newline_for_literal(p);
open := expect_token(p, .Open_Brace); open := expect_token(p, .Open_Brace);
asm_string := parse_expr(p, false); asm_string := parse_expr(p, false);
expect_token(p, .Comma); expect_token(p, .Comma);
@@ -2811,7 +2839,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
open := expect_token(p, .Open_Bracket); open := expect_token(p, .Open_Bracket);
#partial switch p.curr_tok.kind { #partial switch p.curr_tok.kind {
case .Colon, .Ellipsis, .Range_Half: case .Colon, .Ellipsis, .Range_Half, .Range_Full:
// NOTE(bill): Do not err yet // NOTE(bill): Do not err yet
break; break;
case: case:
@@ -2819,7 +2847,7 @@ parse_atom_expr :: proc(p: ^Parser, value: ^ast.Expr, lhs: bool) -> (operand: ^a
} }
#partial switch p.curr_tok.kind { #partial switch p.curr_tok.kind {
case .Ellipsis, .Range_Half: case .Ellipsis, .Range_Half, .Range_Full:
error(p, p.curr_tok.pos, "expected a colon, not a range"); error(p, p.curr_tok.pos, "expected a colon, not a range");
fallthrough; fallthrough;
case .Colon: case .Colon:
@@ -3150,6 +3178,7 @@ parse_simple_stmt :: proc(p: ^Parser, flags: Stmt_Allow_Flags) -> ^ast.Stmt {
case ast.For_Stmt: n.label = label; case ast.For_Stmt: n.label = label;
case ast.Switch_Stmt: n.label = label; case ast.Switch_Stmt: n.label = label;
case ast.Type_Switch_Stmt: n.label = label; case ast.Type_Switch_Stmt: n.label = label;
case ast.Range_Stmt: n.label = label;
} }
} }
+924
View File
@@ -0,0 +1,924 @@
package odin_printer
import "core:odin/ast"
import "core:odin/tokenizer"
import "core:strings"
import "core:runtime"
import "core:fmt"
import "core:unicode/utf8"
import "core:mem"
Type_Enum :: enum {Line_Comment, Value_Decl, Switch_Stmt, Struct, Assign, Call, Enum, If, For, Proc_Lit};
Line_Type :: bit_set[Type_Enum];
/*
Represents an unwrapped line
*/
Line :: struct {
format_tokens: [dynamic]Format_Token,
finalized: bool,
used: bool,
depth: int,
types: Line_Type, //for performance, so you don't have to verify what types are in it by going through the tokens - might give problems when adding linebreaking
}
/*
Represents a singular token in a unwrapped line
*/
Format_Token :: struct {
kind: tokenizer.Token_Kind,
text: string,
type: Type_Enum,
spaces_before: int,
parameter_count: int,
}
Printer :: struct {
string_builder: strings.Builder,
config: Config,
depth: int, //the identation depth
comments: [dynamic]^ast.Comment_Group,
latest_comment_index: int,
allocator: mem.Allocator,
file: ^ast.File,
source_position: tokenizer.Pos,
last_source_position: tokenizer.Pos,
lines: [dynamic]Line, //need to look into a better data structure, one that can handle inserting lines rather than appending
skip_semicolon: bool,
current_line: ^Line,
current_line_index: int,
last_line_index: int,
last_token: ^Format_Token,
merge_next_token: bool,
space_next_token: bool,
debug: bool,
}
Config :: struct {
spaces: int, //Spaces per indentation
newline_limit: int, //The limit of newlines between statements and declarations.
tabs: bool, //Enable or disable tabs
convert_do: bool, //Convert all do statements to brace blocks
semicolons: bool, //Enable semicolons
split_multiple_stmts: bool,
align_switch: bool,
brace_style: Brace_Style,
align_assignments: bool,
align_structs: bool,
align_style: Alignment_Style,
align_enums: bool,
align_length_break: int,
indent_cases: bool,
newline_style: Newline_Style,
}
Brace_Style :: enum {
_1TBS,
Allman,
Stroustrup,
K_And_R,
}
Block_Type :: enum {
None,
If_Stmt,
Proc,
Generic,
Comp_Lit,
Switch_Stmt,
}
Alignment_Style :: enum {
Align_On_Type_And_Equals,
Align_On_Colon_And_Equals,
}
Newline_Style :: enum {
CRLF,
LF,
}
default_style := Config {
spaces = 4,
newline_limit = 2,
convert_do = false,
semicolons = true,
tabs = true,
brace_style = ._1TBS,
split_multiple_stmts = true,
align_assignments = true,
align_style = .Align_On_Type_And_Equals,
indent_cases = false,
align_switch = true,
align_structs = true,
align_enums = true,
newline_style = .CRLF,
align_length_break = 9,
};
make_printer :: proc(config: Config, allocator := context.allocator) -> Printer {
return {
config = config,
allocator = allocator,
debug = false,
};
}
print :: proc(p: ^Printer, file: ^ast.File) -> string {
p.comments = file.comments;
if len(file.decls) > 0 {
p.lines = make([dynamic]Line, 0, (file.decls[len(file.decls) - 1].end.line - file.decls[0].pos.line) * 2, context.temp_allocator);
}
set_source_position(p, file.pkg_token.pos);
p.last_source_position.line = 1;
set_line(p, 0);
push_generic_token(p, .Package, 0);
push_ident_token(p, file.pkg_name, 1);
for decl in file.decls {
visit_decl(p, cast(^ast.Decl)decl);
}
if len(p.comments) > 0 {
infinite := p.comments[len(p.comments) - 1].end;
infinite.offset = 9999999;
push_comments(p, infinite);
}
fix_lines(p);
builder := strings.make_builder(0, mem.megabytes(5), p.allocator);
last_line := 0;
newline: string;
if p.config.newline_style == .LF {
newline = "\n";
} else {
newline = "\r\n";
}
for line, line_index in p.lines {
diff_line := line_index - last_line;
for i := 0; i < diff_line; i += 1 {
strings.write_string(&builder, newline);
}
if p.config.tabs {
for i := 0; i < line.depth; i += 1 {
strings.write_byte(&builder, '\t');
}
} else {
for i := 0; i < line.depth * p.config.spaces; i += 1 {
strings.write_byte(&builder, ' ');
}
}
if p.debug {
strings.write_string(&builder, fmt.tprintf("line %v: ", line_index));
}
for format_token in line.format_tokens {
for i := 0; i < format_token.spaces_before; i += 1 {
strings.write_byte(&builder, ' ');
}
strings.write_string(&builder, format_token.text);
}
last_line = line_index;
}
strings.write_string(&builder, newline);
return strings.to_string(builder);
}
fix_lines :: proc(p: ^Printer) {
align_var_decls(p);
format_generic(p);
align_comments(p); //align them last since they rely on the other alignments
}
format_value_decl :: proc(p: ^Printer, index: int) {
eq_found := false;
eq_token: Format_Token;
eq_line: int;
largest := 0;
found_eq: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
largest += len(format_token.text) + format_token.spaces_before;
if format_token.kind == .Eq {
eq_token = format_token;
eq_line = line_index + index;
eq_found = true;
break found_eq;
}
}
}
if !eq_found {
return;
}
align_next := false;
//check to see if there is a binary operator in the last token(this is guaranteed by the ast visit), otherwise it's not multilined
for line, line_index in p.lines[eq_line:] {
if len(line.format_tokens) == 0 {
break;
}
if align_next {
line.format_tokens[0].spaces_before = largest + 1;
align_next = false;
}
kind := find_last_token(line.format_tokens).kind;
if tokenizer.Token_Kind.B_Operator_Begin < kind && kind <= tokenizer.Token_Kind.Cmp_Or {
align_next = true;
}
if !align_next {
break;
}
}
}
find_last_token :: proc(format_tokens: [dynamic]Format_Token) -> Format_Token {
for i := len(format_tokens) - 1; i >= 0; i -= 1 {
if format_tokens[i].kind != .Comment {
return format_tokens[i];
}
}
panic("not possible");
}
format_assignment :: proc(p: ^Printer, index: int) {
}
format_call :: proc(p: ^Printer, line_index: int, format_index: int) {
paren_found := false;
paren_token: Format_Token;
paren_line: int;
paren_token_index: int;
largest := 0;
found_paren: for line, i in p.lines[line_index:] {
for format_token, j in line.format_tokens {
largest += len(format_token.text) + format_token.spaces_before;
if i == 0 && j < format_index {
continue;
}
if format_token.kind == .Open_Paren && format_token.type == .Call {
paren_token = format_token;
paren_line = line_index + i;
paren_found = true;
paren_token_index = j;
break found_paren;
}
}
}
if !paren_found {
panic("Should not be possible");
}
paren_count := 1;
done := false;
for line, line_index in p.lines[paren_line:] {
if len(line.format_tokens) == 0 {
continue;
}
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
continue;
}
if line_index == 0 && i <= paren_token_index {
continue;
}
if format_token.kind == .Open_Paren {
paren_count += 1;
} else if format_token.kind == .Close_Paren {
paren_count -= 1;
}
if paren_count == 0 {
done = true;
}
}
if line_index != 0 {
line.format_tokens[0].spaces_before = largest;
}
if done {
return;
}
}
}
format_keyword_to_brace :: proc(p: ^Printer, line_index: int, format_index: int, keyword: tokenizer.Token_Kind) {
keyword_found := false;
keyword_token: Format_Token;
keyword_line: int;
largest := 0;
brace_count := 0;
done := false;
found_keyword: for line, i in p.lines[line_index:] {
for format_token in line.format_tokens {
largest += len(format_token.text) + format_token.spaces_before;
if format_token.kind == keyword {
keyword_token = format_token;
keyword_line = line_index + i;
keyword_found = true;
break found_keyword;
}
}
}
if !keyword_found {
panic("Should not be possible");
}
for line, line_index in p.lines[keyword_line:] {
if len(line.format_tokens) == 0 {
continue;
}
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
break;
} else if format_token.kind == .Undef {
return;
}
if line_index == 0 && i <= format_index {
continue;
}
if format_token.kind == .Open_Brace {
brace_count += 1;
} else if format_token.kind == .Close_Brace {
brace_count -= 1;
}
if brace_count == 1 {
done = true;
}
}
if line_index != 0 {
line.format_tokens[0].spaces_before = largest + 1;
}
if done {
return;
}
}
}
format_generic :: proc(p: ^Printer) {
next_struct_line := 0;
for line, line_index in p.lines {
if len(line.format_tokens) <= 0 {
continue;
}
for format_token, token_index in line.format_tokens {
#partial switch format_token.kind {
case .For, .If, .When, .Switch:
format_keyword_to_brace(p, line_index, token_index, format_token.kind);
case .Proc:
if format_token.type == .Proc_Lit {
format_keyword_to_brace(p, line_index, token_index, format_token.kind);
}
case:
if format_token.type == .Call {
format_call(p, line_index, token_index);
}
}
}
if .Switch_Stmt in line.types && p.config.align_switch {
align_switch_stmt(p, line_index);
}
if .Enum in line.types && p.config.align_enums {
align_enum(p, line_index);
}
if .Struct in line.types && p.config.align_structs && next_struct_line <= 0 {
next_struct_line = align_struct(p, line_index);
}
if .Value_Decl in line.types {
format_value_decl(p, line_index);
}
if .Assign in line.types {
format_assignment(p, line_index);
}
next_struct_line -= 1;
}
}
align_var_decls :: proc(p: ^Printer) {
current_line: int;
current_typed: bool;
current_not_mutable: bool;
largest_lhs := 0;
largest_rhs := 0;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
colon_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
type_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
equal_tokens := make([dynamic]TokenAndLength, 0, 10, context.temp_allocator);
for line, line_index in p.lines {
//It is only possible to align value decls that are one one line, otherwise just ignore them
if .Value_Decl not_in line.types {
continue;
}
typed := true;
not_mutable := false;
continue_flag := false;
for i := 0; i < len(line.format_tokens); i += 1 {
if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Eq {
typed = false;
}
if line.format_tokens[i].kind == .Colon && line.format_tokens[min(i + 1, len(line.format_tokens) - 1)].kind == .Colon {
not_mutable = true;
}
if line.format_tokens[i].kind == .Union ||
line.format_tokens[i].kind == .Enum ||
line.format_tokens[i].kind == .Struct ||
line.format_tokens[i].kind == .For ||
line.format_tokens[i].kind == .If ||
line.format_tokens[i].kind == .Comment {
continue_flag = true;
}
//enforced undef is always on the last line, if it exists
if line.format_tokens[i].kind == .Proc && line.format_tokens[len(line.format_tokens)-1].kind != .Undef {
continue_flag = true;
}
}
if continue_flag {
continue;
}
if line_index != current_line + 1 || typed != current_typed || not_mutable != current_not_mutable {
if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
for colon_token in colon_tokens {
colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
}
} else if p.config.align_style == .Align_On_Type_And_Equals {
for type_token in type_tokens {
type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
}
}
if current_typed {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
}
} else {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = 0;
}
}
clear(&colon_tokens);
clear(&type_tokens);
clear(&equal_tokens);
largest_rhs = 0;
largest_lhs = 0;
current_typed = typed;
current_not_mutable = not_mutable;
}
current_line = line_index;
current_token_index := 0;
lhs_length := 0;
rhs_length := 0;
//calcuate the length of lhs of a value decl i.e. `a, b:`
for; current_token_index < len(line.format_tokens); current_token_index += 1 {
lhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
if line.format_tokens[current_token_index].kind == .Colon {
append(&colon_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = lhs_length});
if len(line.format_tokens) > current_token_index && line.format_tokens[current_token_index + 1].kind != .Eq {
append(&type_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index + 1], length = lhs_length});
}
current_token_index += 1;
largest_lhs = max(largest_lhs, lhs_length);
break;
}
}
//calcuate the length of the rhs i.e. `[dynamic]int = 123123`
for; current_token_index < len(line.format_tokens); current_token_index += 1 {
rhs_length += len(line.format_tokens[current_token_index].text) + line.format_tokens[current_token_index].spaces_before;
if line.format_tokens[current_token_index].kind == .Eq {
append(&equal_tokens, TokenAndLength {format_token = &line.format_tokens[current_token_index], length = rhs_length});
largest_rhs = max(largest_rhs, rhs_length);
break;
}
}
}
//repeating myself, move to sub procedure
if p.config.align_style == .Align_On_Colon_And_Equals || !current_typed || current_not_mutable {
for colon_token in colon_tokens {
colon_token.format_token.spaces_before = largest_lhs - colon_token.length + 1;
}
} else if p.config.align_style == .Align_On_Type_And_Equals {
for type_token in type_tokens {
type_token.format_token.spaces_before = largest_lhs - type_token.length + 1;
}
}
if current_typed {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = largest_rhs - equal_token.length + 1;
}
} else {
for equal_token in equal_tokens {
equal_token.format_token.spaces_before = 0;
}
}
}
align_switch_stmt :: proc(p: ^Printer, index: int) {
switch_found := false;
brace_token: Format_Token;
brace_line: int;
found_switch_brace: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
if format_token.kind == .Open_Brace && switch_found {
brace_token = format_token;
brace_line = line_index + index;
break found_switch_brace;
} else if format_token.kind == .Open_Brace {
break;
} else if format_token.kind == .Switch {
switch_found = true;
}
}
}
if !switch_found {
return;
}
largest := 0;
case_count := 0;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
//find all the switch cases that are one lined
for line, line_index in p.lines[brace_line + 1:] {
case_found := false;
colon_found := false;
length := 0;
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
break;
}
//this will only happen if the case is one lined
if case_found && colon_found {
append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
largest = max(length, largest);
break;
}
if format_token.kind == .Case {
case_found = true;
case_count += 1;
} else if format_token.kind == .Colon {
colon_found = true;
}
length += len(format_token.text) + format_token.spaces_before;
}
if case_count >= brace_token.parameter_count {
break;
}
}
for token in format_tokens {
token.format_token.spaces_before = largest - token.length + 1;
}
}
align_enum :: proc(p: ^Printer, index: int) {
enum_found := false;
brace_token: Format_Token;
brace_line: int;
found_enum_brace: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
if format_token.kind == .Open_Brace && enum_found {
brace_token = format_token;
brace_line = line_index + index;
break found_enum_brace;
} else if format_token.kind == .Open_Brace {
break;
} else if format_token.kind == .Enum {
enum_found = true;
}
}
}
if !enum_found {
return;
}
largest := 0;
comma_count := 0;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
format_tokens := make([dynamic]TokenAndLength, 0, brace_token.parameter_count, context.temp_allocator);
for line, line_index in p.lines[brace_line + 1:] {
length := 0;
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
break;
}
if format_token.kind == .Eq {
append(&format_tokens, TokenAndLength {format_token = &line.format_tokens[i], length = length});
largest = max(length, largest);
break;
} else if format_token.kind == .Comma {
comma_count += 1;
}
length += len(format_token.text) + format_token.spaces_before;
}
if comma_count >= brace_token.parameter_count {
break;
}
}
for token in format_tokens {
token.format_token.spaces_before = largest - token.length + 1;
}
}
align_struct :: proc(p: ^Printer, index: int) -> int {
struct_found := false;
brace_token: Format_Token;
brace_line: int;
found_struct_brace: for line, line_index in p.lines[index:] {
for format_token in line.format_tokens {
if format_token.kind == .Open_Brace && struct_found {
brace_token = format_token;
brace_line = line_index + index;
break found_struct_brace;
} else if format_token.kind == .Open_Brace {
break;
} else if format_token.kind == .Struct {
struct_found = true;
}
}
}
if !struct_found {
return 0;
}
largest := 0;
colon_count := 0;
nested := false;
seen_brace := false;
TokenAndLength :: struct {
format_token: ^Format_Token,
length: int,
};
format_tokens := make([]TokenAndLength, brace_token.parameter_count, context.temp_allocator);
if brace_token.parameter_count == 0 {
return 0;
}
end_line_index := 0;
for line, line_index in p.lines[brace_line + 1:] {
length := 0;
for format_token, i in line.format_tokens {
//give up on nested structs
if format_token.kind == .Comment {
break;
} else if format_token.kind == .Open_Paren {
break;
} else if format_token.kind == .Open_Brace {
seen_brace = true;
} else if format_token.kind == .Close_Brace {
seen_brace = false;
} else if seen_brace {
continue;
}
if format_token.kind == .Colon {
format_tokens[colon_count] = {format_token = &line.format_tokens[i + 1], length = length};
if format_tokens[colon_count].format_token.kind == .Struct {
nested = true;
}
colon_count += 1;
largest = max(length, largest);
}
length += len(format_token.text) + format_token.spaces_before;
}
if nested {
end_line_index = line_index + brace_line + 1;
}
if colon_count >= brace_token.parameter_count {
break;
}
}
//give up aligning nested, it never looks good
if nested {
for line, line_index in p.lines[end_line_index:] {
for format_token in line.format_tokens {
if format_token.kind == .Close_Brace {
return end_line_index + line_index - index;
}
}
}
}
for token in format_tokens {
token.format_token.spaces_before = largest - token.length + 1;
}
return 0;
}
align_comments :: proc(p: ^Printer) {
Comment_Align_Info :: struct {
length: int,
begin: int,
end: int,
depth: int,
};
comment_infos := make([dynamic]Comment_Align_Info, 0, context.temp_allocator);
current_info: Comment_Align_Info;
for line, line_index in p.lines {
if len(line.format_tokens) <= 0 {
continue;
}
if .Line_Comment in line.types {
if current_info.end + 1 != line_index || current_info.depth != line.depth ||
(current_info.begin == current_info.end && current_info.length == 0) {
if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
append(&comment_infos, current_info);
}
current_info.begin = line_index;
current_info.end = line_index;
current_info.depth = line.depth;
current_info.length = 0;
}
length := 0;
for format_token, i in line.format_tokens {
if format_token.kind == .Comment {
current_info.length = max(current_info.length, length);
current_info.end = line_index;
}
length += format_token.spaces_before + len(format_token.text);
}
}
}
if (current_info.begin != 0 && current_info.end != 0) || current_info.length > 0 {
append(&comment_infos, current_info);
}
for info in comment_infos {
if info.begin == info.end || info.length == 0 {
continue;
}
for i := info.begin; i <= info.end; i += 1 {
l := p.lines[i];
length := 0;
for format_token, i in l.format_tokens {
if format_token.kind == .Comment {
if len(l.format_tokens) == 1 {
l.format_tokens[i].spaces_before = info.length + 1;
} else {
l.format_tokens[i].spaces_before = info.length - length + 1;
}
}
length += format_token.spaces_before + len(format_token.text);
}
}
}
}
File diff suppressed because it is too large Load Diff
+2
View File
@@ -107,6 +107,7 @@ Token_Kind :: enum u32 {
Comma, // , Comma, // ,
Ellipsis, // .. Ellipsis, // ..
Range_Half, // ..< Range_Half, // ..<
Range_Full, // ..=
Back_Slash, // \ Back_Slash, // \
B_Operator_End, B_Operator_End,
@@ -233,6 +234,7 @@ tokens := [Token_Kind.COUNT]string {
",", ",",
"..", "..",
"..<", "..<",
"..=",
"\\", "\\",
"", "",
+7 -4
View File
@@ -14,7 +14,7 @@ Flags :: distinct bit_set[Flag; u32];
Tokenizer :: struct { Tokenizer :: struct {
// Immutable data // Immutable data
path: string, path: string,
src: []byte, src: string,
err: Error_Handler, err: Error_Handler,
flags: Flags, flags: Flags,
@@ -31,7 +31,7 @@ Tokenizer :: struct {
error_count: int, error_count: int,
} }
init :: proc(t: ^Tokenizer, src: []byte, path: string, err: Error_Handler = default_error_handler) { init :: proc(t: ^Tokenizer, src: string, path: string, err: Error_Handler = default_error_handler) {
t.src = src; t.src = src;
t.err = err; t.err = err;
t.ch = ' '; t.ch = ' ';
@@ -87,7 +87,7 @@ advance_rune :: proc(using t: ^Tokenizer) {
case r == 0: case r == 0:
error(t, t.offset, "illegal character NUL"); error(t, t.offset, "illegal character NUL");
case r >= utf8.RUNE_SELF: case r >= utf8.RUNE_SELF:
r, w = utf8.decode_rune(src[read_offset:]); r, w = utf8.decode_rune_in_string(src[read_offset:]);
if r == utf8.RUNE_ERROR && w == 1 { if r == utf8.RUNE_ERROR && w == 1 {
error(t, t.offset, "illegal UTF-8 encoding"); error(t, t.offset, "illegal UTF-8 encoding");
} else if r == utf8.RUNE_BOM && offset > 0 { } else if r == utf8.RUNE_BOM && offset > 0 {
@@ -608,7 +608,7 @@ scan :: proc(t: ^Tokenizer) -> Token {
kind = switch3(t, .And, .And_Eq, '&', .Cmp_And); kind = switch3(t, .And, .And_Eq, '&', .Cmp_And);
} }
case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or); case '|': kind = switch3(t, .Or, .Or_Eq, '|', .Cmp_Or);
case '~': kind = .Xor; case '~': kind = switch2(t, .Xor, .Xor_Eq);
case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq); case '<': kind = switch4(t, .Lt, .Lt_Eq, '<', .Shl, .Shl_Eq);
case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq); case '>': kind = switch4(t, .Gt, .Gt_Eq, '>', .Shr,.Shr_Eq);
@@ -623,6 +623,9 @@ scan :: proc(t: ^Tokenizer) -> Token {
if t.ch == '<' { if t.ch == '<' {
advance_rune(t); advance_rune(t);
kind = .Range_Half; kind = .Range_Half;
} else if t.ch == '=' {
advance_rune(t);
kind = .Range_Full;
} }
} }
} }
+2 -2
View File
@@ -273,7 +273,7 @@ is_file :: proc(path: string) -> bool {
attribs := win32.GetFileAttributesW(wpath); attribs := win32.GetFileAttributesW(wpath);
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES { if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == win32.FILE_ATTRIBUTE_DIRECTORY; return attribs & win32.FILE_ATTRIBUTE_DIRECTORY == 0;
} }
return false; return false;
} }
@@ -283,7 +283,7 @@ is_dir :: proc(path: string) -> bool {
attribs := win32.GetFileAttributesW(wpath); attribs := win32.GetFileAttributesW(wpath);
if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES { if i32(attribs) != win32.INVALID_FILE_ATTRIBUTES {
return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != win32.FILE_ATTRIBUTE_DIRECTORY; return attribs & win32.FILE_ATTRIBUTE_DIRECTORY != 0;
} }
return false; return false;
} }
+18 -47
View File
@@ -1,11 +1,8 @@
package os2 package os2
Platform_Error_Min_Bits :: 32; import "core:io"
Error :: enum u64 { General_Error :: enum u32 {
None = 0,
// General Errors
Invalid_Argument, Invalid_Argument,
Permission_Denied, Permission_Denied,
@@ -13,43 +10,20 @@ Error :: enum u64 {
Not_Exist, Not_Exist,
Closed, Closed,
// Timeout Errors
Timeout, Timeout,
// I/O Errors
// EOF is the error returned by `read` when no more input is available
EOF,
// Unexpected_EOF means that EOF was encountered in the middle of reading a fixed-sized block of data
Unexpected_EOF,
// Short_Write means that a write accepted fewer bytes than requested but failed to return an explicit error
Short_Write,
// Invalid_Write means that a write returned an impossible count
Invalid_Write,
// Short_Buffer means that a read required a longer buffer than was provided
Short_Buffer,
// No_Progress is returned by some implementations of `io.Reader` when many calls
// to `read` have failed to return any data or error.
// This is usually a signed of a broken `io.Reader` implementation
No_Progress,
Invalid_Whence,
Invalid_Offset,
Invalid_Unread,
Negative_Read,
Negative_Write,
Negative_Count,
Buffer_Full,
// Platform Specific Errors
Platform_Minimum = 1<<Platform_Error_Min_Bits,
} }
Platform_Error :: struct {
err: i32,
}
Error :: union {
General_Error,
io.Error,
Platform_Error,
}
#assert(size_of(Error) == size_of(u64));
Path_Error :: struct { Path_Error :: struct {
op: string, op: string,
path: string, path: string,
@@ -83,20 +57,17 @@ link_error_delete :: proc(lerr: Maybe(Link_Error)) {
is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) { is_platform_error :: proc(ferr: Error) -> (err: i32, ok: bool) {
if ferr >= .Platform_Minimum { v: Platform_Error;
err = i32(u64(ferr)>>Platform_Error_Min_Bits); if v, ok = ferr.(Platform_Error); ok {
ok = true; err = v.err;
} }
return; return;
} }
error_from_platform_error :: proc(errno: i32) -> Error {
return Error(u64(errno) << Platform_Error_Min_Bits);
}
error_string :: proc(ferr: Error) -> string { error_string :: proc(ferr: Error) -> string {
#partial switch ferr { switch ferr {
case .None: return ""; case nil: return "";
case .Invalid_Argument: return "invalid argument"; case .Invalid_Argument: return "invalid argument";
case .Permission_Denied: return "permission denied"; case .Permission_Denied: return "permission denied";
case .Exist: return "file already exists"; case .Exist: return "file already exists";
+7 -16
View File
@@ -10,23 +10,14 @@ file_to_stream :: proc(fd: Handle) -> (s: io.Stream) {
@(private) @(private)
error_to_io_error :: proc(ferr: Error) -> io.Error { error_to_io_error :: proc(ferr: Error) -> io.Error {
#partial switch ferr { if ferr == nil {
case .None: return .None; return .None;
case .EOF: return .EOF;
case .Unexpected_EOF: return .Unexpected_EOF;
case .Short_Write: return .Short_Write;
case .Invalid_Write: return .Invalid_Write;
case .Short_Buffer: return .Short_Buffer;
case .No_Progress: return .No_Progress;
case .Invalid_Whence: return .Invalid_Whence;
case .Invalid_Offset: return .Invalid_Offset;
case .Invalid_Unread: return .Invalid_Unread;
case .Negative_Read: return .Negative_Read;
case .Negative_Write: return .Negative_Write;
case .Negative_Count: return .Negative_Count;
case .Buffer_Full: return .Buffer_Full;
} }
return .Unknown; err, ok := ferr.(io.Error);
if !ok {
err = .Unknown;
}
return err;
} }
+1
View File
@@ -1,6 +1,7 @@
package os2 package os2
import "core:mem" import "core:mem"
import "core:io"
import "core:strconv" import "core:strconv"
import "core:unicode/utf8" import "core:unicode/utf8"
+10 -10
View File
@@ -5,19 +5,19 @@ import "core:io"
import "core:time" import "core:time"
_create :: proc(name: string) -> (Handle, Error) { _create :: proc(name: string) -> (Handle, Error) {
return 0, .None; return 0, nil;
} }
_open :: proc(name: string) -> (Handle, Error) { _open :: proc(name: string) -> (Handle, Error) {
return 0, .None; return 0, nil;
} }
_open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) { _open_file :: proc(name: string, flag: int, perm: File_Mode) -> (Handle, Error) {
return 0, .None; return 0, nil;
} }
_close :: proc(fd: Handle) -> Error { _close :: proc(fd: Handle) -> Error {
return .None; return nil;
} }
_name :: proc(fd: Handle, allocator := context.allocator) -> string { _name :: proc(fd: Handle, allocator := context.allocator) -> string {
@@ -58,11 +58,11 @@ _file_size :: proc(fd: Handle) -> (n: i64, err: Error) {
_sync :: proc(fd: Handle) -> Error { _sync :: proc(fd: Handle) -> Error {
return .None; return nil;
} }
_flush :: proc(fd: Handle) -> Error { _flush :: proc(fd: Handle) -> Error {
return .None; return nil;
} }
_truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) { _truncate :: proc(fd: Handle, size: i64) -> Maybe(Path_Error) {
@@ -92,20 +92,20 @@ _read_link :: proc(name: string) -> (string, Maybe(Path_Error)) {
_chdir :: proc(fd: Handle) -> Error { _chdir :: proc(fd: Handle) -> Error {
return .None; return nil;
} }
_chmod :: proc(fd: Handle, mode: File_Mode) -> Error { _chmod :: proc(fd: Handle, mode: File_Mode) -> Error {
return .None; return nil;
} }
_chown :: proc(fd: Handle, uid, gid: int) -> Error { _chown :: proc(fd: Handle, uid, gid: int) -> Error {
return .None; return nil;
} }
_lchown :: proc(name: string, uid, gid: int) -> Error { _lchown :: proc(name: string, uid, gid: int) -> Error {
return .None; return nil;
} }
+1 -1
View File
@@ -6,7 +6,7 @@ import win32 "core:sys/windows"
_pipe :: proc() -> (r, w: Handle, err: Error) { _pipe :: proc() -> (r, w: Handle, err: Error) {
p: [2]win32.HANDLE; p: [2]win32.HANDLE;
if !win32.CreatePipe(&p[0], &p[1], nil, 0) { if !win32.CreatePipe(&p[0], &p[1], nil, 0) {
return 0, 0, error_from_platform_error(i32(win32.GetLastError())); return 0, 0, Platform_Error{i32(win32.GetLastError())};
} }
return Handle(p[0]), Handle(p[1]), nil; return Handle(p[0]), Handle(p[1]), nil;
} }
+3 -3
View File
@@ -40,7 +40,7 @@ _same_file :: proc(fi1, fi2: File_Info) -> bool {
_stat_errno :: proc(errno: win32.DWORD) -> Path_Error { _stat_errno :: proc(errno: win32.DWORD) -> Path_Error {
return Path_Error{err = error_from_platform_error(i32(errno))}; return Path_Error{err = Platform_Error{i32(errno)}};
} }
@@ -89,7 +89,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
fd: win32.WIN32_FIND_DATAW; fd: win32.WIN32_FIND_DATAW;
sh := win32.FindFirstFileW(wname, &fd); sh := win32.FindFirstFileW(wname, &fd);
if sh == win32.INVALID_HANDLE_VALUE { if sh == win32.INVALID_HANDLE_VALUE {
e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))}; e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}};
return; return;
} }
win32.FindClose(sh); win32.FindClose(sh);
@@ -99,7 +99,7 @@ internal_stat :: proc(name: string, create_file_attributes: u32, allocator := co
h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil); h := win32.CreateFileW(wname, 0, 0, nil, win32.OPEN_EXISTING, create_file_attributes, nil);
if h == win32.INVALID_HANDLE_VALUE { if h == win32.INVALID_HANDLE_VALUE {
e = Path_Error{err = error_from_platform_error(i32(win32.GetLastError()))}; e = Path_Error{err = Platform_Error{i32(win32.GetLastError())}};
return; return;
} }
defer win32.CloseHandle(h); defer win32.CloseHandle(h);
+2 -2
View File
@@ -4,11 +4,11 @@ package os2
import win32 "core:sys/windows" import win32 "core:sys/windows"
_create_temp :: proc(dir, pattern: string) -> (Handle, Error) { _create_temp :: proc(dir, pattern: string) -> (Handle, Error) {
return 0, .None; return 0, nil;
} }
_mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) { _mkdir_temp :: proc(dir, pattern: string, allocator := context.allocator) -> (string, Error) {
return "", .None; return "", nil;
} }
_temp_dir :: proc(allocator := context.allocator) -> string { _temp_dir :: proc(allocator := context.allocator) -> string {
+1 -1
View File
@@ -10,7 +10,7 @@ import "core:c"
Handle :: distinct i32; Handle :: distinct i32;
File_Time :: distinct u64; File_Time :: distinct u64;
Errno :: distinct i32; Errno :: distinct i32;
Syscall :: distinct int; Syscall :: distinct i32;
INVALID_HANDLE :: ~Handle(0); INVALID_HANDLE :: ~Handle(0);
+3 -3
View File
@@ -11,7 +11,7 @@ import "core:strconv"
Handle :: distinct i32; Handle :: distinct i32;
File_Time :: distinct u64; File_Time :: distinct u64;
Errno :: distinct i32; Errno :: distinct i32;
Syscall :: distinct int; Syscall :: distinct i32;
INVALID_HANDLE :: ~Handle(0); INVALID_HANDLE :: ~Handle(0);
@@ -269,7 +269,7 @@ SYS_GETTID: Syscall : 186;
foreign libc { foreign libc {
@(link_name="__errno_location") __errno_location :: proc() -> ^int ---; @(link_name="__errno_location") __errno_location :: proc() -> ^int ---;
@(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> int ---; @(link_name="syscall") syscall :: proc(number: Syscall, #c_vararg args: ..any) -> i32 ---;
@(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---; @(link_name="open") _unix_open :: proc(path: cstring, flags: c.int, mode: c.int) -> Handle ---;
@(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---; @(link_name="close") _unix_close :: proc(fd: Handle) -> c.int ---;
@@ -595,7 +595,7 @@ exit :: proc "contextless" (code: int) -> ! {
} }
current_thread_id :: proc "contextless" () -> int { current_thread_id :: proc "contextless" () -> int {
return syscall(SYS_GETTID); return cast(int)syscall(SYS_GETTID);
} }
dlopen :: proc(filename: string, flags: int) -> rawptr { dlopen :: proc(filename: string, flags: int) -> rawptr {
+41 -2
View File
@@ -1,6 +1,6 @@
package runtime package runtime
when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" { when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" || ODIN_OS == "js" {
// mem.nil_allocator reimplementation // mem.nil_allocator reimplementation
default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode, default_allocator_proc :: proc(allocator_data: rawptr, mode: mem.Allocator_Mode,
@@ -15,7 +15,46 @@ when ODIN_DEFAULT_TO_NIL_ALLOCATOR || ODIN_OS == "freestanding" {
data = nil, data = nil,
}; };
} }
} else when ODIN_OS != "windows" {
} else when ODIN_OS == "windows" {
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
switch mode {
case .Alloc:
return _windows_default_alloc(size, alignment);
case .Free:
_windows_default_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
case .Resize:
return _windows_default_resize(old_memory, old_size, size, alignment);
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features};
}
return nil, nil;
case .Query_Info:
return nil, nil;
}
return nil, nil;
}
default_allocator :: proc() -> Allocator {
return Allocator{
procedure = default_allocator_proc,
data = nil,
};
}
} else {
// TODO(bill): reimplement these procedures in the os_specific stuff // TODO(bill): reimplement these procedures in the os_specific stuff
import "core:os" import "core:os"
+12 -33
View File
@@ -97,7 +97,7 @@ mem_zero :: proc "contextless" (data: rawptr, len: int) -> rawptr {
if len < 0 { if len < 0 {
return data; return data;
} }
memset(data, 0, len); intrinsics.mem_zero(data, len);
return data; return data;
} }
@@ -105,17 +105,9 @@ mem_copy :: proc "contextless" (dst, src: rawptr, len: int) -> rawptr {
if src == nil { if src == nil {
return dst; return dst;
} }
// NOTE(bill): This _must_ be implemented like C's memmove // NOTE(bill): This _must_ be implemented like C's memmove
foreign _ { intrinsics.mem_copy(dst, src, len);
when size_of(rawptr) == 8 {
@(link_name="llvm.memmove.p0i8.p0i8.i64")
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memmove.p0i8.p0i8.i32")
llvm_memmove :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
}
llvm_memmove(dst, src, len);
return dst; return dst;
} }
@@ -123,17 +115,9 @@ mem_copy_non_overlapping :: proc "contextless" (dst, src: rawptr, len: int) -> r
if src == nil { if src == nil {
return dst; return dst;
} }
// NOTE(bill): This _must_ be implemented like C's memcpy // NOTE(bill): This _must_ be implemented like C's memcpy
foreign _ { intrinsics.mem_copy_non_overlapping(dst, src, len);
when size_of(rawptr) == 8 {
@(link_name="llvm.memcpy.p0i8.p0i8.i64")
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
} else {
@(link_name="llvm.memcpy.p0i8.p0i8.i32")
llvm_memcpy :: proc "none" (dst, src: rawptr, len: int, is_volatile: bool = false) ---;
}
}
llvm_memcpy(dst, src, len);
return dst; return dst;
} }
@@ -409,11 +393,6 @@ string_decode_rune :: #force_inline proc "contextless" (s: string) -> (rune, int
return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4; return rune(s0&MASK4)<<18 | rune(b1&MASKX)<<12 | rune(b2&MASKX)<<6 | rune(b3&MASKX), 4;
} }
@(default_calling_convention = "none")
foreign {
@(link_name="llvm.sqrt.f32") _sqrt_f32 :: proc(x: f32) -> f32 ---
@(link_name="llvm.sqrt.f64") _sqrt_f64 :: proc(x: f64) -> f64 ---
}
abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 { abs_f16 :: #force_inline proc "contextless" (x: f16) -> f16 {
return -x if x < 0 else x; return -x if x < 0 else x;
} }
@@ -445,27 +424,27 @@ max_f64 :: proc(a, b: f64) -> f64 {
abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 { abs_complex32 :: #force_inline proc "contextless" (x: complex32) -> f16 {
r, i := real(x), imag(x); r, i := real(x), imag(x);
return f16(_sqrt_f32(f32(r*r + i*i))); return f16(intrinsics.sqrt(f32(r*r + i*i)));
} }
abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 { abs_complex64 :: #force_inline proc "contextless" (x: complex64) -> f32 {
r, i := real(x), imag(x); r, i := real(x), imag(x);
return _sqrt_f32(r*r + i*i); return intrinsics.sqrt(r*r + i*i);
} }
abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 { abs_complex128 :: #force_inline proc "contextless" (x: complex128) -> f64 {
r, i := real(x), imag(x); r, i := real(x), imag(x);
return _sqrt_f64(r*r + i*i); return intrinsics.sqrt(r*r + i*i);
} }
abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 { abs_quaternion64 :: #force_inline proc "contextless" (x: quaternion64) -> f16 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x); r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return f16(_sqrt_f32(f32(r*r + i*i + j*j + k*k))); return f16(intrinsics.sqrt(f32(r*r + i*i + j*j + k*k)));
} }
abs_quaternion128 :: #force_inline proc "contextless" (x: quaternion128) -> f32 { abs_quaternion128 :: #force_inline proc "contextless" (x: quaternion128) -> f32 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x); r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f32(r*r + i*i + j*j + k*k); return intrinsics.sqrt(r*r + i*i + j*j + k*k);
} }
abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64 { abs_quaternion256 :: #force_inline proc "contextless" (x: quaternion256) -> f64 {
r, i, j, k := real(x), imag(x), jmag(x), kmag(x); r, i, j, k := real(x), imag(x), jmag(x), kmag(x);
return _sqrt_f64(r*r + i*i + j*j + k*k); return intrinsics.sqrt(r*r + i*i + j*j + k*k);
} }
@@ -644,7 +623,7 @@ truncsfhf2 :: proc "c" (value: f32) -> u16 {
} }
if (e > 30) { if (e > 30) {
f := 1e12; f := i64(1e12);
for j := 0; j < 10; j += 1 { for j := 0; j < 10; j += 1 {
/* NOTE(bill): Cause overflow */ /* NOTE(bill): Cause overflow */
g := intrinsics.volatile_load(&f); g := intrinsics.volatile_load(&f);
+8
View File
@@ -1,3 +1,11 @@
package runtime package runtime
_OS_Errno :: distinct int; _OS_Errno :: distinct int;
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
return _os_write(data);
}
current_thread_id :: proc "contextless" () -> int {
return _current_thread_id();
}
+2 -2
View File
@@ -6,12 +6,12 @@ import "core:os"
// TODO(bill): reimplement `os.write` so that it does not rely on package os // TODO(bill): reimplement `os.write` so that it does not rely on package os
// NOTE: Use os_specific_linux.odin, os_specific_darwin.odin, etc // NOTE: Use os_specific_linux.odin, os_specific_darwin.odin, etc
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { _os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
context = default_context(); context = default_context();
n, err := os.write(os.stderr, data); n, err := os.write(os.stderr, data);
return int(n), _OS_Errno(err); return int(n), _OS_Errno(err);
} }
current_thread_id :: proc "contextless" () -> int { _current_thread_id :: proc "contextless" () -> int {
return os.current_thread_id(); return os.current_thread_id();
} }
+2 -2
View File
@@ -2,10 +2,10 @@
package runtime package runtime
// TODO(bill): reimplement `os.write` // TODO(bill): reimplement `os.write`
os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) { _os_write :: proc "contextless" (data: []byte) -> (int, _OS_Errno) {
return 0, -1; return 0, -1;
} }
current_thread_id :: proc "contextless" () -> int { _current_thread_id :: proc "contextless" () -> int {
return 0; return 0;
} }
+48 -78
View File
@@ -1,3 +1,4 @@
//+private
//+build windows //+build windows
package runtime package runtime
@@ -24,7 +25,7 @@ foreign kernel32 {
HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 --- HeapFree :: proc(hHeap: rawptr, dwFlags: u32, lpMem: rawptr) -> b32 ---
} }
os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) { _os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
if len(data) == 0 { if len(data) == 0 {
return 0, 0; return 0, 0;
} }
@@ -58,7 +59,7 @@ os_write :: proc "contextless" (data: []byte) -> (n: int, err: _OS_Errno) {
return; return;
} }
current_thread_id :: proc "contextless" () -> int { _current_thread_id :: proc "contextless" () -> int {
return int(GetCurrentThreadId()); return int(GetCurrentThreadId());
} }
@@ -86,89 +87,58 @@ heap_free :: proc "contextless" (ptr: rawptr) {
HeapFree(GetProcessHeap(), 0, ptr); HeapFree(GetProcessHeap(), 0, ptr);
} }
default_allocator_proc :: proc(allocator_data: rawptr, mode: Allocator_Mode,
size, alignment: int,
old_memory: rawptr, old_size: int, loc := #caller_location) -> ([]byte, Allocator_Error) {
// //
// NOTE(tetra, 2020-01-14): The heap doesn't respect alignment. // NOTE(tetra, 2020-01-14): The heap doesn't respect alignment.
// Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert // Instead, we overallocate by `alignment + size_of(rawptr) - 1`, and insert
// padding. We also store the original pointer returned by heap_alloc right before // padding. We also store the original pointer returned by heap_alloc right before
// the pointer we return to the user. // the pointer we return to the user.
// //
aligned_alloc :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
a := max(alignment, align_of(rawptr));
space := size + a - 1;
allocated_mem: rawptr;
if old_ptr != nil {
original_old_ptr := ptr_offset((^rawptr)(old_ptr), -1)^;
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
} else {
allocated_mem = heap_alloc(space+size_of(rawptr));
}
aligned_mem := rawptr(ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
ptr := uintptr(aligned_mem); _windows_default_alloc_or_resize :: proc "contextless" (size, alignment: int, old_ptr: rawptr = nil) -> ([]byte, Allocator_Error) {
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a); if size == 0 {
diff := int(aligned_ptr - ptr); _windows_default_free(old_ptr);
if (size + diff) > space {
return nil, .Out_Of_Memory;
}
aligned_mem = rawptr(aligned_ptr);
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
return byte_slice(aligned_mem, size), nil;
}
aligned_free :: proc "contextless" (p: rawptr) {
if p != nil {
heap_free(ptr_offset((^rawptr)(p), -1)^);
}
}
aligned_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
if p == nil {
return nil, nil;
}
return aligned_alloc(new_size, new_alignment, p);
}
switch mode {
case .Alloc:
return aligned_alloc(size, alignment);
case .Free:
aligned_free(old_memory);
case .Free_All:
// NOTE(tetra): Do nothing.
case .Resize:
if old_memory == nil {
return aligned_alloc(size, alignment);
}
return aligned_resize(old_memory, old_size, size, alignment);
case .Query_Features:
set := (^Allocator_Mode_Set)(old_memory);
if set != nil {
set^ = {.Alloc, .Free, .Resize, .Query_Features};
}
return nil, nil;
case .Query_Info:
return nil, nil; return nil, nil;
} }
return nil, nil; a := max(alignment, align_of(rawptr));
space := size + a - 1;
allocated_mem: rawptr;
if old_ptr != nil {
original_old_ptr := ptr_offset((^rawptr)(old_ptr), -1)^;
allocated_mem = heap_resize(original_old_ptr, space+size_of(rawptr));
} else {
allocated_mem = heap_alloc(space+size_of(rawptr));
}
aligned_mem := rawptr(ptr_offset((^u8)(allocated_mem), size_of(rawptr)));
ptr := uintptr(aligned_mem);
aligned_ptr := (ptr - 1 + uintptr(a)) & -uintptr(a);
diff := int(aligned_ptr - ptr);
if (size + diff) > space {
return nil, .Out_Of_Memory;
}
aligned_mem = rawptr(aligned_ptr);
ptr_offset((^rawptr)(aligned_mem), -1)^ = allocated_mem;
return byte_slice(aligned_mem, size), nil;
} }
default_allocator :: proc() -> Allocator { _windows_default_alloc :: proc "contextless" (size, alignment: int) -> ([]byte, Allocator_Error) {
return Allocator{ return _windows_default_alloc_or_resize(size, alignment, nil);
procedure = default_allocator_proc, }
data = nil,
};
_windows_default_free :: proc "contextless" (ptr: rawptr) {
if ptr != nil {
heap_free(ptr_offset((^rawptr)(ptr), -1)^);
}
}
_windows_default_resize :: proc "contextless" (p: rawptr, old_size: int, new_size: int, new_alignment: int) -> ([]byte, Allocator_Error) {
return _windows_default_alloc_or_resize(new_size, new_alignment, p);
} }
+9 -5
View File
@@ -1,18 +1,22 @@
package runtime package runtime
import "core:sys/es"
@(link_name="memset") @(link_name="memset")
memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr { memset :: proc "c" (ptr: rawptr, val: i32, len: int) -> rawptr {
return es.CRTmemset(ptr, val, len); addr := 0x1000 + 196 * size_of(int);
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
return ((proc "c" (rawptr, i32, int) -> rawptr)(fp))(ptr, val, len);
} }
@(link_name="memmove") @(link_name="memmove")
memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr { memmove :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
return es.CRTmemmove(dst, src, len); addr := 0x1000 + 195 * size_of(int);
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
return ((proc "c" (rawptr, rawptr, int) -> rawptr)(fp))(dst, src, len);
} }
@(link_name="memcpy") @(link_name="memcpy")
memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr { memcpy :: proc "c" (dst, src: rawptr, len: int) -> rawptr {
return es.CRTmemcpy(dst, src, len); addr := 0x1000 + 194 * size_of(int);
fp := (rawptr(((^uintptr)(uintptr(addr)))^));
return ((proc "c" (rawptr, rawptr, int) -> rawptr)(fp))(dst, src, len);
} }
+1 -1
View File
@@ -221,7 +221,7 @@ pop_rune :: proc(b: ^Builder) -> (r: rune, width: int) {
} }
@(private, static) @(private)
DIGITS_LOWER := "0123456789abcdefx"; DIGITS_LOWER := "0123456789abcdefx";
write_quoted_string :: proc{ write_quoted_string :: proc{
+8
View File
@@ -541,6 +541,14 @@ replace :: proc(s, old, new: string, n: int, allocator := context.allocator) ->
return; return;
} }
remove :: proc(s, key: string, n: int, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return replace(s, key, "", n, allocator);
}
remove_all :: proc(s, key: string, allocator := context.allocator) -> (output: string, was_allocation: bool) {
return remove(s, key, -1, allocator);
}
@(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1}; @(private) _ascii_space := [256]u8{'\t' = 1, '\n' = 1, '\v' = 1, '\f' = 1, '\r' = 1, ' ' = 1};
+2
View File
@@ -56,6 +56,7 @@ atomic_exchange_release :: intrinsics.atomic_xchg_rel;
atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel; atomic_exchange_acqrel :: intrinsics.atomic_xchg_acqrel;
atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed; atomic_exchange_relaxed :: intrinsics.atomic_xchg_relaxed;
// Returns value and optional ok boolean
atomic_compare_exchange_strong :: intrinsics.atomic_cxchg; atomic_compare_exchange_strong :: intrinsics.atomic_cxchg;
atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq; atomic_compare_exchange_strong_acquire :: intrinsics.atomic_cxchg_acq;
atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel; atomic_compare_exchange_strong_release :: intrinsics.atomic_cxchg_rel;
@@ -66,6 +67,7 @@ atomic_compare_exchange_strong_failacquire :: intrinsics.atomic_cxchg_fa
atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed; atomic_compare_exchange_strong_acquire_failrelaxed :: intrinsics.atomic_cxchg_acq_failrelaxed;
atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed; atomic_compare_exchange_strong_acqrel_failrelaxed :: intrinsics.atomic_cxchg_acqrel_failrelaxed;
// Returns value and optional ok boolean
atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak; atomic_compare_exchange_weak :: intrinsics.atomic_cxchgweak;
atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq; atomic_compare_exchange_weak_acquire :: intrinsics.atomic_cxchgweak_acq;
atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel; atomic_compare_exchange_weak_release :: intrinsics.atomic_cxchgweak_rel;
-886
View File
@@ -1,886 +0,0 @@
package sync2
// TODO(bill): The Channel implementation needs a complete rewrite for this new package sync design
// Especially how the `select` things work
import "core:mem"
import "core:time"
import "core:math/rand"
_, _ :: time, rand;
Channel_Direction :: enum i8 {
Both = 0,
Send = +1,
Recv = -1,
}
Channel :: struct(T: typeid, Direction := Channel_Direction.Both) {
using _internal: ^Raw_Channel,
}
channel_init :: proc(ch: ^$C/Channel($T, $D), cap := 0, allocator := context.allocator) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Both)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make_send :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Send)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_make_recv :: proc($T: typeid, cap := 0, allocator := context.allocator) -> (ch: Channel(T, .Recv)) {
context.allocator = allocator;
ch._internal = raw_channel_create(size_of(T), align_of(T), cap);
return;
}
channel_destroy :: proc(ch: $C/Channel($T, $D)) {
raw_channel_destroy(ch._internal);
}
channel_as_send :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Send)) {
res._internal = ch._internal;
return;
}
channel_as_recv :: proc(ch: $C/Channel($T, .Both)) -> (res: Channel(T, .Recv)) {
res._internal = ch._internal;
return;
}
channel_len :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.len if ch._internal != nil else 0;
}
channel_cap :: proc(ch: $C/Channel($T, $D)) -> int {
return ch._internal.cap if ch._internal != nil else 0;
}
channel_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) where D >= .Both {
msg := msg;
_ = raw_channel_send_impl(ch._internal, &msg, /*block*/true, loc);
}
channel_try_send :: proc(ch: $C/Channel($T, $D), msg: T, loc := #caller_location) -> bool where D >= .Both {
msg := msg;
return raw_channel_send_impl(ch._internal, &msg, /*block*/false, loc);
}
channel_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T) where D <= .Both {
c := ch._internal;
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc);
}
mutex_lock(&c.mutex);
raw_channel_recv_impl(c, &msg, loc);
mutex_unlock(&c.mutex);
return;
}
channel_try_recv :: proc(ch: $C/Channel($T, $D), loc := #caller_location) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if c != nil && mutex_try_lock(&c.mutex) {
if c.len > 0 {
raw_channel_recv_impl(c, &msg, loc);
ok = true;
}
mutex_unlock(&c.mutex);
}
return;
}
channel_try_recv_ptr :: proc(ch: $C/Channel($T, $D), msg: ^T, loc := #caller_location) -> (ok: bool) where D <= .Both {
res: T;
res, ok = channel_try_recv(ch, loc);
if ok && msg != nil {
msg^ = res;
}
return;
}
channel_is_nil :: proc(ch: $C/Channel($T, $D)) -> bool {
return ch._internal == nil;
}
channel_is_open :: proc(ch: $C/Channel($T, $D)) -> bool {
c := ch._internal;
return c != nil && !c.closed;
}
channel_eq :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal == b._internal;
}
channel_ne :: proc(a, b: $C/Channel($T, $D)) -> bool {
return a._internal != b._internal;
}
channel_can_send :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D >= .Both {
return raw_channel_can_send(ch._internal);
}
channel_can_recv :: proc(ch: $C/Channel($T, $D)) -> (ok: bool) where D <= .Both {
return raw_channel_can_recv(ch._internal);
}
channel_peek :: proc(ch: $C/Channel($T, $D)) -> int {
c := ch._internal;
if c == nil {
return -1;
}
if atomic_load(&c.closed) {
return -1;
}
return atomic_load(&c.len);
}
channel_close :: proc(ch: $C/Channel($T, $D), loc := #caller_location) {
raw_channel_close(ch._internal, loc);
}
channel_iterator :: proc(ch: $C/Channel($T, $D)) -> (msg: T, ok: bool) where D <= .Both {
c := ch._internal;
if c == nil {
return;
}
if !c.closed || c.len > 0 {
msg, ok = channel_recv(ch), true;
}
return;
}
channel_drain :: proc(ch: $C/Channel($T, $D)) where D >= .Both {
raw_channel_drain(ch._internal);
}
channel_move :: proc(dst: $C1/Channel($T, $D1) src: $C2/Channel(T, $D2)) where D1 <= .Both, D2 >= .Both {
for msg in channel_iterator(src) {
channel_send(dst, msg);
}
}
Raw_Channel_Wait_Queue :: struct {
next: ^Raw_Channel_Wait_Queue,
state: ^uintptr,
}
Raw_Channel :: struct {
closed: bool,
ready: bool, // ready to recv
data_offset: u16, // data is stored at the end of this data structure
elem_size: u32,
len, cap: int,
read, write: int,
mutex: Mutex,
cond: Cond,
allocator: mem.Allocator,
sendq: ^Raw_Channel_Wait_Queue,
recvq: ^Raw_Channel_Wait_Queue,
}
raw_channel_wait_queue_insert :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
val.next = head^;
head^ = val;
}
raw_channel_wait_queue_remove :: proc(head: ^^Raw_Channel_Wait_Queue, val: ^Raw_Channel_Wait_Queue) {
p := head;
for p^ != nil && p^ != val {
p = &p^.next;
}
if p != nil {
p^ = p^.next;
}
}
raw_channel_create :: proc(elem_size, elem_align: int, cap := 0) -> ^Raw_Channel {
assert(int(u32(elem_size)) == elem_size);
s := size_of(Raw_Channel);
s = mem.align_forward_int(s, elem_align);
data_offset := uintptr(s);
s += elem_size * max(cap, 1);
a := max(elem_align, align_of(Raw_Channel));
c := (^Raw_Channel)(mem.alloc(s, a));
if c == nil {
return nil;
}
c.data_offset = u16(data_offset);
c.elem_size = u32(elem_size);
c.len, c.cap = 0, max(cap, 0);
c.read, c.write = 0, 0;
c.allocator = context.allocator;
c.closed = false;
return c;
}
raw_channel_destroy :: proc(c: ^Raw_Channel) {
if c == nil {
return;
}
context.allocator = c.allocator;
atomic_store(&c.closed, true);
free(c);
}
raw_channel_close :: proc(c: ^Raw_Channel, loc := #caller_location) {
if c == nil {
panic(message="cannot close nil channel", loc=loc);
}
mutex_lock(&c.mutex);
defer mutex_unlock(&c.mutex);
atomic_store(&c.closed, true);
// Release readers and writers
raw_channel_wait_queue_broadcast(c.recvq);
raw_channel_wait_queue_broadcast(c.sendq);
cond_broadcast(&c.cond);
}
raw_channel_send_impl :: proc(c: ^Raw_Channel, msg: rawptr, block: bool, loc := #caller_location) -> bool {
send :: proc(c: ^Raw_Channel, src: rawptr) {
data := uintptr(c) + uintptr(c.data_offset);
dst := data + uintptr(c.write * int(c.elem_size));
mem.copy(rawptr(dst), src, int(c.elem_size));
c.len += 1;
c.write = (c.write + 1) % max(c.cap, 1);
}
switch {
case c == nil:
panic(message="cannot send message; channel is nil", loc=loc);
case c.closed:
panic(message="cannot send message; channel is closed", loc=loc);
}
mutex_lock(&c.mutex);
defer mutex_unlock(&c.mutex);
if c.cap > 0 {
if !block && c.len >= c.cap {
return false;
}
for c.len >= c.cap {
cond_wait(&c.cond, &c.mutex);
}
} else if c.len > 0 { // TODO(bill): determine correct behaviour
if !block {
return false;
}
cond_wait(&c.cond, &c.mutex);
} else if c.len == 0 && !block {
return false;
}
send(c, msg);
cond_signal(&c.cond);
raw_channel_wait_queue_signal(c.recvq);
return true;
}
raw_channel_recv_impl :: proc(c: ^Raw_Channel, res: rawptr, loc := #caller_location) {
recv :: proc(c: ^Raw_Channel, dst: rawptr, loc := #caller_location) {
if c.len < 1 {
panic(message="cannot recv message; channel is empty", loc=loc);
}
c.len -= 1;
data := uintptr(c) + uintptr(c.data_offset);
src := data + uintptr(c.read * int(c.elem_size));
mem.copy(dst, rawptr(src), int(c.elem_size));
c.read = (c.read + 1) % max(c.cap, 1);
}
if c == nil {
panic(message="cannot recv message; channel is nil", loc=loc);
}
atomic_store(&c.ready, true);
for c.len < 1 {
raw_channel_wait_queue_signal(c.sendq);
cond_wait(&c.cond, &c.mutex);
}
atomic_store(&c.ready, false);
recv(c, res, loc);
if c.cap > 0 {
if c.len == c.cap - 1 {
// NOTE(bill): Only signal on the last one
cond_signal(&c.cond);
}
} else {
cond_signal(&c.cond);
}
}
raw_channel_can_send :: proc(c: ^Raw_Channel) -> (ok: bool) {
if c == nil {
return false;
}
mutex_lock(&c.mutex);
switch {
case c.closed:
ok = false;
case c.cap > 0:
ok = c.ready && c.len < c.cap;
case:
ok = c.ready && c.len == 0;
}
mutex_unlock(&c.mutex);
return;
}
raw_channel_can_recv :: proc(c: ^Raw_Channel) -> (ok: bool) {
if c == nil {
return false;
}
mutex_lock(&c.mutex);
ok = c.len > 0;
mutex_unlock(&c.mutex);
return;
}
raw_channel_drain :: proc(c: ^Raw_Channel) {
if c == nil {
return;
}
mutex_lock(&c.mutex);
c.len = 0;
c.read = 0;
c.write = 0;
mutex_unlock(&c.mutex);
}
MAX_SELECT_CHANNELS :: 64;
SELECT_MAX_TIMEOUT :: max(time.Duration);
Select_Command :: enum {
Recv,
Send,
}
Select_Channel :: struct {
channel: ^Raw_Channel,
command: Select_Command,
}
select :: proc(channels: ..Select_Channel) -> (index: int) {
return select_timeout(SELECT_MAX_TIMEOUT, ..channels);
}
select_timeout :: proc(timeout: time.Duration, channels: ..Select_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
if c.channel == nil {
continue;
}
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i;
count += 1;
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i;
count += 1;
}
}
}
if count == 0 {
wait_state: uintptr = 0;
for _, i in channels {
q := &queues[i];
q.state = &wait_state;
}
for c, i in channels {
if c.channel == nil {
continue;
}
q := &queues[i];
switch c.command {
case .Recv: raw_channel_wait_queue_insert(&c.channel.recvq, q);
case .Send: raw_channel_wait_queue_insert(&c.channel.sendq, q);
}
}
raw_channel_wait_queue_wait_on(&wait_state, timeout);
for c, i in channels {
if c.channel == nil {
continue;
}
q := &queues[i];
switch c.command {
case .Recv: raw_channel_wait_queue_remove(&c.channel.recvq, q);
case .Send: raw_channel_wait_queue_remove(&c.channel.sendq, q);
}
}
for c, i in channels {
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i;
count += 1;
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i;
count += 1;
}
}
}
if count == 0 && timeout == SELECT_MAX_TIMEOUT {
index = -1;
return;
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
msg = channel_recv(channels[index]);
return;
}
select_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.recvq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.recvq, q);
}
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
if msg != nil {
channel_send(channels[index], msg);
}
return;
}
select_send :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
queues: [MAX_SELECT_CHANNELS]Raw_Channel_Wait_Queue;
count := u32(0);
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
state: uintptr;
for c, i in channels {
q := &queues[i];
q.state = &state;
raw_channel_wait_queue_insert(&c.sendq, q);
}
raw_channel_wait_queue_wait_on(&state, SELECT_MAX_TIMEOUT);
for c, i in channels {
q := &queues[i];
raw_channel_wait_queue_remove(&c.sendq, q);
}
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
assert(count != 0);
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try :: proc(channels: ..Select_Channel) -> (index: int) {
switch len(channels) {
case 0:
panic("sync: select with no channels");
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
backing: [MAX_SELECT_CHANNELS]int;
candidates := backing[:];
cap := len(channels);
candidates = candidates[:cap];
count := u32(0);
for c, i in channels {
switch c.command {
case .Recv:
if raw_channel_can_recv(c.channel) {
candidates[count] = i;
count += 1;
}
case .Send:
if raw_channel_can_send(c.channel) {
candidates[count] = i;
count += 1;
}
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try_recv :: proc(channels: ..^Raw_Channel) -> (index: int) {
switch len(channels) {
case 0:
index = -1;
return;
case 1:
index = -1;
if raw_channel_can_recv(channels[0]) {
index = 0;
}
return;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try_send :: proc(channels: ..^Raw_Channel) -> (index: int) #no_bounds_check {
switch len(channels) {
case 0:
return -1;
case 1:
if raw_channel_can_send(channels[0]) {
return 0;
}
return -1;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
return;
}
select_try_recv_msg :: proc(channels: ..$C/Channel($T, $D)) -> (msg: T, index: int) {
switch len(channels) {
case 0:
index = -1;
return;
case 1:
ok: bool;
if msg, ok = channel_try_recv(channels[0]); ok {
index = 0;
}
return;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if channel_can_recv(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
msg = channel_recv(channels[index]);
return;
}
select_try_send_msg :: proc(msg: $T, channels: ..$C/Channel(T, $D)) -> (index: int) {
index = -1;
switch len(channels) {
case 0:
return;
case 1:
if channel_try_send(channels[0], msg) {
index = 0;
}
return;
}
assert(len(channels) <= MAX_SELECT_CHANNELS);
candidates: [MAX_SELECT_CHANNELS]int;
count := u32(0);
for c, i in channels {
if raw_channel_can_send(c) {
candidates[count] = i;
count += 1;
}
}
if count == 0 {
index = -1;
return;
}
t := time.now();
r := rand.create(transmute(u64)t);
i := rand.uint32(&r);
index = candidates[i % count];
channel_send(channels[index], msg);
return;
}
-17
View File
@@ -1,17 +0,0 @@
//+build linux, darwin, freebsd
//+private
package sync2
import "core:time"
raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
// stub
}
raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
// stub
}
raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
// stub
}
-34
View File
@@ -1,34 +0,0 @@
//+build windows
//+private
package sync2
import win32 "core:sys/windows"
import "core:time"
raw_channel_wait_queue_wait_on :: proc(state: ^uintptr, timeout: time.Duration) {
ms: win32.DWORD = win32.INFINITE;
if max(time.Duration) != SELECT_MAX_TIMEOUT {
ms = win32.DWORD((max(time.duration_nanoseconds(timeout), 0) + 999999)/1000000);
}
v := atomic_load(state);
for v == 0 {
win32.WaitOnAddress(state, &v, size_of(state^), ms);
v = atomic_load(state);
}
atomic_store(state, 0);
}
raw_channel_wait_queue_signal :: proc(q: ^Raw_Channel_Wait_Queue) {
for x := q; x != nil; x = x.next {
atomic_add(x.state, 1);
win32.WakeByAddressSingle(x.state);
}
}
raw_channel_wait_queue_broadcast :: proc(q: ^Raw_Channel_Wait_Queue) {
for x := q; x != nil; x = x.next {
atomic_add(x.state, 1);
win32.WakeByAddressAll(x.state);
}
}
+2 -2
View File
@@ -15,7 +15,7 @@ mutex_lock :: proc(m: ^Mutex) {
_mutex_lock(m); _mutex_lock(m);
} }
// mutex_lock unlocks m // mutex_unlock unlocks m
mutex_unlock :: proc(m: ^Mutex) { mutex_unlock :: proc(m: ^Mutex) {
_mutex_unlock(m); _mutex_unlock(m);
} }
@@ -103,7 +103,7 @@ rw_mutex_shared_guard :: proc(m: ^RW_Mutex) -> bool {
// A Recusrive_Mutex is a recursive mutual exclusion lock // A Recursive_Mutex is a recursive mutual exclusion lock
// The zero value for a Recursive_Mutex is an unlocked mutex // The zero value for a Recursive_Mutex is an unlocked mutex
// //
// A Recursive_Mutex must not be copied after first use // A Recursive_Mutex must not be copied after first use
+269 -185
View File
@@ -1,159 +1,193 @@
//+build linux, darwin, freebsd
//+private
package sync2 package sync2
when !#config(ODIN_SYNC_USE_PTHREADS, true) {
import "core:time" import "core:time"
import "core:runtime" import "core:runtime"
_Mutex_State :: enum i32 { Atomic_Mutex_State :: enum i32 {
Unlocked = 0, Unlocked = 0,
Locked = 1, Locked = 1,
Waiting = 2, Waiting = 2,
} }
_Mutex :: struct {
state: _Mutex_State,
// An Atomic_Mutex is a mutual exclusion lock
// The zero value for a Atomic_Mutex is an unlocked mutex
//
// An Atomic_Mutex must not be copied after first use
Atomic_Mutex :: struct {
state: Atomic_Mutex_State,
} }
_mutex_lock :: proc(m: ^Mutex) { // atomic_mutex_lock locks m
if atomic_xchg_rel(&m.impl.state, .Unlocked) != .Unlocked { atomic_mutex_lock :: proc(m: ^Atomic_Mutex) {
_mutex_unlock_slow(m); @(cold)
lock_slow :: proc(m: ^Atomic_Mutex, curr_state: Atomic_Mutex_State) {
new_state := curr_state; // Make a copy of it
spin_lock: for spin in 0..<i32(100) {
state, ok := atomic_compare_exchange_weak_acquire(&m.state, .Unlocked, new_state);
if ok {
return;
}
if state == .Waiting {
break spin_lock;
}
for i := min(spin+1, 32); i > 0; i -= 1 {
cpu_relax();
}
}
for {
if atomic_exchange_acquire(&m.state, .Waiting) == .Unlocked {
return;
}
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
cpu_relax();
}
}
switch v := atomic_exchange_acquire(&m.state, .Locked); v {
case .Unlocked:
// Okay
case: fallthrough;
case .Locked, .Waiting:
lock_slow(m, v);
} }
} }
_mutex_unlock :: proc(m: ^Mutex) { // atomic_mutex_unlock unlocks m
switch atomic_xchg_rel(&m.impl.state, .Unlocked) { atomic_mutex_unlock :: proc(m: ^Atomic_Mutex) {
@(cold)
unlock_slow :: proc(m: ^Atomic_Mutex) {
// TODO(bill): Use a Futex here for Linux to improve performance and error handling
}
switch atomic_exchange_release(&m.state, .Unlocked) {
case .Unlocked: case .Unlocked:
unreachable(); unreachable();
case .Locked: case .Locked:
// Okay // Okay
case .Waiting: case .Waiting:
_mutex_unlock_slow(m); unlock_slow(m);
} }
} }
_mutex_try_lock :: proc(m: ^Mutex) -> bool { // atomic_mutex_try_lock tries to lock m, will return true on success, and false on failure
_, ok := atomic_cxchg_acq(&m.impl.state, .Unlocked, .Locked); atomic_mutex_try_lock :: proc(m: ^Atomic_Mutex) -> bool {
_, ok := atomic_compare_exchange_strong_acquire(&m.state, .Unlocked, .Locked);
return ok; return ok;
} }
@(cold) // Example:
_mutex_lock_slow :: proc(m: ^Mutex, curr_state: _Mutex_State) { //
new_state := curr_state; // Make a copy of it // if atomic_mutex_guard(&m) {
// ...
// }
//
@(deferred_in=atomic_mutex_unlock)
atomic_mutex_guard :: proc(m: ^Atomic_Mutex) -> bool {
atomic_mutex_lock(m);
return true;
}
spin_lock: for spin in 0..<i32(100) {
state, ok := atomic_cxchgweak_acq(&m.impl.state, .Unlocked, new_state);
if ok {
return;
}
if state == .Waiting { Atomic_RW_Mutex_State :: distinct uint;
break spin_lock; Atomic_RW_Mutex_State_Half_Width :: size_of(Atomic_RW_Mutex_State)*8/2;
} Atomic_RW_Mutex_State_Is_Writing :: Atomic_RW_Mutex_State(1);
Atomic_RW_Mutex_State_Writer :: Atomic_RW_Mutex_State(1)<<1;
Atomic_RW_Mutex_State_Reader :: Atomic_RW_Mutex_State(1)<<Atomic_RW_Mutex_State_Half_Width;
for i := min(spin+1, 32); i > 0; i -= 1 { Atomic_RW_Mutex_State_Writer_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_State_Half_Width-1) - 1) << 1;
cpu_relax(); Atomic_RW_Mutex_State_Reader_Mask :: Atomic_RW_Mutex_State(1<<(Atomic_RW_Mutex_State_Half_Width-1) - 1) << Atomic_RW_Mutex_State_Half_Width;
}
}
for {
if atomic_xchg_acq(&m.impl.state, .Waiting) == .Unlocked {
return;
}
// TODO(bill): Use a Futex here for Linux to improve performance and error handling // An Atomic_RW_Mutex is a reader/writer mutual exclusion lock
cpu_relax(); // The lock can be held by any arbitrary number of readers or a single writer
// The zero value for an Atomic_RW_Mutex is an unlocked mutex
//
// An Atomic_RW_Mutex must not be copied after first use
Atomic_RW_Mutex :: struct {
state: Atomic_RW_Mutex_State,
mutex: Atomic_Mutex,
sema: Atomic_Sema,
}
// atomic_rw_mutex_lock locks rw for writing (with a single writer)
// If the mutex is already locked for reading or writing, the mutex blocks until the mutex is available.
atomic_rw_mutex_lock :: proc(rw: ^Atomic_RW_Mutex) {
_ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Writer);
atomic_mutex_lock(&rw.mutex);
state := atomic_or(&rw.state, Atomic_RW_Mutex_State_Writer);
if state & Atomic_RW_Mutex_State_Reader_Mask != 0 {
atomic_sema_wait(&rw.sema);
} }
} }
// atomic_rw_mutex_unlock unlocks rw for writing (with a single writer)
@(cold) atomic_rw_mutex_unlock :: proc(rw: ^Atomic_RW_Mutex) {
_mutex_unlock_slow :: proc(m: ^Mutex) { _ = atomic_and(&rw.state, ~Atomic_RW_Mutex_State_Is_Writing);
// TODO(bill): Use a Futex here for Linux to improve performance and error handling atomic_mutex_unlock(&rw.mutex);
} }
// atomic_rw_mutex_try_lock tries to lock rw for writing (with a single writer)
RW_Mutex_State :: distinct uint; atomic_rw_mutex_try_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
RW_Mutex_State_Half_Width :: size_of(RW_Mutex_State)*8/2; if atomic_mutex_try_lock(&rw.mutex) {
RW_Mutex_State_Is_Writing :: RW_Mutex_State(1); state := atomic_load(&rw.state);
RW_Mutex_State_Writer :: RW_Mutex_State(1)<<1; if state & Atomic_RW_Mutex_State_Reader_Mask == 0 {
RW_Mutex_State_Reader :: RW_Mutex_State(1)<<RW_Mutex_State_Half_Width; _ = atomic_or(&rw.state, Atomic_RW_Mutex_State_Is_Writing);
RW_Mutex_State_Writer_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << 1;
RW_Mutex_State_Reader_Mask :: RW_Mutex_State(1<<(RW_Mutex_State_Half_Width-1) - 1) << RW_Mutex_State_Half_Width;
_RW_Mutex :: struct {
state: RW_Mutex_State,
mutex: Mutex,
sema: Sema,
}
_rw_mutex_lock :: proc(rw: ^RW_Mutex) {
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Writer);
mutex_lock(&rw.impl.mutex);
state := atomic_or(&rw.impl.state, RW_Mutex_State_Writer);
if state & RW_Mutex_State_Reader_Mask != 0 {
sema_wait(&rw.impl.sema);
}
}
_rw_mutex_unlock :: proc(rw: ^RW_Mutex) {
_ = atomic_and(&rw.impl.state, ~RW_Mutex_State_Is_Writing);
mutex_unlock(&rw.impl.mutex);
}
_rw_mutex_try_lock :: proc(rw: ^RW_Mutex) -> bool {
if mutex_try_lock(&rw.impl.mutex) {
state := atomic_load(&rw.impl.state);
if state & RW_Mutex_State_Reader_Mask == 0 {
_ = atomic_or(&rw.impl.state, RW_Mutex_State_Is_Writing);
return true; return true;
} }
mutex_unlock(&rw.impl.mutex); atomic_mutex_unlock(&rw.mutex);
} }
return false; return false;
} }
_rw_mutex_shared_lock :: proc(rw: ^RW_Mutex) { // atomic_rw_mutex_shared_lock locks rw for reading (with arbitrary number of readers)
state := atomic_load(&rw.impl.state); atomic_rw_mutex_shared_lock :: proc(rw: ^Atomic_RW_Mutex) {
for state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 { state := atomic_load(&rw.state);
for state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
ok: bool; ok: bool;
state, ok = atomic_cxchgweak(&rw.impl.state, state, state + RW_Mutex_State_Reader); state, ok = atomic_compare_exchange_weak(&rw.state, state, state + Atomic_RW_Mutex_State_Reader);
if ok { if ok {
return; return;
} }
} }
mutex_lock(&rw.impl.mutex); atomic_mutex_lock(&rw.mutex);
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader); _ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Reader);
mutex_unlock(&rw.impl.mutex); atomic_mutex_unlock(&rw.mutex);
} }
_rw_mutex_shared_unlock :: proc(rw: ^RW_Mutex) { // atomic_rw_mutex_shared_unlock unlocks rw for reading (with arbitrary number of readers)
state := atomic_sub(&rw.impl.state, RW_Mutex_State_Reader); atomic_rw_mutex_shared_unlock :: proc(rw: ^Atomic_RW_Mutex) {
state := atomic_sub(&rw.state, Atomic_RW_Mutex_State_Reader);
if (state & RW_Mutex_State_Reader_Mask == RW_Mutex_State_Reader) && if (state & Atomic_RW_Mutex_State_Reader_Mask == Atomic_RW_Mutex_State_Reader) &&
(state & RW_Mutex_State_Is_Writing != 0) { (state & Atomic_RW_Mutex_State_Is_Writing != 0) {
sema_post(&rw.impl.sema); atomic_sema_post(&rw.sema);
} }
} }
_rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool { // atomic_rw_mutex_try_shared_lock tries to lock rw for reading (with arbitrary number of readers)
state := atomic_load(&rw.impl.state); atomic_rw_mutex_try_shared_lock :: proc(rw: ^Atomic_RW_Mutex) -> bool {
if state & (RW_Mutex_State_Is_Writing|RW_Mutex_State_Writer_Mask) == 0 { state := atomic_load(&rw.state);
_, ok := atomic_cxchg(&rw.impl.state, state, state + RW_Mutex_State_Reader); if state & (Atomic_RW_Mutex_State_Is_Writing|Atomic_RW_Mutex_State_Writer_Mask) == 0 {
_, ok := atomic_compare_exchange_strong(&rw.state, state, state + Atomic_RW_Mutex_State_Reader);
if ok { if ok {
return true; return true;
} }
} }
if mutex_try_lock(&rw.impl.mutex) { if atomic_mutex_try_lock(&rw.mutex) {
_ = atomic_add(&rw.impl.state, RW_Mutex_State_Reader); _ = atomic_add(&rw.state, Atomic_RW_Mutex_State_Reader);
mutex_unlock(&rw.impl.mutex); atomic_mutex_unlock(&rw.mutex);
return true; return true;
} }
@@ -161,127 +195,177 @@ _rw_mutex_try_shared_lock :: proc(rw: ^RW_Mutex) -> bool {
} }
_Recursive_Mutex :: struct { // Example:
owner: int, //
recursion: int, // if atomic_rw_mutex_guard(&m) {
mutex: Mutex, // ...
// }
//
@(deferred_in=atomic_rw_mutex_unlock)
atomic_rw_mutex_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
atomic_rw_mutex_lock(m);
return true;
} }
_recursive_mutex_lock :: proc(m: ^Recursive_Mutex) { // Example:
tid := runtime.current_thread_id(); //
if tid != m.impl.owner { // if atomic_rw_mutex_shared_guard(&m) {
mutex_lock(&m.impl.mutex); // ...
} // }
// inside the lock //
m.impl.owner = tid; @(deferred_in=atomic_rw_mutex_shared_unlock)
m.impl.recursion += 1; atomic_rw_mutex_shared_guard :: proc(m: ^Atomic_RW_Mutex) -> bool {
} atomic_rw_mutex_shared_lock(m);
_recursive_mutex_unlock :: proc(m: ^Recursive_Mutex) {
tid := runtime.current_thread_id();
assert(tid == m.impl.owner);
m.impl.recursion -= 1;
recursion := m.impl.recursion;
if recursion == 0 {
m.impl.owner = 0;
}
if recursion == 0 {
mutex_unlock(&m.impl.mutex);
}
// outside the lock
}
_recursive_mutex_try_lock :: proc(m: ^Recursive_Mutex) -> bool {
tid := runtime.current_thread_id();
if m.impl.owner == tid {
return mutex_try_lock(&m.impl.mutex);
}
if !mutex_try_lock(&m.impl.mutex) {
return false;
}
// inside the lock
m.impl.owner = tid;
m.impl.recursion += 1;
return true; return true;
} }
// An Atomic_Recursive_Mutex is a recursive mutual exclusion lock
// The zero value for a Recursive_Mutex is an unlocked mutex
//
// An Atomic_Recursive_Mutex must not be copied after first use
Atomic_Recursive_Mutex :: struct {
owner: int,
recursion: int,
mutex: Mutex,
}
atomic_recursive_mutex_lock :: proc(m: ^Atomic_Recursive_Mutex) {
tid := runtime.current_thread_id();
if tid != m.owner {
mutex_lock(&m.mutex);
}
// inside the lock
m.owner = tid;
m.recursion += 1;
}
atomic_recursive_mutex_unlock :: proc(m: ^Atomic_Recursive_Mutex) {
tid := runtime.current_thread_id();
assert(tid == m.owner);
m.recursion -= 1;
recursion := m.recursion;
if recursion == 0 {
m.owner = 0;
}
if recursion == 0 {
mutex_unlock(&m.mutex);
}
// outside the lock
}
atomic_recursive_mutex_try_lock :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
tid := runtime.current_thread_id();
if m.owner == tid {
return mutex_try_lock(&m.mutex);
}
if !mutex_try_lock(&m.mutex) {
return false;
}
// inside the lock
m.owner = tid;
m.recursion += 1;
return true;
}
// Example:
//
// if atomic_recursive_mutex_guard(&m) {
// ...
// }
//
@(deferred_in=atomic_recursive_mutex_unlock)
atomic_recursive_mutex_guard :: proc(m: ^Atomic_Recursive_Mutex) -> bool {
atomic_recursive_mutex_lock(m);
return true;
}
@(private="file")
Queue_Item :: struct { Queue_Item :: struct {
next: ^Queue_Item, next: ^Queue_Item,
futex: i32, futex: i32,
} }
@(private="file")
queue_item_wait :: proc(item: ^Queue_Item) { queue_item_wait :: proc(item: ^Queue_Item) {
for atomic_load_acq(&item.futex) == 0 { for atomic_load_acquire(&item.futex) == 0 {
// TODO(bill): Use a Futex here for Linux to improve performance and error handling // TODO(bill): Use a Futex here for Linux to improve performance and error handling
cpu_relax(); cpu_relax();
} }
} }
@(private="file")
queue_item_signal :: proc(item: ^Queue_Item) { queue_item_signal :: proc(item: ^Queue_Item) {
atomic_store_rel(&item.futex, 1); atomic_store_release(&item.futex, 1);
// TODO(bill): Use a Futex here for Linux to improve performance and error handling // TODO(bill): Use a Futex here for Linux to improve performance and error handling
} }
_Cond :: struct { // Atomic_Cond implements a condition variable, a rendezvous point for threads
queue_mutex: Mutex, // waiting for signalling the occurence of an event
//
// An Atomic_Cond must not be copied after first use
Atomic_Cond :: struct {
queue_mutex: Atomic_Mutex,
queue_head: ^Queue_Item, queue_head: ^Queue_Item,
pending: bool, pending: bool,
} }
_cond_wait :: proc(c: ^Cond, m: ^Mutex) { atomic_cond_wait :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex) {
waiter := &Queue_Item{}; waiter := &Queue_Item{};
mutex_lock(&c.impl.queue_mutex); atomic_mutex_lock(&c.queue_mutex);
waiter.next = c.impl.queue_head; waiter.next = c.queue_head;
c.impl.queue_head = waiter; c.queue_head = waiter;
atomic_store(&c.impl.pending, true); atomic_store(&c.pending, true);
mutex_unlock(&c.impl.queue_mutex); atomic_mutex_unlock(&c.queue_mutex);
mutex_unlock(m); atomic_mutex_unlock(m);
queue_item_wait(waiter); queue_item_wait(waiter);
mutex_lock(m); atomic_mutex_lock(m);
} }
_cond_wait_with_timeout :: proc(c: ^Cond, m: ^Mutex, timeout: time.Duration) -> bool { atomic_cond_wait_with_timeout :: proc(c: ^Atomic_Cond, m: ^Atomic_Mutex, timeout: time.Duration) -> bool {
// TODO(bill): _cond_wait_with_timeout for unix // TODO(bill): _cond_wait_with_timeout for unix
return false; return false;
} }
_cond_signal :: proc(c: ^Cond) { atomic_cond_signal :: proc(c: ^Atomic_Cond) {
if !atomic_load(&c.impl.pending) { if !atomic_load(&c.pending) {
return; return;
} }
mutex_lock(&c.impl.queue_mutex); atomic_mutex_lock(&c.queue_mutex);
waiter := c.impl.queue_head; waiter := c.queue_head;
if c.impl.queue_head != nil { if c.queue_head != nil {
c.impl.queue_head = c.impl.queue_head.next; c.queue_head = c.queue_head.next;
} }
atomic_store(&c.impl.pending, c.impl.queue_head != nil); atomic_store(&c.pending, c.queue_head != nil);
mutex_unlock(&c.impl.queue_mutex); atomic_mutex_unlock(&c.queue_mutex);
if waiter != nil { if waiter != nil {
queue_item_signal(waiter); queue_item_signal(waiter);
} }
} }
_cond_broadcast :: proc(c: ^Cond) { atomic_cond_broadcast :: proc(c: ^Atomic_Cond) {
if !atomic_load(&c.impl.pending) { if !atomic_load(&c.pending) {
return; return;
} }
atomic_store(&c.impl.pending, false); atomic_store(&c.pending, false);
mutex_lock(&c.impl.queue_mutex); atomic_mutex_lock(&c.queue_mutex);
waiters := c.impl.queue_head; waiters := c.queue_head;
c.impl.queue_head = nil; c.queue_head = nil;
mutex_unlock(&c.impl.queue_mutex); atomic_mutex_unlock(&c.queue_mutex);
for waiters != nil { for waiters != nil {
queue_item_signal(waiters); queue_item_signal(waiters);
@@ -289,35 +373,35 @@ _cond_broadcast :: proc(c: ^Cond) {
} }
} }
_Sema :: struct { // When waited upon, blocks until the internal count is greater than zero, then subtracts one.
mutex: Mutex, // Posting to the semaphore increases the count by one, or the provided amount.
cond: Cond, //
// An Atomic_Sema must not be copied after first use
Atomic_Sema :: struct {
mutex: Atomic_Mutex,
cond: Atomic_Cond,
count: int, count: int,
} }
_sema_wait :: proc(s: ^Sema) { atomic_sema_wait :: proc(s: ^Atomic_Sema) {
mutex_lock(&s.impl.mutex); atomic_mutex_lock(&s.mutex);
defer mutex_unlock(&s.impl.mutex); defer atomic_mutex_unlock(&s.mutex);
for s.impl.count == 0 { for s.count == 0 {
cond_wait(&s.impl.cond, &s.impl.mutex); atomic_cond_wait(&s.cond, &s.mutex);
} }
s.impl.count -= 1; s.count -= 1;
if s.impl.count > 0 { if s.count > 0 {
cond_signal(&s.impl.cond); atomic_cond_signal(&s.cond);
} }
} }
_sema_post :: proc(s: ^Sema, count := 1) { atomic_sema_post :: proc(s: ^Atomic_Sema, count := 1) {
mutex_lock(&s.impl.mutex); atomic_mutex_lock(&s.mutex);
defer mutex_unlock(&s.impl.mutex); defer atomic_mutex_unlock(&s.mutex);
s.impl.count += count; s.count += count;
cond_signal(&s.impl.cond); atomic_cond_signal(&s.cond);
} }
} // !ODIN_SYNC_USE_PTHREADS
+1 -1
View File
@@ -1,4 +1,4 @@
//+build linux, darwin, freebsd //+build linux, freebsd
//+private //+private
package sync2 package sync2
-10
View File
@@ -12,7 +12,6 @@ package unicode
@(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case. @(private) pLo :: pLl | pLu; // a letter that is neither upper nor lower case.
@(private) pLmask :: pLo; @(private) pLmask :: pLo;
@(static)
char_properties := [MAX_LATIN1+1]u8{ char_properties := [MAX_LATIN1+1]u8{
0x00 = pC, // '\x00' 0x00 = pC, // '\x00'
0x01 = pC, // '\x01' 0x01 = pC, // '\x01'
@@ -273,7 +272,6 @@ char_properties := [MAX_LATIN1+1]u8{
}; };
@(static)
alpha_ranges := [?]i32{ alpha_ranges := [?]i32{
0x00d8, 0x00f6, 0x00d8, 0x00f6,
0x00f8, 0x01f5, 0x00f8, 0x01f5,
@@ -429,7 +427,6 @@ alpha_ranges := [?]i32{
0xffda, 0xffdc, 0xffda, 0xffdc,
}; };
@(static)
alpha_singlets := [?]i32{ alpha_singlets := [?]i32{
0x00aa, 0x00aa,
0x00b5, 0x00b5,
@@ -465,7 +462,6 @@ alpha_singlets := [?]i32{
0xfe74, 0xfe74,
}; };
@(static)
space_ranges := [?]i32{ space_ranges := [?]i32{
0x0009, 0x000d, // tab and newline 0x0009, 0x000d, // tab and newline
0x0020, 0x0020, // space 0x0020, 0x0020, // space
@@ -481,7 +477,6 @@ space_ranges := [?]i32{
0xfeff, 0xfeff, 0xfeff, 0xfeff,
}; };
@(static)
unicode_spaces := [?]i32{ unicode_spaces := [?]i32{
0x0009, // tab 0x0009, // tab
0x000a, // LF 0x000a, // LF
@@ -499,7 +494,6 @@ unicode_spaces := [?]i32{
0xfeff, // unknown 0xfeff, // unknown
}; };
@(static)
to_upper_ranges := [?]i32{ to_upper_ranges := [?]i32{
0x0061, 0x007a, 468, // a-z A-Z 0x0061, 0x007a, 468, // a-z A-Z
0x00e0, 0x00f6, 468, 0x00e0, 0x00f6, 468,
@@ -538,7 +532,6 @@ to_upper_ranges := [?]i32{
0xff41, 0xff5a, 468, 0xff41, 0xff5a, 468,
}; };
@(static)
to_upper_singlets := [?]i32{ to_upper_singlets := [?]i32{
0x00ff, 621, 0x00ff, 621,
0x0101, 499, 0x0101, 499,
@@ -882,7 +875,6 @@ to_upper_singlets := [?]i32{
0x1ff3, 509, 0x1ff3, 509,
}; };
@(static)
to_lower_ranges := [?]i32{ to_lower_ranges := [?]i32{
0x0041, 0x005a, 532, // A-Z a-z 0x0041, 0x005a, 532, // A-Z a-z
0x00c0, 0x00d6, 532, // - - 0x00c0, 0x00d6, 532, // - -
@@ -922,7 +914,6 @@ to_lower_ranges := [?]i32{
0xff21, 0xff3a, 532, // - - 0xff21, 0xff3a, 532, // - -
}; };
@(static)
to_lower_singlets := [?]i32{ to_lower_singlets := [?]i32{
0x0100, 501, 0x0100, 501,
0x0102, 501, 0x0102, 501,
@@ -1259,7 +1250,6 @@ to_lower_singlets := [?]i32{
0x1ffc, 491, 0x1ffc, 491,
}; };
@(static)
to_title_singlets := [?]i32{ to_title_singlets := [?]i32{
0x01c4, 501, 0x01c4, 501,
0x01c6, 499, 0x01c6, 499,
+29 -7
View File
@@ -1352,8 +1352,8 @@ bit_set_type :: proc() {
d: Days; d: Days;
d = {Sunday, Monday}; d = {Sunday, Monday};
e := d | WEEKEND; e := d + WEEKEND;
e |= {Monday}; e += {Monday};
fmt.println(d, e); fmt.println(d, e);
ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types ok := Saturday in e; // `in` is only allowed for `map` and `bit_set` types
@@ -1372,12 +1372,12 @@ bit_set_type :: proc() {
fmt.println(typeid_of(type_of(x))); // bit_set[A..Z] fmt.println(typeid_of(type_of(x))); // bit_set[A..Z]
fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16] fmt.println(typeid_of(type_of(y))); // bit_set[0..8; u16]
incl(&x, 'F'); x += {'F'};
assert('F' in x); assert('F' in x);
excl(&x, 'F'); x -= {'F'};
assert('F' not_in x); assert('F' not_in x);
y |= {1, 4, 2}; y += {1, 4, 2};
assert(2 in y); assert(2 in y);
} }
{ {
@@ -1760,8 +1760,6 @@ range_statements_with_multiple_return_values :: proc() {
soa_struct_layout :: proc() { soa_struct_layout :: proc() {
// IMPORTANT NOTE(bill, 2019-11-03): This feature is subject to be changed/removed
// NOTE(bill): Most likely #soa [N]T
fmt.println("\n#SOA Struct Layout"); fmt.println("\n#SOA Struct Layout");
{ {
@@ -1858,6 +1856,30 @@ soa_struct_layout :: proc() {
fmt.println(cap(d)); fmt.println(cap(d));
fmt.println(d[:]); fmt.println(d[:]);
} }
{ // soa_zip and soa_unzip
fmt.println("\nsoa_zip and soa_unzip");
x := []i32{1, 3, 9};
y := []f32{2, 4, 16};
z := []b32{true, false, true};
// produce an #soa slice the normal slices passed
s := soa_zip(a=x, b=y, c=z);
// iterate over the #soa slice
for v, i in s {
fmt.println(v, i); // exactly the same as s[i]
// NOTE: 'v' is NOT a temporary value but has a specialized addressing mode
// which means that when accessing v.a etc, it does the correct transformation
// internally:
// s[i].a === s.a[i]
fmt.println(v.a, v.b, v.c);
}
// Recover the slices from the #soa slice
a, b, c := soa_unzip(s);
fmt.println(a, b, c);
}
} }
constant_literal_expressions :: proc() { constant_literal_expressions :: proc() {
+5 -5
View File
@@ -1347,8 +1347,8 @@ bit_set_type :: proc() {
d: Days d: Days
d = {Sunday, Monday} d = {Sunday, Monday}
e := d | WEEKEND e := d + WEEKEND
e |= {Monday} e += {Monday}
fmt.println(d, e) fmt.println(d, e)
ok := Saturday in e // `in` is only allowed for `map` and `bit_set` types ok := Saturday in e // `in` is only allowed for `map` and `bit_set` types
@@ -1367,12 +1367,12 @@ bit_set_type :: proc() {
fmt.println(typeid_of(type_of(x))) // bit_set[A..Z] fmt.println(typeid_of(type_of(x))) // bit_set[A..Z]
fmt.println(typeid_of(type_of(y))) // bit_set[0..8; u16] fmt.println(typeid_of(type_of(y))) // bit_set[0..8; u16]
incl(&x, 'F') x += {'F'};
assert('F' in x) assert('F' in x)
excl(&x, 'F') x -= {'F'};
assert('F' not_in x) assert('F' not_in x)
y |= {1, 4, 2} y += {1, 4, 2}
assert(2 in y) assert(2 in y)
} }
{ {
+5
View File
@@ -200,8 +200,10 @@ struct BuildContext {
bool disallow_do; bool disallow_do;
bool insert_semicolon; bool insert_semicolon;
bool ignore_warnings; bool ignore_warnings;
bool warnings_as_errors; bool warnings_as_errors;
bool show_error_line;
bool use_subsystem_windows; bool use_subsystem_windows;
bool ignore_microsoft_magic; bool ignore_microsoft_magic;
@@ -746,6 +748,9 @@ String get_fullpath_core(gbAllocator a, String path) {
return path_to_fullpath(a, res); return path_to_fullpath(a, res);
} }
bool show_error_line(void) {
return build_context.show_error_line;
}
void init_build_context(TargetMetrics *cross_target) { void init_build_context(TargetMetrics *cross_target) {
+164 -2
View File
@@ -87,7 +87,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_DIRECTIVE: { case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc); ast_node(bd, BasicDirective, ce->proc);
String name = bd->name; String name = bd->name.string;
if (name == "defined") { if (name == "defined") {
break; break;
} }
@@ -124,7 +124,7 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
case BuiltinProc_DIRECTIVE: { case BuiltinProc_DIRECTIVE: {
ast_node(bd, BasicDirective, ce->proc); ast_node(bd, BasicDirective, ce->proc);
String name = bd->name; String name = bd->name.string;
if (name == "location") { if (name == "location") {
if (ce->args.count > 1) { if (ce->args.count > 1) {
error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count); error(ce->args[0], "'#location' expects either 0 or 1 arguments, got %td", ce->args.count);
@@ -2026,6 +2026,128 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
} }
break; break;
case BuiltinProc_sqrt:
{
Operand x = {};
check_expr(c, &x, ce->args[0]);
if (x.mode == Addressing_Invalid) {
return false;
}
if (!is_type_float(x.type)) {
gbString xts = type_to_string(x.type);
error(x.expr, "Expected a floating point value for '%.*s', got %s", LIT(builtin_procs[id].name), xts);
gb_string_free(xts);
return false;
}
if (x.mode == Addressing_Constant) {
f64 v = exact_value_to_f64(x.value);
operand->mode = Addressing_Constant;
operand->type = x.type;
operand->value = exact_value_float(gb_sqrt(v));
break;
}
operand->mode = Addressing_Value;
operand->type = default_type(x.type);
}
break;
case BuiltinProc_mem_copy:
case BuiltinProc_mem_copy_non_overlapping:
{
operand->mode = Addressing_NoValue;
operand->type = t_invalid;
Operand dst = {};
Operand src = {};
Operand len = {};
check_expr(c, &dst, ce->args[0]);
check_expr(c, &src, ce->args[1]);
check_expr(c, &len, ce->args[2]);
if (dst.mode == Addressing_Invalid) {
return false;
}
if (src.mode == Addressing_Invalid) {
return false;
}
if (len.mode == Addressing_Invalid) {
return false;
}
if (!is_type_pointer(dst.type)) {
gbString str = type_to_string(dst.type);
error(dst.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (!is_type_pointer(src.type)) {
gbString str = type_to_string(src.type);
error(src.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (!is_type_integer(len.type)) {
gbString str = type_to_string(len.type);
error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (len.mode == Addressing_Constant) {
i64 n = exact_value_to_i64(len.value);
if (n < 0) {
gbString str = expr_to_string(len.expr);
error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
}
}
}
break;
case BuiltinProc_mem_zero:
{
operand->mode = Addressing_NoValue;
operand->type = t_invalid;
Operand ptr = {};
Operand len = {};
check_expr(c, &ptr, ce->args[0]);
check_expr(c, &len, ce->args[1]);
if (ptr.mode == Addressing_Invalid) {
return false;
}
if (len.mode == Addressing_Invalid) {
return false;
}
if (!is_type_pointer(ptr.type)) {
gbString str = type_to_string(ptr.type);
error(ptr.expr, "Expected a pointer value for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (!is_type_integer(len.type)) {
gbString str = type_to_string(len.type);
error(len.expr, "Expected an integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
return false;
}
if (len.mode == Addressing_Constant) {
i64 n = exact_value_to_i64(len.value);
if (n < 0) {
gbString str = expr_to_string(len.expr);
error(len.expr, "Expected a non-negative integer value for the number of bytes for '%.*s', got %s", LIT(builtin_procs[id].name), str);
gb_string_free(str);
}
}
}
break;
case BuiltinProc_atomic_fence: case BuiltinProc_atomic_fence:
case BuiltinProc_atomic_fence_acq: case BuiltinProc_atomic_fence_acq:
case BuiltinProc_atomic_fence_rel: case BuiltinProc_atomic_fence_rel:
@@ -2429,6 +2551,46 @@ bool check_builtin_procedure(CheckerContext *c, Operand *operand, Ast *call, i32
} }
break; break;
case BuiltinProc_type_is_variant_of:
{
if (operand->mode != Addressing_Type) {
error(operand->expr, "Expected a type for '%.*s'", LIT(builtin_name));
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
return false;
}
Type *u = operand->type;
if (!is_type_union(u)) {
error(operand->expr, "Expected a union type for '%.*s'", LIT(builtin_name));
operand->mode = Addressing_Invalid;
operand->type = t_invalid;
return false;
}
Type *v = check_type(c, ce->args[1]);
u = base_type(u);
GB_ASSERT(u->kind == Type_Union);
bool is_variant = false;
for_array(i, u->Union.variants) {
Type *vt = u->Union.variants[i];
if (are_types_identical(v, vt)) {
is_variant = true;
break;
}
}
operand->mode = Addressing_Constant;
operand->type = t_untyped_bool;
operand->value = exact_value_bool(is_variant);
}
break;
case BuiltinProc_type_struct_field_count: case BuiltinProc_type_struct_field_count:
operand->value = exact_value_i64(0); operand->value = exact_value_i64(0);
if (operand->mode != Addressing_Type) { if (operand->mode != Addressing_Type) {
+18 -14
View File
@@ -289,17 +289,6 @@ void check_type_decl(CheckerContext *ctx, Entity *e, Ast *init_expr, Type *def)
if (decl != nullptr) { if (decl != nullptr) {
AttributeContext ac = {}; AttributeContext ac = {};
check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac); check_decl_attributes(ctx, decl->attributes, type_decl_attribute, &ac);
if (ac.atom_op_table != nullptr) {
Type *bt = base_type(e->type);
switch (bt->kind) {
case Type_Struct:
bt->Struct.atom_op_table = ac.atom_op_table;
break;
default:
error(e->token, "Only struct types can have custom atom operations");
break;
}
}
} }
@@ -376,6 +365,7 @@ void override_entity_in_scope(Entity *original_entity, Entity *new_entity) {
void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) { void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init, Type *named_type) {
GB_ASSERT(e->type == nullptr); GB_ASSERT(e->type == nullptr);
GB_ASSERT(e->kind == Entity_Constant); GB_ASSERT(e->kind == Entity_Constant);
init = unparen_expr(init);
if (e->flags & EntityFlag_Visited) { if (e->flags & EntityFlag_Visited) {
e->type = t_invalid; e->type = t_invalid;
@@ -409,6 +399,18 @@ void check_const_decl(CheckerContext *ctx, Entity *e, Ast *type_expr, Ast *init,
e->kind = Entity_TypeName; e->kind = Entity_TypeName;
e->type = nullptr; e->type = nullptr;
if (entity != nullptr && entity->type != nullptr &&
is_type_polymorphic_record_unspecialized(entity->type)) {
DeclInfo *decl = decl_info_of_entity(e);
if (decl != nullptr) {
if (decl->attributes.count > 0) {
error(decl->attributes[0], "Constant alias declarations cannot have attributes");
}
}
override_entity_in_scope(e, entity);
return;
}
check_type_decl(ctx, e, ctx->decl->init_expr, named_type); check_type_decl(ctx, e, ctx->decl->init_expr, named_type);
return; return;
} }
@@ -897,10 +899,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
e->Variable.thread_local_model = ac.thread_local_model; e->Variable.thread_local_model = ac.thread_local_model;
e->Variable.is_export = ac.is_export; e->Variable.is_export = ac.is_export;
e->flags &= ~EntityFlag_Static;
if (ac.is_static) { if (ac.is_static) {
e->flags |= EntityFlag_Static; error(e->token, "@(static) is not supported for global variables, nor required");
} else {
e->flags &= ~EntityFlag_Static;
} }
ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix); ac.link_name = handle_link_name(ctx, e->token, ac.link_name, ac.link_prefix);
@@ -933,6 +934,9 @@ void check_global_variable_decl(CheckerContext *ctx, Entity *&e, Ast *type_expr,
if (ac.link_name.len > 0) { if (ac.link_name.len > 0) {
e->Variable.link_name = ac.link_name; e->Variable.link_name = ac.link_name;
} }
if (ac.link_section.len > 0) {
e->Variable.link_section = ac.link_section;
}
if (e->Variable.is_foreign || e->Variable.is_export) { if (e->Variable.is_foreign || e->Variable.is_export) {
String name = e->token.string; String name = e->token.string;
+96 -96
View File
@@ -621,7 +621,9 @@ i64 check_distance_between_types(CheckerContext *c, Operand *operand, Type *type
} }
PolyProcData poly_proc_data = {}; PolyProcData poly_proc_data = {};
if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) { if (check_polymorphic_procedure_assignment(c, operand, type, operand->expr, &poly_proc_data)) {
add_entity_use(c, operand->expr, poly_proc_data.gen_entity); Entity *e = poly_proc_data.gen_entity;
add_type_and_value(c->info, operand->expr, Addressing_Value, e->type, {});
add_entity_use(c, operand->expr, e);
return 4; return 4;
} }
} }
@@ -1113,6 +1115,7 @@ bool check_cycle(CheckerContext *c, Entity *curr, bool report) {
error(curr->token, "\t%.*s refers to", LIT(curr->token.string)); error(curr->token, "\t%.*s refers to", LIT(curr->token.string));
} }
error(curr->token, "\t%.*s", LIT(curr->token.string)); error(curr->token, "\t%.*s", LIT(curr->token.string));
curr->type = t_invalid;
} }
return true; return true;
} }
@@ -1130,7 +1133,7 @@ Entity *check_ident(CheckerContext *c, Operand *o, Ast *n, Type *named_type, Typ
Entity *e = scope_lookup(c->scope, name); Entity *e = scope_lookup(c->scope, name);
if (e == nullptr) { if (e == nullptr) {
if (is_blank_ident(name)) { if (is_blank_ident(name)) {
error(n, "'_' cannot be used as a value type"); error(n, "'_' cannot be used as a value");
} else { } else {
error(n, "Undeclared name: %.*s", LIT(name)); error(n, "Undeclared name: %.*s", LIT(name));
} }
@@ -2213,6 +2216,10 @@ void check_shift(CheckerContext *c, Operand *x, Operand *y, Ast *node, Type *typ
return; return;
} }
if (is_type_untyped(y->type)) {
convert_to_typed(c, y, t_uint);
}
x->mode = Addressing_Value; x->mode = Addressing_Value;
} }
@@ -2380,9 +2387,15 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
if (core_type(bt)->kind == Type_Basic) { if (core_type(bt)->kind == Type_Basic) {
if (check_representable_as_constant(c, x->value, bt, &x->value)) { if (check_representable_as_constant(c, x->value, bt, &x->value)) {
return true; return true;
} else if (is_type_pointer(type) && check_is_castable_to(c, x, type)) { } else if (check_is_castable_to(c, x, type)) {
return true; if (is_type_pointer(type)) {
return true;
}
} }
} else if (check_is_castable_to(c, x, type)) {
x->value = {};
x->mode = Addressing_Value;
return true;
} }
} else if (check_is_castable_to(c, x, type)) { } else if (check_is_castable_to(c, x, type)) {
if (x->mode != Addressing_Constant) { if (x->mode != Addressing_Constant) {
@@ -2392,6 +2405,9 @@ bool check_cast_internal(CheckerContext *c, Operand *x, Type *type) {
} else if (is_type_union(type)) { } else if (is_type_union(type)) {
x->mode = Addressing_Value; x->mode = Addressing_Value;
} }
if (x->mode == Addressing_Value) {
x->value = {};
}
return true; return true;
} }
return false; return false;
@@ -2874,7 +2890,7 @@ void update_expr_type(CheckerContext *c, Ast *e, Type *type, bool final) {
if (token_is_comparison(be->op.kind)) { if (token_is_comparison(be->op.kind)) {
// NOTE(bill): Do nothing as the types are fine // NOTE(bill): Do nothing as the types are fine
} else if (token_is_shift(be->op.kind)) { } else if (token_is_shift(be->op.kind)) {
update_expr_type(c, be->left, type, final); update_expr_type(c, be->left, type, final);
} else { } else {
update_expr_type(c, be->left, type, final); update_expr_type(c, be->left, type, final);
update_expr_type(c, be->right, type, final); update_expr_type(c, be->right, type, final);
@@ -3188,8 +3204,8 @@ void convert_to_typed(CheckerContext *c, Operand *operand, Type *target_type) {
break; break;
} }
operand->type = target_type;
update_expr_type(c, operand->expr, target_type, true); update_expr_type(c, operand->expr, target_type, true);
operand->type = target_type;
} }
bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) { bool check_index_value(CheckerContext *c, bool open_range, Ast *index_value, i64 max_count, i64 *value, Type *type_hint=nullptr) {
@@ -3376,7 +3392,7 @@ ExactValue get_constant_field_single(CheckerContext *c, ExactValue value, i32 in
GB_ASSERT(bt->kind == Type_EnumeratedArray); GB_ASSERT(bt->kind == Type_EnumeratedArray);
corrected_index = index + exact_value_to_i64(bt->EnumeratedArray.min_value); corrected_index = index + exact_value_to_i64(bt->EnumeratedArray.min_value);
} }
if (op == Token_Ellipsis) { if (op != Token_RangeHalf) {
if (lo <= corrected_index && corrected_index <= hi) { if (lo <= corrected_index && corrected_index <= hi) {
TypeAndValue tav = fv->value->tav; TypeAndValue tav = fv->value->tav;
if (success_) *success_ = true; if (success_) *success_ = true;
@@ -3938,6 +3954,16 @@ bool check_assignment_arguments(CheckerContext *ctx, Array<Operand> const &lhs,
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
} }
if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) {
// NOTE(bill): Used only for optimizations in the backend
if (is_blank_ident(lhs[0].expr)) {
expr->TypeAssertion.ignores[0] = true;
}
if (is_blank_ident(lhs[1].expr)) {
expr->TypeAssertion.ignores[1] = true;
}
}
array_add(operands, val0); array_add(operands, val0);
array_add(operands, val1); array_add(operands, val1);
optional_ok = true; optional_ok = true;
@@ -4052,6 +4078,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value); add_type_and_value(&c->checker->info, o.expr, o.mode, tuple, o.value);
} }
if (o.mode == Addressing_OptionalOk && expr->kind == Ast_TypeAssertion) {
// NOTE(bill): Used only for optimizations in the backend
if (is_blank_ident(lhs[0]->token)) {
expr->TypeAssertion.ignores[0] = true;
}
if (is_blank_ident(lhs[1]->token)) {
expr->TypeAssertion.ignores[1] = true;
}
}
array_add(operands, val0); array_add(operands, val0);
array_add(operands, val1); array_add(operands, val1);
optional_ok = true; optional_ok = true;
@@ -4076,6 +4112,16 @@ bool check_unpack_arguments(CheckerContext *ctx, Entity **lhs, isize lhs_count,
} }
bool is_expr_constant_zero(Ast *expr) {
GB_ASSERT(expr != nullptr);
auto v = exact_value_to_integer(expr->tav.value);
if (v.kind == ExactValue_Integer) {
return big_int_cmp_zero(&v.value_integer) == 0;
}
return false;
}
CALL_ARGUMENT_CHECKER(check_call_arguments_internal) { CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
ast_node(ce, CallExpr, call); ast_node(ce, CallExpr, call);
GB_ASSERT(is_type_proc(proc_type)); GB_ASSERT(is_type_proc(proc_type));
@@ -4250,6 +4296,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
err = CallArgumentError_WrongTypes; err = CallArgumentError_WrongTypes;
} }
} }
} else if (show_error) {
check_assignment(c, &o, t, str_lit("argument"));
} }
score += s; score += s;
@@ -4265,7 +4313,10 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
if (o.mode == Addressing_Type && is_type_typeid(e->type)) { if (o.mode == Addressing_Type && is_type_typeid(e->type)) {
add_type_info_type(c, o.type); add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type)); add_type_and_value(c->info, o.expr, Addressing_Value, e->type, exact_value_typeid(o.type));
} else if (show_error && is_type_untyped(o.type)) {
update_expr_type(c, o.expr, t, true);
} }
} }
if (variadic) { if (variadic) {
@@ -4303,6 +4354,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
check_assignment(c, &o, t, str_lit("argument")); check_assignment(c, &o, t, str_lit("argument"));
} }
err = CallArgumentError_WrongTypes; err = CallArgumentError_WrongTypes;
} else if (show_error) {
check_assignment(c, &o, t, str_lit("argument"));
} }
score += s; score += s;
if (is_type_any(elem)) { if (is_type_any(elem)) {
@@ -4311,6 +4364,8 @@ CALL_ARGUMENT_CHECKER(check_call_arguments_internal) {
if (o.mode == Addressing_Type && is_type_typeid(t)) { if (o.mode == Addressing_Type && is_type_typeid(t)) {
add_type_info_type(c, o.type); add_type_info_type(c, o.type);
add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type)); add_type_and_value(c->info, o.expr, Addressing_Value, t, exact_value_typeid(o.type));
} else if (show_error && is_type_untyped(o.type)) {
update_expr_type(c, o.expr, t, true);
} }
} }
} }
@@ -4525,6 +4580,8 @@ CALL_ARGUMENT_CHECKER(check_named_call_arguments) {
err = CallArgumentError_NoneConstantParameter; err = CallArgumentError_NoneConstantParameter;
} }
} }
} else if (show_error) {
check_assignment(c, o, e->type, str_lit("procedure argument"));
} }
score += s; score += s;
} }
@@ -5459,7 +5516,7 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
if (proc != nullptr && if (proc != nullptr &&
proc->kind == Ast_BasicDirective) { proc->kind == Ast_BasicDirective) {
ast_node(bd, BasicDirective, proc); ast_node(bd, BasicDirective, proc);
String name = bd->name; String name = bd->name.string;
if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") { if (name == "location" || name == "assert" || name == "panic" || name == "defined" || name == "config" || name == "load") {
operand->mode = Addressing_Builtin; operand->mode = Addressing_Builtin;
operand->builtin_id = BuiltinProc_DIRECTIVE; operand->builtin_id = BuiltinProc_DIRECTIVE;
@@ -5563,9 +5620,15 @@ ExprKind check_call_expr(CheckerContext *c, Operand *operand, Ast *call, Ast *pr
} }
check_expr(c, operand, arg); check_expr(c, operand, arg);
if (operand->mode != Addressing_Invalid) { if (operand->mode != Addressing_Invalid) {
check_cast(c, operand, t); if (is_type_polymorphic(t)) {
error(call, "A polymorphic type cannot be used in a type conversion");
} else {
// NOTE(bill): Otherwise the compiler can override the polymorphic type
// as it assumes it is determining the type
check_cast(c, operand, t);
}
} }
operand->type = t;
break; break;
} }
} }
@@ -5715,7 +5778,7 @@ void check_expr_with_type_hint(CheckerContext *c, Operand *o, Ast *e, Type *t) {
break; break;
case Addressing_Type: case Addressing_Type:
if (t == nullptr || !is_type_typeid(t)) { if (t == nullptr || !is_type_typeid(t)) {
err_str = "is not an expression but a type"; err_str = "is not an expression but a type, in this context it is ambiguous";
} }
break; break;
case Addressing_Builtin: case Addressing_Builtin:
@@ -5903,8 +5966,9 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
TokenKind op = Token_Lt; TokenKind op = Token_Lt;
switch (ie->op.kind) { switch (ie->op.kind) {
case Token_Ellipsis: op = Token_LtEq; break; case Token_Ellipsis: op = Token_LtEq; break; // ..
case Token_RangeHalf: op = Token_Lt; break; case Token_RangeFull: op = Token_LtEq; break; // ..=
case Token_RangeHalf: op = Token_Lt; break; // ..<
default: error(ie->op, "Invalid range operator"); break; default: error(ie->op, "Invalid range operator"); break;
} }
bool ok = compare_exact_values(op, a, b); bool ok = compare_exact_values(op, a, b);
@@ -5915,7 +5979,7 @@ bool check_range(CheckerContext *c, Ast *node, Operand *x, Operand *y, ExactValu
} }
ExactValue inline_for_depth = exact_value_sub(b, a); ExactValue inline_for_depth = exact_value_sub(b, a);
if (ie->op.kind == Token_Ellipsis) { if (ie->op.kind != Token_RangeHalf) {
inline_for_depth = exact_value_increment_one(inline_for_depth); inline_for_depth = exact_value_increment_one(inline_for_depth);
} }
@@ -6127,13 +6191,14 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
case_ast_node(bd, BasicDirective, node); case_ast_node(bd, BasicDirective, node);
o->mode = Addressing_Constant; o->mode = Addressing_Constant;
if (bd->name == "file") { String name = bd->name.string;
if (name == "file") {
o->type = t_untyped_string; o->type = t_untyped_string;
o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id)); o->value = exact_value_string(get_file_path_string(bd->token.pos.file_id));
} else if (bd->name == "line") { } else if (name == "line") {
o->type = t_untyped_integer; o->type = t_untyped_integer;
o->value = exact_value_i64(bd->token.pos.line); o->value = exact_value_i64(bd->token.pos.line);
} else if (bd->name == "procedure") { } else if (name == "procedure") {
if (c->curr_proc_decl == nullptr) { if (c->curr_proc_decl == nullptr) {
error(node, "#procedure may only be used within procedures"); error(node, "#procedure may only be used within procedures");
o->type = t_untyped_string; o->type = t_untyped_string;
@@ -6142,7 +6207,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
o->type = t_untyped_string; o->type = t_untyped_string;
o->value = exact_value_string(c->proc_name); o->value = exact_value_string(c->proc_name);
} }
} else if (bd->name == "caller_location") { } else if (name == "caller_location") {
init_core_source_code_location(c->checker); init_core_source_code_location(c->checker);
error(node, "#caller_location may only be used as a default argument parameter"); error(node, "#caller_location may only be used as a default argument parameter");
o->type = t_source_code_location; o->type = t_source_code_location;
@@ -6309,7 +6374,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (cl->type->ArrayType.tag != nullptr) { if (cl->type->ArrayType.tag != nullptr) {
Ast *tag = cl->type->ArrayType.tag; Ast *tag = cl->type->ArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective); GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name; String name = tag->BasicDirective.name.string;
if (name == "soa") { if (name == "soa") {
error(node, "#soa arrays are not supported for compound literals"); error(node, "#soa arrays are not supported for compound literals");
return kind; return kind;
@@ -6321,7 +6386,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (cl->elems.count > 0) { if (cl->elems.count > 0) {
Ast *tag = cl->type->DynamicArrayType.tag; Ast *tag = cl->type->DynamicArrayType.tag;
GB_ASSERT(tag->kind == Ast_BasicDirective); GB_ASSERT(tag->kind == Ast_BasicDirective);
String name = tag->BasicDirective.name; String name = tag->BasicDirective.name.string;
if (name == "soa") { if (name == "soa") {
error(node, "#soa arrays are not supported for compound literals"); error(node, "#soa arrays are not supported for compound literals");
return kind; return kind;
@@ -7536,47 +7601,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return Expr_Expr; return Expr_Expr;
} }
if (t->kind == Type_Struct) {
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
if (atom_op_table != nullptr) {
if (atom_op_table->op[TypeAtomOp_index_set]) {
if (c->assignment_lhs_hint == node) {
o->mode = Addressing_AtomOpAssign;
o->type = o->type;
o->expr = node;
return kind;
}
}
if (atom_op_table->op[TypeAtomOp_index_get]) {
Entity *e = atom_op_table->op[TypeAtomOp_index_get];
if (ie->index == nullptr) {
gbString str = expr_to_string(o->expr);
error(o->expr, "Missing index for '%s'", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
GB_ASSERT(e->identifier != nullptr);
Ast *proc_ident = clone_ast(e->identifier);
auto args = array_make<Ast *>(heap_allocator(), 2);
args[0] = ie->expr;
args[1] = ie->index;
GB_ASSERT(c->file != nullptr);
Ast *fake_call = ast_call_expr(c->file, proc_ident, args, ie->open, ie->close, {});
check_expr_base(c, o, fake_call, type_hint);
AtomOpMapEntry entry = {TypeAtomOp_index_get, fake_call};
map_set(&c->info->atom_op_map, hash_pointer(node), entry);
o->expr = node;
return kind;
}
}
}
i64 max_count = -1; i64 max_count = -1;
bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type); bool valid = check_set_index_data(o, t, is_ptr, &max_count, o->type);
@@ -7715,37 +7739,6 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
if (is_type_soa_struct(t)) { if (is_type_soa_struct(t)) {
valid = true; valid = true;
o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem); o->type = make_soa_struct_slice(c, nullptr, nullptr, t->Struct.soa_elem);
} else {
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
if (atom_op_table != nullptr && atom_op_table->op[TypeAtomOp_slice]) {
Entity *e = atom_op_table->op[TypeAtomOp_slice];
GB_ASSERT(e->identifier != nullptr);
Ast *proc_ident = clone_ast(e->identifier);
Ast *expr = se->expr;
if (o->mode == Addressing_Variable) {
expr = ast_unary_expr(c->file, {Token_And, STR_LIT("&")}, expr);
} else if (is_type_pointer(o->type)) {
// Okay
} else {
gbString str = expr_to_string(node);
error(node, "Cannot slice '%s', value is not addressable", str);
gb_string_free(str);
o->mode = Addressing_Invalid;
o->expr = node;
return kind;
}
auto args = array_make<Ast *>(heap_allocator(), 1);
args[0] = expr;
GB_ASSERT(c->file != nullptr);
Ast *fake_call = ast_call_expr(c->file, proc_ident, args, se->open, se->close, {});
check_expr_base(c, o, fake_call, type_hint);
AtomOpMapEntry entry = {TypeAtomOp_slice, fake_call};
map_set(&c->info->atom_op_map, hash_pointer(node), entry);
valid = true;
}
} }
break; break;
@@ -7774,10 +7767,7 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
return kind; return kind;
} }
o->mode = Addressing_Value;
if (se->low == nullptr && se->high != nullptr) { if (se->low == nullptr && se->high != nullptr) {
// error(se->interval0, "1st index is required if a 2nd index is specified");
// It is okay to continue as it will assume the 1st index is zero // It is okay to continue as it will assume the 1st index is zero
} }
@@ -7812,6 +7802,16 @@ ExprKind check_expr_base_internal(CheckerContext *c, Operand *o, Ast *node, Type
} }
} }
if (max_count < 0) {
if (o->mode == Addressing_Constant) {
gbString s = expr_to_string(se->expr);
error(se->expr, "Cannot slice constant value '%s'", s);
gb_string_free(s);
}
}
o->mode = Addressing_Value;
if (is_type_string(t) && max_count >= 0) { if (is_type_string(t) && max_count >= 0) {
bool all_constant = true; bool all_constant = true;
for (isize i = 0; i < gb_count_of(nodes); i++) { for (isize i = 0; i < gb_count_of(nodes); i++) {
@@ -8152,7 +8152,7 @@ gbString write_expr_to_string(gbString str, Ast *node, bool shorthand) {
case_ast_node(bd, BasicDirective, node); case_ast_node(bd, BasicDirective, node);
str = gb_string_append_rune(str, '#'); str = gb_string_append_rune(str, '#');
str = string_append_string(str, bd->name); str = string_append_string(str, bd->name.string);
case_end; case_end;
case_ast_node(ud, Undef, node); case_ast_node(ud, Undef, node);
+70 -55
View File
@@ -7,7 +7,7 @@ bool is_diverging_stmt(Ast *stmt) {
return false; return false;
} }
if (expr->CallExpr.proc->kind == Ast_BasicDirective) { if (expr->CallExpr.proc->kind == Ast_BasicDirective) {
String name = expr->CallExpr.proc->BasicDirective.name; String name = expr->CallExpr.proc->BasicDirective.name.string;
return name == "panic"; return name == "panic";
} }
Ast *proc = unparen_expr(expr->CallExpr.proc); Ast *proc = unparen_expr(expr->CallExpr.proc);
@@ -939,6 +939,7 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
TokenKind upper_op = Token_Invalid; TokenKind upper_op = Token_Invalid;
switch (be->op.kind) { switch (be->op.kind) {
case Token_Ellipsis: upper_op = Token_GtEq; break; case Token_Ellipsis: upper_op = Token_GtEq; break;
case Token_RangeFull: upper_op = Token_GtEq; break;
case Token_RangeHalf: upper_op = Token_Gt; break; case Token_RangeHalf: upper_op = Token_Gt; break;
default: GB_PANIC("Invalid range operator"); break; default: GB_PANIC("Invalid range operator"); break;
} }
@@ -960,9 +961,44 @@ void check_switch_stmt(CheckerContext *ctx, Ast *node, u32 mod_flags) {
Operand b1 = rhs; Operand b1 = rhs;
check_comparison(ctx, &a1, &b1, Token_LtEq); check_comparison(ctx, &a1, &b1, Token_LtEq);
add_constant_switch_case(ctx, &seen, lhs); if (is_type_enum(x.type)) {
if (upper_op == Token_GtEq) { // TODO(bill): Fix this logic so it's fast!!!
add_constant_switch_case(ctx, &seen, rhs);
i64 v0 = exact_value_to_i64(lhs.value);
i64 v1 = exact_value_to_i64(rhs.value);
Operand v = {};
v.mode = Addressing_Constant;
v.type = x.type;
v.expr = x.expr;
Type *bt = base_type(x.type);
GB_ASSERT(bt->kind == Type_Enum);
for (i64 vi = v0; vi <= v1; vi++) {
if (upper_op != Token_GtEq && vi == v1) {
break;
}
bool found = false;
for_array(j, bt->Enum.fields) {
Entity *f = bt->Enum.fields[j];
GB_ASSERT(f->kind == Entity_Constant);
i64 fv = exact_value_to_i64(f->Constant.value);
if (fv == vi) {
found = true;
break;
}
}
if (found) {
v.value = exact_value_i64(vi);
add_constant_switch_case(ctx, &seen, v);
}
}
} else {
add_constant_switch_case(ctx, &seen, lhs);
if (upper_op == Token_GtEq) {
add_constant_switch_case(ctx, &seen, rhs);
}
} }
if (is_type_string(x.type)) { if (is_type_string(x.type)) {
@@ -1400,6 +1436,28 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
gbString expr_str = expr_to_string(operand.expr); gbString expr_str = expr_to_string(operand.expr);
error(node, "Expression is not used: '%s'", expr_str); error(node, "Expression is not used: '%s'", expr_str);
gb_string_free(expr_str); gb_string_free(expr_str);
if (operand.expr->kind == Ast_BinaryExpr) {
ast_node(be, BinaryExpr, operand.expr);
if (be->op.kind != Token_CmpEq) {
break;
}
switch (be->left->tav.mode) {
case Addressing_Context:
case Addressing_Variable:
case Addressing_MapIndex:
case Addressing_SoaVariable:
{
gbString lhs = expr_to_string(be->left);
gbString rhs = expr_to_string(be->right);
error_line("\tSuggestion: Did you mean to do an assignment?\n", lhs, rhs);
error_line("\t '%s = %s;'\n", lhs, rhs);
gb_string_free(rhs);
gb_string_free(lhs);
}
break;
}
}
break; break;
} }
@@ -1454,53 +1512,6 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count); auto lhs_to_ignore = array_make<bool>(temporary_allocator(), lhs_count);
isize max = gb_min(lhs_count, rhs_count); isize max = gb_min(lhs_count, rhs_count);
// NOTE(bill, 2020-05-02): This is an utter hack to get these custom atom operations working
// correctly for assignments
for (isize i = 0; i < max; i++) {
if (lhs_operands[i].mode == Addressing_AtomOpAssign) {
Operand lhs = lhs_operands[i];
Type *t = base_type(lhs.type);
GB_ASSERT(t->kind == Type_Struct);
ast_node(ie, IndexExpr, unparen_expr(lhs.expr));
TypeAtomOpTable *atom_op_table = t->Struct.atom_op_table;
GB_ASSERT(atom_op_table->op[TypeAtomOp_index_set] != nullptr);
Entity *e = atom_op_table->op[TypeAtomOp_index_set];
GB_ASSERT(e->identifier != nullptr);
Ast *proc_ident = clone_ast(e->identifier);
GB_ASSERT(ctx->file != nullptr);
TypeAndValue tv = type_and_value_of_expr(ie->expr);
Ast *expr = ie->expr;
if (is_type_pointer(tv.type)) {
// Okay
} else if (tv.mode == Addressing_Variable) {
// NOTE(bill): Hack it to take the address instead
expr = ast_unary_expr(ctx->file, {Token_And, STR_LIT("&")}, ie->expr);
} else {
continue;
}
auto args = array_make<Ast *>(heap_allocator(), 3);
args[0] = expr;
args[1] = ie->index;
args[2] = rhs_operands[i].expr;
Ast *fake_call = ast_call_expr(ctx->file, proc_ident, args, ie->open, ie->close, {});
Operand fake_operand = {};
fake_operand.expr = lhs.expr;
check_expr_base(ctx, &fake_operand, fake_call, nullptr);
AtomOpMapEntry entry = {TypeAtomOp_index_set, fake_call};
map_set(&ctx->info->atom_op_map, hash_pointer(lhs.expr), entry);
lhs_to_ignore[i] = true;
}
}
for (isize i = 0; i < max; i++) { for (isize i = 0; i < max; i++) {
if (lhs_to_ignore[i]) { if (lhs_to_ignore[i]) {
continue; continue;
@@ -1526,8 +1537,8 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} }
Operand lhs = {Addressing_Invalid}; Operand lhs = {Addressing_Invalid};
Operand rhs = {Addressing_Invalid}; Operand rhs = {Addressing_Invalid};
Ast binary_expr = {Ast_BinaryExpr}; Ast *binary_expr = alloc_ast_node(node->file, Ast_BinaryExpr);
ast_node(be, BinaryExpr, &binary_expr); ast_node(be, BinaryExpr, binary_expr);
be->op = op; be->op = op;
be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add)); be->op.kind = cast(TokenKind)(cast(i32)be->op.kind - (Token_AddEq - Token_Add));
// NOTE(bill): Only use the first one will be used // NOTE(bill): Only use the first one will be used
@@ -1535,7 +1546,7 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
be->right = as->rhs[0]; be->right = as->rhs[0];
check_expr(ctx, &lhs, as->lhs[0]); check_expr(ctx, &lhs, as->lhs[0]);
check_binary_expr(ctx, &rhs, &binary_expr, nullptr, true); check_binary_expr(ctx, &rhs, binary_expr, nullptr, true);
if (rhs.mode == Addressing_Invalid) { if (rhs.mode == Addressing_Invalid) {
return; return;
} }
@@ -1632,7 +1643,11 @@ void check_stmt_internal(CheckerContext *ctx, Ast *node, u32 flags) {
} else { } else {
for (isize i = 0; i < result_count; i++) { for (isize i = 0; i < result_count; i++) {
Entity *e = pt->results->Tuple.variables[i]; Entity *e = pt->results->Tuple.variables[i];
check_assignment(ctx, &operands[i], e->type, str_lit("return statement")); Operand *o = &operands[i];
check_assignment(ctx, o, e->type, str_lit("return statement"));
if (is_type_untyped(o->type)) {
update_expr_type(ctx, o->expr, e->type, true);
}
} }
} }
case_end; case_end;
+45 -43
View File
@@ -322,19 +322,6 @@ void add_polymorphic_record_entity(CheckerContext *ctx, Ast *node, Type *named_t
array_add(&array, e); array_add(&array, e);
map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array); map_set(&ctx->checker->info.gen_types, hash_pointer(original_type), array);
} }
{
Type *dst_bt = base_type(named_type);
Type *src_bt = base_type(original_type);
if ((dst_bt != nullptr && src_bt != nullptr) &&
(dst_bt->kind == src_bt->kind)){
if (dst_bt->kind == Type_Struct) {
if (dst_bt->Struct.atom_op_table == nullptr) {
dst_bt->Struct.atom_op_table = src_bt->Struct.atom_op_table;
}
}
}
}
} }
Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params, Type *check_record_polymorphic_params(CheckerContext *ctx, Ast *polymorphic_params,
@@ -944,6 +931,7 @@ void check_bit_set_type(CheckerContext *c, Type *type, Type *named_type, Ast *no
switch (be->op.kind) { switch (be->op.kind) {
case Token_Ellipsis: case Token_Ellipsis:
case Token_RangeFull:
if (upper - lower >= bits) { if (upper - lower >= bits) {
error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1)); error(bs->elem, "bit_set range is greater than %lld bits, %lld bits are required", bits, (upper-lower+1));
} }
@@ -1203,10 +1191,15 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
if (allow_caller_location && if (allow_caller_location &&
expr->kind == Ast_BasicDirective && expr->kind == Ast_BasicDirective &&
expr->BasicDirective.name == "caller_location") { expr->BasicDirective.name.string == "caller_location") {
init_core_source_code_location(ctx->checker); init_core_source_code_location(ctx->checker);
param_value.kind = ParameterValue_Location; param_value.kind = ParameterValue_Location;
o.type = t_source_code_location; o.type = t_source_code_location;
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
} else { } else {
if (in_type) { if (in_type) {
check_expr_with_type_hint(ctx, &o, expr, in_type); check_expr_with_type_hint(ctx, &o, expr, in_type);
@@ -1214,6 +1207,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
check_expr(ctx, &o, expr); check_expr(ctx, &o, expr);
} }
if (in_type) {
check_assignment(ctx, &o, in_type, str_lit("parameter value"));
}
if (is_operand_nil(o)) { if (is_operand_nil(o)) {
param_value.kind = ParameterValue_Nil; param_value.kind = ParameterValue_Nil;
} else if (o.mode != Addressing_Constant) { } else if (o.mode != Addressing_Constant) {
@@ -1221,16 +1219,7 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
param_value.kind = ParameterValue_Constant; param_value.kind = ParameterValue_Constant;
param_value.value = exact_value_procedure(expr); param_value.value = exact_value_procedure(expr);
} else { } else {
Entity *e = nullptr; Entity *e = entity_from_expr(o.expr);
// if (o.mode == Addressing_Value && is_type_proc(o.type)) {
if (o.mode == Addressing_Value || o.mode == Addressing_Variable) {
Operand x = {};
if (expr->kind == Ast_Ident) {
e = check_ident(ctx, &x, expr, nullptr, nullptr, false);
} else if (expr->kind == Ast_SelectorExpr) {
e = check_selector(ctx, &x, expr, nullptr);
}
}
if (e != nullptr) { if (e != nullptr) {
if (e->kind == Entity_Procedure) { if (e->kind == Entity_Procedure) {
@@ -1253,8 +1242,11 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
} else if (allow_caller_location && o.mode == Addressing_Context) { } else if (allow_caller_location && o.mode == Addressing_Context) {
param_value.kind = ParameterValue_Value; param_value.kind = ParameterValue_Value;
param_value.ast_value = expr; param_value.ast_value = expr;
} else if (o.value.kind != ExactValue_Invalid) {
param_value.kind = ParameterValue_Constant;
param_value.value = o.value;
} else { } else {
error(expr, "Default parameter must be a constant"); error(expr, "Default parameter must be a constant, %d", o.mode);
} }
} }
} else { } else {
@@ -1267,12 +1259,14 @@ ParameterValue handle_parameter_value(CheckerContext *ctx, Type *in_type, Type *
} }
} }
if (in_type) { if (out_type_) {
check_assignment(ctx, &o, in_type, str_lit("parameter value")); if (in_type != nullptr) {
*out_type_ = in_type;
} else {
*out_type_ = default_type(o.type);
}
} }
if (out_type_) *out_type_ = default_type(o.type);
return param_value; return param_value;
} }
@@ -1389,6 +1383,9 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
} }
} }
} }
if (type == nullptr) { if (type == nullptr) {
error(param, "Invalid parameter type"); error(param, "Invalid parameter type");
type = t_invalid; type = t_invalid;
@@ -1408,6 +1405,21 @@ Type *check_get_params(CheckerContext *ctx, Scope *scope, Ast *_params, bool *is
type = t_invalid; type = t_invalid;
} }
if (is_type_polymorphic(type)) {
switch (param_value.kind) {
case ParameterValue_Invalid:
case ParameterValue_Constant:
case ParameterValue_Nil:
break;
case ParameterValue_Location:
case ParameterValue_Value:
gbString str = type_to_string(type);
error(params[i], "A default value for a parameter must not be a polymorphic constant type, got %s", str);
gb_string_free(str);
break;
}
}
if (p->flags&FieldFlag_c_vararg) { if (p->flags&FieldFlag_c_vararg) {
if (p->type == nullptr || if (p->type == nullptr ||
@@ -2517,16 +2529,6 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
return true; return true;
} }
} }
// if (ctx->type_level == 0 && entity->state == EntityState_InProgress) {
// error(entity->token, "Illegal declaration cycle of `%.*s`", LIT(entity->token.string));
// for_array(j, *ctx->type_path) {
// Entity *k = (*ctx->type_path)[j];
// error(k->token, "\t%.*s refers to", LIT(k->token.string));
// }
// error(entity->token, "\t%.*s", LIT(entity->token.string));
// *type = t_invalid;
// }
return true; return true;
} }
@@ -2709,7 +2711,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
bool is_partial = false; bool is_partial = false;
if (at->tag != nullptr) { if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective); GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name; String name = at->tag->BasicDirective.name.string;
if (name == "partial") { if (name == "partial") {
is_partial = true; is_partial = true;
} else { } else {
@@ -2743,7 +2745,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
if (at->tag != nullptr) { if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective); GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name; String name = at->tag->BasicDirective.name.string;
if (name == "soa") { if (name == "soa") {
*type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type); *type = make_soa_struct_fixed(ctx, e, at->elem, elem, count, generic_type);
} else if (name == "simd") { } else if (name == "simd") {
@@ -2768,7 +2770,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
if (at->tag != nullptr) { if (at->tag != nullptr) {
GB_ASSERT(at->tag->kind == Ast_BasicDirective); GB_ASSERT(at->tag->kind == Ast_BasicDirective);
String name = at->tag->BasicDirective.name; String name = at->tag->BasicDirective.name.string;
if (name == "soa") { if (name == "soa") {
*type = make_soa_struct_slice(ctx, e, at->elem, elem); *type = make_soa_struct_slice(ctx, e, at->elem, elem);
} else { } else {
@@ -2788,7 +2790,7 @@ bool check_type_internal(CheckerContext *ctx, Ast *e, Type **type, Type *named_t
Type *elem = check_type(ctx, dat->elem); Type *elem = check_type(ctx, dat->elem);
if (dat->tag != nullptr) { if (dat->tag != nullptr) {
GB_ASSERT(dat->tag->kind == Ast_BasicDirective); GB_ASSERT(dat->tag->kind == Ast_BasicDirective);
String name = dat->tag->BasicDirective.name; String name = dat->tag->BasicDirective.name.string;
if (name == "soa") { if (name == "soa") {
*type = make_soa_struct_dynamic_array(ctx, e, dat->elem, elem); *type = make_soa_struct_dynamic_array(ctx, e, dat->elem, elem);
} else { } else {
+10 -200
View File
@@ -2650,6 +2650,16 @@ DECL_ATTRIBUTE_PROC(var_decl_attribute) {
error(elem, "Expected a string value for '%.*s'", LIT(name)); error(elem, "Expected a string value for '%.*s'", LIT(name));
} }
return true; return true;
} else if (name == "link_section") {
if (ev.kind == ExactValue_String) {
ac->link_section = ev.value_string;
if (!is_foreign_name_valid(ac->link_section)) {
error(elem, "Invalid link section: %.*s", LIT(ac->link_section));
}
} else {
error(elem, "Expected a string value for '%.*s'", LIT(name));
}
return true;
} }
return false; return false;
} }
@@ -2666,206 +2676,6 @@ DECL_ATTRIBUTE_PROC(type_decl_attribute) {
if (name == "private") { if (name == "private") {
// NOTE(bill): Handled elsewhere `check_collect_value_decl` // NOTE(bill): Handled elsewhere `check_collect_value_decl`
return true; return true;
} else if (name == "index_get") {
if (value != nullptr) {
Operand o = {};
check_expr_or_type(c, &o, value);
Entity *e = entity_of_node(value);
if (e != nullptr && e->kind == Entity_Procedure) {
if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
}
bool valid = true;
{
Type *pt = base_type(e->type);
GB_ASSERT(pt->kind == Type_Proc);
if (pt->Proc.result_count == 0) {
error(value, "'%s' attribute must return something", LIT(name));
valid = false;
}
if (pt->Proc.param_count < 2) {
error(value, "'%s' attribute must allow for 2 parameters", LIT(name));
valid = false;
} else {
isize minimum_param_count = 0;
for_array(i, pt->Proc.params->Tuple.variables) {
Entity *param = pt->Proc.params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
minimum_param_count += 1;
} else {
break;
}
} else if (param->kind == Entity_Constant) {
minimum_param_count += 1;
} else {
break;
}
}
if (minimum_param_count > 2) {
error(value, "'%s' attribute must allow for at a minimum 2 parameters", LIT(name));
valid = false;
}
}
}
if (valid) {
if (ac->atom_op_table == nullptr) {
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_index_get] = e;
}
return true;
}
}
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
return false;
} else if (name == "index_set") {
if (value != nullptr) {
Operand o = {};
check_expr_or_type(c, &o, value);
Entity *e = entity_of_node(value);
if (e != nullptr && e->kind == Entity_Procedure) {
if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
}
bool valid = true;
{
Type *pt = base_type(e->type);
GB_ASSERT(pt->kind == Type_Proc);
if (pt->Proc.param_count < 3) {
error(value, "'%s' attribute must allow for 3 parameters", LIT(name));
valid = false;
} else {
isize minimum_param_count = 0;
for_array(i, pt->Proc.params->Tuple.variables) {
Entity *param = pt->Proc.params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
minimum_param_count += 1;
} else {
break;
}
} else if (param->kind == Entity_Constant) {
minimum_param_count += 1;
} else {
break;
}
}
if (minimum_param_count > 3) {
error(value, "'%s' attribute must allow for at a minimum 3 parameters", LIT(name));
valid = false;
}
}
if (pt->Proc.variadic || pt->Proc.c_vararg) {
error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
valid = false;
}
}
if (valid) {
if (ac->atom_op_table == nullptr) {
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_index_set] = e;
}
return true;
}
}
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
return false;
} else if (name == "slice") {
if (value != nullptr) {
Operand o = {};
check_expr_or_type(c, &o, value);
Entity *e = entity_of_node(value);
if (e != nullptr && e->kind == Entity_Procedure) {
if (ac->deferred_procedure.entity != nullptr) {
error(elem, "Previous usage of the '%.*s' attribute", LIT(name));
}
bool valid = true;
{
Type *pt = base_type(e->type);
GB_ASSERT(pt->kind == Type_Proc);
if (pt->Proc.param_count < 1) {
error(value, "'%s' attribute must allow for 1 parameter", LIT(name));
valid = false;
} else {
isize minimum_param_count = 0;
for_array(i, pt->Proc.params->Tuple.variables) {
Entity *param = pt->Proc.params->Tuple.variables[i];
if (param->kind == Entity_Variable) {
if (param->Variable.param_value.kind == ParameterValue_Invalid) {
minimum_param_count += 1;
} else {
break;
}
} else if (param->kind == Entity_Constant) {
minimum_param_count += 1;
} else {
break;
}
}
if (minimum_param_count > 1) {
error(value, "'%s' attribute must allow for at a minimum 1 parameter", LIT(name));
valid = false;
}
{
Entity *param = pt->Proc.params->Tuple.variables[0];
Type *param_type = base_type(param->type);
if (is_type_pointer(param_type) && !is_type_rawptr(param_type)) {
// okay
} else {
error(value, "'%s' attribute's first parameter must be a pointer", LIT(name));
valid = false;
}
}
}
if (pt->Proc.variadic || pt->Proc.c_vararg) {
error(value, "'%s' attribute does not allow variadic procedures", LIT(name));
valid = false;
}
if (pt->Proc.result_count != 1) {
error(value, "'%s' attribute must return 1 result", LIT(name));
valid = false;
} else {
Type *rt = pt->Proc.results->Tuple.variables[0]->type;
rt = base_type(rt);
if (!is_type_slice(rt)) {
error(value, "'%s' attribute must return a slice", LIT(name));
valid = false;
}
}
}
if (valid) {
if (ac->atom_op_table == nullptr) {
ac->atom_op_table = gb_alloc_item(permanent_allocator(), TypeAtomOpTable);
}
ac->atom_op_table->op[TypeAtomOp_slice] = e;
}
return true;
}
}
error(elem, "Expected a procedure entity for '%.*s'", LIT(name));
return false;
} }
return false; return false;
} }
+1 -1
View File
@@ -109,12 +109,12 @@ struct AttributeContext {
bool set_cold; bool set_cold;
String link_name; String link_name;
String link_prefix; String link_prefix;
String link_section;
isize init_expr_list_count; isize init_expr_list_count;
String thread_local_model; String thread_local_model;
String deprecated_message; String deprecated_message;
DeferredProcedure deferred_procedure; DeferredProcedure deferred_procedure;
u32 optimization_mode; // ProcedureOptimizationMode u32 optimization_mode; // ProcedureOptimizationMode
struct TypeAtomOpTable *atom_op_table;
}; };
AttributeContext make_attribute_context(String link_prefix) { AttributeContext make_attribute_context(String link_prefix) {
+16
View File
@@ -56,6 +56,12 @@ enum BuiltinProcId {
BuiltinProc_overflow_sub, BuiltinProc_overflow_sub,
BuiltinProc_overflow_mul, BuiltinProc_overflow_mul,
BuiltinProc_sqrt,
BuiltinProc_mem_copy,
BuiltinProc_mem_copy_non_overlapping,
BuiltinProc_mem_zero,
BuiltinProc_volatile_store, BuiltinProc_volatile_store,
BuiltinProc_volatile_load, BuiltinProc_volatile_load,
@@ -197,6 +203,8 @@ BuiltinProc__type_simple_boolean_end,
BuiltinProc_type_is_specialization_of, BuiltinProc_type_is_specialization_of,
BuiltinProc_type_is_variant_of,
BuiltinProc_type_struct_field_count, BuiltinProc_type_struct_field_count,
BuiltinProc_type_proc_parameter_count, BuiltinProc_type_proc_parameter_count,
@@ -276,6 +284,12 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_sub"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("overflow_mul"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("sqrt"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_copy_non_overlapping"), 3, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("mem_zero"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_store"), 2, false, Expr_Stmt, BuiltinProcPkg_intrinsics},
{STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("volatile_load"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
@@ -415,6 +429,8 @@ gb_global BuiltinProc builtin_procs[BuiltinProc_COUNT] = {
{STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_is_specialization_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_is_variant_of"), 2, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_struct_field_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
{STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics}, {STR_LIT("type_proc_parameter_count"), 1, false, Expr_Expr, BuiltinProcPkg_intrinsics},
+1
View File
@@ -158,6 +158,7 @@ struct Entity {
Ast * foreign_library_ident; Ast * foreign_library_ident;
String link_name; String link_name;
String link_prefix; String link_prefix;
String link_section;
bool is_foreign; bool is_foreign;
bool is_export; bool is_export;
} Variable; } Variable;
+77 -54
View File
@@ -10,21 +10,37 @@ struct lbArgType {
LLVMTypeRef cast_type; // Optional LLVMTypeRef cast_type; // Optional
LLVMTypeRef pad_type; // Optional LLVMTypeRef pad_type; // Optional
LLVMAttributeRef attribute; // Optional LLVMAttributeRef attribute; // Optional
LLVMAttributeRef align_attribute; // Optional
i64 byval_alignment;
bool is_byval;
}; };
i64 lb_sizeof(LLVMTypeRef type);
i64 lb_alignof(LLVMTypeRef type);
lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) { lbArgType lb_arg_type_direct(LLVMTypeRef type, LLVMTypeRef cast_type, LLVMTypeRef pad_type, LLVMAttributeRef attr) {
return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr}; return lbArgType{lbArg_Direct, type, cast_type, pad_type, attr, nullptr, 0, false};
} }
lbArgType lb_arg_type_direct(LLVMTypeRef type) { lbArgType lb_arg_type_direct(LLVMTypeRef type) {
return lb_arg_type_direct(type, nullptr, nullptr, nullptr); return lb_arg_type_direct(type, nullptr, nullptr, nullptr);
} }
lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) { lbArgType lb_arg_type_indirect(LLVMTypeRef type, LLVMAttributeRef attr) {
return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr}; return lbArgType{lbArg_Indirect, type, nullptr, nullptr, attr, nullptr, 0, false};
}
lbArgType lb_arg_type_indirect_byval(LLVMContextRef c, LLVMTypeRef type) {
i64 alignment = lb_alignof(type);
alignment = gb_max(alignment, 8);
LLVMAttributeRef byval_attr = lb_create_enum_attribute_with_type(c, "byval", type);
LLVMAttributeRef align_attr = lb_create_enum_attribute(c, "align", alignment);
return lbArgType{lbArg_Indirect, type, nullptr, nullptr, byval_attr, align_attr, alignment, true};
} }
lbArgType lb_arg_type_ignore(LLVMTypeRef type) { lbArgType lb_arg_type_ignore(LLVMTypeRef type) {
return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr}; return lbArgType{lbArg_Ignore, type, nullptr, nullptr, nullptr, nullptr, 0, false};
} }
struct lbFunctionType { struct lbFunctionType {
@@ -121,6 +137,9 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
if (arg->attribute) { if (arg->attribute) {
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute); LLVMAddAttributeAtIndex(fn, arg_index+1, arg->attribute);
} }
if (arg->align_attribute) {
LLVMAddAttributeAtIndex(fn, arg_index+1, arg->align_attribute);
}
arg_index++; arg_index++;
} }
@@ -145,8 +164,6 @@ void lb_add_function_type_attributes(LLVMValueRef fn, lbFunctionType *ft, ProcCa
} }
i64 lb_sizeof(LLVMTypeRef type);
i64 lb_alignof(LLVMTypeRef type);
i64 lb_sizeof(LLVMTypeRef type) { i64 lb_sizeof(LLVMTypeRef type) {
LLVMTypeKind kind = LLVMGetTypeKind(type); LLVMTypeKind kind = LLVMGetTypeKind(type);
@@ -328,7 +345,7 @@ namespace lbAbi386 {
if (sz == 0) { if (sz == 0) {
args[i] = lb_arg_type_ignore(t); args[i] = lb_arg_type_ignore(t);
} else { } else {
args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval")); args[i] = lb_arg_type_indirect(t, nullptr);
} }
} else { } else {
args[i] = non_struct(c, t, false); args[i] = non_struct(c, t, false);
@@ -348,7 +365,7 @@ namespace lbAbi386 {
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
} }
LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret"); LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr); return lb_arg_type_indirect(return_type, attr);
} }
return non_struct(c, return_type, true); return non_struct(c, return_type, true);
@@ -419,8 +436,14 @@ namespace lbAbiAmd64SysV {
switch (reg_class) { switch (reg_class) {
case RegClass_SSEFs: case RegClass_SSEFs:
case RegClass_SSEFv: case RegClass_SSEFv:
case RegClass_SSEDs:
case RegClass_SSEDv: case RegClass_SSEDv:
return true; return true;
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
return true;
} }
return false; return false;
} }
@@ -437,11 +460,10 @@ namespace lbAbiAmd64SysV {
Amd64TypeAttribute_StructRect, Amd64TypeAttribute_StructRect,
}; };
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count);
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined); lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined);
void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off); void classify_with(LLVMTypeRef t, Array<RegClass> *cls, i64 ix, i64 off);
void fixup(LLVMTypeRef t, Array<RegClass> *cls); void fixup(LLVMTypeRef t, Array<RegClass> *cls);
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind); lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention);
Array<RegClass> classify(LLVMTypeRef t); Array<RegClass> classify(LLVMTypeRef t);
LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes); LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes);
@@ -452,11 +474,11 @@ namespace lbAbiAmd64SysV {
ft->args = array_make<lbArgType>(heap_allocator(), arg_count); ft->args = array_make<lbArgType>(heap_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) { for (unsigned i = 0; i < arg_count; i++) {
ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal); ft->args[i] = amd64_type(c, arg_types[i], Amd64TypeAttribute_ByVal, calling_convention);
} }
if (return_is_defined) { if (return_is_defined) {
ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect); ft->ret = amd64_type(c, return_type, Amd64TypeAttribute_StructRect, calling_convention);
} else { } else {
ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c)); ft->ret = lb_arg_type_direct(LLVMVoidTypeInContext(c));
} }
@@ -493,7 +515,7 @@ namespace lbAbiAmd64SysV {
return false; return false;
} }
lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind) { lbArgType amd64_type(LLVMContextRef c, LLVMTypeRef type, Amd64TypeAttributeKind attribute_kind, ProcCallingConvention calling_convention) {
if (is_register(type)) { if (is_register(type)) {
LLVMAttributeRef attribute = nullptr; LLVMAttributeRef attribute = nullptr;
if (type == LLVMInt1TypeInContext(c)) { if (type == LLVMInt1TypeInContext(c)) {
@@ -506,9 +528,12 @@ namespace lbAbiAmd64SysV {
if (is_mem_cls(cls, attribute_kind)) { if (is_mem_cls(cls, attribute_kind)) {
LLVMAttributeRef attribute = nullptr; LLVMAttributeRef attribute = nullptr;
if (attribute_kind == Amd64TypeAttribute_ByVal) { if (attribute_kind == Amd64TypeAttribute_ByVal) {
attribute = lb_create_enum_attribute(c, "byval"); if (!is_calling_convention_odin(calling_convention)) {
return lb_arg_type_indirect_byval(c, type);
}
attribute = nullptr;
} else if (attribute_kind == Amd64TypeAttribute_StructRect) { } else if (attribute_kind == Amd64TypeAttribute_StructRect) {
attribute = lb_create_enum_attribute(c, "sret"); attribute = lb_create_enum_attribute_with_type(c, "sret", type);
} }
return lb_arg_type_indirect(type, attribute); return lb_arg_type_indirect(type, attribute);
} else { } else {
@@ -538,30 +563,48 @@ namespace lbAbiAmd64SysV {
return reg_classes; return reg_classes;
} }
void unify(Array<RegClass> *cls, i64 i, RegClass newv) { void unify(Array<RegClass> *cls, i64 i, RegClass const newv) {
RegClass &oldv = (*cls)[i]; RegClass const oldv = (*cls)[i];
if (oldv == newv) { if (oldv == newv) {
return; return;
} else if (oldv == RegClass_NoClass) { }
oldv = newv;
RegClass to_write = newv;
if (oldv == RegClass_NoClass) {
to_write = newv;
} else if (newv == RegClass_NoClass) { } else if (newv == RegClass_NoClass) {
return; return;
} else if (oldv == RegClass_Memory || newv == RegClass_Memory) { } else if (oldv == RegClass_Memory || newv == RegClass_Memory) {
return; to_write = RegClass_Memory;
} else if (oldv == RegClass_Int || newv == RegClass_Int) { } else if (oldv == RegClass_Int || newv == RegClass_Int) {
return; to_write = RegClass_Int;
} else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87 || } else if (oldv == RegClass_X87 || oldv == RegClass_X87Up || oldv == RegClass_ComplexX87) {
newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) { to_write = RegClass_Memory;
oldv = RegClass_Memory; } else if (newv == RegClass_X87 || newv == RegClass_X87Up || newv == RegClass_ComplexX87) {
} else { to_write = RegClass_Memory;
oldv = newv; } else if (newv == RegClass_SSEUp) {
switch (oldv) {
case RegClass_SSEFv:
case RegClass_SSEFs:
case RegClass_SSEDv:
case RegClass_SSEDs:
case RegClass_SSEInt8:
case RegClass_SSEInt16:
case RegClass_SSEInt32:
case RegClass_SSEInt64:
return;
}
} }
(*cls)[i] = to_write;
} }
void fixup(LLVMTypeRef t, Array<RegClass> *cls) { void fixup(LLVMTypeRef t, Array<RegClass> *cls) {
i64 i = 0; i64 i = 0;
i64 e = cls->count; i64 e = cls->count;
if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) || lb_is_type_kind(t, LLVMArrayTypeKind))) { if (e > 2 && (lb_is_type_kind(t, LLVMStructTypeKind) ||
lb_is_type_kind(t, LLVMArrayTypeKind) ||
lb_is_type_kind(t, LLVMVectorTypeKind))) {
RegClass &oldv = (*cls)[i]; RegClass &oldv = (*cls)[i];
if (is_sse(oldv)) { if (is_sse(oldv)) {
for (i++; i < e; i++) { for (i++; i < e; i++) {
@@ -605,8 +648,8 @@ namespace lbAbiAmd64SysV {
unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) { unsigned llvec_len(Array<RegClass> const &reg_classes, isize offset) {
unsigned len = 1; unsigned len = 1;
for (isize i = offset+1; i < reg_classes.count; i++) { for (isize i = offset; i < reg_classes.count; i++) {
if (reg_classes[offset] != RegClass_SSEFv && reg_classes[i] != RegClass_SSEUp) { if (reg_classes[i] != RegClass_SSEUp) {
break; break;
} }
len++; len++;
@@ -617,7 +660,7 @@ namespace lbAbiAmd64SysV {
LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) { LLVMTypeRef llreg(LLVMContextRef c, Array<RegClass> const &reg_classes) {
auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count); auto types = array_make<LLVMTypeRef>(heap_allocator(), 0, reg_classes.count);
for_array(i, reg_classes) { for (isize i = 0; i < reg_classes.count; /**/) {
RegClass reg_class = reg_classes[i]; RegClass reg_class = reg_classes[i];
switch (reg_class) { switch (reg_class) {
case RegClass_Int: case RegClass_Int:
@@ -659,7 +702,7 @@ namespace lbAbiAmd64SysV {
break; break;
} }
unsigned vec_len = llvec_len(reg_classes, i); unsigned vec_len = llvec_len(reg_classes, i+1);
LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word); LLVMTypeRef vec_type = LLVMVectorType(elem_type, vec_len * elems_per_word);
array_add(&types, vec_type); array_add(&types, vec_type);
i += vec_len; i += vec_len;
@@ -675,9 +718,9 @@ namespace lbAbiAmd64SysV {
default: default:
GB_PANIC("Unhandled RegClass"); GB_PANIC("Unhandled RegClass");
} }
i += 1;
} }
GB_ASSERT(types.count != 0);
if (types.count == 1) { if (types.count == 1) {
return types[0]; return types[0];
} }
@@ -778,26 +821,6 @@ namespace lbAbiAmd64SysV {
} }
} }
Array<lbArgType> compute_arg_types(LLVMContextRef c, LLVMTypeRef *arg_types, unsigned arg_count) {
auto args = array_make<lbArgType>(heap_allocator(), arg_count);
for (unsigned i = 0; i < arg_count; i++) {
LLVMTypeRef t = arg_types[i];
LLVMTypeKind kind = LLVMGetTypeKind(t);
if (kind == LLVMStructTypeKind) {
i64 sz = lb_sizeof(t);
if (sz == 0) {
args[i] = lb_arg_type_ignore(t);
} else {
args[i] = lb_arg_type_indirect(t, lb_create_enum_attribute(c, "byval"));
}
} else {
args[i] = non_struct(c, t);
}
}
return args;
}
lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) { lbArgType compute_return_type(LLVMContextRef c, LLVMTypeRef return_type, bool return_is_defined) {
if (!return_is_defined) { if (!return_is_defined) {
return lb_arg_type_direct(LLVMVoidTypeInContext(c)); return lb_arg_type_direct(LLVMVoidTypeInContext(c));
@@ -809,7 +832,7 @@ namespace lbAbiAmd64SysV {
case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr); case 4: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 32), nullptr, nullptr);
case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr); case 8: return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 64), nullptr, nullptr);
} }
LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret"); LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", return_type);
return lb_arg_type_indirect(return_type, attr); return lb_arg_type_indirect(return_type, attr);
} else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) { } else if (build_context.metrics.os == TargetOs_windows && lb_is_type_kind(return_type, LLVMIntegerTypeKind) && lb_sizeof(return_type) == 16) {
return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr); return lb_arg_type_direct(return_type, LLVMIntTypeInContext(c, 128), nullptr, nullptr);
@@ -959,7 +982,7 @@ namespace lbAbiArm64 {
} }
return lb_arg_type_direct(type, cast_type, nullptr, nullptr); return lb_arg_type_direct(type, cast_type, nullptr, nullptr);
} else { } else {
LLVMAttributeRef attr = lb_create_enum_attribute(c, "sret"); LLVMAttributeRef attr = lb_create_enum_attribute_with_type(c, "sret", type);
return lb_arg_type_indirect(type, attr); return lb_arg_type_indirect(type, attr);
} }
} }
+864 -672
View File
File diff suppressed because it is too large Load Diff
+9 -3
View File
@@ -215,6 +215,12 @@ enum lbProcedureFlag : u32 {
lbProcedureFlag_WithoutMemcpyPass = 1<<0, lbProcedureFlag_WithoutMemcpyPass = 1<<0,
}; };
struct lbCopyElisionHint {
lbValue ptr;
Ast * ast;
bool used;
};
struct lbProcedure { struct lbProcedure {
u32 flags; u32 flags;
u16 state_flags; u16 state_flags;
@@ -260,9 +266,7 @@ struct lbProcedure {
LLVMMetadataRef debug_info; LLVMMetadataRef debug_info;
lbValue return_ptr_hint_value; lbCopyElisionHint copy_elision_hint;
Ast * return_ptr_hint_ast;
bool return_ptr_hint_used;
}; };
@@ -276,6 +280,7 @@ String lb_mangle_name(lbModule *m, Entity *e);
String lb_get_entity_name(lbModule *m, Entity *e, String name = {}); String lb_get_entity_name(lbModule *m, Entity *e, String name = {});
LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0); LLVMAttributeRef lb_create_enum_attribute(LLVMContextRef ctx, char const *name, u64 value=0);
LLVMAttributeRef lb_create_enum_attribute_with_type(LLVMContextRef ctx, char const *name, LLVMTypeRef type);
void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value); void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name, u64 value);
void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name); void lb_add_proc_attribute_at_index(lbProcedure *p, isize index, char const *name);
lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false); lbProcedure *lb_create_procedure(lbModule *module, Entity *entity, bool ignore_body=false);
@@ -412,6 +417,7 @@ lbValue lb_emit_reverse_bits(lbProcedure *p, lbValue x, Type *type);
lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x); lbValue lb_emit_bit_set_card(lbProcedure *p, lbValue x);
void lb_mem_zero_addr(lbProcedure *p, LLVMValueRef ptr, Type *type);
#define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime" #define LB_STARTUP_RUNTIME_PROC_NAME "__$startup_runtime"
+127 -4
View File
@@ -1,7 +1,25 @@
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level); void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level);
void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level); void lb_add_function_simplifcation_passes(LLVMPassManagerRef mpm, i32 optimization_level);
void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level); void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPassManagerRef mpm, i32 optimization_level);
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level); void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level);
LLVMBool lb_must_preserve_predicate_callback(LLVMValueRef value, void *user_data) {
lbModule *m = cast(lbModule *)user_data;
if (m == nullptr) {
return false;
}
if (value == nullptr) {
return false;
}
return LLVMIsAAllocaInst(value) != nullptr;
}
void lb_add_must_preserve_predicate_pass(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
if (false && optimization_level == 0 && m->debug_builder) {
// LLVMAddInternalizePassWithMustPreservePredicate(fpm, m, lb_must_preserve_predicate_callback);
}
}
void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) { void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
LLVMAddPromoteMemoryToRegisterPass(fpm); LLVMAddPromoteMemoryToRegisterPass(fpm);
@@ -15,11 +33,13 @@ void lb_basic_populate_function_pass_manager(LLVMPassManagerRef fpm) {
LLVMAddCFGSimplificationPass(fpm); LLVMAddCFGSimplificationPass(fpm);
} }
void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) { void lb_populate_function_pass_manager(lbModule *m, LLVMPassManagerRef fpm, bool ignore_memcpy_pass, i32 optimization_level) {
// NOTE(bill): Treat -opt:3 as if it was -opt:2 // NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place // TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2); optimization_level = gb_clamp(optimization_level, 0, 2);
lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
if (ignore_memcpy_pass) { if (ignore_memcpy_pass) {
lb_basic_populate_function_pass_manager(fpm); lb_basic_populate_function_pass_manager(fpm);
return; return;
@@ -57,11 +77,13 @@ void lb_populate_function_pass_manager(LLVMPassManagerRef fpm, bool ignore_memcp
#endif #endif
} }
void lb_populate_function_pass_manager_specific(LLVMPassManagerRef fpm, i32 optimization_level) { void lb_populate_function_pass_manager_specific(lbModule *m, LLVMPassManagerRef fpm, i32 optimization_level) {
// NOTE(bill): Treat -opt:3 as if it was -opt:2 // NOTE(bill): Treat -opt:3 as if it was -opt:2
// TODO(bill): Determine which opt definitions should exist in the first place // TODO(bill): Determine which opt definitions should exist in the first place
optimization_level = gb_clamp(optimization_level, 0, 2); optimization_level = gb_clamp(optimization_level, 0, 2);
lb_add_must_preserve_predicate_pass(m, fpm, optimization_level);
if (optimization_level == 0) { if (optimization_level == 0) {
LLVMAddMemCpyOptPass(fpm); LLVMAddMemCpyOptPass(fpm);
lb_basic_populate_function_pass_manager(fpm); lb_basic_populate_function_pass_manager(fpm);
@@ -226,3 +248,104 @@ void lb_populate_module_pass_manager(LLVMTargetMachineRef target_machine, LLVMPa
LLVMAddCFGSimplificationPass(mpm); LLVMAddCFGSimplificationPass(mpm);
} }
void lb_run_remove_dead_instruction_pass(lbProcedure *p) {
isize removal_count = 0;
isize pass_count = 0;
isize const max_pass_count = 10;
isize original_instruction_count = 0;
// Custom remove dead instruction pass
for (; pass_count < max_pass_count; pass_count++) {
bool was_dead_instructions = false;
// NOTE(bill): Iterate backwards
// reduces the number of passes as things later on will depend on things previously
for (LLVMBasicBlockRef block = LLVMGetLastBasicBlock(p->value);
block != nullptr;
block = LLVMGetPreviousBasicBlock(block)) {
// NOTE(bill): Iterate backwards
// reduces the number of passes as things later on will depend on things previously
for (LLVMValueRef instr = LLVMGetLastInstruction(block);
instr != nullptr;
/**/) {
if (pass_count == 0) {
original_instruction_count += 1;
}
LLVMValueRef curr_instr = instr;
instr = LLVMGetPreviousInstruction(instr);
LLVMUseRef first_use = LLVMGetFirstUse(curr_instr);
if (first_use != nullptr) {
continue;
}
if (LLVMTypeOf(curr_instr) == nullptr) {
continue;
}
// NOTE(bill): Explicit instructions are set here because some instructions could have side effects
switch (LLVMGetInstructionOpcode(curr_instr)) {
case LLVMFNeg:
case LLVMAdd:
case LLVMFAdd:
case LLVMSub:
case LLVMFSub:
case LLVMMul:
case LLVMFMul:
case LLVMUDiv:
case LLVMSDiv:
case LLVMFDiv:
case LLVMURem:
case LLVMSRem:
case LLVMFRem:
case LLVMShl:
case LLVMLShr:
case LLVMAShr:
case LLVMAnd:
case LLVMOr:
case LLVMXor:
case LLVMAlloca:
case LLVMLoad:
case LLVMGetElementPtr:
case LLVMTrunc:
case LLVMZExt:
case LLVMSExt:
case LLVMFPToUI:
case LLVMFPToSI:
case LLVMUIToFP:
case LLVMSIToFP:
case LLVMFPTrunc:
case LLVMFPExt:
case LLVMPtrToInt:
case LLVMIntToPtr:
case LLVMBitCast:
case LLVMAddrSpaceCast:
case LLVMICmp:
case LLVMFCmp:
case LLVMSelect:
case LLVMExtractElement:
case LLVMShuffleVector:
case LLVMExtractValue:
removal_count += 1;
LLVMInstructionEraseFromParent(curr_instr);
was_dead_instructions = true;
break;
}
}
}
if (!was_dead_instructions) {
break;
}
}
}
void lb_run_function_pass_manager(LLVMPassManagerRef fpm, lbProcedure *p) {
LLVMRunFunctionPassManager(fpm, p->value);
// NOTE(bill): LLVMAddDCEPass doesn't seem to be exported in the official DLL's for LLVM
// which means we cannot rely upon it
// This is also useful for read the .ll for debug purposes because a lot of instructions
// are not removed
lb_run_remove_dead_instruction_pass(p);
}
+7 -1
View File
@@ -621,6 +621,7 @@ enum BuildFlagKind {
BuildFlag_IgnoreWarnings, BuildFlag_IgnoreWarnings,
BuildFlag_WarningsAsErrors, BuildFlag_WarningsAsErrors,
BuildFlag_VerboseErrors,
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
BuildFlag_IgnoreVsSearch, BuildFlag_IgnoreVsSearch,
@@ -741,6 +742,7 @@ bool parse_build_flags(Array<String> args) {
add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_IgnoreWarnings, str_lit("ignore-warnings"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all); add_flag(&build_flags, BuildFlag_WarningsAsErrors, str_lit("warnings-as-errors"), BuildFlagParam_None, Command_all);
add_flag(&build_flags, BuildFlag_VerboseErrors, str_lit("verbose-errors"), BuildFlagParam_None, Command_all);
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build); add_flag(&build_flags, BuildFlag_IgnoreVsSearch, str_lit("ignore-vs-search"), BuildFlagParam_None, Command__does_build);
@@ -1320,6 +1322,10 @@ bool parse_build_flags(Array<String> args) {
} }
break; break;
case BuildFlag_VerboseErrors:
build_context.show_error_line = true;
break;
#if defined(GB_SYSTEM_WINDOWS) #if defined(GB_SYSTEM_WINDOWS)
case BuildFlag_IgnoreVsSearch: case BuildFlag_IgnoreVsSearch:
GB_ASSERT(value.kind == ExactValue_Invalid); GB_ASSERT(value.kind == ExactValue_Invalid);
@@ -1719,7 +1725,7 @@ void print_show_help(String const arg0, String const &command) {
print_usage_line(2, "Disables automatic linking with the C Run Time"); print_usage_line(2, "Disables automatic linking with the C Run Time");
print_usage_line(0, ""); print_usage_line(0, "");
print_usage_line(1, "-use-lld"); print_usage_line(1, "-lld");
print_usage_line(2, "Use the LLD linker rather than the default"); print_usage_line(2, "Use the LLD linker rather than the default");
print_usage_line(0, ""); print_usage_line(0, "");
+126 -143
View File
@@ -1,109 +1,4 @@
Token ast_token(Ast *node) { #include "parser_pos.cpp"
switch (node->kind) {
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
case Ast_Undef: return node->Undef;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.token;
case Ast_ProcLit: return ast_token(node->ProcLit.type);
case Ast_CompoundLit:
if (node->CompoundLit.type != nullptr) {
return ast_token(node->CompoundLit.type);
}
return node->CompoundLit.open;
case Ast_TagExpr: return node->TagExpr.token;
case Ast_BadExpr: return node->BadExpr.begin;
case Ast_UnaryExpr: return node->UnaryExpr.op;
case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left);
case Ast_ParenExpr: return node->ParenExpr.open;
case Ast_CallExpr: return ast_token(node->CallExpr.proc);
case Ast_SelectorExpr:
if (node->SelectorExpr.selector != nullptr) {
return ast_token(node->SelectorExpr.selector);
}
return node->SelectorExpr.token;
case Ast_SelectorCallExpr:
if (node->SelectorCallExpr.expr != nullptr) {
return ast_token(node->SelectorCallExpr.expr);
}
return node->SelectorCallExpr.token;
case Ast_ImplicitSelectorExpr:
if (node->ImplicitSelectorExpr.selector != nullptr) {
return ast_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
case Ast_IndexExpr: return node->IndexExpr.open;
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr);
case Ast_TypeCast: return node->TypeCast.token;
case Ast_AutoCast: return node->AutoCast.token;
case Ast_InlineAsmExpr: return node->InlineAsmExpr.token;
case Ast_BadStmt: return node->BadStmt.begin;
case Ast_EmptyStmt: return node->EmptyStmt.token;
case Ast_ExprStmt: return ast_token(node->ExprStmt.expr);
case Ast_TagStmt: return node->TagStmt.token;
case Ast_AssignStmt: return node->AssignStmt.op;
case Ast_BlockStmt: return node->BlockStmt.open;
case Ast_IfStmt: return node->IfStmt.token;
case Ast_WhenStmt: return node->WhenStmt.token;
case Ast_ReturnStmt: return node->ReturnStmt.token;
case Ast_ForStmt: return node->ForStmt.token;
case Ast_RangeStmt: return node->RangeStmt.token;
case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token;
case Ast_CaseClause: return node->CaseClause.token;
case Ast_SwitchStmt: return node->SwitchStmt.token;
case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token;
case Ast_DeferStmt: return node->DeferStmt.token;
case Ast_BranchStmt: return node->BranchStmt.token;
case Ast_UsingStmt: return node->UsingStmt.token;
case Ast_BadDecl: return node->BadDecl.begin;
case Ast_Label: return node->Label.token;
case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]);
case Ast_PackageDecl: return node->PackageDecl.token;
case Ast_ImportDecl: return node->ImportDecl.token;
case Ast_ForeignImportDecl: return node->ForeignImportDecl.token;
case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token;
case Ast_Attribute:
return node->Attribute.token;
case Ast_Field:
if (node->Field.names.count > 0) {
return ast_token(node->Field.names[0]);
}
return ast_token(node->Field.type);
case Ast_FieldList:
return node->FieldList.token;
case Ast_TypeidType: return node->TypeidType.token;
case Ast_HelperType: return node->HelperType.token;
case Ast_DistinctType: return node->DistinctType.token;
case Ast_PolyType: return node->PolyType.token;
case Ast_ProcType: return node->ProcType.token;
case Ast_RelativeType: return ast_token(node->RelativeType.tag);
case Ast_PointerType: return node->PointerType.token;
case Ast_ArrayType: return node->ArrayType.token;
case Ast_DynamicArrayType: return node->DynamicArrayType.token;
case Ast_StructType: return node->StructType.token;
case Ast_UnionType: return node->UnionType.token;
case Ast_EnumType: return node->EnumType.token;
case Ast_BitSetType: return node->BitSetType.token;
case Ast_MapType: return node->MapType.token;
}
return empty_token;
}
Token token_end_of_line(AstFile *f, Token tok) { Token token_end_of_line(AstFile *f, Token tok) {
u8 const *start = f->tokenizer.start + tok.pos.offset; u8 const *start = f->tokenizer.start + tok.pos.offset;
@@ -115,6 +10,48 @@ Token token_end_of_line(AstFile *f, Token tok) {
return tok; return tok;
} }
gbString get_file_line_as_string(TokenPos const &pos, i32 *offset_) {
AstFile *file = get_ast_file_from_id(pos.file_id);
if (file == nullptr) {
return nullptr;
}
isize offset = pos.offset;
u8 *start = file->tokenizer.start;
u8 *end = file->tokenizer.end;
isize len = end-start;
if (len < offset) {
return nullptr;
}
u8 *pos_offset = start+offset;
u8 *line_start = pos_offset;
u8 *line_end = pos_offset;
while (line_start >= start) {
if (*line_start == '\n') {
line_start += 1;
break;
}
line_start -= 1;
}
while (line_end < end) {
if (*line_end == '\n') {
line_end -= 1;
break;
}
line_end += 1;
}
String the_line = make_string(line_start, line_end-line_start);
the_line = string_trim_whitespace(the_line);
if (offset_) *offset_ = cast(i32)(pos_offset - the_line.text);
return gb_string_make_length(heap_allocator(), the_line.text, the_line.len);
}
isize ast_node_size(AstKind kind) { isize ast_node_size(AstKind kind) {
return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *)); return align_formula_isize(gb_size_of(AstCommonStuff) + ast_variant_sizes[kind], gb_align_of(void *));
@@ -432,12 +369,15 @@ Ast *clone_ast(Ast *node) {
void error(Ast *node, char const *fmt, ...) { void error(Ast *node, char const *fmt, ...) {
Token token = {}; Token token = {};
TokenPos end_pos = {};
if (node != nullptr) { if (node != nullptr) {
token = ast_token(node); token = ast_token(node);
end_pos = ast_end_pos(node);
} }
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
error_va(token, fmt, va); error_va(token.pos, end_pos, fmt, va);
va_end(va); va_end(va);
if (node != nullptr && node->file != nullptr) { if (node != nullptr && node->file != nullptr) {
node->file->error_count += 1; node->file->error_count += 1;
@@ -451,7 +391,7 @@ void error_no_newline(Ast *node, char const *fmt, ...) {
} }
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
error_no_newline_va(token, fmt, va); error_no_newline_va(token.pos, fmt, va);
va_end(va); va_end(va);
if (node != nullptr && node->file != nullptr) { if (node != nullptr && node->file != nullptr) {
node->file->error_count += 1; node->file->error_count += 1;
@@ -459,16 +399,28 @@ void error_no_newline(Ast *node, char const *fmt, ...) {
} }
void warning(Ast *node, char const *fmt, ...) { void warning(Ast *node, char const *fmt, ...) {
Token token = {};
TokenPos end_pos = {};
if (node != nullptr) {
token = ast_token(node);
end_pos = ast_end_pos(node);
}
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
warning_va(ast_token(node), fmt, va); warning_va(token.pos, end_pos, fmt, va);
va_end(va); va_end(va);
} }
void syntax_error(Ast *node, char const *fmt, ...) { void syntax_error(Ast *node, char const *fmt, ...) {
Token token = {};
TokenPos end_pos = {};
if (node != nullptr) {
token = ast_token(node);
end_pos = ast_end_pos(node);
}
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
syntax_error_va(ast_token(node), fmt, va); syntax_error_va(token.pos, end_pos, fmt, va);
va_end(va); va_end(va);
if (node != nullptr && node->file != nullptr) { if (node != nullptr && node->file != nullptr) {
node->file->error_count += 1; node->file->error_count += 1;
@@ -640,7 +592,7 @@ Ast *ast_basic_lit(AstFile *f, Token basic_lit) {
return result; return result;
} }
Ast *ast_basic_directive(AstFile *f, Token token, String name) { Ast *ast_basic_directive(AstFile *f, Token token, Token name) {
Ast *result = alloc_ast_node(f, Ast_BasicDirective); Ast *result = alloc_ast_node(f, Ast_BasicDirective);
result->BasicDirective.token = token; result->BasicDirective.token = token;
result->BasicDirective.name = name; result->BasicDirective.name = name;
@@ -1344,6 +1296,7 @@ Token expect_token_after(AstFile *f, TokenKind kind, char const *msg) {
bool is_token_range(TokenKind kind) { bool is_token_range(TokenKind kind) {
switch (kind) { switch (kind) {
case Token_Ellipsis: case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf: case Token_RangeHalf:
return true; return true;
} }
@@ -1574,6 +1527,10 @@ void expect_semicolon(AstFile *f, Ast *s) {
return; return;
} }
if (f->curr_token.kind == Token_EOF) {
return;
}
if (s != nullptr) { if (s != nullptr) {
bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0; bool insert_semi = (f->tokenizer.flags & TokenizerFlag_InsertSemicolon) != 0;
if (insert_semi) { if (insert_semi) {
@@ -1994,35 +1951,28 @@ Ast *parse_operand(AstFile *f, bool lhs) {
Token name = expect_token(f, Token_Ident); Token name = expect_token(f, Token_Ident);
if (name.string == "type") { if (name.string == "type") {
return ast_helper_type(f, token, parse_type(f)); return ast_helper_type(f, token, parse_type(f));
} /* else if (name.string == "no_deferred") { } else if (name.string == "file") {
operand = parse_expr(f, false); return ast_basic_directive(f, token, name);
if (unparen_expr(operand)->kind != Ast_CallExpr) { } else if (name.string == "line") { return ast_basic_directive(f, token, name);
syntax_error(operand, "#no_deferred can only be applied to procedure calls"); } else if (name.string == "procedure") { return ast_basic_directive(f, token, name);
operand = ast_bad_expr(f, token, f->curr_token); } else if (name.string == "caller_location") { return ast_basic_directive(f, token, name);
}
operand->state_flags |= StateFlag_no_deferred;
} */ else if (name.string == "file") {
return ast_basic_directive(f, token, name.string);
} else if (name.string == "line") { return ast_basic_directive(f, token, name.string);
} else if (name.string == "procedure") { return ast_basic_directive(f, token, name.string);
} else if (name.string == "caller_location") { return ast_basic_directive(f, token, name.string);
} else if (name.string == "location") { } else if (name.string == "location") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag); return parse_call_expr(f, tag);
} else if (name.string == "load") { } else if (name.string == "load") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag); return parse_call_expr(f, tag);
} else if (name.string == "assert") { } else if (name.string == "assert") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag); return parse_call_expr(f, tag);
} else if (name.string == "defined") { } else if (name.string == "defined") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag); return parse_call_expr(f, tag);
} else if (name.string == "config") { } else if (name.string == "config") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
return parse_call_expr(f, tag); return parse_call_expr(f, tag);
} else if (name.string == "soa" || name.string == "simd") { } else if (name.string == "soa" || name.string == "simd") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f); Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type); Ast *type = unparen_expr(original_type);
switch (type->kind) { switch (type->kind) {
@@ -2034,7 +1984,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
} }
return original_type; return original_type;
} else if (name.string == "partial") { } else if (name.string == "partial") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
Ast *original_type = parse_type(f); Ast *original_type = parse_type(f);
Ast *type = unparen_expr(original_type); Ast *type = unparen_expr(original_type);
switch (type->kind) { switch (type->kind) {
@@ -2046,6 +1996,10 @@ Ast *parse_operand(AstFile *f, bool lhs) {
return original_type; return original_type;
} else if (name.string == "bounds_check") { } else if (name.string == "bounds_check") {
Ast *operand = parse_expr(f, lhs); Ast *operand = parse_expr(f, lhs);
if (operand == nullptr) {
syntax_error(token, "Invalid expresssion for #%.*s", LIT(name.string));
return nullptr;
}
operand->state_flags |= StateFlag_bounds_check; operand->state_flags |= StateFlag_bounds_check;
if ((operand->state_flags & StateFlag_no_bounds_check) != 0) { if ((operand->state_flags & StateFlag_no_bounds_check) != 0) {
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
@@ -2053,13 +2007,17 @@ Ast *parse_operand(AstFile *f, bool lhs) {
return operand; return operand;
} else if (name.string == "no_bounds_check") { } else if (name.string == "no_bounds_check") {
Ast *operand = parse_expr(f, lhs); Ast *operand = parse_expr(f, lhs);
if (operand == nullptr) {
syntax_error(token, "Invalid expresssion for #%.*s", LIT(name.string));
return nullptr;
}
operand->state_flags |= StateFlag_no_bounds_check; operand->state_flags |= StateFlag_no_bounds_check;
if ((operand->state_flags & StateFlag_bounds_check) != 0) { if ((operand->state_flags & StateFlag_bounds_check) != 0) {
syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together"); syntax_error(token, "#bounds_check and #no_bounds_check cannot be applied together");
} }
return operand; return operand;
} else if (name.string == "relative") { } else if (name.string == "relative") {
Ast *tag = ast_basic_directive(f, token, name.string); Ast *tag = ast_basic_directive(f, token, name);
tag = parse_call_expr(f, tag); tag = parse_call_expr(f, tag);
Ast *type = parse_type(f); Ast *type = parse_type(f);
return ast_relative_type(f, tag, type); return ast_relative_type(f, tag, type);
@@ -2314,7 +2272,7 @@ Ast *parse_operand(AstFile *f, bool lhs) {
f->expr_level = prev_level; f->expr_level = prev_level;
} }
skip_possible_newline_for_literal(f);
Token open = expect_token_after(f, Token_OpenBrace, "struct"); Token open = expect_token_after(f, Token_OpenBrace, "struct");
isize name_count = 0; isize name_count = 0;
@@ -2674,6 +2632,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
switch (f->curr_token.kind) { switch (f->curr_token.kind) {
case Token_Ellipsis: case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf: case Token_RangeHalf:
// NOTE(bill): Do not err yet // NOTE(bill): Do not err yet
case Token_Colon: case Token_Colon:
@@ -2685,6 +2644,7 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
switch (f->curr_token.kind) { switch (f->curr_token.kind) {
case Token_Ellipsis: case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf: case Token_RangeHalf:
syntax_error(f->curr_token, "Expected a colon, not a range"); syntax_error(f->curr_token, "Expected a colon, not a range");
/* fallthrough */ /* fallthrough */
@@ -2723,6 +2683,16 @@ Ast *parse_atom_expr(AstFile *f, Ast *operand, bool lhs) {
} }
break; break;
case Token_Increment:
case Token_Decrement:
if (!lhs) {
Token token = advance_token(f);
syntax_error(token, "Postfix '%.*s' operator is not supported", LIT(token.string));
} else {
loop = false;
}
break;
default: default:
loop = false; loop = false;
break; break;
@@ -2753,16 +2723,26 @@ Ast *parse_unary_expr(AstFile *f, bool lhs) {
return ast_auto_cast(f, token, expr); return ast_auto_cast(f, token, expr);
} }
case Token_Add: case Token_Add:
case Token_Sub: case Token_Sub:
case Token_Not:
case Token_Xor: case Token_Xor:
case Token_And: { case Token_And:
case Token_Not: {
Token token = advance_token(f); Token token = advance_token(f);
Ast *expr = parse_unary_expr(f, lhs); Ast *expr = parse_unary_expr(f, lhs);
return ast_unary_expr(f, token, expr); return ast_unary_expr(f, token, expr);
} }
case Token_Increment:
case Token_Decrement: {
Token token = advance_token(f);
syntax_error(token, "Unary '%.*s' operator is not supported", LIT(token.string));
Ast *expr = parse_unary_expr(f, lhs);
return ast_unary_expr(f, token, expr);
}
case Token_Period: { case Token_Period: {
Token token = expect_token(f, Token_Period); Token token = expect_token(f, Token_Period);
Ast *ident = parse_ident(f); Ast *ident = parse_ident(f);
@@ -2791,6 +2771,7 @@ i32 token_precedence(AstFile *f, TokenKind t) {
case Token_when: case Token_when:
return 1; return 1;
case Token_Ellipsis: case Token_Ellipsis:
case Token_RangeFull:
case Token_RangeHalf: case Token_RangeHalf:
if (!f->allow_range) { if (!f->allow_range) {
return 0; return 0;
@@ -3152,6 +3133,13 @@ Ast *parse_simple_stmt(AstFile *f, u32 flags) {
return ast_bad_stmt(f, token, f->curr_token); return ast_bad_stmt(f, token, f->curr_token);
} }
switch (token.kind) {
case Token_Increment:
case Token_Decrement:
advance_token(f);
syntax_error(token, "Postfix '%.*s' statement is not supported", LIT(token.string));
break;
}
#if 0 #if 0
@@ -3898,12 +3886,6 @@ Ast *parse_return_stmt(AstFile *f) {
while (f->curr_token.kind != Token_Semicolon) { while (f->curr_token.kind != Token_Semicolon) {
Ast *arg = parse_expr(f, false); Ast *arg = parse_expr(f, false);
// if (f->curr_token.kind == Token_Eq) {
// Token eq = expect_token(f, Token_Eq);
// Ast *value = parse_value(f);
// arg = ast_field_value(f, arg, value, eq);
// }
array_add(&results, arg); array_add(&results, arg);
if (f->curr_token.kind != Token_Comma || if (f->curr_token.kind != Token_Comma ||
f->curr_token.kind == Token_EOF) { f->curr_token.kind == Token_EOF) {
@@ -4024,7 +4006,7 @@ Ast *parse_case_clause(AstFile *f, bool is_type) {
} }
f->allow_range = prev_allow_range; f->allow_range = prev_allow_range;
f->allow_in_expr = prev_allow_in_expr; f->allow_in_expr = prev_allow_in_expr;
expect_token(f, Token_Colon); // TODO(bill): Is this the best syntax? expect_token(f, Token_Colon);
Array<Ast *> stmts = parse_stmt_list(f); Array<Ast *> stmts = parse_stmt_list(f);
return ast_case_clause(f, token, list, stmts); return ast_case_clause(f, token, list, stmts);
@@ -4482,10 +4464,10 @@ Ast *parse_stmt(AstFile *f) {
} }
return s; return s;
} else if (tag == "assert") { } else if (tag == "assert") {
Ast *t = ast_basic_directive(f, hash_token, tag); Ast *t = ast_basic_directive(f, hash_token, name);
return ast_expr_stmt(f, parse_call_expr(f, t)); return ast_expr_stmt(f, parse_call_expr(f, t));
} else if (tag == "panic") { } else if (tag == "panic") {
Ast *t = ast_basic_directive(f, hash_token, tag); Ast *t = ast_basic_directive(f, hash_token, name);
return ast_expr_stmt(f, parse_call_expr(f, t)); return ast_expr_stmt(f, parse_call_expr(f, t));
} else if (name.string == "force_inline" || } else if (name.string == "force_inline" ||
name.string == "force_no_inline") { name.string == "force_no_inline") {
@@ -4572,6 +4554,7 @@ ParseFileError init_ast_file(AstFile *f, String fullpath, TokenPos *err_pos) {
GB_ASSERT(f != nullptr); GB_ASSERT(f != nullptr);
f->fullpath = string_trim_whitespace(fullpath); // Just in case f->fullpath = string_trim_whitespace(fullpath); // Just in case
set_file_path_string(f->id, fullpath); set_file_path_string(f->id, fullpath);
set_ast_file_from_id(f->id, f);
if (!string_ends_with(f->fullpath, str_lit(".odin"))) { if (!string_ends_with(f->fullpath, str_lit(".odin"))) {
return ParseFile_WrongExtension; return ParseFile_WrongExtension;
} }
+24 -19
View File
@@ -7,22 +7,21 @@ struct AstFile;
struct AstPackage; struct AstPackage;
enum AddressingMode { enum AddressingMode {
Addressing_Invalid, // invalid addressing mode Addressing_Invalid = 0, // invalid addressing mode
Addressing_NoValue, // no value (void in C) Addressing_NoValue = 1, // no value (void in C)
Addressing_Value, // computed value (rvalue) Addressing_Value = 2, // computed value (rvalue)
Addressing_Context, // context value Addressing_Context = 3, // context value
Addressing_Variable, // addressable variable (lvalue) Addressing_Variable = 4, // addressable variable (lvalue)
Addressing_Constant, // constant Addressing_Constant = 5, // constant
Addressing_Type, // type Addressing_Type = 6, // type
Addressing_Builtin, // built-in procedure Addressing_Builtin = 7, // built-in procedure
Addressing_ProcGroup, // procedure group (overloaded procedure) Addressing_ProcGroup = 8, // procedure group (overloaded procedure)
Addressing_MapIndex, // map index expression - Addressing_MapIndex = 9, // map index expression -
// lhs: acts like a Variable // lhs: acts like a Variable
// rhs: acts like OptionalOk // rhs: acts like OptionalOk
Addressing_OptionalOk, // rhs: acts like a value with an optional boolean part (for existence check) Addressing_OptionalOk = 10, // rhs: acts like a value with an optional boolean part (for existence check)
Addressing_SoaVariable, // Struct-Of-Arrays indexed variable Addressing_SoaVariable = 11, // Struct-Of-Arrays indexed variable
Addressing_AtomOpAssign, // Specialized for custom atom operations for assignments
}; };
struct TypeAndValue { struct TypeAndValue {
@@ -287,8 +286,8 @@ char const *inline_asm_dialect_strings[InlineAsmDialect_COUNT] = {
Token token; \ Token token; \
}) \ }) \
AST_KIND(BasicDirective, "basic directive", struct { \ AST_KIND(BasicDirective, "basic directive", struct { \
Token token; \ Token token; \
String name; \ Token name; \
}) \ }) \
AST_KIND(Ellipsis, "ellipsis", struct { \ AST_KIND(Ellipsis, "ellipsis", struct { \
Token token; \ Token token; \
@@ -325,7 +324,7 @@ AST_KIND(_ExprBegin, "", bool) \
AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \ AST_KIND(ImplicitSelectorExpr, "implicit selector expression", struct { Token token; Ast *selector; }) \
AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \ AST_KIND(SelectorCallExpr, "selector call expression", struct { Token token; Ast *expr, *call; bool modified_call; }) \
AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \ AST_KIND(IndexExpr, "index expression", struct { Ast *expr, *index; Token open, close; }) \
AST_KIND(DerefExpr, "dereference expression", struct { Token op; Ast *expr; }) \ AST_KIND(DerefExpr, "dereference expression", struct { Ast *expr; Token op; }) \
AST_KIND(SliceExpr, "slice expression", struct { \ AST_KIND(SliceExpr, "slice expression", struct { \
Ast *expr; \ Ast *expr; \
Token open, close; \ Token open, close; \
@@ -345,7 +344,13 @@ AST_KIND(_ExprBegin, "", bool) \
AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \ AST_KIND(FieldValue, "field value", struct { Token eq; Ast *field, *value; }) \
AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryIfExpr, "ternary if expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \ AST_KIND(TernaryWhenExpr, "ternary when expression", struct { Ast *x, *cond, *y; }) \
AST_KIND(TypeAssertion, "type assertion", struct { Ast *expr; Token dot; Ast *type; Type *type_hint; }) \ AST_KIND(TypeAssertion, "type assertion", struct { \
Ast *expr; \
Token dot; \
Ast *type; \
Type *type_hint; \
bool ignores[2]; \
}) \
AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \ AST_KIND(TypeCast, "type cast", struct { Token token; Ast *type, *expr; }) \
AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \ AST_KIND(AutoCast, "auto_cast", struct { Token token; Ast *expr; }) \
AST_KIND(InlineAsmExpr, "inline asm expression", struct { \ AST_KIND(InlineAsmExpr, "inline asm expression", struct { \
+340
View File
@@ -0,0 +1,340 @@
Token ast_token(Ast *node) {
switch (node->kind) {
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
case Ast_Undef: return node->Undef;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.token;
case Ast_ProcLit: return ast_token(node->ProcLit.type);
case Ast_CompoundLit:
if (node->CompoundLit.type != nullptr) {
return ast_token(node->CompoundLit.type);
}
return node->CompoundLit.open;
case Ast_TagExpr: return node->TagExpr.token;
case Ast_BadExpr: return node->BadExpr.begin;
case Ast_UnaryExpr: return node->UnaryExpr.op;
case Ast_BinaryExpr: return ast_token(node->BinaryExpr.left);
case Ast_ParenExpr: return node->ParenExpr.open;
case Ast_CallExpr: return ast_token(node->CallExpr.proc);
case Ast_SelectorExpr:
if (node->SelectorExpr.selector != nullptr) {
return ast_token(node->SelectorExpr.selector);
}
return node->SelectorExpr.token;
case Ast_SelectorCallExpr:
if (node->SelectorCallExpr.expr != nullptr) {
return ast_token(node->SelectorCallExpr.expr);
}
return node->SelectorCallExpr.token;
case Ast_ImplicitSelectorExpr:
if (node->ImplicitSelectorExpr.selector != nullptr) {
return ast_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
case Ast_IndexExpr: return node->IndexExpr.open;
case Ast_SliceExpr: return node->SliceExpr.open;
case Ast_Ellipsis: return node->Ellipsis.token;
case Ast_FieldValue: return node->FieldValue.eq;
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_token(node->TernaryIfExpr.x);
case Ast_TernaryWhenExpr: return ast_token(node->TernaryWhenExpr.x);
case Ast_TypeAssertion: return ast_token(node->TypeAssertion.expr);
case Ast_TypeCast: return node->TypeCast.token;
case Ast_AutoCast: return node->AutoCast.token;
case Ast_InlineAsmExpr: return node->InlineAsmExpr.token;
case Ast_BadStmt: return node->BadStmt.begin;
case Ast_EmptyStmt: return node->EmptyStmt.token;
case Ast_ExprStmt: return ast_token(node->ExprStmt.expr);
case Ast_TagStmt: return node->TagStmt.token;
case Ast_AssignStmt: return node->AssignStmt.op;
case Ast_BlockStmt: return node->BlockStmt.open;
case Ast_IfStmt: return node->IfStmt.token;
case Ast_WhenStmt: return node->WhenStmt.token;
case Ast_ReturnStmt: return node->ReturnStmt.token;
case Ast_ForStmt: return node->ForStmt.token;
case Ast_RangeStmt: return node->RangeStmt.token;
case Ast_UnrollRangeStmt: return node->UnrollRangeStmt.unroll_token;
case Ast_CaseClause: return node->CaseClause.token;
case Ast_SwitchStmt: return node->SwitchStmt.token;
case Ast_TypeSwitchStmt: return node->TypeSwitchStmt.token;
case Ast_DeferStmt: return node->DeferStmt.token;
case Ast_BranchStmt: return node->BranchStmt.token;
case Ast_UsingStmt: return node->UsingStmt.token;
case Ast_BadDecl: return node->BadDecl.begin;
case Ast_Label: return node->Label.token;
case Ast_ValueDecl: return ast_token(node->ValueDecl.names[0]);
case Ast_PackageDecl: return node->PackageDecl.token;
case Ast_ImportDecl: return node->ImportDecl.token;
case Ast_ForeignImportDecl: return node->ForeignImportDecl.token;
case Ast_ForeignBlockDecl: return node->ForeignBlockDecl.token;
case Ast_Attribute:
return node->Attribute.token;
case Ast_Field:
if (node->Field.names.count > 0) {
return ast_token(node->Field.names[0]);
}
return ast_token(node->Field.type);
case Ast_FieldList:
return node->FieldList.token;
case Ast_TypeidType: return node->TypeidType.token;
case Ast_HelperType: return node->HelperType.token;
case Ast_DistinctType: return node->DistinctType.token;
case Ast_PolyType: return node->PolyType.token;
case Ast_ProcType: return node->ProcType.token;
case Ast_RelativeType: return ast_token(node->RelativeType.tag);
case Ast_PointerType: return node->PointerType.token;
case Ast_ArrayType: return node->ArrayType.token;
case Ast_DynamicArrayType: return node->DynamicArrayType.token;
case Ast_StructType: return node->StructType.token;
case Ast_UnionType: return node->UnionType.token;
case Ast_EnumType: return node->EnumType.token;
case Ast_BitSetType: return node->BitSetType.token;
case Ast_MapType: return node->MapType.token;
}
return empty_token;
}
TokenPos token_pos_end(Token const &token) {
TokenPos pos = token.pos;
pos.offset += cast(i32)token.string.len;
for (isize i = 0; i < token.string.len; i++) {
// TODO(bill): This assumes ASCII
char c = token.string[i];
if (c == '\n') {
pos.line += 1;
pos.column = 1;
} else {
pos.column += 1;
}
}
return pos;
}
Token ast_end_token(Ast *node) {
GB_ASSERT(node != nullptr);
switch (node->kind) {
case Ast_Invalid:
return empty_token;
case Ast_Ident: return node->Ident.token;
case Ast_Implicit: return node->Implicit;
case Ast_Undef: return node->Undef;
case Ast_BasicLit: return node->BasicLit.token;
case Ast_BasicDirective: return node->BasicDirective.token;
case Ast_ProcGroup: return node->ProcGroup.close;
case Ast_ProcLit:
if (node->ProcLit.body) {
return ast_end_token(node->ProcLit.body);
}
return ast_end_token(node->ProcLit.type);
case Ast_CompoundLit:
return node->CompoundLit.close;
case Ast_BadExpr: return node->BadExpr.end;
case Ast_TagExpr:
if (node->TagExpr.expr) {
return ast_end_token(node->TagExpr.expr);
}
return node->TagExpr.name;
case Ast_UnaryExpr: return ast_end_token(node->UnaryExpr.expr);
case Ast_BinaryExpr: return ast_end_token(node->BinaryExpr.right);
case Ast_ParenExpr: return node->ParenExpr.close;
case Ast_CallExpr: return node->CallExpr.close;
case Ast_SelectorExpr:
return ast_end_token(node->SelectorExpr.selector);
case Ast_SelectorCallExpr:
return ast_end_token(node->SelectorCallExpr.call);
case Ast_ImplicitSelectorExpr:
if (node->ImplicitSelectorExpr.selector) {
return ast_end_token(node->ImplicitSelectorExpr.selector);
}
return node->ImplicitSelectorExpr.token;
case Ast_IndexExpr: return node->IndexExpr.close;
case Ast_SliceExpr: return node->SliceExpr.close;
case Ast_Ellipsis:
if (node->Ellipsis.expr) {
return ast_end_token(node->Ellipsis.expr);
}
return node->Ellipsis.token;
case Ast_FieldValue: return ast_end_token(node->FieldValue.value);
case Ast_DerefExpr: return node->DerefExpr.op;
case Ast_TernaryIfExpr: return ast_end_token(node->TernaryIfExpr.y);
case Ast_TernaryWhenExpr: return ast_end_token(node->TernaryWhenExpr.y);
case Ast_TypeAssertion: return ast_end_token(node->TypeAssertion.type);
case Ast_TypeCast: return ast_end_token(node->TypeCast.expr);
case Ast_AutoCast: return ast_end_token(node->AutoCast.expr);
case Ast_InlineAsmExpr: return node->InlineAsmExpr.close;
case Ast_BadStmt: return node->BadStmt.end;
case Ast_EmptyStmt: return node->EmptyStmt.token;
case Ast_ExprStmt: return ast_end_token(node->ExprStmt.expr);
case Ast_TagStmt: return ast_end_token(node->TagStmt.stmt);
case Ast_AssignStmt:
if (node->AssignStmt.rhs.count > 0) {
return ast_end_token(node->AssignStmt.rhs[node->AssignStmt.rhs.count-1]);
}
return node->AssignStmt.op;
case Ast_BlockStmt: return node->BlockStmt.close;
case Ast_IfStmt:
if (node->IfStmt.else_stmt) {
return ast_end_token(node->IfStmt.else_stmt);
}
return ast_end_token(node->IfStmt.body);
case Ast_WhenStmt:
if (node->WhenStmt.else_stmt) {
return ast_end_token(node->WhenStmt.else_stmt);
}
return ast_end_token(node->WhenStmt.body);
case Ast_ReturnStmt:
if (node->ReturnStmt.results.count > 0) {
return ast_end_token(node->ReturnStmt.results[node->ReturnStmt.results.count-1]);
}
return node->ReturnStmt.token;
case Ast_ForStmt: return ast_end_token(node->ForStmt.body);
case Ast_RangeStmt: return ast_end_token(node->RangeStmt.body);
case Ast_UnrollRangeStmt: return ast_end_token(node->UnrollRangeStmt.body);
case Ast_CaseClause:
if (node->CaseClause.stmts.count) {
return ast_end_token(node->CaseClause.stmts[node->CaseClause.stmts.count-1]);
} else if (node->CaseClause.list.count) {
return ast_end_token(node->CaseClause.list[node->CaseClause.list.count-1]);
}
return node->CaseClause.token;
case Ast_SwitchStmt: return ast_end_token(node->SwitchStmt.body);
case Ast_TypeSwitchStmt: return ast_end_token(node->TypeSwitchStmt.body);
case Ast_DeferStmt: return ast_end_token(node->DeferStmt.stmt);
case Ast_BranchStmt:
if (node->BranchStmt.label) {
return ast_end_token(node->BranchStmt.label);
}
return node->BranchStmt.token;
case Ast_UsingStmt:
if (node->UsingStmt.list.count > 0) {
return ast_end_token(node->UsingStmt.list[node->UsingStmt.list.count-1]);
}
return node->UsingStmt.token;
case Ast_BadDecl: return node->BadDecl.end;
case Ast_Label:
if (node->Label.name) {
return ast_end_token(node->Label.name);
}
return node->Label.token;
case Ast_ValueDecl:
if (node->ValueDecl.values.count > 0) {
return ast_end_token(node->ValueDecl.values[node->ValueDecl.values.count-1]);
}
if (node->ValueDecl.type) {
return ast_end_token(node->ValueDecl.type);
}
if (node->ValueDecl.names.count > 0) {
return ast_end_token(node->ValueDecl.names[node->ValueDecl.names.count-1]);
}
return {};
case Ast_PackageDecl: return node->PackageDecl.name;
case Ast_ImportDecl: return node->ImportDecl.relpath;
case Ast_ForeignImportDecl:
if (node->ForeignImportDecl.filepaths.count > 0) {
return node->ForeignImportDecl.filepaths[node->ForeignImportDecl.filepaths.count-1];
}
if (node->ForeignImportDecl.library_name.kind != Token_Invalid) {
return node->ForeignImportDecl.library_name;
}
return node->ForeignImportDecl.token;
case Ast_ForeignBlockDecl:
return ast_end_token(node->ForeignBlockDecl.body);
case Ast_Attribute:
if (node->Attribute.close.kind != Token_Invalid) {
return node->Attribute.close;
}
return ast_end_token(node->Attribute.elems[node->Attribute.elems.count-1]);
case Ast_Field:
if (node->Field.tag.kind != Token_Invalid) {
return node->Field.tag;
}
if (node->Field.default_value) {
return ast_end_token(node->Field.default_value);
}
if (node->Field.type) {
return ast_end_token(node->Field.type);
}
return ast_end_token(node->Field.names[node->Field.names.count-1]);
case Ast_FieldList:
if (node->FieldList.list.count > 0) {
return ast_end_token(node->FieldList.list[node->FieldList.list.count-1]);
}
return node->FieldList.token;
case Ast_TypeidType:
if (node->TypeidType.specialization) {
return ast_end_token(node->TypeidType.specialization);
}
return node->TypeidType.token;
case Ast_HelperType: return ast_end_token(node->HelperType.type);
case Ast_DistinctType: return ast_end_token(node->DistinctType.type);
case Ast_PolyType:
if (node->PolyType.specialization) {
return ast_end_token(node->PolyType.specialization);
}
return ast_end_token(node->PolyType.type);
case Ast_ProcType:
if (node->ProcType.results) {
return ast_end_token(node->ProcType.results);
}
if (node->ProcType.params) {
return ast_end_token(node->ProcType.params);
}
return node->ProcType.token;
case Ast_RelativeType:
return ast_end_token(node->RelativeType.type);
case Ast_PointerType: return ast_end_token(node->PointerType.type);
case Ast_ArrayType: return ast_end_token(node->ArrayType.elem);
case Ast_DynamicArrayType: return ast_end_token(node->DynamicArrayType.elem);
case Ast_StructType:
if (node->StructType.fields.count > 0) {
return ast_end_token(node->StructType.fields[node->StructType.fields.count-1]);
}
return node->StructType.token;
case Ast_UnionType:
if (node->UnionType.variants.count > 0) {
return ast_end_token(node->UnionType.variants[node->UnionType.variants.count-1]);
}
return node->UnionType.token;
case Ast_EnumType:
if (node->EnumType.fields.count > 0) {
return ast_end_token(node->EnumType.fields[node->EnumType.fields.count-1]);
}
if (node->EnumType.base_type) {
return ast_end_token(node->EnumType.base_type);
}
return node->EnumType.token;
case Ast_BitSetType:
if (node->BitSetType.underlying) {
return ast_end_token(node->BitSetType.underlying);
}
return ast_end_token(node->BitSetType.elem);
case Ast_MapType: return ast_end_token(node->MapType.value);
}
return empty_token;
}
TokenPos ast_end_pos(Ast *node) {
return token_pos_end(ast_end_token(node));
}
+170 -51
View File
@@ -51,8 +51,10 @@ TOKEN_KIND(Token__AssignOpBegin, ""), \
TOKEN_KIND(Token_CmpAndEq, "&&="), \ TOKEN_KIND(Token_CmpAndEq, "&&="), \
TOKEN_KIND(Token_CmpOrEq, "||="), \ TOKEN_KIND(Token_CmpOrEq, "||="), \
TOKEN_KIND(Token__AssignOpEnd, ""), \ TOKEN_KIND(Token__AssignOpEnd, ""), \
TOKEN_KIND(Token_ArrowRight, "->"), \ TOKEN_KIND(Token_Increment, "++"), \
TOKEN_KIND(Token_Undef, "---"), \ TOKEN_KIND(Token_Decrement, "--"), \
TOKEN_KIND(Token_ArrowRight,"->"), \
TOKEN_KIND(Token_Undef, "---"), \
\ \
TOKEN_KIND(Token__ComparisonBegin, ""), \ TOKEN_KIND(Token__ComparisonBegin, ""), \
TOKEN_KIND(Token_CmpEq, "=="), \ TOKEN_KIND(Token_CmpEq, "=="), \
@@ -74,6 +76,7 @@ TOKEN_KIND(Token__ComparisonEnd, ""), \
TOKEN_KIND(Token_Period, "."), \ TOKEN_KIND(Token_Period, "."), \
TOKEN_KIND(Token_Comma, ","), \ TOKEN_KIND(Token_Comma, ","), \
TOKEN_KIND(Token_Ellipsis, ".."), \ TOKEN_KIND(Token_Ellipsis, ".."), \
TOKEN_KIND(Token_RangeFull, "..="), \
TOKEN_KIND(Token_RangeHalf, "..<"), \ TOKEN_KIND(Token_RangeHalf, "..<"), \
TOKEN_KIND(Token_BackSlash, "\\"), \ TOKEN_KIND(Token_BackSlash, "\\"), \
TOKEN_KIND(Token__OperatorEnd, ""), \ TOKEN_KIND(Token__OperatorEnd, ""), \
@@ -185,9 +188,11 @@ void init_keyword_hash_table(void) {
GB_ASSERT(max_keyword_size < 16); GB_ASSERT(max_keyword_size < 16);
} }
gb_global Array<String> global_file_path_strings; // index is file id gb_global Array<String> global_file_path_strings; // index is file id
gb_global Array<struct AstFile *> global_files; // index is file id
String get_file_path_string(i32 index); String get_file_path_string(i32 index);
struct AstFile *get_ast_file_from_id(i32 index);
struct TokenPos { struct TokenPos {
i32 file_id; i32 file_id;
@@ -281,6 +286,7 @@ void init_global_error_collector(void) {
array_init(&global_error_collector.errors, heap_allocator()); array_init(&global_error_collector.errors, heap_allocator());
array_init(&global_error_collector.error_buffer, heap_allocator()); array_init(&global_error_collector.error_buffer, heap_allocator());
array_init(&global_file_path_strings, heap_allocator(), 4096); array_init(&global_file_path_strings, heap_allocator(), 4096);
array_init(&global_files, heap_allocator(), 4096);
} }
@@ -302,6 +308,24 @@ bool set_file_path_string(i32 index, String const &path) {
return ok; return ok;
} }
bool set_ast_file_from_id(i32 index, AstFile *file) {
bool ok = false;
GB_ASSERT(index >= 0);
gb_mutex_lock(&global_error_collector.string_mutex);
if (index >= global_files.count) {
array_resize(&global_files, index);
}
AstFile *prev = global_files[index];
if (prev == nullptr) {
global_files[index] = file;
ok = true;
}
gb_mutex_unlock(&global_error_collector.string_mutex);
return ok;
}
String get_file_path_string(i32 index) { String get_file_path_string(i32 index) {
GB_ASSERT(index >= 0); GB_ASSERT(index >= 0);
gb_mutex_lock(&global_error_collector.string_mutex); gb_mutex_lock(&global_error_collector.string_mutex);
@@ -315,6 +339,20 @@ String get_file_path_string(i32 index) {
return path; return path;
} }
AstFile *get_ast_file_from_id(i32 index) {
GB_ASSERT(index >= 0);
gb_mutex_lock(&global_error_collector.string_mutex);
AstFile *file = nullptr;
if (index < global_files.count) {
file = global_files[index];
}
gb_mutex_unlock(&global_error_collector.string_mutex);
return file;
}
void begin_error_block(void) { void begin_error_block(void) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.in_block = true; global_error_collector.in_block = true;
@@ -374,6 +412,8 @@ ErrorOutProc *error_out_va = default_error_out_va;
// NOTE: defined in build_settings.cpp // NOTE: defined in build_settings.cpp
bool global_warnings_as_errors(void); bool global_warnings_as_errors(void);
bool global_ignore_warnings(void); bool global_ignore_warnings(void);
bool show_error_line(void);
gbString get_file_line_as_string(TokenPos const &pos, i32 *offset);
void error_out(char const *fmt, ...) { void error_out(char const *fmt, ...) {
va_list va; va_list va;
@@ -383,17 +423,85 @@ void error_out(char const *fmt, ...) {
} }
void error_va(Token token, char const *fmt, va_list va) { bool show_error_on_line(TokenPos const &pos, TokenPos end) {
if (!show_error_line()) {
return false;
}
i32 offset = 0;
gbString the_line = get_file_line_as_string(pos, &offset);
defer (gb_string_free(the_line));
if (the_line != nullptr) {
String line = make_string(cast(u8 const *)the_line, gb_string_length(the_line));
// TODO(bill): This assumes ASCII
enum {
MAX_LINE_LENGTH = 76,
MAX_TAB_WIDTH = 8,
ELLIPSIS_PADDING = 8
};
error_out("\n\t");
if (line.len+MAX_TAB_WIDTH+ELLIPSIS_PADDING > MAX_LINE_LENGTH) {
i32 const half_width = MAX_LINE_LENGTH/2;
i32 left = cast(i32)(offset);
i32 right = cast(i32)(line.len - offset);
left = gb_min(left, half_width);
right = gb_min(right, half_width);
line.text += offset-left;
line.len -= offset+right-left;
line = string_trim_whitespace(line);
offset = left + ELLIPSIS_PADDING/2;
error_out("... %.*s ...", LIT(line));
} else {
error_out("%.*s", LIT(line));
}
error_out("\n\t");
for (i32 i = 0; i < offset; i++) {
error_out(" ");
}
error_out("^");
if (end.file_id == pos.file_id) {
if (end.line > pos.line) {
for (i32 i = offset; i < line.len; i++) {
error_out("~");
}
} else if (end.line == pos.line && end.column > pos.column) {
i32 length = gb_min(end.offset - pos.offset, cast(i32)(line.len-offset));
for (i32 i = 1; i < length-1; i++) {
error_out("~");
}
if (length > 1) {
error_out("^");
}
}
}
error_out("\n\n");
return true;
}
return false;
}
void error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++; global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (token.pos.line == 0) { if (pos.line == 0) {
error_out("Error: %s\n", gb_bprintf_va(fmt, va)); error_out("Error: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != token.pos) { } else if (global_error_collector.prev != pos) {
global_error_collector.prev = token.pos; global_error_collector.prev = pos;
error_out("%s %s\n", error_out("%s %s\n",
token_pos_to_string(token.pos), token_pos_to_string(pos),
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
} }
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) { if (global_error_collector.count > MAX_ERROR_COLLECTOR_COUNT) {
@@ -401,22 +509,23 @@ void error_va(Token token, char const *fmt, va_list va) {
} }
} }
void warning_va(Token token, char const *fmt, va_list va) { void warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) { if (global_warnings_as_errors()) {
error_va(token, fmt, va); error_va(pos, end, fmt, va);
return; return;
} }
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++; global_error_collector.warning_count++;
if (!global_ignore_warnings()) { if (!global_ignore_warnings()) {
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (token.pos.line == 0) { if (pos.line == 0) {
error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != token.pos) { } else if (global_error_collector.prev != pos) {
global_error_collector.prev = token.pos; global_error_collector.prev = pos;
error_out("%s Warning: %s\n", error_out("%s Warning: %s\n",
token_pos_to_string(token.pos), token_pos_to_string(pos),
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
show_error_on_line(pos, end);
} }
} }
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
@@ -429,16 +538,16 @@ void error_line_va(char const *fmt, va_list va) {
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
} }
void error_no_newline_va(Token token, char const *fmt, va_list va) { void error_no_newline_va(TokenPos const &pos, char const *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++; global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (token.pos.line == 0) { if (pos.line == 0) {
error_out("Error: %s", gb_bprintf_va(fmt, va)); error_out("Error: %s", gb_bprintf_va(fmt, va));
} else if (global_error_collector.prev != token.pos) { } else if (global_error_collector.prev != pos) {
global_error_collector.prev = token.pos; global_error_collector.prev = pos;
error_out("%s %s", error_out("%s %s",
token_pos_to_string(token.pos), token_pos_to_string(pos),
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
} }
gb_mutex_unlock(&global_error_collector.mutex); gb_mutex_unlock(&global_error_collector.mutex);
@@ -448,16 +557,17 @@ void error_no_newline_va(Token token, char const *fmt, va_list va) {
} }
void syntax_error_va(Token token, char const *fmt, va_list va) { void syntax_error_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.count++; global_error_collector.count++;
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != token.pos) { if (global_error_collector.prev != pos) {
global_error_collector.prev = token.pos; global_error_collector.prev = pos;
error_out("%s Syntax Error: %s\n", error_out("%s Syntax Error: %s\n",
token_pos_to_string(token.pos), token_pos_to_string(pos),
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
} else if (token.pos.line == 0) { show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va)); error_out("Syntax Error: %s\n", gb_bprintf_va(fmt, va));
} }
@@ -467,21 +577,22 @@ void syntax_error_va(Token token, char const *fmt, va_list va) {
} }
} }
void syntax_warning_va(Token token, char const *fmt, va_list va) { void syntax_warning_va(TokenPos const &pos, TokenPos end, char const *fmt, va_list va) {
if (global_warnings_as_errors()) { if (global_warnings_as_errors()) {
syntax_error_va(token, fmt, va); syntax_error_va(pos, end, fmt, va);
return; return;
} }
gb_mutex_lock(&global_error_collector.mutex); gb_mutex_lock(&global_error_collector.mutex);
global_error_collector.warning_count++; global_error_collector.warning_count++;
if (!global_ignore_warnings()) { if (!global_ignore_warnings()) {
// NOTE(bill): Duplicate error, skip it // NOTE(bill): Duplicate error, skip it
if (global_error_collector.prev != token.pos) { if (global_error_collector.prev != pos) {
global_error_collector.prev = token.pos; global_error_collector.prev = pos;
error_out("%s Syntax Warning: %s\n", error_out("%s Syntax Warning: %s\n",
token_pos_to_string(token.pos), token_pos_to_string(pos),
gb_bprintf_va(fmt, va)); gb_bprintf_va(fmt, va));
} else if (token.pos.line == 0) { show_error_on_line(pos, end);
} else if (pos.line == 0) {
error_out("Warning: %s\n", gb_bprintf_va(fmt, va)); error_out("Warning: %s\n", gb_bprintf_va(fmt, va));
} }
} }
@@ -490,17 +601,17 @@ void syntax_warning_va(Token token, char const *fmt, va_list va) {
void warning(Token token, char const *fmt, ...) { void warning(Token const &token, char const *fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
warning_va(token, fmt, va); warning_va(token.pos, {}, fmt, va);
va_end(va); va_end(va);
} }
void error(Token token, char const *fmt, ...) { void error(Token const &token, char const *fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
error_va(token, fmt, va); error_va(token.pos, {}, fmt, va);
va_end(va); va_end(va);
} }
@@ -509,7 +620,7 @@ void error(TokenPos pos, char const *fmt, ...) {
va_start(va, fmt); va_start(va, fmt);
Token token = {}; Token token = {};
token.pos = pos; token.pos = pos;
error_va(token, fmt, va); error_va(pos, {}, fmt, va);
va_end(va); va_end(va);
} }
@@ -521,26 +632,24 @@ void error_line(char const *fmt, ...) {
} }
void syntax_error(Token token, char const *fmt, ...) { void syntax_error(Token const &token, char const *fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
syntax_error_va(token, fmt, va); syntax_error_va(token.pos, {}, fmt, va);
va_end(va); va_end(va);
} }
void syntax_error(TokenPos pos, char const *fmt, ...) { void syntax_error(TokenPos pos, char const *fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
Token token = {}; syntax_error_va(pos, {}, fmt, va);
token.pos = pos;
syntax_error_va(token, fmt, va);
va_end(va); va_end(va);
} }
void syntax_warning(Token token, char const *fmt, ...) { void syntax_warning(Token const &token, char const *fmt, ...) {
va_list va; va_list va;
va_start(va, fmt); va_start(va, fmt);
syntax_warning_va(token, fmt, va); syntax_warning_va(token.pos, {}, fmt, va);
va_end(va); va_end(va);
} }
@@ -652,13 +761,14 @@ void tokenizer_err(Tokenizer *t, char const *msg, ...) {
if (column < 1) { if (column < 1) {
column = 1; column = 1;
} }
Token token = {}; TokenPos pos = {};
token.pos.file_id = t->curr_file_id; pos.file_id = t->curr_file_id;
token.pos.line = t->line_count; pos.line = t->line_count;
token.pos.column = cast(i32)column; pos.column = cast(i32)column;
pos.offset = cast(i32)(t->read_curr - t->start);
va_start(va, msg); va_start(va, msg);
syntax_error_va(token, msg, va); syntax_error_va(pos, {}, msg, va);
va_end(va); va_end(va);
t->error_count++; t->error_count++;
@@ -670,11 +780,9 @@ void tokenizer_err(Tokenizer *t, TokenPos const &pos, char const *msg, ...) {
if (column < 1) { if (column < 1) {
column = 1; column = 1;
} }
Token token = {};
token.pos = pos;
va_start(va, msg); va_start(va, msg);
syntax_error_va(token, msg, va); syntax_error_va(pos, {}, msg, va);
va_end(va); va_end(va);
t->error_count++; t->error_count++;
@@ -1202,6 +1310,9 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '<') { if (t->curr_rune == '<') {
advance_to_next_rune(t); advance_to_next_rune(t);
token->kind = Token_RangeHalf; token->kind = Token_RangeHalf;
} else if (t->curr_rune == '=') {
advance_to_next_rune(t);
token->kind = Token_RangeFull;
} }
} else if ('0' <= t->curr_rune && t->curr_rune <= '9') { } else if ('0' <= t->curr_rune && t->curr_rune <= '9') {
scan_number_to_token(t, token, true); scan_number_to_token(t, token, true);
@@ -1287,6 +1398,10 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
if (t->curr_rune == '=') { if (t->curr_rune == '=') {
advance_to_next_rune(t); advance_to_next_rune(t);
token->kind = Token_AddEq; token->kind = Token_AddEq;
} else if (t->curr_rune == '+') {
advance_to_next_rune(t);
token->kind = Token_Increment;
insert_semicolon = true;
} }
break; break;
case '-': case '-':
@@ -1298,6 +1413,10 @@ void tokenizer_get_token(Tokenizer *t, Token *token, int repeat=0) {
advance_to_next_rune(t); advance_to_next_rune(t);
advance_to_next_rune(t); advance_to_next_rune(t);
token->kind = Token_Undef; token->kind = Token_Undef;
} else if (t->curr_rune == '-') {
advance_to_next_rune(t);
token->kind = Token_Decrement;
insert_semicolon = true;
} else if (t->curr_rune == '>') { } else if (t->curr_rune == '>') {
advance_to_next_rune(t); advance_to_next_rune(t);
token->kind = Token_ArrowRight; token->kind = Token_ArrowRight;
-19
View File
@@ -128,21 +128,6 @@ enum StructSoaKind {
StructSoa_Dynamic = 3, StructSoa_Dynamic = 3,
}; };
enum TypeAtomOpKind {
TypeAtomOp_Invalid,
TypeAtomOp_index_get,
TypeAtomOp_index_set,
TypeAtomOp_slice,
TypeAtomOp_index_get_ptr,
TypeAtomOp_COUNT,
};
struct TypeAtomOpTable {
Entity *op[TypeAtomOp_COUNT];
};
struct TypeStruct { struct TypeStruct {
Array<Entity *> fields; Array<Entity *> fields;
Array<String> tags; Array<String> tags;
@@ -156,8 +141,6 @@ struct TypeStruct {
i64 custom_align; i64 custom_align;
Entity * names; Entity * names;
TypeAtomOpTable *atom_op_table;
Type * soa_elem; Type * soa_elem;
i64 soa_count; i64 soa_count;
StructSoaKind soa_kind; StructSoaKind soa_kind;
@@ -180,8 +163,6 @@ struct TypeUnion {
Type * polymorphic_params; // Type_Tuple Type * polymorphic_params; // Type_Tuple
Type * polymorphic_parent; Type * polymorphic_parent;
TypeAtomOpTable *atom_op_table;
bool no_nil; bool no_nil;
bool maybe; bool maybe;
bool is_polymorphic; bool is_polymorphic;
+216
View File
@@ -0,0 +1,216 @@
package flag
import "core:runtime"
import "core:strings"
import "core:reflect"
import "core:fmt"
import "core:mem"
import "core:strconv"
Flag_Error :: enum {
None,
No_Base_Struct,
Arg_Error,
Arg_Unsupported_Field_Type,
Arg_Not_Defined,
Arg_Non_Optional,
Value_Parse_Error,
Tag_Error,
}
Flag :: struct {
optional: bool,
type: ^runtime.Type_Info,
data: rawptr,
tag_ptr: rawptr,
parsed: bool,
}
Flag_Context :: struct {
seen_flags: map[string]Flag,
}
parse_args :: proc(ctx: ^Flag_Context, args: []string) -> Flag_Error {
using runtime;
args := args;
for true {
if len(args) == 0 {
return .None;
}
arg := args[0];
if len(arg) < 2 || arg[0] != '-' {
return .Arg_Error;
}
minus_count := 1;
if arg[1] == '-' {
minus_count += 1;
if len(arg) == 2 {
return .Arg_Error;
}
}
name := arg[minus_count:];
if len(name) == 0 {
return .Arg_Error;
}
args = args[1:];
assign_index := strings.index(name, "=");
value := "";
if assign_index > 0 {
value = name[assign_index + 1:];
name = name[0:assign_index];
}
flag := &ctx.seen_flags[name];
if flag == nil {
return .Arg_Not_Defined;
}
if reflect.is_boolean(flag.type) {
tmp := true;
mem.copy(flag.data, &tmp, flag.type.size);
flag.parsed = true;
continue;
} else
//must be in the next argument
if value == "" {
if len(args) == 0 {
return .Arg_Error;
}
value = args[0];
args = args[1:];
}
#partial switch _ in flag.type.variant {
case Type_Info_Integer:
if v, ok := strconv.parse_int(value); ok {
mem.copy(flag.data, &v, flag.type.size);
} else {
return .Value_Parse_Error;
}
case Type_Info_String:
raw_string := cast(^mem.Raw_String)flag.data;
raw_string.data = strings.ptr_from_string(value);
raw_string.len = len(value);
case Type_Info_Float:
switch flag.type.size {
case 32:
if v, ok := strconv.parse_f32(value); ok {
mem.copy(flag.data, &v, flag.type.size);
} else {
return .Value_Parse_Error;
}
case 64:
if v, ok := strconv.parse_f64(value); ok {
mem.copy(flag.data, &v, flag.type.size);
} else {
return .Value_Parse_Error;
}
}
}
flag.parsed = true;
}
return .None;
}
reflect_args_structure :: proc(ctx: ^Flag_Context, v: any) -> Flag_Error {
using runtime;
if !reflect.is_struct(type_info_of(v.id)) {
return .No_Base_Struct;
}
names := reflect.struct_field_names(v.id);
types := reflect.struct_field_types(v.id);
offsets := reflect.struct_field_offsets(v.id);
tags := reflect.struct_field_tags(v.id);
for name, i in names {
flag: Flag;
type := types[i];
if named_type, ok := type.variant.(Type_Info_Named); ok {
if union_type, ok := named_type.base.variant.(Type_Info_Union); ok && union_type.maybe && len(union_type.variants) == 1 {
flag.optional = true;
flag.tag_ptr = rawptr(uintptr(union_type.tag_offset) + uintptr(v.data) + uintptr(offsets[i]));
type = union_type.variants[0];
} else {
return .Arg_Unsupported_Field_Type;
}
}
#partial switch _ in type.variant {
case Type_Info_Integer, Type_Info_String, Type_Info_Boolean, Type_Info_Float:
flag.type = type;
flag.data = rawptr(uintptr(v.data) + uintptr(offsets[i]));
case:
return .Arg_Unsupported_Field_Type;
}
flag_name: string;
if value, ok := reflect.struct_tag_lookup(tags[i], "flag"); ok {
flag_name = cast(string)value;
} else {
return .Tag_Error;
}
ctx.seen_flags[flag_name] = flag;
}
return .None;
}
parse :: proc(v: any, args: []string) -> Flag_Error {
if v == nil {
return .None;
}
ctx: Flag_Context;
if res := reflect_args_structure(&ctx, v); res != .None {
return res;
}
if res := parse_args(&ctx, args); res != .None {
return res;
}
//validate that the required flags were actually set
for k, v in ctx.seen_flags {
if v.optional && v.parsed {
tag_value: i32 = 1;
mem.copy(v.tag_ptr, &tag_value, 4); //4 constant is probably not portable, but it works for me currently
} else if !v.parsed && !v.optional {
return .Arg_Non_Optional;
}
}
return .None;
}
usage :: proc(v: any) -> string {
return "failed";
}
+125
View File
@@ -0,0 +1,125 @@
package odinfmt
import "core:os"
import "core:odin/format"
import "core:fmt"
import "core:strings"
import "core:path/filepath"
import "core:time"
import "core:mem"
import "flag"
Args :: struct {
write: Maybe(bool) `flag:"w" usage:"write the new format to file"`,
}
print_help :: proc(args: []string) {
if len(args) == 0 {
fmt.eprint("odinfmt ");
} else {
fmt.eprintf("%s ", args[0]);
}
fmt.eprintln();
}
print_arg_error :: proc(error: flag.Flag_Error) {
fmt.println(error);
}
format_file :: proc(filepath: string) -> (string, bool) {
if data, ok := os.read_entire_file(filepath); ok {
return format.format(string(data), format.default_style);
} else {
return "", false;
}
}
files: [dynamic]string;
walk_files :: proc(info: os.File_Info, in_err: os.Errno) -> (err: os.Errno, skip_dir: bool) {
if info.is_dir {
return 0, false;
}
if filepath.ext(info.name) != ".odin" {
return 0, false;
}
append(&files, strings.clone(info.fullpath));
return 0, false;
}
main :: proc() {
init_global_temporary_allocator(mem.megabytes(100));
args: Args;
if len(os.args) < 2 {
print_help(os.args);
os.exit(1);
}
if res := flag.parse(args, os.args[1:len(os.args) - 1]); res != .None {
print_arg_error(res);
os.exit(1);
}
path := os.args[len(os.args) - 1];
tick_time := time.tick_now();
if os.is_file(path) {
if _, ok := args.write.(bool); ok {
backup_path := strings.concatenate({path, "_bk"});
defer delete(backup_path);
if data, ok := format_file(path); ok {
os.rename(path, backup_path);
if os.write_entire_file(path, transmute([]byte)data) {
os.remove(backup_path);
}
} else {
fmt.eprintf("failed to write %v", path);
}
} else {
if data, ok := format_file(path); ok {
fmt.println(data);
}
}
} else if os.is_dir(path) {
filepath.walk(path, walk_files);
for file in files {
fmt.println(file);
backup_path := strings.concatenate({file, "_bk"});
defer delete(backup_path);
if data, ok := format_file(file); ok {
if _, ok := args.write.(bool); ok {
os.rename(file, backup_path);
if os.write_entire_file(file, transmute([]byte)data) {
os.remove(backup_path);
}
} else {
fmt.println(data);
}
} else {
fmt.eprintf("failed to format %v", file);
}
}
fmt.printf("formatted %v files in %vms", len(files), time.duration_milliseconds(time.tick_lap_time(&tick_time)));
} else {
fmt.eprintf("%v is neither a directory nor a file \n", path);
os.exit(1);
}
os.exit(0);
}