فهرست منبع

IR emit C ABI compatible types for calling conventions (Only for x86/amd64 like processors at the moment)

Ginger Bill 8 سال پیش
والد
کامیت
abb9930725
5فایلهای تغییر یافته به همراه819 افزوده شده و 49 حذف شده
  1. 643 10
      code/demo.odin
  2. 58 0
      src/check_expr.c
  3. 80 21
      src/ir.c
  4. 36 18
      src/ir_print.c
  5. 2 0
      src/types.c

+ 643 - 10
code/demo.odin

@@ -1,18 +1,651 @@
 #import "fmt.odin";
 #import "fmt.odin";
+#import "os.odin";
+#import "strconv.odin";
+#import "utf8.odin";
+
+Error :: enum {
+	NONE,
+}
+
+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);
+		}
+	}
+
+
+	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{
+	'"' = "&quot;",
+	'&' = "&amp;",
+	'<' = "&lt;",
+	'>' = "&gt;",
+};
+
 
 
 main :: proc() {
 main :: proc() {
-	immutable program := "+ + * - /";
-	accumulator := 0;
+	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..]);
+	}
 
 
-	for token in program {
-		match token {
-		case '+': accumulator += 1;
-		case '-': accumulator -= 1;
-		case '*': accumulator *= 2;
-		case '/': accumulator /= 2;
-		default: // Ignore everything else
+	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>");
 		}
 		}
 	}
 	}
 
 
-	fmt.printf("The program \"%s\" calculates the value %d\n", program, accumulator);
+	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]);
+	}
 }
 }

+ 58 - 0
src/check_expr.c

@@ -1014,6 +1014,50 @@ Type *check_get_results(Checker *c, Scope *scope, AstNode *_results) {
 	return tuple;
 	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) {
 void check_procedure_type(Checker *c, Type *type, AstNode *proc_type_node) {
 	ast_node(pt, ProcType, 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.result_count       = result_count;
 	type->Proc.variadic           = variadic;
 	type->Proc.variadic           = variadic;
 	type->Proc.calling_convention = pt->calling_convention;
 	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;
+	}
 }
 }
 
 
 
 

+ 80 - 21
src/ir.c

@@ -351,11 +351,20 @@ typedef struct irValueGlobal {
 	bool          is_unnamed_addr;
 	bool          is_unnamed_addr;
 } irValueGlobal;
 } 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 {
 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;
 } irValueParam;
 
 
 typedef struct irValue {
 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
 	array_init(&v->Global.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
 	return v;
 	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);
 	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
 	array_init(&v->Param.referrers, heap_allocator()); // TODO(bill): Replace heap allocator here
 	return v;
 	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));
 	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) {
 irValue *ir_emit_call(irProcedure *p, irValue *value, irValue **args, isize arg_count) {
 	Type *pt = base_type(ir_type(value));
 	Type *pt = base_type(ir_type(value));
 	GB_ASSERT(pt->kind == Type_Proc);
 	GB_ASSERT(pt->kind == Type_Proc);
 	Type *results = pt->Proc.results;
 	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));
 	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));
 	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);
 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++];
 			AstNode *name = field->names.e[q_index++];
 
 
 			Entity *e = params->variables[i];
 			Entity *e = params->variables[i];
+			Type *abi_type = proc->type->Proc.abi_compat_params[i];
 			if (!str_eq(e->token.string, str_lit("")) &&
 			if (!str_eq(e->token.string, str_lit("")) &&
 			    !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);
 				array_add(&proc->params, param);
 			}
 			}
 		}
 		}

+ 36 - 18
src/ir_print.c

@@ -135,6 +135,28 @@ void ir_print_encoded_global(irFileBuffer *f, String name, bool remove_prefix) {
 	ir_print_escape_string(f, name, true);
 	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) {
 void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 	i64 word_bits = 8*build_context.word_size;
 	i64 word_bits = 8*build_context.word_size;
@@ -273,18 +295,15 @@ void ir_print_type(irFileBuffer *f, irModule *m, Type *t) {
 		}
 		}
 		return;
 		return;
 	case Type_Proc: {
 	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, " (");
 		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) {
 			if (i > 0) {
 				ir_fprintf(f, ", ");
 				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, ")*");
 		ir_fprintf(f, ")*");
 	} return;
 	} return;
@@ -1176,7 +1195,7 @@ void ir_print_instr(irFileBuffer *f, irModule *m, irValue *value) {
 		ir_fprintf(f, "call ");
 		ir_fprintf(f, "call ");
 		ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
 		ir_print_calling_convention(f, m, proc_type->Proc.calling_convention);
 		if (result_type) {
 		if (result_type) {
-			ir_print_type(f, m, result_type);
+			ir_print_proc_results(f, m, proc_type);
 		} else {
 		} else {
 			ir_fprintf(f, "void");
 			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++) {
 			for (isize i = 0; i < call->arg_count; i++) {
 				Entity *e = params->variables[i];
 				Entity *e = params->variables[i];
 				GB_ASSERT(e != NULL);
 				GB_ASSERT(e != NULL);
-				Type *t = e->type;
+				Type *t = proc_type->Proc.abi_compat_params[i];
 				if (i > 0) {
 				if (i > 0) {
 					ir_fprintf(f, ", ");
 					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);
 	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, " ");
 	ir_fprintf(f, " ");
 
 
 // #ifndef GB_SYSTEM_WINDOWS
 // #ifndef GB_SYSTEM_WINDOWS
@@ -1423,14 +1439,16 @@ void ir_print_proc(irFileBuffer *f, irModule *m, irProcedure *proc) {
 
 
 	ir_fprintf(f, "(");
 	ir_fprintf(f, "(");
 
 
-	if (proc_type->param_count > 0) {
+	if (param_count > 0) {
 		TypeTuple *params = &proc_type->params->Tuple;
 		TypeTuple *params = &proc_type->params->Tuple;
 		for (isize i = 0; i < params->variable_count; i++) {
 		for (isize i = 0; i < params->variable_count; i++) {
 			Entity *e = params->variables[i];
 			Entity *e = params->variables[i];
+			Type *original_type = e->type;
+			Type *abi_type = proc_type->abi_compat_params[i];
 			if (i > 0) {
 			if (i > 0) {
 				ir_fprintf(f, ", ");
 				ir_fprintf(f, ", ");
 			}
 			}
-			ir_print_type(f, m, e->type);
+			ir_print_type(f, m, abi_type);
 			if (e->flags&EntityFlag_NoAlias) {
 			if (e->flags&EntityFlag_NoAlias) {
 				ir_fprintf(f, " noalias");
 				ir_fprintf(f, " noalias");
 			}
 			}

+ 2 - 0
src/types.c

@@ -132,6 +132,8 @@ typedef struct TypeRecord {
 		Type * results; /* Type_Tuple */                  \
 		Type * results; /* Type_Tuple */                  \
 		i32    param_count;                               \
 		i32    param_count;                               \
 		i32    result_count;                              \
 		i32    result_count;                              \
+		Type **abi_compat_params;                         \
+		Type **abi_compat_results;                        \
 		bool   variadic;                                  \
 		bool   variadic;                                  \
 		ProcCallingConvention calling_convention;         \
 		ProcCallingConvention calling_convention;         \
 	})                                                    \
 	})                                                    \