mirror of
https://github.com/odin-lang/Odin.git
synced 2026-04-18 20:40:28 +00:00
IR emit C ABI compatible types for calling conventions (Only for x86/amd64 like processors at the moment)
This commit is contained in:
655
code/demo.odin
655
code/demo.odin
@@ -1,18 +1,651 @@
|
||||
#import "fmt.odin";
|
||||
#import "os.odin";
|
||||
#import "strconv.odin";
|
||||
#import "utf8.odin";
|
||||
|
||||
main :: proc() {
|
||||
immutable program := "+ + * - /";
|
||||
accumulator := 0;
|
||||
Error :: enum {
|
||||
NONE,
|
||||
}
|
||||
|
||||
for token in program {
|
||||
match token {
|
||||
case '+': accumulator += 1;
|
||||
case '-': accumulator -= 1;
|
||||
case '*': accumulator *= 2;
|
||||
case '/': accumulator /= 2;
|
||||
default: // Ignore everything else
|
||||
Style_Type :: enum {
|
||||
ITALIC,
|
||||
BOLD,
|
||||
STRIKE,
|
||||
}
|
||||
|
||||
Node :: union {
|
||||
children: [dynamic]Node,
|
||||
content: []byte,
|
||||
inline_content: ^Node,
|
||||
line_number: int,
|
||||
|
||||
// Block Variants
|
||||
Header{level: int},
|
||||
Document{},
|
||||
Paragraph{},
|
||||
Quote{},
|
||||
Code_Block{language: string},
|
||||
Horizontal_Rule{},
|
||||
|
||||
// Inline Variants
|
||||
Multiple_Inline{},
|
||||
String_Inline{},
|
||||
Soft_Line_Break{},
|
||||
Hard_Line_Break{},
|
||||
Code_Span{},
|
||||
Style{
|
||||
type: Style_Type,
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Parser :: struct {
|
||||
data: []byte,
|
||||
nodes: [dynamic]Node,
|
||||
}
|
||||
|
||||
parse :: proc(data: []byte) -> ([]Node, Error) {
|
||||
p := Parser{
|
||||
data = data,
|
||||
};
|
||||
err := parse(^p);
|
||||
|
||||
if err != Error.NONE {
|
||||
return nil, err;
|
||||
}
|
||||
|
||||
return p.nodes[..], Error.NONE;
|
||||
}
|
||||
|
||||
parse :: proc(p: ^Parser) -> Error {
|
||||
is_blank :: proc(line: []byte) -> bool {
|
||||
line = trim_whitespace(line);
|
||||
return len(line) == 0;
|
||||
}
|
||||
|
||||
is_horizontal_rule :: proc(line: []byte) -> bool {
|
||||
char: byte;
|
||||
count := 0;
|
||||
for c, i in line {
|
||||
if c != ' ' && c != '\n' {
|
||||
if c != '-' && c != '_' && c != '*' {
|
||||
return false;
|
||||
}
|
||||
if char == 0 {
|
||||
if i >= 4 {
|
||||
return false;
|
||||
}
|
||||
char = c;
|
||||
count = 1;
|
||||
} else if c == char {
|
||||
count++;
|
||||
} else {
|
||||
return false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
return count >= 3;
|
||||
}
|
||||
|
||||
nodes := make([dynamic]Node);
|
||||
|
||||
line_number: int = 0;
|
||||
prev_was_blank := false;
|
||||
in_code_block := false;
|
||||
code_language := "";
|
||||
code_block_start := 0;
|
||||
|
||||
pos := 0;
|
||||
end := len(p.data);
|
||||
for pos < len(p.data) {
|
||||
line_start := pos;
|
||||
line_end := pos;
|
||||
for p.data[line_end] != '\n' {
|
||||
line_end++;
|
||||
}
|
||||
line := p.data[pos..line_end];
|
||||
pos = line_end+1;
|
||||
line_number++;
|
||||
|
||||
line = tabs_to_spaces_and_append_newline(line);
|
||||
str := cast(string)line;
|
||||
|
||||
skip := in_code_block;
|
||||
|
||||
node: Node;
|
||||
if len(line) > 3 && cast(string)line[..3] == "```" {
|
||||
if !in_code_block {
|
||||
code_block_start = line_start+3;
|
||||
in_code_block = true;
|
||||
code_language = "";
|
||||
rest := trim_whitespace(line[3..]);
|
||||
if len(rest) > 0 {
|
||||
code_language = cast(string)rest;
|
||||
}
|
||||
} else {
|
||||
end := line_start-1;
|
||||
str := p.data[code_block_start..end];
|
||||
node = Node.Code_Block{content = str, language = code_language};
|
||||
in_code_block = false;
|
||||
}
|
||||
skip = true;
|
||||
}
|
||||
|
||||
indent_char := line[indentation(line)];
|
||||
if skip {
|
||||
|
||||
} else if indent_char == '>' {
|
||||
node = Node.Quote{content = line};
|
||||
} else if indent_char == '*' {
|
||||
// fmt.println("List Item");
|
||||
} else if level, content := parse_header(line); level > 0 {
|
||||
node = Node.Header{content = content, level = level};
|
||||
} else if is_horizontal_rule(line) {
|
||||
node = Node.Horizontal_Rule{};
|
||||
} else if !is_blank(line) {
|
||||
node = Node.Paragraph{content = line};
|
||||
}
|
||||
|
||||
if node != nil {
|
||||
node.line_number = line_number;
|
||||
append(nodes, node);
|
||||
}
|
||||
}
|
||||
|
||||
fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
|
||||
|
||||
for _, i in nodes {
|
||||
using Node;
|
||||
match n in nodes[i] {
|
||||
case Paragraph, Horizontal_Rule, Header, Code_Block:
|
||||
append(p.nodes, nodes[i]);
|
||||
case Quote:
|
||||
// fmt.println("Quote");
|
||||
}
|
||||
}
|
||||
|
||||
for _, i in p.nodes {
|
||||
process_inlines(^p.nodes[i]);
|
||||
}
|
||||
|
||||
|
||||
return Error.NONE;
|
||||
}
|
||||
|
||||
process_inlines :: proc(node: ^Node) {
|
||||
using Node;
|
||||
match n in node {
|
||||
case Header:
|
||||
n.inline_content = parse_inlines(n.content);
|
||||
case Paragraph:
|
||||
n.inline_content = parse_inlines(trim_right_space(n.content));
|
||||
}
|
||||
|
||||
for _, i in node.children {
|
||||
process_inlines(^node.children[i]);
|
||||
}
|
||||
}
|
||||
|
||||
Inline_Parser :: struct {
|
||||
data: []byte,
|
||||
pos: int,
|
||||
string_start: int,
|
||||
root: ^Node,
|
||||
}
|
||||
|
||||
parse_inlines :: proc(data: []byte) -> ^Node {
|
||||
reset_string :: proc(p: ^Inline_Parser) {
|
||||
p.string_start = p.pos;
|
||||
}
|
||||
finalize_string :: proc(p: ^Inline_Parser) {
|
||||
if p.string_start >= p.pos {
|
||||
return;
|
||||
}
|
||||
|
||||
|
||||
str := p.data[p.string_start..p.pos];
|
||||
append(p.root.children, Node.String_Inline{content = trim_right_whitespace(str)});
|
||||
}
|
||||
|
||||
p := Inline_Parser{
|
||||
data = data,
|
||||
root = new(Node),
|
||||
};
|
||||
p.root^ = Node.Multiple_Inline{};
|
||||
|
||||
using Node;
|
||||
|
||||
for p.pos < len(p.data) {
|
||||
node: Node;
|
||||
match p.data[p.pos] {
|
||||
default: p.pos++;
|
||||
|
||||
case '\n':
|
||||
hard_break := false;
|
||||
new_line_pos := p.pos;
|
||||
|
||||
if p.pos >= 2 && p.data[p.pos-1] == ' ' && p.data[p.pos-2] == ' ' {
|
||||
hard_break = true;
|
||||
p.pos -= 2;
|
||||
}
|
||||
|
||||
if p.pos >= 1 && p.data[p.pos-1] == '\\' {
|
||||
hard_break = true;
|
||||
p.pos--;
|
||||
}
|
||||
|
||||
|
||||
for p.pos > 0 && p.data[p.pos-1] == ' ' {
|
||||
p.pos--;
|
||||
}
|
||||
finalize_string(^p);
|
||||
|
||||
if hard_break {
|
||||
node = Hard_Line_Break{};
|
||||
} else {
|
||||
node = Soft_Line_Break{};
|
||||
}
|
||||
|
||||
p.pos = new_line_pos + 1;
|
||||
|
||||
for p.pos < len(p.data) && p.data[p.pos] == ' ' {
|
||||
p.pos++;
|
||||
}
|
||||
reset_string(^p);
|
||||
|
||||
case '`':
|
||||
// "A backtick string is a string of one or more backtick
|
||||
// characters (`) that is neither preceded nor followed by a
|
||||
// backtick."
|
||||
backtick_count: int;
|
||||
for p.pos+backtick_count < len(p.data) && p.data[p.pos+backtick_count] == '`' {
|
||||
backtick_count++;
|
||||
}
|
||||
closing := char_string_index(p.data, '`', p.pos+backtick_count, backtick_count);
|
||||
if closing == -1 {
|
||||
p.pos += backtick_count;
|
||||
break;
|
||||
}
|
||||
|
||||
finalize_string(^p);
|
||||
p.pos += backtick_count;
|
||||
|
||||
content := p.data[p.pos..closing];
|
||||
content = collapse_space(trim_whitespace(content));
|
||||
|
||||
node = Code_Span{content = content};
|
||||
|
||||
p.pos = closing + backtick_count;
|
||||
reset_string(^p);
|
||||
|
||||
case '\\':
|
||||
// "Backslashes before other characters are treated as literal backslashes."
|
||||
if p.pos+1 >= len(p.data) || !is_ascii_punc(p.data[p.pos+1]) {
|
||||
p.pos++;
|
||||
break;
|
||||
}
|
||||
// "Any ASCII punctuation character may be backslash-escaped."
|
||||
finalize_string(^p);
|
||||
p.pos++;
|
||||
node = String_Inline{content = p.data[p.pos..p.pos+1]};
|
||||
p.pos++;
|
||||
reset_string(^p);
|
||||
|
||||
case '&':
|
||||
// "[A]ll valid HTML entities in any context are recognized as such
|
||||
// and converted into unicode characters before they are stored in
|
||||
// the AST."
|
||||
semicolon := -1;
|
||||
for c, i in p.data[p.pos+1..] {
|
||||
if c == ';' {
|
||||
semicolon = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if semicolon < 0 {
|
||||
p.pos++;
|
||||
break;
|
||||
}
|
||||
|
||||
semicolon += p.pos+1;
|
||||
entity := cast(string)p.data[p.pos+1..semicolon];
|
||||
|
||||
codepoints := make([dynamic]byte, 0, 6);
|
||||
|
||||
if len(entity) > 0 {
|
||||
if entity[0] != '#' {
|
||||
append(codepoints, '&');
|
||||
append(codepoints, ..cast([]byte)entity);
|
||||
append(codepoints, ';');
|
||||
} else {
|
||||
if len(entity) > 1 {
|
||||
base := 10;
|
||||
if entity[1] == 'x' || entity[1] == 'X' {
|
||||
// "Hexadecimal entities consist of &# + either X or x + a
|
||||
// string of 1-8 hexadecimal digits + ;."
|
||||
base = 16;
|
||||
} else {
|
||||
// "Decimal entities consist of &# + a string of 1–8 arabic
|
||||
// digits + ;. Again, these entities need to be recognised and
|
||||
// tranformed into their corresponding UTF8 codepoints. Invalid
|
||||
// Unicode codepoints will be written as the “unknown
|
||||
// codepoint” character (0xFFFD)."
|
||||
}
|
||||
codepoint := strconv.parse_uint(entity[2..], base);
|
||||
data, len := utf8.encode_rune(cast(rune)codepoint);
|
||||
append(codepoints, ..data[..len]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if len(codepoints) == 0 {
|
||||
p.pos++;
|
||||
break;
|
||||
}
|
||||
|
||||
finalize_string(^p);
|
||||
node = String_Inline{content = codepoints[..]};
|
||||
p.pos = semicolon+1;
|
||||
reset_string(^p);
|
||||
}
|
||||
|
||||
if node != nil {
|
||||
append(p.root.children, node);
|
||||
}
|
||||
}
|
||||
|
||||
finalize_string(^p);
|
||||
|
||||
return p.root;
|
||||
}
|
||||
|
||||
is_ascii_punc :: proc(char: byte) -> bool {
|
||||
match char {
|
||||
case '!', '"', '#', '$', '%',
|
||||
'&', '\'', '(', ')',
|
||||
'*', '+', ',', '-',
|
||||
'.', '/', ':', ';',
|
||||
'<', '=', '>', '?', '@', '[', '\\', ']',
|
||||
'^', '_', '`', '{', '|', '}', '~':
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
char_string_index :: proc(data: []byte, char: byte, start, length: int) -> int {
|
||||
count: int;
|
||||
for i in start..len(data) {
|
||||
if data[i] == char {
|
||||
count++;
|
||||
if count == length {
|
||||
if i+1 >= len(data) || data[i+1] != char {
|
||||
return i+1 - count;
|
||||
}
|
||||
}
|
||||
} else {
|
||||
count = 0;
|
||||
}
|
||||
}
|
||||
return -1;
|
||||
}
|
||||
|
||||
collapse_space :: proc(data: []byte) -> []byte {
|
||||
out := make([]byte, 0, len(data));
|
||||
prev_was_space := false;
|
||||
for c in data {
|
||||
if c == ' ' || c == '\n' {
|
||||
if !prev_was_space {
|
||||
append(out, ' ');
|
||||
prev_was_space = true;
|
||||
}
|
||||
} else {
|
||||
append(out, c);
|
||||
prev_was_space = false;
|
||||
}
|
||||
}
|
||||
|
||||
return out;
|
||||
}
|
||||
|
||||
|
||||
parse_header :: proc(line: []byte) -> (int, []byte) {
|
||||
// "The opening # character may be indented 0-3 spaces."
|
||||
indent := indentation(line);
|
||||
if indent > 3 {
|
||||
return -1, nil;
|
||||
}
|
||||
line = line[indent..];
|
||||
|
||||
// "The header level is equal to the number of # characters in the opening sequence."
|
||||
level := 0;
|
||||
for c, i in line {
|
||||
if c != '#' {
|
||||
level = i;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if level < 1 || level > 6 {
|
||||
return -1, nil;
|
||||
}
|
||||
line = line[level..];
|
||||
// "The opening sequence of # characters cannot be followed directly by a
|
||||
// nonspace character."
|
||||
if line[0] != ' ' && line[0] != '\n' {
|
||||
return -1, nil;
|
||||
}
|
||||
// "The optional closing sequence of #s [...] may be followed by spaces
|
||||
// only."
|
||||
|
||||
trailer_start := len(line) - 1;
|
||||
for trailer_start > 0 && line[trailer_start-1] == ' ' {
|
||||
trailer_start--;
|
||||
}
|
||||
for trailer_start > 0 && line[trailer_start-1] == '#' {
|
||||
trailer_start--;
|
||||
}
|
||||
// "The optional closing sequence of #s must be preceded by a space [...]."
|
||||
// Note that (if the header is empty) this may be the same space as after
|
||||
// the opening sequence.
|
||||
if trailer_start > 0 && line[trailer_start-1] == ' ' {
|
||||
line = line[..trailer_start];
|
||||
}
|
||||
|
||||
// "The raw contents of the header are stripped of leading and trailing
|
||||
// spaces before being parsed as inline content."
|
||||
line = trim_space(line);
|
||||
return level, line;
|
||||
|
||||
}
|
||||
|
||||
indentation :: proc(line: []byte) -> int {
|
||||
for c, i in line {
|
||||
if c != ' ' {
|
||||
return i;
|
||||
}
|
||||
}
|
||||
panic("indentation() expects line to end in newline character");
|
||||
return 0;
|
||||
}
|
||||
|
||||
|
||||
TAB_STOP :: 4;
|
||||
|
||||
tabs_to_spaces_and_append_newline :: proc(line: []byte) -> []byte {
|
||||
tab_count: int;
|
||||
for c in line {
|
||||
if c == '\t' {
|
||||
tab_count++;
|
||||
}
|
||||
}
|
||||
|
||||
out := make([]byte, 0, len(line) + tab_count*(TAB_STOP-1) + 1);
|
||||
|
||||
rune_count: int;
|
||||
for r in cast(string)line {
|
||||
if r == '\t' {
|
||||
spaces_count := TAB_STOP - rune_count%TAB_STOP;
|
||||
for i in 0..spaces_count {
|
||||
append(out, ' ');
|
||||
}
|
||||
rune_count += spaces_count;
|
||||
} else {
|
||||
match r {
|
||||
case '\r', '\v', '\f':
|
||||
append(out, ' ');
|
||||
default:
|
||||
c, l := utf8.encode_rune(r);
|
||||
append(out, ..c[0..l]);
|
||||
}
|
||||
rune_count++;
|
||||
}
|
||||
}
|
||||
append(out, '\n');
|
||||
return out;
|
||||
}
|
||||
|
||||
trim_right_whitespace :: proc(data: []byte) -> []byte {
|
||||
c := data;
|
||||
for i := len(c)-1; i >= 0; i-- {
|
||||
match c[i] {
|
||||
case ' ', '\t', '\v', '\f', '\r', '\n':
|
||||
c = c[..i];
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
|
||||
trim_right_space :: proc(data: []byte) -> []byte {
|
||||
c := data;
|
||||
for i := len(c)-1; i >= 0; i-- {
|
||||
if c[i] != ' ' {
|
||||
break;
|
||||
}
|
||||
c = c[..i];
|
||||
}
|
||||
|
||||
return c;
|
||||
}
|
||||
|
||||
trim_whitespace :: proc(data: []byte) -> []byte {
|
||||
data = trim_right_whitespace(data);
|
||||
index := 0;
|
||||
for c in data {
|
||||
match c {
|
||||
case ' ', '\t', '\v', '\f', '\r':
|
||||
index++;
|
||||
continue;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return data[index..];
|
||||
}
|
||||
|
||||
trim_space :: proc(data: []byte) -> []byte {
|
||||
index := 0;
|
||||
for c in data {
|
||||
if c != ' ' {
|
||||
break;
|
||||
}
|
||||
index++;
|
||||
}
|
||||
data = data[index..];
|
||||
|
||||
for i := len(data)-1; i >= 0; i-- {
|
||||
if data[i] != ' ' {
|
||||
break;
|
||||
}
|
||||
data = data[..i];
|
||||
}
|
||||
|
||||
return data;
|
||||
}
|
||||
|
||||
escape_map := map[byte]string{
|
||||
'"' = """,
|
||||
'&' = "&",
|
||||
'<' = "<",
|
||||
'>' = ">",
|
||||
};
|
||||
|
||||
|
||||
main :: proc() {
|
||||
data, ok := os.read_entire_file("W:/Odin/misc/markdown_test.md");
|
||||
if !ok {
|
||||
fmt.println("Failure to load file");
|
||||
return;
|
||||
}
|
||||
|
||||
nodes, err := parse(data);
|
||||
if err != Error.NONE {
|
||||
fmt.println("Failure to parse file");
|
||||
return;
|
||||
}
|
||||
|
||||
write_espaced :: proc(data: []byte) {
|
||||
start: int;
|
||||
for c, i in data {
|
||||
if escaped, ok := escape_map[c]; ok {
|
||||
fmt.print(cast(string)data[start..i]);
|
||||
fmt.print(escaped);
|
||||
start = i+1;
|
||||
}
|
||||
}
|
||||
fmt.print(cast(string)data[start..]);
|
||||
}
|
||||
|
||||
print_inline_as_html :: proc(node: ^Node) {
|
||||
using Node;
|
||||
match n in node {
|
||||
case Multiple_Inline:
|
||||
for _, i in n.children {
|
||||
print_inline_as_html(^n.children[i]);
|
||||
}
|
||||
case String_Inline:
|
||||
write_espaced(n.content);
|
||||
case Soft_Line_Break:
|
||||
// fmt.println();
|
||||
case Hard_Line_Break:
|
||||
fmt.println("<br>");
|
||||
case Code_Span:
|
||||
fmt.print("<code>");
|
||||
write_espaced(n.content);
|
||||
fmt.print("</code>");
|
||||
}
|
||||
}
|
||||
|
||||
print_node_as_html :: proc(node: ^Node) {
|
||||
using Node;
|
||||
match n in node {
|
||||
case Header:
|
||||
fmt.printf("<h%d>", n.level);
|
||||
print_inline_as_html(n.inline_content);
|
||||
fmt.printf("</h%d>\n", n.level);
|
||||
case Paragraph:
|
||||
fmt.print("<p>");
|
||||
print_inline_as_html(n.inline_content);
|
||||
fmt.println("</p>");
|
||||
case Horizontal_Rule:
|
||||
fmt.println("<hr>");
|
||||
case Code_Block:
|
||||
if n.language != "" {
|
||||
fmt.printf("<pre><code class=\"language-%s\">", n.language);
|
||||
} else {
|
||||
fmt.print("<pre><code>");
|
||||
}
|
||||
fmt.print(cast(string)n.content);
|
||||
fmt.println("</code></pre>");
|
||||
case Quote:
|
||||
}
|
||||
}
|
||||
|
||||
for _, i in nodes {
|
||||
print_node_as_html(^nodes[i]);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1014,6 +1014,50 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) {
|
||||
return tuple;
|
||||
}
|
||||
|
||||
Type *type_to_abi_compat_param_type(gbAllocator a, Type *original_type) {
|
||||
Type *new_type = original_type;
|
||||
// NOTE(bill): Changing the passing parameter value type is to match C's ABI
|
||||
// IMPORTANT TODO(bill): This only matches the ABI on MSVC at the moment
|
||||
Type *bt = core_type(original_type);
|
||||
switch (bt->kind) {
|
||||
// Okay to pass by value
|
||||
// Especially the only Odin types
|
||||
case Type_Basic: break;
|
||||
case Type_Pointer: break;
|
||||
case Type_Proc: break; // NOTE(bill): Just a pointer
|
||||
|
||||
// Odin only types
|
||||
case Type_Slice:
|
||||
case Type_DynamicArray:
|
||||
case Type_Map:
|
||||
break;
|
||||
|
||||
// Odin specific
|
||||
case Type_Array:
|
||||
case Type_Vector:
|
||||
// Could be in C too
|
||||
case Type_Record: {
|
||||
i64 size = type_size_of(a, original_type);
|
||||
switch (8*size) {
|
||||
case 8: new_type = t_u8; break;
|
||||
case 16: new_type = t_u16; break;
|
||||
case 32: new_type = t_u32; break;
|
||||
case 64: new_type = t_u64; break;
|
||||
default:
|
||||
// NOTE(bill): It could be an empty struct that is passed
|
||||
// and if that is the case, no need to pass by pointer
|
||||
// (I think..)
|
||||
if (size > 0) {
|
||||
new_type = make_type_pointer(a, original_type);
|
||||
}
|
||||
break;
|
||||
}
|
||||
} break;
|
||||
}
|
||||
|
||||
return new_type;
|
||||
}
|
||||
|
||||
|
||||
void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
||||
ast_node(pt, ProcType, proc_type_node);
|
||||
@@ -1034,6 +1078,20 @@ void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
|
||||
type->Proc.result_count = result_count;
|
||||
type->Proc.variadic = variadic;
|
||||
type->Proc.calling_convention = pt->calling_convention;
|
||||
|
||||
|
||||
type->Proc.abi_compat_params = gb_alloc_array(c->allocator, Type *, param_count);
|
||||
for (isize i = 0; i < param_count; i++) {
|
||||
Type *original_type = type->Proc.params->Tuple.variables[i]->type;
|
||||
Type *new_type = type_to_abi_compat_param_type(c->allocator, original_type);
|
||||
type->Proc.abi_compat_params[i] = new_type;
|
||||
}
|
||||
|
||||
// NOTE(bill): The types are the same
|
||||
type->Proc.abi_compat_results = gb_alloc_array(c->allocator, Type *, result_count);
|
||||
for (isize i = 0; i < result_count; i++) {
|
||||
type->Proc.abi_compat_results[i] = type->Proc.results->Tuple.variables[i]->type;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
101
src/ir.c
101
src/ir.c
@@ -351,11 +351,20 @@ typedef struct irValueGlobal {
|
||||
bool is_unnamed_addr;
|
||||
} irValueGlobal;
|
||||
|
||||
|
||||
typedef enum irParamPasskind {
|
||||
irParamPass_Value, // Pass by value
|
||||
irParamPass_Pointer, // Pass as a pointer rather than by value
|
||||
irParamPass_Integer, // Pass as an integer of the same size
|
||||
} irParamPasskind;
|
||||
|
||||
typedef struct irValueParam {
|
||||
irProcedure *parent;
|
||||
Entity * entity;
|
||||
Type * type;
|
||||
irValueArray referrers;
|
||||
irParamPasskind kind;
|
||||
irProcedure * parent;
|
||||
Entity * entity;
|
||||
Type * type;
|
||||
Type * original_type;
|
||||
irValueArray referrers;
|
||||
} irValueParam;
|
||||
|
||||
typedef struct irValue {
|
||||
@@ -755,11 +764,23 @@ irValue *ir_value_global(gbAllocator a, Entity *e, irValue *value) {
|
||||
array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
|
||||
return v;
|
||||
}
|
||||
irValue *ir_value_param(gbAllocator a, irProcedure *parent, Entity *e) {
|
||||
irValue *ir_value_param(gbAllocator a, irProcedure *parent, Entity *e, Type *abi_type) {
|
||||
irValue *v = ir_alloc_value(a, irValue_Param);
|
||||
v->Param.parent = parent;
|
||||
v->Param.entity = e;
|
||||
v->Param.type = e->type;
|
||||
v->Param.kind = irParamPass_Value;
|
||||
v->Param.parent = parent;
|
||||
v->Param.entity = e;
|
||||
v->Param.original_type = e->type;
|
||||
v->Param.type = abi_type;
|
||||
|
||||
if (abi_type != e->type) {
|
||||
if (is_type_pointer(abi_type)) {
|
||||
v->Param.kind = irParamPass_Pointer;
|
||||
} else if (is_type_integer(abi_type)) {
|
||||
v->Param.kind = irParamPass_Integer;
|
||||
} else {
|
||||
GB_PANIC("Invalid abi type pass kind");
|
||||
}
|
||||
}
|
||||
array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
|
||||
return v;
|
||||
}
|
||||
@@ -1280,16 +1301,30 @@ irValue *ir_add_local_generated(irProcedure *proc, Type *type) {
|
||||
}
|
||||
|
||||
|
||||
irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr) {
|
||||
irValue *v = ir_value_param(proc->module->allocator, proc, e);
|
||||
#if 1
|
||||
irValue *l = ir_add_local(proc, e, expr);
|
||||
ir_emit_store(proc, l, v);
|
||||
irValue *ir_add_param(irProcedure *proc, Entity *e, AstNode *expr, Type *abi_type) {
|
||||
irValue *v = ir_value_param(proc->module->allocator, proc, e, abi_type);
|
||||
irValueParam *p = &v->Param;
|
||||
|
||||
#else
|
||||
ir_module_add_value(proc->module, e, v);
|
||||
#endif
|
||||
return v;
|
||||
switch (p->kind) {
|
||||
case irParamPass_Value: {
|
||||
irValue *l = ir_add_local(proc, e, expr);
|
||||
ir_emit_store(proc, l, v);
|
||||
return v;
|
||||
}
|
||||
case irParamPass_Pointer: {
|
||||
ir_module_add_value(proc->module, e, v);
|
||||
return ir_emit_load(proc, v);
|
||||
}
|
||||
case irParamPass_Integer: {
|
||||
irValue *l = ir_add_local(proc, e, expr);
|
||||
irValue *iptr = ir_emit_conv(proc, l, make_type_pointer(proc->module->allocator, p->type));
|
||||
ir_emit_store(proc, iptr, v);
|
||||
return ir_emit_load(proc, l);
|
||||
}
|
||||
}
|
||||
|
||||
GB_PANIC("Unreachable");
|
||||
return NULL;
|
||||
}
|
||||
|
||||
|
||||
@@ -1383,11 +1418,36 @@ irValue *ir_emit_comment(irProcedure *p, String text) {
|
||||
return ir_emit(p, ir_instr_comment(p, text));
|
||||
}
|
||||
|
||||
irValue *ir_copy_value_to_ptr(irProcedure *proc, irValue *val) {
|
||||
Type *t = ir_type(val);
|
||||
irValue *ptr = ir_add_local_generated(proc, t);
|
||||
ir_emit_store(proc, ptr, val);
|
||||
return ptr;
|
||||
}
|
||||
|
||||
irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) {
|
||||
return ir_emit(proc, ir_instr_conv(proc, irConv_bitcast, data, ir_type(data), type));
|
||||
}
|
||||
|
||||
irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) {
|
||||
Type *pt = base_type(ir_type(value));
|
||||
GB_ASSERT(pt->kind == Type_Proc);
|
||||
Type *results = pt->Proc.results;
|
||||
|
||||
isize param_count = pt->Proc.param_count;
|
||||
GB_ASSERT(param_count == arg_count);
|
||||
for (isize i = 0; i < param_count; i++) {
|
||||
Type *original_type = pt->Proc.params->Tuple.variables[i]->type;
|
||||
Type *new_type = pt->Proc.abi_compat_params[i];
|
||||
if (original_type != new_type) {
|
||||
if (is_type_pointer(new_type)) {
|
||||
args[i] = ir_copy_value_to_ptr(p, args[i]);
|
||||
} else if (is_type_integer(new_type)) {
|
||||
args[i] = ir_emit_bitcast(p, args[i], new_type);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return ir_emit(p, ir_instr_call(p, value, args, arg_count, results));
|
||||
}
|
||||
|
||||
@@ -1475,9 +1535,7 @@ void ir_emit_startup_runtime(irProcedure *proc) {
|
||||
ir_emit(proc, ir_alloc_instr(proc, irInstr_StartupRuntime));
|
||||
}
|
||||
|
||||
irValue *ir_emit_bitcast(irProcedure *proc, irValue *data, Type *type) {
|
||||
return ir_emit(proc, ir_instr_conv(proc, irConv_bitcast, data, ir_type(data), type));
|
||||
}
|
||||
|
||||
|
||||
|
||||
irValue *ir_emit_struct_ep(irProcedure *proc, irValue *s, i32 index);
|
||||
@@ -6436,9 +6494,10 @@ void ir_begin_procedure_body(irProcedure *proc) {
|
||||
AstNode *name = field->names.e[q_index++];
|
||||
|
||||
Entity *e = params->variables[i];
|
||||
Type *abi_type = proc->type->Proc.abi_compat_params[i];
|
||||
if (!str_eq(e->token.string, str_lit("")) &&
|
||||
!str_eq(e->token.string, str_lit("_"))) {
|
||||
irValue *param = ir_add_param(proc, e, name);
|
||||
irValue *param = ir_add_param(proc, e, name, abi_type);
|
||||
array_add(&proc->params, param);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -135,6 +135,28 @@ void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) {
|
||||
ir_print_escape_string(f, name, true);
|
||||
}
|
||||
|
||||
void ir_print_type(irFileBuffer *f, irModule *m, Type *t);
|
||||
|
||||
void ir_print_proc_results(irFileBuffer *f, irModule *m, Type *t) {
|
||||
GB_ASSERT(is_type_proc(t));
|
||||
t = base_type(t);
|
||||
isize result_count = t->Proc.result_count;
|
||||
if (result_count == 0) {
|
||||
ir_fprintf(f, "void");
|
||||
} else if (result_count == 1) {
|
||||
ir_print_type(f, m, t->Proc.abi_compat_results[0]);
|
||||
} else {
|
||||
ir_fprintf(f, "{");
|
||||
for (isize i = 0; i < result_count; i++) {
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_print_type(f, m, t->Proc.abi_compat_results[i]);
|
||||
}
|
||||
ir_fprintf(f, "}");
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
i64 word_bits = 8*build_context.word_size;
|
||||
@@ -273,18 +295,15 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
|
||||
}
|
||||
return;
|
||||
case Type_Proc: {
|
||||
if (t->Proc.result_count == 0) {
|
||||
ir_fprintf(f, "void");
|
||||
} else {
|
||||
ir_print_type(f, m, t->Proc.results);
|
||||
}
|
||||
isize param_count = t->Proc.param_count;
|
||||
isize result_count = t->Proc.result_count;
|
||||
ir_print_proc_results(f, m, t);
|
||||
ir_fprintf(f, " (");
|
||||
TypeTuple *params = &t->Proc.params->Tuple;
|
||||
for (isize i = 0; i < t->Proc.param_count; i++) {
|
||||
for (isize i = 0; i < param_count; i++) {
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_print_type(f, m, params->variables[i]->type);
|
||||
ir_print_type(f, m, t->Proc.abi_compat_params[i]);
|
||||
}
|
||||
ir_fprintf(f, ")*");
|
||||
} return;
|
||||
@@ -1176,7 +1195,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
ir_fprintf(f, "call ");
|
||||
ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
|
||||
if (result_type) {
|
||||
ir_print_type(f, m, result_type);
|
||||
ir_print_proc_results(f, m, proc_type);
|
||||
} else {
|
||||
ir_fprintf(f, "void");
|
||||
}
|
||||
@@ -1192,7 +1211,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
|
||||
for (isize i = 0; i < call->arg_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
GB_ASSERT(e != NULL);
|
||||
Type *t = e->type;
|
||||
Type *t = proc_type->Proc.abi_compat_params[i];
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
@@ -1405,12 +1424,9 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
||||
|
||||
ir_print_calling_convention(f, m, proc_type->calling_convention);
|
||||
|
||||
if (proc_type->result_count == 0) {
|
||||
ir_fprintf(f, "void");
|
||||
} else {
|
||||
ir_print_type(f, m, proc_type->results);
|
||||
}
|
||||
|
||||
isize param_count = proc_type->param_count;
|
||||
isize result_count = proc_type->result_count;
|
||||
ir_print_proc_results(f, m, proc->type);
|
||||
ir_fprintf(f, " ");
|
||||
|
||||
// #ifndef GB_SYSTEM_WINDOWS
|
||||
@@ -1423,14 +1439,16 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
|
||||
|
||||
ir_fprintf(f, "(");
|
||||
|
||||
if (proc_type->param_count > 0) {
|
||||
if (param_count > 0) {
|
||||
TypeTuple *params = &proc_type->params->Tuple;
|
||||
for (isize i = 0; i < params->variable_count; i++) {
|
||||
Entity *e = params->variables[i];
|
||||
Type *original_type = e->type;
|
||||
Type *abi_type = proc_type->abi_compat_params[i];
|
||||
if (i > 0) {
|
||||
ir_fprintf(f, ", ");
|
||||
}
|
||||
ir_print_type(f, m, e->type);
|
||||
ir_print_type(f, m, abi_type);
|
||||
if (e->flags&EntityFlag_NoAlias) {
|
||||
ir_fprintf(f, " noalias");
|
||||
}
|
||||
|
||||
@@ -132,6 +132,8 @@ typedef struct TypeRecord {
|
||||
Type * results; /* Type_Tuple */ \
|
||||
i32 param_count; \
|
||||
i32 result_count; \
|
||||
Type **abi_compat_params; \
|
||||
Type **abi_compat_results; \
|
||||
bool variadic; \
|
||||
ProcCallingConvention calling_convention; \
|
||||
}) \
|
||||
|
||||
Reference in New Issue
Block a user