mirror of
https://github.com/c3lang/c3c.git
synced 2026-02-27 12:01:16 +00:00
Moved examples around. Updated (and corrected) const initialization. Removed "in" keyword. Added "member" attribute domain. Many fixes in struct padding and alignment and tests. Fixed extern global.
This commit is contained in:
committed by
Christoffer Lerno
parent
564c93700e
commit
3a24fbfa6d
@@ -1,7 +1,13 @@
|
||||
module base64;
|
||||
// Based on the C2 version.
|
||||
|
||||
const char[] LUT_ENC = {
|
||||
error InvalidCharacter
|
||||
{
|
||||
int index;
|
||||
char c;
|
||||
}
|
||||
|
||||
const char[64] LUT_ENC = {
|
||||
'A', 'B', 'C', 'D', 'E', 'F', 'G', 'H',
|
||||
'I', 'J', 'K', 'L', 'M', 'N', 'O', 'P',
|
||||
'Q', 'R', 'S', 'T', 'U', 'V', 'W', 'X',
|
||||
@@ -14,27 +20,22 @@ const char[] LUT_ENC = {
|
||||
|
||||
const byte ERR = 0xFF;
|
||||
|
||||
const byte[256] LUT_DEC = {
|
||||
// '+', ',', '-', '.', '/', '0', '1', '2'
|
||||
62, ERR, ERR, ERR, 63, 52, 53, 54,
|
||||
// '3', '4', '5', '6', '7', '8', '9', ':'
|
||||
55, 56, 57, 58, 59, 60, 61, ERR,
|
||||
// ';', '<', '=', '>', '?', '@', 'A', 'B'
|
||||
ERR, ERR, ERR, ERR, ERR, ERR, 0, 1,
|
||||
// 'C', 'D', 'E', 'F', 'G', 'H', 'I', 'J'
|
||||
2, 3, 4, 5, 6, 7, 8, 9,
|
||||
// 'K', 'L', 'M', 'N', 'O', 'P', 'Q', 'R'
|
||||
10, 11, 12, 13, 14, 15, 16, 17,
|
||||
// 'S', 'T', 'U', 'V', 'W', 'X', 'Y', 'Z'
|
||||
18, 19, 20, 21, 22, 23, 24, 25,
|
||||
// '[', '\', ']', '^', '_', '`', 'a', 'b'
|
||||
ERR, ERR, ERR, ERR, ERR, ERR, 26, 27,
|
||||
// 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j'
|
||||
28, 29, 30, 31, 32, 33, 34, 35,
|
||||
// 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r'
|
||||
36, 37, 38, 39, 40, 41, 42, 43,
|
||||
// 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'
|
||||
44, 45, 46, 47, 48, 49, 50, 51,
|
||||
const byte[256] LUT_DEC =
|
||||
{
|
||||
[0..255] = ERR,
|
||||
['A'] = 0, ['B'] = 1, ['C'] = 2, ['D'] = 3, ['E'] = 4,
|
||||
['F'] = 5, ['G'] = 6, ['H'] = 7, ['I'] = 8, ['J'] = 9,
|
||||
['K'] = 10, ['L'] = 11, ['M'] = 12, ['N'] = 13, ['O'] = 14,
|
||||
['P'] = 15, ['Q'] = 16, ['R'] = 17, ['S'] = 18, ['T'] = 19,
|
||||
['U'] = 20, ['V'] = 21, ['W'] = 22, ['X'] = 23, ['Y'] = 24,
|
||||
['Z'] = 25, ['a'] = 26, ['b'] = 27, ['c'] = 28, ['d'] = 29,
|
||||
['e'] = 30, ['f'] = 31, ['g'] = 32, ['h'] = 33, ['i'] = 34,
|
||||
['j'] = 35, ['k'] = 36, ['l'] = 37, ['m'] = 38, ['n'] = 39,
|
||||
['o'] = 40, ['p'] = 41, ['q'] = 42, ['r'] = 43, ['s'] = 44,
|
||||
['t'] = 45, ['u'] = 46, ['v'] = 47, ['w'] = 48, ['x'] = 49,
|
||||
['y'] = 50, ['z'] = 51, ['0'] = 52, ['1'] = 53, ['2'] = 54,
|
||||
['3'] = 55, ['4'] = 56, ['5'] = 57, ['6'] = 58, ['7'] = 59,
|
||||
['8'] = 60, ['9'] = 61, ['+'] = 62, ['/'] = 63
|
||||
};
|
||||
|
||||
|
||||
@@ -42,33 +43,26 @@ const char PAD = '=';
|
||||
const char FIRST = '+';
|
||||
const char LAST = 'z';
|
||||
|
||||
public error Base64Error
|
||||
{
|
||||
INVALID_CHARACTER
|
||||
}
|
||||
|
||||
public func void encode(byte[] in, string *out)
|
||||
public func void encode(byte[] in, char *out)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < in.len; i++);
|
||||
char c = LUT_ENC[1];
|
||||
for (int i = 0; i < in.len(); i++)
|
||||
{
|
||||
switch (i % 3)
|
||||
{
|
||||
case 0:
|
||||
out[j++] = LUT_ENC[(in[i] >> 2) & 0x3F];
|
||||
case 1:
|
||||
out[j++] = LUT_ENC[(in[i-1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)];
|
||||
out[j++] = LUT_ENC[(in[i - 1] & 0x3) << 4 + ((in[i] >> 4) & 0xF)];
|
||||
case 2:
|
||||
out[j++] = LUT_ENC[(in[i-1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)];
|
||||
out[j++] = LUT_ENC[(in[i - 1] & 0xF) << 2 + ((in[i] >> 6) & 0x3)];
|
||||
out[j++] = LUT_ENC[in[i] & 0x3F];
|
||||
}
|
||||
i++;
|
||||
}
|
||||
|
||||
// move back
|
||||
int last = in.len - 1;
|
||||
|
||||
usize last = in.len() - 1;
|
||||
// check the last and add padding
|
||||
switch (last % 3)
|
||||
{
|
||||
@@ -82,46 +76,54 @@ public func void encode(byte[] in, string *out)
|
||||
}
|
||||
}
|
||||
|
||||
error InvalidCharacter
|
||||
{
|
||||
int index;
|
||||
char c;
|
||||
}
|
||||
|
||||
public func int! decode(string in, byte[] out)
|
||||
public func int! decode(char[] in, byte* out)
|
||||
{
|
||||
int j = 0;
|
||||
|
||||
for (int i = 0; i < len; i++)
|
||||
for (int i = 0; i < in.len(); i++)
|
||||
{
|
||||
char value = in[i];
|
||||
|
||||
if (value == PAD) return j;
|
||||
|
||||
if (value < FIRST || in[i] > LAST) return! InvalidCharacter(i, value);
|
||||
byte c = LUT_DEC[in[i] - FIRST);
|
||||
if (c == ERR) return! InvalidCharacter(i, value);
|
||||
byte c = LUT_DEC[in[i]];
|
||||
if (c == ERR) return InvalidCharacter({i, value})!;
|
||||
|
||||
switch (i % 4)
|
||||
{
|
||||
case 0:
|
||||
out[j] = c << 2;
|
||||
case 1:
|
||||
out[j++] += (c >> 4) & 0x3;
|
||||
out[j++] += c >> 4 & 0x3;
|
||||
// if not last char with padding
|
||||
if (i < (len - 3) || in[len - 2] != PAD)
|
||||
if (i < (in.len() - 3) || in[cast(in.len() as long) - 2] != PAD)
|
||||
{
|
||||
out[j] = (c & 0xF) << 4;
|
||||
}
|
||||
case 2:
|
||||
out[j++] += (c >> 2) & 0xF;
|
||||
if (i < (len -2) || in[len -1] != PAD)
|
||||
out[j++] += c >> 2 & 0xF;
|
||||
if (i < (in.len() - 2) || in[cast(in.len() as long) - 1] != PAD)
|
||||
{
|
||||
out[j] = (c & 0x3) << 6;
|
||||
}
|
||||
case 3:
|
||||
out[j++] += c;
|
||||
}
|
||||
|
||||
}
|
||||
return j;
|
||||
}
|
||||
|
||||
extern func void printf(char *fmt, ...);
|
||||
public func void main()
|
||||
{
|
||||
printf("Startin...\n");
|
||||
char *helloworld = "Hello World\n";
|
||||
char[1000] buffer;
|
||||
encode(cast(helloworld as byte*)[0..12], &buffer);
|
||||
printf("Printres\n");
|
||||
printf("Result: %s\n", &buffer);
|
||||
char *to_decode = "aGVsbG8gd29ybGRcMA==";
|
||||
decode(to_decode[0..19], cast(&buffer as byte*));
|
||||
printf("2Result: %s\n", &buffer);
|
||||
}
|
||||
36
resources/examples/notworking/globals.c3
Normal file
36
resources/examples/notworking/globals.c3
Normal file
@@ -0,0 +1,36 @@
|
||||
module globals;
|
||||
|
||||
|
||||
const string CLICK_ME = "Click Me";
|
||||
var uint counter = 0;
|
||||
|
||||
func void clickedme(GtkButton *o, void *d)
|
||||
{
|
||||
cast(d as GtkLabel*).set_text(string.format("You clicked me %d times" as ++counter));
|
||||
}
|
||||
|
||||
int main(int argc as string[] argv)
|
||||
{
|
||||
gtk::init(&argc, &argv);
|
||||
|
||||
GtkWindow *win = gtk::windowCreate(GtkWindow::TOPLEVEL);
|
||||
win.set_title(CLICK_ME);
|
||||
|
||||
GtkButton *button = gtk::buttonCreateWithLabel(CLICK_ME);
|
||||
|
||||
GtkLabel *label = GtkLabel.new("There have been no clicks yet");
|
||||
label.setSingleLineMode(true);
|
||||
|
||||
GtkVBox vbox = gtk::vBoxCreate(true, 1);
|
||||
vbox.add(label);
|
||||
vbox.add(button);
|
||||
|
||||
win.add(vbox);
|
||||
|
||||
win.connectSignal("delete-event", gtk::mainQuit, NULL);
|
||||
button.connectSignal("clicked", &clickedme, label);
|
||||
|
||||
win.showAll();
|
||||
gtk::main();
|
||||
return 0;
|
||||
}
|
||||
@@ -37,6 +37,7 @@ public func uint crc64(byte[*] data)
|
||||
return ~result;
|
||||
}
|
||||
|
||||
|
||||
public func uint fnv32(byte[*] data)
|
||||
{
|
||||
uint h = 0x811c9dc5;
|
||||
19
resources/examples/notworking/retry.c3
Normal file
19
resources/examples/notworking/retry.c3
Normal file
@@ -0,0 +1,19 @@
|
||||
module test;
|
||||
|
||||
|
||||
public macro retry(#function, int retries = 3)
|
||||
{
|
||||
error e;
|
||||
while (1)
|
||||
{
|
||||
auto! result = #function;
|
||||
try (result) return result;
|
||||
catch (e = result);
|
||||
} while (retries-- > 0)
|
||||
return e!;
|
||||
}
|
||||
|
||||
func void main()
|
||||
{
|
||||
int! result = @retry(eventually_succeed());
|
||||
}
|
||||
21
resources/examples/notworking/swap.c3
Normal file
21
resources/examples/notworking/swap.c3
Normal file
@@ -0,0 +1,21 @@
|
||||
public test;
|
||||
|
||||
/**
|
||||
* @require parse(a = b), parse(b = a)
|
||||
*/
|
||||
public macro void swap(&a, &b)
|
||||
{
|
||||
typeof(a) temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
|
||||
/**
|
||||
* @require parse(a = b), parse(b = a)
|
||||
*/
|
||||
public macro void swap2(auto &a, auto &b)
|
||||
{
|
||||
auto temp = a;
|
||||
a = b;
|
||||
b = temp;
|
||||
}
|
||||
13
resources/examples/notworking/timeit.c3
Normal file
13
resources/examples/notworking/timeit.c3
Normal file
@@ -0,0 +1,13 @@
|
||||
module test;
|
||||
import std::time;
|
||||
import std::io;
|
||||
|
||||
public macro timeit(#call)
|
||||
{
|
||||
Time t = time::current();
|
||||
typeof(#call) result = #call;
|
||||
TimeDiff diff = time::current() - t;
|
||||
io::printf("'%s' took %f ms\n", $stringify(#call), diff * 1000);
|
||||
return result;
|
||||
}
|
||||
|
||||
51
resources/examples/notworking/virtuals.c3
Normal file
51
resources/examples/notworking/virtuals.c3
Normal file
@@ -0,0 +1,51 @@
|
||||
module comparable;
|
||||
import std::math;
|
||||
|
||||
interface Geometry
|
||||
{
|
||||
func double area();
|
||||
func double perim();
|
||||
}
|
||||
|
||||
struct Rect
|
||||
{
|
||||
double width, height;
|
||||
}
|
||||
|
||||
struct Circle
|
||||
{
|
||||
double radius;
|
||||
}
|
||||
|
||||
func double Rect.area(Rect *r)
|
||||
{
|
||||
return r.width * r.height;
|
||||
}
|
||||
|
||||
func double Rect.perim(Rect *r)
|
||||
{
|
||||
return 2 * r.width + 2 * r.height;
|
||||
}
|
||||
|
||||
func double Circle.area(Circle *c)
|
||||
{
|
||||
return math::PI * c.radius * c.radius
|
||||
}
|
||||
|
||||
func double Circle.perim(Circle *c)
|
||||
{
|
||||
return math::PI * c.radius * 2;
|
||||
}
|
||||
|
||||
func void measure(virtual Geometry g)
|
||||
{
|
||||
printf("area: %f, perimeter: %f\n", g.area(), g.perim());
|
||||
}
|
||||
|
||||
func void main()
|
||||
{
|
||||
Rect r = { 3, 4 };
|
||||
Circle c = { 5 };
|
||||
measure(&r);
|
||||
measure(&c);
|
||||
}
|
||||
17
resources/examples/notworking/virtuals2.c3
Normal file
17
resources/examples/notworking/virtuals2.c3
Normal file
@@ -0,0 +1,17 @@
|
||||
module std::io;
|
||||
|
||||
interface File : Closable, Readable, Seekable
|
||||
{
|
||||
FileInfo[]! readdir(int count);
|
||||
FileInfo! stat();
|
||||
}
|
||||
|
||||
|
||||
interface File
|
||||
{
|
||||
inline Closable;
|
||||
inline Readable;
|
||||
inline Seekable;
|
||||
FileInfo[]! readdir(int count);
|
||||
FileInfo! stat();
|
||||
}
|
||||
9
resources/lib/std/math.c3
Normal file
9
resources/lib/std/math.c3
Normal file
@@ -0,0 +1,9 @@
|
||||
module std::math;
|
||||
|
||||
extern func double _log10(double) @cname("log10");
|
||||
|
||||
public func double log10(double d) @inline
|
||||
{
|
||||
return _log10(d);
|
||||
}
|
||||
|
||||
18
resources/testfragments/structo.c3
Normal file
18
resources/testfragments/structo.c3
Normal file
@@ -0,0 +1,18 @@
|
||||
module struct2;
|
||||
|
||||
struct Blend_Map_Entry
|
||||
{
|
||||
union vals {
|
||||
float[5] colour;
|
||||
double[2] point_Slope;
|
||||
}
|
||||
}
|
||||
|
||||
Blend_Map_Entry a = { .vals = { .colour = { 1, 2, 3, 4, 5 } } };
|
||||
Blend_Map_Entry b = { .vals = { .point_Slope = { 6, 7 } } };
|
||||
Blend_Map_Entry c = { .vals.colour[2] = 1 };
|
||||
Blend_Map_Entry d = { .vals.colour = { 1, 2, 3, 4, 5 } };
|
||||
|
||||
func void test(Blend_Map_Entry* foo)
|
||||
{
|
||||
}
|
||||
@@ -12,7 +12,7 @@ typedef enum
|
||||
} ByVal;
|
||||
|
||||
static inline ABIArgInfo *abi_arg_by_reg_attr(ABIArgInfo *info);
|
||||
ByteSize abi_arg_expanded_size(ABIArgInfo *type_info, Type *type);
|
||||
|
||||
bool abi_arg_is_indirect(ABIArgInfo *info);
|
||||
ABIArgInfo *abi_arg_ignore(void);
|
||||
ABIArgInfo *abi_arg_new_direct_pair(AbiType *low_type, AbiType *high_type);
|
||||
|
||||
@@ -82,6 +82,7 @@ void compiler_compile(BuildTarget *target)
|
||||
vec_add(target->sources, strformat("%s/std/io.c3", compiler.lib_dir));
|
||||
vec_add(target->sources, strformat("%s/std/mem.c3", compiler.lib_dir));
|
||||
vec_add(target->sources, strformat("%s/std/array.c3", compiler.lib_dir));
|
||||
vec_add(target->sources, strformat("%s/std/math.c3", compiler.lib_dir));
|
||||
}
|
||||
VECEACH(target->sources, i)
|
||||
{
|
||||
|
||||
@@ -17,6 +17,7 @@
|
||||
|
||||
typedef uint64_t ByteSize;
|
||||
typedef int64_t ArrayIndex;
|
||||
typedef int64_t IndexDiff;
|
||||
typedef int32_t MemberIndex;
|
||||
typedef int32_t AlignSize;
|
||||
typedef int32_t ScopeId;
|
||||
@@ -269,6 +270,7 @@ typedef struct
|
||||
uint64_t size;
|
||||
Decl **members;
|
||||
MemberIndex union_rep;
|
||||
int16_t padding;
|
||||
} StructDecl;
|
||||
|
||||
|
||||
@@ -442,7 +444,8 @@ typedef struct _Decl
|
||||
const char *cname;
|
||||
AlignSize alignment;
|
||||
const char *section;
|
||||
ArrayIndex offset;
|
||||
ArrayIndex offset : 32;
|
||||
ArrayIndex padding : 32;
|
||||
/* bool is_exported : 1;
|
||||
bool is_used : 1;
|
||||
bool is_used_public : 1;
|
||||
@@ -603,44 +606,39 @@ typedef struct DesignatorElement_
|
||||
typedef enum
|
||||
{
|
||||
CONST_INIT_ZERO,
|
||||
CONST_INIT_EXPANDED,
|
||||
CONST_SELECTED,
|
||||
CONST_VALUE,
|
||||
CONST_INIT_ARRAY_SPLIT,
|
||||
CONST_INIT_ARRAY_RANGE_ZERO,
|
||||
CONST_INIT_ARRAY_VALUE_FRAGMENT
|
||||
CONST_INIT_STRUCT,
|
||||
CONST_INIT_UNION,
|
||||
CONST_INIT_VALUE,
|
||||
CONST_INIT_ARRAY,
|
||||
CONST_INIT_ARRAY_FULL,
|
||||
CONST_INIT_ARRAY_VALUE,
|
||||
} ConstInitType;
|
||||
|
||||
|
||||
typedef struct ConstInitializer_
|
||||
{
|
||||
Type *type;
|
||||
ConstInitType kind;
|
||||
// Type initialized
|
||||
Type *type;
|
||||
union
|
||||
{
|
||||
struct ConstInitializer_ **elements;
|
||||
Expr *value;
|
||||
struct ConstInitializer_ **init_struct;
|
||||
Expr *init_value;
|
||||
struct
|
||||
{
|
||||
struct ConstInitializer_ *element;
|
||||
MemberIndex index;
|
||||
} union_const;
|
||||
} init_union;
|
||||
struct
|
||||
{
|
||||
struct ConstInitializer_ *low;
|
||||
struct ConstInitializer_ *mid;
|
||||
struct ConstInitializer_ *hi;
|
||||
} split_const;
|
||||
struct
|
||||
{
|
||||
ArrayIndex low;
|
||||
ArrayIndex high;
|
||||
} array_range_zero;
|
||||
struct ConstInitializer_ **elements;
|
||||
} init_array;
|
||||
struct ConstInitializer_ **init_array_full;
|
||||
struct
|
||||
{
|
||||
struct ConstInitializer_ *element;
|
||||
ArrayIndex index;
|
||||
} single_array_index;
|
||||
} init_array_value;
|
||||
};
|
||||
} ConstInitializer;
|
||||
|
||||
|
||||
@@ -413,7 +413,6 @@ typedef enum
|
||||
TOKEN_GENERIC,
|
||||
TOKEN_IF,
|
||||
TOKEN_IMPORT,
|
||||
TOKEN_IN,
|
||||
TOKEN_LOCAL,
|
||||
TOKEN_MACRO,
|
||||
TOKEN_MODULE,
|
||||
@@ -564,7 +563,8 @@ typedef enum
|
||||
ATTR_UNION = 1 << 4,
|
||||
ATTR_CONST = 1 << 5,
|
||||
ATTR_ERROR = 1 << 6,
|
||||
ATTR_TYPEDEF = 1 << 7
|
||||
ATTR_TYPEDEF = 1 << 7,
|
||||
ATTR_MEMBER = 1 << 8,
|
||||
} AttributeDomain;
|
||||
|
||||
typedef enum
|
||||
|
||||
@@ -68,87 +68,207 @@ LLVMValueRef llvm_emit_memclear(GenContext *c, BEValue *ref)
|
||||
return llvm_emit_memclear_size_align(c, ref->value, type_size(ref->type), ref->alignment, true);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_const_array_padding(LLVMTypeRef element_type, IndexDiff diff, bool *modified)
|
||||
{
|
||||
if (diff == 1) return LLVMConstNull(element_type);
|
||||
*modified = true;
|
||||
LLVMTypeRef padding_type = LLVMArrayType(element_type, (unsigned)diff);
|
||||
return LLVMConstNull(padding_type);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_const_initializer(GenContext *c, ConstInitializer *const_init, bool *modified)
|
||||
{
|
||||
switch (const_init->kind)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
return LLVMConstNull(llvm_get_type(c, const_init->type));
|
||||
case CONST_VALUE:
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
UNREACHABLE
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
{
|
||||
BEValue val;
|
||||
llvm_emit_expr(c, &val, const_init->value);
|
||||
return llvm_value_rvalue_store(c, &val);
|
||||
}
|
||||
case CONST_INIT_ARRAY_SPLIT:
|
||||
{
|
||||
*modified = true;
|
||||
LLVMValueRef pieces[3];
|
||||
unsigned count = 0;
|
||||
if (const_init->split_const.low)
|
||||
bool was_modified = false;
|
||||
Type *array_type = const_init->type;
|
||||
Type *element_type = array_type->array.base;
|
||||
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
|
||||
ConstInitializer **elements = const_init->init_array_full;
|
||||
ArrayIndex size = array_type->array.len;
|
||||
assert(size > 0);
|
||||
LLVMValueRef *parts = VECNEW(LLVMValueRef, size);
|
||||
for (ArrayIndex i = 0; i < size; i++)
|
||||
{
|
||||
pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.low, modified);
|
||||
vec_add(parts, llvm_emit_const_initializer(c, elements[i], &was_modified));
|
||||
}
|
||||
pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.mid, modified);
|
||||
if (const_init->split_const.hi)
|
||||
if (was_modified) *modified = was_modified;
|
||||
if (was_modified)
|
||||
{
|
||||
pieces[count++] = llvm_emit_const_initializer(c, const_init->split_const.hi, modified);
|
||||
return LLVMConstStructInContext(c->context, parts, vec_size(parts), true);
|
||||
}
|
||||
if (count == 1) return pieces[0];
|
||||
return LLVMConstStructInContext(c->context, pieces, count, true);
|
||||
return LLVMConstArray(element_type_llvm, parts, vec_size(parts));
|
||||
}
|
||||
case CONST_INIT_ARRAY_VALUE_FRAGMENT:
|
||||
*modified = true;
|
||||
return llvm_emit_const_initializer(c, const_init->single_array_index.element, modified);
|
||||
case CONST_SELECTED:
|
||||
|
||||
case CONST_INIT_ARRAY:
|
||||
{
|
||||
Type *member_type = const_init->union_const.element->type;
|
||||
ByteSize union_size = type_size(const_init->type);
|
||||
Type *rep_type = const_init->type->decl->strukt.members[const_init->type->decl->strukt.union_rep]->type;
|
||||
LLVMTypeRef rep_type_llvm = llvm_get_type(c, rep_type);
|
||||
LLVMTypeRef member_type_llvm = llvm_get_type(c, member_type);
|
||||
bool is_modified = rep_type_llvm != member_type_llvm;
|
||||
if (is_modified) *modified = true;
|
||||
bool was_modified = false;
|
||||
Type *array_type = const_init->type;
|
||||
Type *element_type = array_type->array.base;
|
||||
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
|
||||
ConstInitializer **elements = const_init->init_array.elements;
|
||||
unsigned element_count = vec_size(elements);
|
||||
assert(element_count > 0 && "Array should always have gotten at least one element.");
|
||||
ArrayIndex current_index = 0;
|
||||
unsigned alignment = 0;
|
||||
LLVMValueRef *parts = NULL;
|
||||
bool pack = false;
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
ConstInitializer *element = elements[i];
|
||||
assert(element->kind == CONST_INIT_ARRAY_VALUE);
|
||||
ArrayIndex element_index = element->init_array_value.index;
|
||||
IndexDiff diff = element_index - current_index;
|
||||
unsigned curr_alignment = type_abi_alignment(element->type);
|
||||
if (alignment && curr_alignment != alignment)
|
||||
{
|
||||
pack = true;
|
||||
}
|
||||
alignment = curr_alignment;
|
||||
// Add zeroes
|
||||
if (diff > 0)
|
||||
{
|
||||
vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, diff, &was_modified));
|
||||
}
|
||||
vec_add(parts, llvm_emit_const_initializer(c, element->init_array_value.element, &was_modified));
|
||||
current_index = element_index + 1;
|
||||
}
|
||||
|
||||
IndexDiff end_diff = array_type->array.len - current_index;
|
||||
if (end_diff > 0)
|
||||
{
|
||||
vec_add(parts, llvm_emit_const_array_padding(element_type_llvm, end_diff, &was_modified));
|
||||
}
|
||||
if (was_modified) *modified = was_modified;
|
||||
if (was_modified)
|
||||
{
|
||||
return LLVMConstStructInContext(c->context, parts, vec_size(parts), pack);
|
||||
}
|
||||
return LLVMConstArray(element_type_llvm, parts, vec_size(parts));
|
||||
}
|
||||
case CONST_INIT_UNION:
|
||||
{
|
||||
Decl *decl = const_init->type->decl;
|
||||
// Get the type of the member that is used for this particular
|
||||
// constant value, for example if this is a union { double, int }
|
||||
// then the type may be double or int.
|
||||
Type *member_type = decl->strukt.members[const_init->init_union.index]->type->canonical;
|
||||
|
||||
// Emit our value.
|
||||
LLVMValueRef result = llvm_emit_const_initializer(c, const_init->union_const.element, modified);
|
||||
LLVMValueRef result = llvm_emit_const_initializer(c, const_init->init_union.element, modified);
|
||||
|
||||
LLVMValueRef values[2] = {
|
||||
result,
|
||||
NULL
|
||||
};
|
||||
// Get the union value
|
||||
LLVMTypeRef union_type_llvm = llvm_get_type(c, decl->type);
|
||||
|
||||
// There is a case, where we need additional padding for this member, in that case
|
||||
// Create a struct for it.
|
||||
// Take the first type in the union (note that there may be padding!)
|
||||
LLVMTypeRef first_type = LLVMStructGetTypeAtIndex(union_type_llvm, 0);
|
||||
|
||||
// We need to calculate some possible padding.
|
||||
ByteSize union_size = type_size(const_init->type);
|
||||
ByteSize member_size = type_size(member_type);
|
||||
if (union_size > member_size)
|
||||
ByteSize padding = union_size - member_size;
|
||||
|
||||
// Create the resulting values:
|
||||
LLVMValueRef values[2] = { result, NULL };
|
||||
unsigned value_count = 1;
|
||||
|
||||
// Add possible padding as and i8 array.
|
||||
if (padding > 0)
|
||||
{
|
||||
values[1] = llvm_const_padding(c, union_size - member_size);
|
||||
// Now we use an unnamed struct.
|
||||
return LLVMConstStructInContext(c->context, values, 2, const_init->type->decl->is_packed);
|
||||
values[1] = llvm_emit_const_padding(c, padding);
|
||||
value_count = 2;
|
||||
}
|
||||
|
||||
return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), values, 1);
|
||||
// Is this another type than usual for the union?
|
||||
if (first_type != llvm_get_type(c, member_type))
|
||||
{
|
||||
// Yes, so the type needs to be modified.
|
||||
*modified = true;
|
||||
}
|
||||
|
||||
// If it is modified we simply create a packed struct for representation.
|
||||
if (*modified)
|
||||
{
|
||||
return LLVMConstStructInContext(c->context, values, value_count, false);
|
||||
}
|
||||
|
||||
return LLVMConstNamedStruct(union_type_llvm, values, value_count);
|
||||
}
|
||||
case CONST_INIT_EXPANDED:
|
||||
case CONST_INIT_STRUCT:
|
||||
{
|
||||
MemberIndex count = vec_size(const_init->type->decl->strukt.members);
|
||||
LLVMValueRef *entries = MALLOC(sizeof(LLVMValueRef) * count);
|
||||
Decl *decl = const_init->type->decl;
|
||||
Decl **members = decl->strukt.members;
|
||||
MemberIndex count = vec_size(members);
|
||||
LLVMValueRef *entries = NULL;
|
||||
for (MemberIndex i = 0; i < count; i++)
|
||||
{
|
||||
entries[i] = llvm_emit_const_initializer(c, const_init->elements[i], modified);
|
||||
if (members[i]->padding)
|
||||
{
|
||||
vec_add(entries, llvm_emit_const_padding(c, members[i]->padding));
|
||||
}
|
||||
vec_add(entries, llvm_emit_const_initializer(c, const_init->init_struct[i], modified));
|
||||
}
|
||||
if (decl->strukt.padding)
|
||||
{
|
||||
vec_add(entries, llvm_emit_const_padding(c, decl->strukt.padding));
|
||||
}
|
||||
if (*modified)
|
||||
{
|
||||
return LLVMConstStructInContext(c->context, entries, count, const_init->type->decl->is_packed);
|
||||
return LLVMConstStructInContext(c->context, entries, vec_size(entries), decl->is_packed);
|
||||
}
|
||||
return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, count);
|
||||
return LLVMConstNamedStruct(llvm_get_type(c, const_init->type), entries, vec_size(entries));
|
||||
}
|
||||
case CONST_INIT_VALUE:
|
||||
{
|
||||
BEValue value;
|
||||
llvm_emit_expr(c, &value, const_init->init_value);
|
||||
LLVMValueRef llvm_value = llvm_value_rvalue_store(c, &value);
|
||||
LLVMTypeRef expected_type = llvm_get_type(c, const_init->type);
|
||||
if (expected_type != LLVMTypeOf(llvm_value)) *modified = true;
|
||||
return llvm_value;
|
||||
}
|
||||
case CONST_INIT_ARRAY_RANGE_ZERO:
|
||||
return LLVMConstNull(llvm_get_type(c, const_init->type));
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static LLVMValueRef llvm_emit_const_initializer_simple(GenContext *c, Expr *expr, bool *modified)
|
||||
{
|
||||
Expr **elements = expr->initializer_expr.initializer_expr;
|
||||
unsigned element_count = vec_size(elements);
|
||||
LLVMValueRef* values = malloc_arena(element_count * sizeof(LLVMValueRef));
|
||||
Type *expr_type = expr->type->canonical;
|
||||
LLVMTypeRef array_type = expr_type->type_kind == TYPE_ARRAY ? llvm_get_type(c, expr_type->array.base) : NULL;
|
||||
assert(array_type || type_is_structlike(expr_type));
|
||||
for (unsigned i = 0; i < element_count; i++)
|
||||
{
|
||||
BEValue value;
|
||||
llvm_emit_expr(c, &value, elements[i]);
|
||||
LLVMValueRef llvm_value = llvm_value_rvalue_store(c, &value);
|
||||
LLVMTypeRef expected_type = array_type ? array_type : llvm_get_type(c, expr_type->decl->strukt.members[0]->type);
|
||||
if (expected_type != LLVMTypeOf(llvm_value)) *modified = true;
|
||||
values[i] = llvm_value;
|
||||
}
|
||||
if (array_type)
|
||||
{
|
||||
if (*modified)
|
||||
{
|
||||
return LLVMConstStructInContext(c->context, values, element_count, true);
|
||||
}
|
||||
return LLVMConstArray(array_type, values, element_count);
|
||||
}
|
||||
if (*modified)
|
||||
{
|
||||
return LLVMConstStructInContext(c->context, values, element_count, true);
|
||||
}
|
||||
return LLVMConstNamedStruct(llvm_get_type(c, expr_type), values, element_count);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified)
|
||||
{
|
||||
switch (expr->initializer_expr.init_type)
|
||||
@@ -159,7 +279,7 @@ LLVMValueRef llvm_emit_const_aggregate(GenContext *c, Expr *expr, bool *modified
|
||||
case INITIALIZER_CONST:
|
||||
return llvm_emit_const_initializer(c, expr->initializer_expr.initializer, modified);
|
||||
case INITIALIZER_NORMAL:
|
||||
TODO
|
||||
return llvm_emit_const_initializer_simple(c, expr, modified);
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -197,7 +317,10 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl
|
||||
// TODO fix name
|
||||
decl->backend_ref = LLVMAddGlobal(c->module, LLVMTypeOf(init_value), decl->name);
|
||||
LLVMSetAlignment(decl->backend_ref, alignment);
|
||||
LLVMSetInitializer(decl->backend_ref, init_value);
|
||||
if (decl->visibility != VISIBLE_EXTERN)
|
||||
{
|
||||
LLVMSetInitializer(decl->backend_ref, init_value);
|
||||
}
|
||||
|
||||
LLVMSetGlobalConstant(decl->backend_ref, decl->var.kind == VARDECL_CONST);
|
||||
|
||||
@@ -210,6 +333,9 @@ static void gencontext_emit_global_variable_definition(GenContext *c, Decl *decl
|
||||
LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_EXTERN:
|
||||
LLVMSetLinkage(decl->backend_ref, LLVMExternalLinkage);
|
||||
//LLVMSetVisibility(decl->backend_ref, LLVMDefaultVisibility);
|
||||
break;
|
||||
case VISIBLE_LOCAL:
|
||||
LLVMSetVisibility(decl->backend_ref, LLVMHiddenVisibility);
|
||||
break;
|
||||
|
||||
@@ -107,56 +107,6 @@ ABIArgInfo *abi_arg_new_indirect_not_by_val(void)
|
||||
return info;
|
||||
}
|
||||
|
||||
ByteSize abi_arg_expanded_size(ABIArgInfo *type_info, Type *type)
|
||||
{
|
||||
switch (type->type_kind)
|
||||
{
|
||||
case TYPE_TYPEDEF:
|
||||
return abi_arg_expanded_size(type_info, type->canonical);
|
||||
case TYPE_ARRAY:
|
||||
return abi_arg_expanded_size(type_info, type->array.base) * type->array.len;
|
||||
case TYPE_STRUCT:
|
||||
{
|
||||
Decl **members = type->decl->strukt.members;
|
||||
ByteSize result = 0;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
members += abi_arg_expanded_size(type_info, members[i]->type);
|
||||
}
|
||||
return result;
|
||||
}
|
||||
case TYPE_UNION:
|
||||
{
|
||||
Type *max_union = type_find_largest_union_element(type);
|
||||
if (!max_union) return 0;
|
||||
return abi_arg_expanded_size(type_info, max_union);
|
||||
}
|
||||
case TYPE_COMPLEX:
|
||||
case TYPE_SUBARRAY:
|
||||
case TYPE_STRING:
|
||||
// Complex is { real, real }, Sub array { pointer, len } = String?
|
||||
return 2;
|
||||
case TYPE_ERR_UNION:
|
||||
case TYPE_VOID:
|
||||
case TYPE_BOOL:
|
||||
case ALL_FLOATS:
|
||||
case ALL_INTS:
|
||||
case TYPE_TYPEID:
|
||||
case TYPE_POINTER:
|
||||
case TYPE_ENUM:
|
||||
case TYPE_ERRTYPE:
|
||||
case TYPE_VARARRAY:
|
||||
case TYPE_VECTOR:
|
||||
return 1;
|
||||
case TYPE_POISONED:
|
||||
case TYPE_FUNC:
|
||||
case TYPE_TYPEINFO:
|
||||
case TYPE_MEMBER:
|
||||
UNREACHABLE
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
ABIArgInfo *abi_arg_new_direct_int_ext(Type *int_to_extend)
|
||||
{
|
||||
|
||||
@@ -412,7 +412,7 @@ Decl *x64_get_member_at_offset(Decl *decl, unsigned offset)
|
||||
Decl *last_match = NULL;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
if (members[i]->offset > offset) break;
|
||||
if (members[i]->offset > (ArrayIndex)offset) break;
|
||||
last_match = members[i];
|
||||
}
|
||||
assert(last_match);
|
||||
|
||||
@@ -21,9 +21,16 @@ LLVMValueRef llvm_emit_is_no_error(GenContext *c, LLVMValueRef error)
|
||||
return LLVMBuildICmp(c->builder, LLVMIntEQ, domain, llvm_get_zero(c, type_usize), "noerr");
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_const_padding(GenContext *c, ByteSize size)
|
||||
LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size)
|
||||
{
|
||||
return LLVMGetUndef(LLVMArrayType(llvm_get_type(c, type_byte), size));
|
||||
assert(size > 0);
|
||||
if (size == 1) return llvm_get_type(c, type_byte);
|
||||
return LLVMArrayType(llvm_get_type(c, type_byte), size);
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size)
|
||||
{
|
||||
return LLVMGetUndef(llvm_const_padding_type(c, size));
|
||||
}
|
||||
|
||||
static inline LLVMValueRef gencontext_emit_add_int(GenContext *context, Type *type, bool use_mod, LLVMValueRef left, LLVMValueRef right)
|
||||
@@ -300,7 +307,7 @@ static int find_member_index(Decl *parent, Decl *member)
|
||||
return -1;
|
||||
}
|
||||
|
||||
static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRef value, Decl *parent, Decl *member)
|
||||
static void gencontext_emit_member_addr(GenContext *c, BEValue *value, Decl *parent, Decl *member)
|
||||
{
|
||||
assert(member->resolve_status == RESOLVE_DONE);
|
||||
|
||||
@@ -314,20 +321,18 @@ static LLVMValueRef gencontext_emit_member_addr(GenContext *context, LLVMValueRe
|
||||
switch (parent->type->canonical->type_kind)
|
||||
{
|
||||
case TYPE_UNION:
|
||||
value = LLVMBuildBitCast(context->builder, value, LLVMPointerType(llvm_get_type(context, found->type), 0), name);
|
||||
llvm_value_addr(c, value);
|
||||
llvm_value_set_address_align(value, llvm_emit_bitcast(c, value->value, type_get_ptr(found->type)), found->type, value->alignment);
|
||||
break;
|
||||
case TYPE_ERRTYPE:
|
||||
value = LLVMBuildStructGEP2(context->builder, llvm_get_type(context, parent->type), value, index + 1, name);
|
||||
break;
|
||||
case TYPE_STRUCT:
|
||||
value = LLVMBuildStructGEP2(context->builder, llvm_get_type(context, parent->type), value, index, name);
|
||||
llvm_value_struct_gep(c, value, value, index);
|
||||
break;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
parent = found;
|
||||
} while (found != member);
|
||||
return value;
|
||||
}
|
||||
|
||||
|
||||
@@ -337,7 +342,7 @@ static inline void gencontext_emit_access_addr(GenContext *context, BEValue *be_
|
||||
llvm_emit_expr(context, be_value, parent);
|
||||
Decl *member = expr->access_expr.ref;
|
||||
|
||||
llvm_value_set_address(be_value, gencontext_emit_member_addr(context, be_value->value, parent->type->canonical->decl, member), expr->type);
|
||||
gencontext_emit_member_addr(context, be_value, parent->type->canonical->decl, member);
|
||||
}
|
||||
|
||||
static void gencontext_emit_scoped_expr(GenContext *context, BEValue *value, Expr *expr)
|
||||
@@ -390,6 +395,8 @@ LLVMValueRef gencontext_emit_value_bitcast(GenContext *context, LLVMValueRef val
|
||||
LLVMValueRef ptr_cast = llvm_emit_bitcast(context, ptr, type_get_ptr(to_type));
|
||||
return gencontext_emit_load(context, to_type, ptr_cast);
|
||||
}
|
||||
|
||||
|
||||
static LLVMValueRef gencontext_emit_cast_inner(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type)
|
||||
{
|
||||
switch (cast_kind)
|
||||
@@ -404,10 +411,18 @@ static LLVMValueRef gencontext_emit_cast_inner(GenContext *c, CastKind cast_kind
|
||||
UNREACHABLE
|
||||
case CAST_PTRPTR:
|
||||
llvm_value_rvalue(c, value);
|
||||
return LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr");
|
||||
if (c->builder)
|
||||
{
|
||||
return LLVMBuildPointerCast(c->builder, value->value, llvm_get_type(c, to_type), "ptrptr");
|
||||
}
|
||||
return LLVMConstPointerCast(value->value, llvm_get_type(c, to_type));
|
||||
case CAST_PTRXI:
|
||||
llvm_value_rvalue(c, value);
|
||||
return LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi");
|
||||
if (c->builder)
|
||||
{
|
||||
return LLVMBuildPtrToInt(c->builder, value->value, llvm_get_type(c, to_type), "ptrxi");
|
||||
}
|
||||
return LLVMConstBitCast(value->value, llvm_get_type(c, to_type));
|
||||
case CAST_APTSA:
|
||||
gencontext_emit_arr_to_subarray_cast(c, value, to_type, from_type);
|
||||
return value->value;
|
||||
@@ -524,8 +539,6 @@ static inline void gencontext_emit_cast_expr(GenContext *context, BEValue *be_va
|
||||
expr->cast_expr.expr->type->canonical);
|
||||
}
|
||||
|
||||
static LLVMValueRef llvm_emit_initializer_list_expr_const(GenContext *c, Expr *expr, bool *modified);
|
||||
|
||||
|
||||
static LLVMValueRef llvm_recursive_set_value(GenContext *c, DesignatorElement **current_element_ptr, LLVMValueRef parent, DesignatorElement **last_element_ptr, Expr *value)
|
||||
{
|
||||
@@ -588,73 +601,14 @@ static LLVMValueRef llvm_recursive_set_const_value(GenContext *context, Designat
|
||||
}
|
||||
|
||||
|
||||
static LLVMValueRef llvm_emit_initializer_list_expr_const(GenContext *c, Expr *expr, bool *modified)
|
||||
{
|
||||
Type *canonical = expr->type->canonical;
|
||||
LLVMTypeRef type = llvm_get_type(c, canonical);
|
||||
|
||||
if (expr->initializer_expr.init_type == INITIALIZER_CONST)
|
||||
{
|
||||
return llvm_emit_const_aggregate(c, expr, modified);
|
||||
}
|
||||
|
||||
|
||||
bool is_error = expr->type->canonical->type_kind == TYPE_ERRTYPE;
|
||||
|
||||
if (is_error)
|
||||
{
|
||||
TODO
|
||||
}
|
||||
|
||||
Expr **elements = expr->initializer_expr.initializer_expr;
|
||||
|
||||
bool is_union = expr->type->canonical->type_kind == TYPE_UNION;
|
||||
|
||||
if (expr->initializer_expr.init_type == INITIALIZER_NORMAL && is_union)
|
||||
{
|
||||
assert(vec_size(elements) == 1);
|
||||
|
||||
TODO
|
||||
// gencontext_construct_const_union_struct(c, be_value, canonical, elements[0]);
|
||||
}
|
||||
|
||||
if (expr->initializer_expr.init_type == INITIALIZER_NORMAL)
|
||||
{
|
||||
LLVMValueRef value = LLVMGetUndef(type);
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
Expr *element = elements[i];
|
||||
if (element->expr_kind == EXPR_INITIALIZER_LIST)
|
||||
{
|
||||
value = LLVMConstInsertValue(value, llvm_emit_initializer_list_expr_const(c, element, modified), &i, 1);
|
||||
continue;
|
||||
}
|
||||
BEValue be_value;
|
||||
llvm_emit_expr(c, &be_value, element);
|
||||
value = LLVMConstInsertValue(value, llvm_value_rvalue_store(c, &be_value), &i, 1);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
LLVMValueRef value = LLVMConstNull(type);
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
Expr *element = elements[i];
|
||||
Type *parent_type = expr->type->canonical;
|
||||
value = llvm_recursive_set_const_value(c,
|
||||
element->designator_expr.path,
|
||||
value,
|
||||
parent_type,
|
||||
element->designator_expr.value);
|
||||
}
|
||||
return value;
|
||||
}
|
||||
|
||||
static inline void llvm_emit_initialize_reference_small_const(GenContext *c, BEValue *ref, Expr *expr)
|
||||
static inline void llvm_emit_initialize_reference_temporary_const(GenContext *c, BEValue *ref, Expr *expr)
|
||||
{
|
||||
bool modified = false;
|
||||
// First create the constant value.
|
||||
LLVMValueRef value = llvm_emit_initializer_list_expr_const(c, expr, &modified);
|
||||
|
||||
Type *canonical = expr->type->canonical;
|
||||
|
||||
LLVMValueRef value = llvm_emit_const_aggregate(c, expr, &modified);
|
||||
|
||||
// Create a global const.
|
||||
LLVMTypeRef type = modified ? LLVMTypeOf(value) : llvm_get_type(c, expr->type);
|
||||
@@ -675,6 +629,116 @@ static inline void llvm_emit_initialize_reference_small_const(GenContext *c, BEV
|
||||
llvm_emit_memcpy(c, ref->value, ref->alignment, global_copy, alignment, type_size(expr->type));
|
||||
}
|
||||
|
||||
static void llvm_emit_inititialize_reference_const(GenContext *c, BEValue *ref, ConstInitializer *const_init)
|
||||
{
|
||||
switch (const_init->kind)
|
||||
{
|
||||
case CONST_INIT_ZERO:
|
||||
if (type_is_builtin(ref->type->canonical->type_kind) || ref->type->canonical->type_kind == TYPE_ARRAY)
|
||||
{
|
||||
llvm_store_bevalue_raw(c, ref, llvm_get_zero(c, ref->type));
|
||||
return;
|
||||
}
|
||||
llvm_emit_memclear(c, ref);
|
||||
return;
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
UNREACHABLE
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
{
|
||||
LLVMValueRef array_ref = ref->value;
|
||||
Type *array_type = const_init->type;
|
||||
Type *element_type = array_type->array.base;
|
||||
ArrayIndex size = array_type->array.len;
|
||||
LLVMTypeRef array_type_llvm = llvm_get_type(c, array_type);
|
||||
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
|
||||
assert(size <= UINT32_MAX);
|
||||
for (ArrayIndex i = 0; i < size; i++)
|
||||
{
|
||||
LLVMValueRef index = llvm_const_int(c, type_uint, i);
|
||||
LLVMValueRef array_pointer = LLVMBuildInBoundsGEP2(c->builder, element_type_llvm, array_ref, &index, 1, "");
|
||||
BEValue value;
|
||||
llvm_value_set_address(&value, array_pointer, element_type);
|
||||
llvm_emit_inititialize_reference_const(c, &value, const_init->init_array_full[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_ARRAY:
|
||||
{
|
||||
LLVMValueRef array_ref = ref->value;
|
||||
llvm_emit_memclear(c, ref);
|
||||
Type *array_type = const_init->type;
|
||||
Type *element_type = array_type->array.base;
|
||||
LLVMTypeRef element_type_llvm = llvm_get_type(c, element_type);
|
||||
ConstInitializer **elements = const_init->init_array.elements;
|
||||
unsigned element_count = vec_size(elements);
|
||||
ArrayIndex current_index = 0;
|
||||
LLVMValueRef *parts = NULL;
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
ConstInitializer *element = elements[i];
|
||||
assert(element->kind == CONST_INIT_ARRAY_VALUE);
|
||||
ArrayIndex element_index = element->init_array_value.index;
|
||||
LLVMValueRef index = llvm_const_int(c, element_index <= UINT32_MAX ? type_uint : type_usize, element_index);
|
||||
LLVMValueRef array_pointer = LLVMBuildInBoundsGEP2(c->builder, element_type_llvm, array_ref, &index, 1, "");
|
||||
BEValue value;
|
||||
llvm_value_set_address(&value, array_pointer, element_type);
|
||||
llvm_emit_inititialize_reference_const(c, &value, element->init_array_value.element);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_UNION:
|
||||
{
|
||||
Decl *decl = const_init->type->decl;
|
||||
MemberIndex index = const_init->init_union.index;
|
||||
Type *type = decl->strukt.members[index]->type->canonical;
|
||||
// Bitcast.
|
||||
BEValue value = *ref;
|
||||
llvm_value_set_address(&value, llvm_emit_bitcast(c, ref->value, type_get_ptr(type)), type);
|
||||
// Emit our value.
|
||||
llvm_emit_inititialize_reference_const(c, &value, const_init->init_union.element);
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_STRUCT:
|
||||
{
|
||||
Decl *decl = const_init->type->decl;
|
||||
Decl **members = decl->strukt.members;
|
||||
MemberIndex count = vec_size(members);
|
||||
LLVMValueRef *entries = NULL;
|
||||
for (MemberIndex i = 0; i < count; i++)
|
||||
{
|
||||
BEValue value;
|
||||
llvm_value_struct_gep(c, &value, ref, i);
|
||||
llvm_emit_inititialize_reference_const(c, &value, const_init->init_struct[i]);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_VALUE:
|
||||
{
|
||||
BEValue value;
|
||||
llvm_emit_expr(c, &value, const_init->init_value);
|
||||
llvm_store_bevalue(c, ref, &value);
|
||||
return;
|
||||
}
|
||||
}
|
||||
UNREACHABLE
|
||||
|
||||
}
|
||||
static inline void llvm_emit_initialize_reference_const(GenContext *c, BEValue *ref, Expr *expr)
|
||||
{
|
||||
// Getting ready to initialize, get the real type.
|
||||
Type *real_type = ref->type->canonical;
|
||||
ConstInitializer *initializer = expr->initializer_expr.initializer;
|
||||
|
||||
// Make sure we have an address.
|
||||
llvm_value_addr(c, ref);
|
||||
|
||||
Type *canonical = expr->type->canonical;
|
||||
LLVMTypeRef type = llvm_get_type(c, canonical);
|
||||
|
||||
llvm_emit_inititialize_reference_const(c, ref, initializer);
|
||||
|
||||
}
|
||||
|
||||
static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *ref, Expr *expr)
|
||||
{
|
||||
// Getting ready to initialize, get the real type.
|
||||
@@ -696,45 +760,46 @@ static inline void llvm_emit_initialize_reference_list(GenContext *c, BEValue *r
|
||||
LLVMTypeRef llvm_type = llvm_get_type(c, real_type);
|
||||
bool is_struct = type_is_structlike(real_type);
|
||||
bool is_array = real_type->type_kind == TYPE_ARRAY;
|
||||
|
||||
Type *array_element_type = is_array ? real_type->array.base : NULL;
|
||||
LLVMTypeRef array_element_type_llvm = is_array ? llvm_get_type(c, real_type->array.base) : NULL;
|
||||
// Now walk through the elements.
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
Expr *element = elements[i];
|
||||
BEValue init_value;
|
||||
if (element->expr_kind == EXPR_COMPOUND_LITERAL)
|
||||
{
|
||||
element = element->expr_compound_literal.initializer;
|
||||
}
|
||||
unsigned offset = 0;
|
||||
LLVMValueRef pointer;
|
||||
BEValue pointer;
|
||||
if (is_struct)
|
||||
{
|
||||
Decl *member = real_type->decl->strukt.members[i];
|
||||
offset = member->offset;
|
||||
pointer = LLVMBuildStructGEP2(c->builder, llvm_type, value, i, "");
|
||||
llvm_value_struct_gep(c, &pointer, ref, i);
|
||||
}
|
||||
else if (is_array)
|
||||
{
|
||||
// Todo optimize
|
||||
offset = i * type_size(element->type);
|
||||
LLVMValueRef indices[2] = { 0, llvm_const_int(c, type_uint, i) };
|
||||
pointer = LLVMBuildInBoundsGEP2(c->builder, llvm_type, value, indices, 2, "");
|
||||
offset = i * type_size(array_element_type);
|
||||
LLVMValueRef index = llvm_const_int(c, type_uint, i);
|
||||
LLVMValueRef ptr = LLVMBuildInBoundsGEP2(c->builder, array_element_type_llvm, value, &index, 1, "");
|
||||
unsigned alignment = aligned_offset(offset, ref->alignment);
|
||||
llvm_value_set_address_align(&pointer, ptr, element->type, alignment);
|
||||
}
|
||||
else
|
||||
{
|
||||
pointer = value;
|
||||
llvm_value_set_address_align(&pointer, value, element->type, 0);
|
||||
}
|
||||
unsigned alignment = aligned_offset(offset, ref->alignment);
|
||||
// If this is an initializer, we want to actually run the initialization recursively.
|
||||
if (element->expr_kind == EXPR_INITIALIZER_LIST)
|
||||
{
|
||||
llvm_value_set_address_align(&init_value, pointer, element->type, alignment);
|
||||
llvm_emit_initialize_reference(c, &init_value, element);
|
||||
llvm_emit_initialize_reference(c, &pointer, element);
|
||||
continue;
|
||||
}
|
||||
BEValue init_value;
|
||||
llvm_emit_expr(c, &init_value, element);
|
||||
llvm_store_aligned(c, pointer, llvm_value_rvalue_store(c, &init_value), alignment);
|
||||
llvm_store_bevalue(c, &pointer, &init_value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -854,9 +919,7 @@ static void llvm_emit_initialize_designated(GenContext *c, BEValue *ref, uint64_
|
||||
}
|
||||
else
|
||||
{
|
||||
unsigned alignment = 1;
|
||||
LLVMValueRef ptr = llvm_emit_struct_gep(c, ref->value, llvm_get_type(c, ref->type), curr->index, decl_alignment, offset, &alignment);
|
||||
llvm_value_set_address_align(&value, ptr, type, alignment);
|
||||
llvm_value_struct_gep(c, &value, ref, curr->index);
|
||||
}
|
||||
llvm_emit_initialize_designated(c, &value, offset, current + 1, last, expr, emitted_value);
|
||||
break;
|
||||
@@ -934,7 +997,7 @@ static inline void llvm_emit_initialize_reference_designated(GenContext *c, BEVa
|
||||
static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, Expr *expr)
|
||||
{
|
||||
// In the case of a const, we have some optimizations:
|
||||
if (expr->constant)
|
||||
if (expr->initializer_expr.init_type == INITIALIZER_CONST)
|
||||
{
|
||||
ConstInitializer *initializer = expr->initializer_expr.initializer;
|
||||
if (initializer->kind == CONST_INIT_ZERO)
|
||||
@@ -943,10 +1006,10 @@ static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, E
|
||||
llvm_emit_memclear(c, ref);
|
||||
return;
|
||||
}
|
||||
// In case of small const initializers, use copy.
|
||||
if (type_size(expr->type) <= 1000 * type_size(type_voidptr) * 4)
|
||||
// In case of small const initializers, or full arrays - use copy.
|
||||
if (initializer->kind == CONST_INIT_ARRAY_FULL || type_size(expr->type) <= 32)
|
||||
{
|
||||
llvm_emit_initialize_reference_small_const(c, ref, expr);
|
||||
llvm_emit_initialize_reference_temporary_const(c, ref, expr);
|
||||
return;
|
||||
}
|
||||
}
|
||||
@@ -955,7 +1018,7 @@ static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, E
|
||||
{
|
||||
case INITIALIZER_CONST:
|
||||
// Just clear memory
|
||||
llvm_emit_memclear(c, ref);
|
||||
llvm_emit_initialize_reference_const(c, ref, expr);
|
||||
break;
|
||||
case INITIALIZER_NORMAL:
|
||||
llvm_emit_initialize_reference_list(c, ref, expr);
|
||||
@@ -966,61 +1029,6 @@ static inline void llvm_emit_initialize_reference(GenContext *c, BEValue *ref, E
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
|
||||
return;
|
||||
TODO
|
||||
/*
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
if (is_error) TODO
|
||||
Expr *field = elements[i];
|
||||
|
||||
DesignatedPath *path = field->designated_init_expr.path;
|
||||
BEValue sub_value;
|
||||
llvm_emit_expr(c, &sub_value, field->designated_init_expr.value);
|
||||
LLVMValueRef sub_ref = ref;
|
||||
Type *parent_type = expr->type->canonical;
|
||||
while (path)
|
||||
{
|
||||
switch (path->kind)
|
||||
{
|
||||
case DESIGNATED_IDENT:
|
||||
if (parent_type->canonical->type_kind == TYPE_UNION)
|
||||
{
|
||||
sub_ref = LLVMBuildBitCast(c->builder, sub_ref, llvm_get_ptr_type(c, path->type), path->type->name);
|
||||
}
|
||||
else
|
||||
{
|
||||
sub_ref = LLVMBuildStructGEP2(c->builder, llvm_get_type(c, parent_type), sub_ref, path->index, path->type->name);
|
||||
}
|
||||
break;
|
||||
case DESIGNATED_SUBSCRIPT:
|
||||
{
|
||||
// TODO range, more arrays
|
||||
LLVMValueRef zero = llvm_get_zero(c, type_int);
|
||||
BEValue index;
|
||||
llvm_emit_expr(c, &index, path->index_expr);
|
||||
LLVMValueRef indices[2] = {
|
||||
zero,
|
||||
llvm_value_rvalue_store(c, &index),
|
||||
};
|
||||
sub_ref = LLVMBuildInBoundsGEP2(c->builder,
|
||||
llvm_get_type(c, parent_type),
|
||||
sub_ref, indices, 2, "arrsub");
|
||||
break;
|
||||
}
|
||||
default:
|
||||
UNREACHABLE;
|
||||
|
||||
}
|
||||
parent_type = path->type;
|
||||
path = path->sub_path;
|
||||
}
|
||||
LLVMBuildStore(c->builder, llvm_value_rvalue_store(c, &sub_value), sub_ref);
|
||||
}
|
||||
return ref;
|
||||
*/
|
||||
}
|
||||
|
||||
static inline void llvm_emit_inc_dec_change(GenContext *c, bool use_mod, BEValue *addr, BEValue *after, BEValue *before, Expr *expr, int diff)
|
||||
@@ -2351,24 +2359,35 @@ static void llvm_expand_type_to_args(GenContext *context, Type *param_type, LLVM
|
||||
}
|
||||
}
|
||||
|
||||
LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment)
|
||||
LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment)
|
||||
{
|
||||
*alignment = type_min_alignment(offset, struct_alignment);
|
||||
LLVMValueRef addr = LLVMBuildStructGEP2(context->builder, struct_type, ptr, index, "");
|
||||
return addr;
|
||||
}
|
||||
|
||||
void llvm_value_struct_gep(GenContext *c, BEValue *element, BEValue *struct_pointer, unsigned index)
|
||||
{
|
||||
llvm_value_fold_failable(c, struct_pointer);
|
||||
Decl *member = struct_pointer->type->decl->strukt.members[index];
|
||||
ArrayIndex actual_index = -1;
|
||||
Decl *member;
|
||||
for (ArrayIndex i = 0; i <= index; i++)
|
||||
{
|
||||
member = struct_pointer->type->decl->strukt.members[i];
|
||||
if (member->padding)
|
||||
{
|
||||
actual_index++;
|
||||
}
|
||||
actual_index++;
|
||||
}
|
||||
unsigned alignment;
|
||||
LLVMValueRef ref = llvm_emit_struct_gep(c,
|
||||
struct_pointer->value,
|
||||
llvm_get_type(c, struct_pointer->type),
|
||||
index,
|
||||
struct_pointer->alignment,
|
||||
member->offset,
|
||||
&alignment);
|
||||
LLVMValueRef ref = llvm_emit_struct_gep_raw(c,
|
||||
struct_pointer->value,
|
||||
llvm_get_type(c, struct_pointer->type),
|
||||
actual_index,
|
||||
struct_pointer->alignment,
|
||||
member->offset,
|
||||
&alignment);
|
||||
llvm_value_set_address(element, ref, member->type);
|
||||
element->alignment = alignment;
|
||||
}
|
||||
@@ -2649,9 +2668,9 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
||||
|
||||
// Find the address to the low value
|
||||
unsigned alignment;
|
||||
LLVMValueRef lo = llvm_emit_struct_gep(context, coerce, coerce_type, ret_info->coerce_expand.lo_index,
|
||||
type_abi_alignment(return_type),
|
||||
ret_info->coerce_expand.offset_lo, &alignment);
|
||||
LLVMValueRef lo = llvm_emit_struct_gep_raw(context, coerce, coerce_type, ret_info->coerce_expand.lo_index,
|
||||
type_abi_alignment(return_type),
|
||||
ret_info->coerce_expand.offset_lo, &alignment);
|
||||
|
||||
// If there is only a single field, we simply store the value.
|
||||
if (!ret_info->coerce_expand.hi)
|
||||
@@ -2669,9 +2688,9 @@ void llvm_emit_call_expr(GenContext *context, BEValue *be_value, Expr *expr)
|
||||
// Store the low value.
|
||||
llvm_store_aligned(context, lo, lo_value, alignment);
|
||||
|
||||
LLVMValueRef hi = llvm_emit_struct_gep(context, coerce, coerce_type, ret_info->coerce_expand.hi_index,
|
||||
type_abi_alignment(return_type),
|
||||
ret_info->coerce_expand.offset_hi, &alignment);
|
||||
LLVMValueRef hi = llvm_emit_struct_gep_raw(context, coerce, coerce_type, ret_info->coerce_expand.hi_index,
|
||||
type_abi_alignment(return_type),
|
||||
ret_info->coerce_expand.offset_hi, &alignment);
|
||||
|
||||
// Store the high value.
|
||||
llvm_store_aligned(context, lo, lo_value, alignment);
|
||||
|
||||
@@ -296,10 +296,10 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
// We might have only one value, in that case, build a GEP to that one.
|
||||
LLVMValueRef lo_val;
|
||||
unsigned alignment;
|
||||
LLVMValueRef lo = llvm_emit_struct_gep(c, coerce, coerce_type, info->coerce_expand.lo_index,
|
||||
return_value->alignment,
|
||||
info->coerce_expand.offset_lo, &alignment);
|
||||
LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.hi);
|
||||
LLVMValueRef lo = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.lo_index,
|
||||
return_value->alignment,
|
||||
info->coerce_expand.offset_lo, &alignment);
|
||||
LLVMTypeRef lo_type = llvm_abi_type(c, info->coerce_expand.lo);
|
||||
lo_val = llvm_emit_load_aligned(c, lo_type, lo, alignment, "");
|
||||
|
||||
// We're done if there's a single field.
|
||||
@@ -310,9 +310,9 @@ void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failabl
|
||||
}
|
||||
|
||||
// Let's make a first class aggregate
|
||||
LLVMValueRef hi = llvm_emit_struct_gep(c, coerce, coerce_type, info->coerce_expand.hi_index,
|
||||
return_value->alignment,
|
||||
info->coerce_expand.offset_hi, &alignment);
|
||||
LLVMValueRef hi = llvm_emit_struct_gep_raw(c, coerce, coerce_type, info->coerce_expand.hi_index,
|
||||
return_value->alignment,
|
||||
info->coerce_expand.offset_hi, &alignment);
|
||||
LLVMTypeRef hi_type = llvm_abi_type(c, info->coerce_expand.hi);
|
||||
LLVMValueRef hi_val = llvm_emit_load_aligned(c, hi_type, hi, alignment, "");
|
||||
|
||||
|
||||
@@ -201,7 +201,8 @@ void llvm_attribute_add_string(GenContext *c, LLVMValueRef value_to_add_attribut
|
||||
void llvm_attribute_add_int(GenContext *c, LLVMValueRef value_to_add_attribute_to, unsigned attribute_id, uint64_t val, int index);
|
||||
LLVMBasicBlockRef llvm_basic_block_new(GenContext *c, const char *name);
|
||||
static inline LLVMValueRef llvm_const_int(GenContext *c, Type *type, uint64_t val);
|
||||
|
||||
LLVMValueRef llvm_emit_const_padding(GenContext *c, ByteSize size);
|
||||
LLVMTypeRef llvm_const_padding_type(GenContext *c, ByteSize size);
|
||||
LLVMValueRef llvm_emit_alloca(GenContext *context, LLVMTypeRef type, unsigned alignment, const char *name);
|
||||
LLVMValueRef llvm_emit_alloca_aligned(GenContext *c, Type *type, const char *name);
|
||||
LLVMValueRef llvm_emit_assign_expr(GenContext *context, LLVMValueRef ref, Expr *expr, LLVMValueRef failable);
|
||||
@@ -215,7 +216,6 @@ void llvm_emit_function_body(GenContext *context, Decl *decl);
|
||||
void llvm_emit_function_decl(GenContext *c, Decl *decl);
|
||||
LLVMValueRef llvm_emit_call_intrinsic(GenContext *c, unsigned intrinsic_id, LLVMTypeRef *types, unsigned type_count, LLVMValueRef *values, unsigned arg_count);
|
||||
void llvm_emit_cast(GenContext *c, CastKind cast_kind, BEValue *value, Type *to_type, Type *from_type);
|
||||
LLVMValueRef llvm_const_padding(GenContext *c, ByteSize size);
|
||||
void llvm_emit_cond_br(GenContext *context, BEValue *value, LLVMBasicBlockRef then_block, LLVMBasicBlockRef else_block);
|
||||
void llvm_emit_debug_function(GenContext *c, Decl *decl);
|
||||
void llvm_emit_debug_location(GenContext *context, SourceSpan location);
|
||||
@@ -237,7 +237,7 @@ static inline LLVMValueRef llvm_emit_store(GenContext *context, Decl *decl, LLVM
|
||||
void llvm_emit_panic_on_true(GenContext *c, LLVMValueRef value, const char *panic_name);
|
||||
void llvm_emit_return_abi(GenContext *c, BEValue *return_value, BEValue *failable);
|
||||
void llvm_emit_return_implicit(GenContext *c);
|
||||
LLVMValueRef llvm_emit_struct_gep(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment);
|
||||
LLVMValueRef llvm_emit_struct_gep_raw(GenContext *context, LLVMValueRef ptr, LLVMTypeRef struct_type, unsigned index, unsigned struct_alignment, unsigned offset, unsigned *alignment);
|
||||
LLVMValueRef llvm_get_next_param(GenContext *context, unsigned *index);
|
||||
LLVMTypeRef llvm_get_coerce_type(GenContext *c, ABIArgInfo *arg_info);
|
||||
static inline LLVMBasicBlockRef llvm_get_current_block_if_in_use(GenContext *context);
|
||||
|
||||
@@ -6,6 +6,12 @@
|
||||
|
||||
void gencontext_emit_try_stmt(GenContext *context, Ast *pAst);
|
||||
|
||||
bool ast_is_empty_compound_stmt(Ast *ast)
|
||||
{
|
||||
if (ast->ast_kind != AST_COMPOUND_STMT) return false;
|
||||
return !vec_size(ast->compound_stmt.stmts) && ast->compound_stmt.defer_list.start == ast->compound_stmt.defer_list.end;
|
||||
}
|
||||
|
||||
void llvm_emit_compound_stmt(GenContext *context, Ast *ast)
|
||||
{
|
||||
if (llvm_use_debug(context))
|
||||
@@ -61,8 +67,9 @@ static LLVMValueRef llvm_emit_decl(GenContext *c, Ast *ast)
|
||||
}
|
||||
else
|
||||
{
|
||||
Type *type = decl->type->canonical;
|
||||
// Normal case, zero init.
|
||||
if (type_is_builtin(decl->type->canonical->type_kind))
|
||||
if (type_is_builtin(type->type_kind) || type->type_kind == TYPE_POINTER)
|
||||
{
|
||||
llvm_emit_store(c, decl, LLVMConstNull(alloc_type));
|
||||
}
|
||||
@@ -208,65 +215,80 @@ static inline void gencontext_emit_return(GenContext *c, Ast *ast)
|
||||
|
||||
|
||||
|
||||
void gencontext_emit_if(GenContext *c, Ast *ast)
|
||||
/**
|
||||
* Emit if (...) { ... } else { ... }
|
||||
*
|
||||
* This code is slightly optimized to omit branches when not needed. This is something LLVM
|
||||
* will optimize as well, but it is convenient to make the code slightly smaller for LLVM to work with:
|
||||
* 1. If the "then" branch is empty, replace it with "exit".
|
||||
* 2. If the "else" branch is empty or missing, replace if with "exit".
|
||||
* 3. If both "else" and "then" branches are empty, replace it with just the condition and remove the "exit"
|
||||
*/
|
||||
void llvm_emit_if(GenContext *c, Ast *ast)
|
||||
{
|
||||
// We need at least the exit block and the "then" block.
|
||||
LLVMBasicBlockRef exit_block = llvm_basic_block_new(c, "if.exit");
|
||||
LLVMBasicBlockRef then_block = llvm_basic_block_new(c, "if.then");
|
||||
LLVMBasicBlockRef else_block = NULL;
|
||||
LLVMBasicBlockRef then_block = exit_block;
|
||||
LLVMBasicBlockRef else_block = exit_block;
|
||||
|
||||
// Only generate a target if
|
||||
if (!ast_is_empty_compound_stmt(ast->if_stmt.then_body))
|
||||
{
|
||||
then_block = llvm_basic_block_new(c, "if.then");
|
||||
}
|
||||
|
||||
// We have an optional else block.
|
||||
if (ast->if_stmt.else_body)
|
||||
if (ast->if_stmt.else_body && !ast_is_empty_compound_stmt(ast->if_stmt.else_body))
|
||||
{
|
||||
else_block = llvm_basic_block_new(c, "if.else");
|
||||
}
|
||||
else
|
||||
{
|
||||
else_block = exit_block;
|
||||
}
|
||||
|
||||
ast->if_stmt.break_block = exit_block;
|
||||
|
||||
// Output boolean value and switch.
|
||||
|
||||
PUSH_ERROR();
|
||||
|
||||
c->catch_block = else_block ?: exit_block;
|
||||
c->error_var = NULL;
|
||||
|
||||
BEValue be_value = {};
|
||||
gencontext_emit_decl_expr_list(c, &be_value, ast->if_stmt.cond, true);
|
||||
llvm_value_rvalue(c, &be_value);
|
||||
|
||||
assert(llvm_value_is_bool(&be_value));
|
||||
|
||||
if (llvm_value_is_const(&be_value))
|
||||
{
|
||||
if (LLVMConstIntGetZExtValue(be_value.value))
|
||||
{
|
||||
llvm_emit_br(c, then_block);
|
||||
else_block = NULL;
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_emit_br(c, else_block);
|
||||
then_block = NULL;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_emit_cond_br(c, &be_value, then_block, else_block);
|
||||
}
|
||||
|
||||
POP_ERROR();
|
||||
|
||||
Decl *label = ast->if_stmt.flow.label;
|
||||
if (label)
|
||||
{
|
||||
label->label.break_target = exit_block;
|
||||
}
|
||||
|
||||
BEValue be_value = {};
|
||||
gencontext_emit_decl_expr_list(c, &be_value, ast->if_stmt.cond, true);
|
||||
llvm_value_rvalue(c, &be_value);
|
||||
|
||||
assert(llvm_value_is_bool(&be_value));
|
||||
|
||||
bool exit_in_use = true;
|
||||
|
||||
if (llvm_value_is_const(&be_value) && then_block != else_block)
|
||||
{
|
||||
if (LLVMConstIntGetZExtValue(be_value.value))
|
||||
{
|
||||
llvm_emit_br(c, then_block);
|
||||
else_block = exit_block;
|
||||
}
|
||||
else
|
||||
{
|
||||
llvm_emit_br(c, else_block);
|
||||
then_block = exit_block;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
if (then_block != else_block)
|
||||
{
|
||||
llvm_emit_cond_br(c, &be_value, then_block, else_block);
|
||||
}
|
||||
else
|
||||
{
|
||||
exit_in_use = LLVMGetFirstUse(LLVMBasicBlockAsValue(exit_block)) != NULL;
|
||||
if (exit_in_use) llvm_emit_br(c, exit_block);
|
||||
}
|
||||
}
|
||||
|
||||
// Emit the 'then' code.
|
||||
if (then_block)
|
||||
if (then_block != exit_block)
|
||||
{
|
||||
llvm_emit_block(c, then_block);
|
||||
llvm_emit_stmt(c, ast->if_stmt.then_body);
|
||||
@@ -275,9 +297,8 @@ void gencontext_emit_if(GenContext *c, Ast *ast)
|
||||
llvm_emit_br(c, exit_block);
|
||||
}
|
||||
|
||||
|
||||
// Emit the 'else' branch if present.
|
||||
if (else_block && else_block != exit_block)
|
||||
if (else_block != exit_block)
|
||||
{
|
||||
llvm_emit_block(c, else_block);
|
||||
llvm_emit_stmt(c, ast->if_stmt.else_body);
|
||||
@@ -285,7 +306,10 @@ void gencontext_emit_if(GenContext *c, Ast *ast)
|
||||
}
|
||||
|
||||
// And now we just emit the exit block.
|
||||
llvm_emit_block(c, exit_block);
|
||||
if (exit_in_use)
|
||||
{
|
||||
llvm_emit_block(c, exit_block);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1044,7 +1068,7 @@ void llvm_emit_stmt(GenContext *c, Ast *ast)
|
||||
gencontext_emit_continue(c, ast);
|
||||
break;
|
||||
case AST_IF_STMT:
|
||||
gencontext_emit_if(c, ast);
|
||||
llvm_emit_if(c, ast);
|
||||
break;
|
||||
case AST_RETURN_STMT:
|
||||
gencontext_emit_return(c, ast);
|
||||
|
||||
@@ -5,7 +5,7 @@
|
||||
#include "llvm_codegen_internal.h"
|
||||
|
||||
|
||||
static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||
static inline LLVMTypeRef llvm_type_from_decl(GenContext *c, Decl *decl)
|
||||
{
|
||||
static LLVMTypeRef params[MAX_PARAMS];
|
||||
switch (decl->decl_kind)
|
||||
@@ -19,49 +19,43 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||
{
|
||||
VECEACH(decl->func.function_signature.params, i)
|
||||
{
|
||||
params[i] = llvm_get_type(context, decl->func.function_signature.params[i]->type);
|
||||
params[i] = llvm_get_type(c, decl->func.function_signature.params[i]->type);
|
||||
}
|
||||
unsigned param_size = vec_size(decl->func.function_signature.params);
|
||||
return LLVMFunctionType(llvm_get_type(context, decl->func.function_signature.rtype->type),
|
||||
return LLVMFunctionType(llvm_get_type(c, decl->func.function_signature.rtype->type),
|
||||
params,
|
||||
param_size,
|
||||
decl->func.function_signature.variadic);
|
||||
|
||||
}
|
||||
case DECL_TYPEDEF:
|
||||
return llvm_get_type(context, decl->typedef_decl.type_info->type);
|
||||
return llvm_get_type(c, decl->typedef_decl.type_info->type);
|
||||
case DECL_STRUCT:
|
||||
{
|
||||
LLVMTypeRef *types = NULL;
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->external_name);
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(c->context, decl->external_name);
|
||||
// Avoid recursive issues.
|
||||
decl->type->backend_type = type;
|
||||
Decl **members = decl->strukt.members;
|
||||
VECEACH(members, i)
|
||||
{
|
||||
vec_add(types, llvm_get_type(context, members[i]->type));
|
||||
Decl *member = members[i];
|
||||
if (member->padding)
|
||||
{
|
||||
vec_add(types, llvm_const_padding_type(c, member->padding));
|
||||
}
|
||||
vec_add(types, llvm_get_type(c, members[i]->type));
|
||||
}
|
||||
if (decl->needs_additional_pad)
|
||||
if (decl->strukt.padding)
|
||||
{
|
||||
Decl *last_member = VECLAST(members);
|
||||
unsigned member_end = last_member->offset + type_size(last_member->type);
|
||||
unsigned bytes = decl->strukt.size - member_end;
|
||||
assert(bytes > 0);
|
||||
if (bytes == 1)
|
||||
{
|
||||
vec_add(types, llvm_get_type(context, type_byte));
|
||||
}
|
||||
else
|
||||
{
|
||||
vec_add(types, LLVMArrayType(llvm_get_type(context, type_byte), bytes));
|
||||
}
|
||||
vec_add(types, llvm_const_padding_type(c, decl->strukt.padding));
|
||||
}
|
||||
LLVMStructSetBody(type, types, vec_size(types), decl->is_packed);
|
||||
return type;
|
||||
}
|
||||
case DECL_UNION:
|
||||
{
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(context->context, decl->external_name);
|
||||
LLVMTypeRef type = LLVMStructCreateNamed(c->context, decl->external_name);
|
||||
// Avoid recursive issues.
|
||||
decl->type->backend_type = type;
|
||||
Decl **members = decl->strukt.members;
|
||||
@@ -70,14 +64,13 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||
|
||||
Decl *rep_type = members[decl->strukt.union_rep];
|
||||
LLVMTypeRef type_ref[2] = {
|
||||
llvm_get_type(context, rep_type->type),
|
||||
llvm_get_type(c, rep_type->type),
|
||||
NULL
|
||||
};
|
||||
unsigned elements = 1;
|
||||
if (decl->needs_additional_pad)
|
||||
if (decl->strukt.padding)
|
||||
{
|
||||
type_ref[elements++] = LLVMArrayType(llvm_get_type(context, type_bool), type_size(decl->type) - type_size(rep_type->type));
|
||||
|
||||
type_ref[elements++] = llvm_const_padding_type(c, decl->strukt.padding);
|
||||
}
|
||||
LLVMStructSetBody(type, type_ref, elements, decl->is_packed);
|
||||
}
|
||||
@@ -88,15 +81,14 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||
return type;
|
||||
}
|
||||
case DECL_ENUM:
|
||||
return llvm_get_type(context, decl->type);
|
||||
return llvm_get_type(c, decl->type);
|
||||
case DECL_ERR:
|
||||
{
|
||||
LLVMTypeRef err_type = LLVMStructCreateNamed(context->context, decl->external_name);
|
||||
LLVMTypeRef err_type = LLVMStructCreateNamed(c->context, decl->external_name);
|
||||
// Avoid recursive issues.
|
||||
decl->type->backend_type = err_type;
|
||||
LLVMTypeRef *types = NULL;
|
||||
vec_add(types, llvm_get_type(context, type_typeid));
|
||||
unsigned size = type_size(type_typeid);
|
||||
unsigned size = 0;
|
||||
VECEACH(decl->strukt.members, i)
|
||||
{
|
||||
Type *type = decl->strukt.members[i]->type->canonical;
|
||||
@@ -106,12 +98,11 @@ static inline LLVMTypeRef llvm_type_from_decl(GenContext *context, Decl *decl)
|
||||
size += alignment - size % alignment;
|
||||
}
|
||||
size += type_size(type);
|
||||
vec_add(types, llvm_get_type(context, type));
|
||||
vec_add(types, llvm_get_type(c, type));
|
||||
}
|
||||
unsigned padding = type_size(type_error) - size;
|
||||
if (padding > 0)
|
||||
if (decl->strukt.padding)
|
||||
{
|
||||
vec_add(types, LLVMIntTypeInContext(context->context, padding * 8));
|
||||
vec_add(types, llvm_const_padding_type(c, decl->strukt.padding));
|
||||
}
|
||||
LLVMStructSetBody(err_type, types, vec_size(types), false);
|
||||
return err_type;
|
||||
|
||||
@@ -436,10 +436,10 @@ static Expr *parse_subscript_expr(Context *context, Expr *left)
|
||||
else
|
||||
{
|
||||
index = EXPR_NEW_TOKEN(EXPR_CONST, context->tok);
|
||||
index->type = type_usize;
|
||||
index->type = type_uint;
|
||||
index->constant = true;
|
||||
index->resolve_status = RESOLVE_DONE;
|
||||
expr_const_set_int(&index->const_expr, 0, type_usize->canonical->type_kind);
|
||||
expr_const_set_int(&index->const_expr, 0, type_uint->type_kind);
|
||||
}
|
||||
if (try_consume(context, TOKEN_DOTDOT))
|
||||
{
|
||||
|
||||
@@ -1190,6 +1190,7 @@ bool parse_struct_body(Context *context, Decl *parent)
|
||||
return false;
|
||||
}
|
||||
advance(context);
|
||||
if (!parse_attributes(context, member)) return false;
|
||||
if (!try_consume(context, TOKEN_COMMA)) break;
|
||||
if (was_inline)
|
||||
{
|
||||
@@ -1888,7 +1889,7 @@ Decl *parse_top_level_statement(Context *context)
|
||||
{
|
||||
Ast *ast = TRY_AST_OR(parse_ct_assert_stmt(context), false);
|
||||
vec_add(context->ct_asserts, ast);
|
||||
return poisoned_decl;
|
||||
return NULL;
|
||||
}
|
||||
case TOKEN_CT_IF:
|
||||
if (!check_no_visibility_before(context, visibility)) return poisoned_decl;
|
||||
|
||||
@@ -786,7 +786,7 @@ static inline Ast* parse_ct_for_stmt(Context *context)
|
||||
}
|
||||
ast->ct_for_stmt.value = context->tok.id;
|
||||
TRY_CONSUME_OR(TOKEN_CT_IDENT, "Expected a compile time variable", poisoned_ast);
|
||||
TRY_CONSUME_OR(TOKEN_IN, "Expected 'in'.", poisoned_ast);
|
||||
TRY_CONSUME_OR(TOKEN_COLON, "Expected ':'.", poisoned_ast);
|
||||
ast->ct_for_stmt.expr = TRY_EXPR_OR(parse_expr(context), poisoned_ast);
|
||||
CONSUME_OR(TOKEN_RPAREN, poisoned_ast);
|
||||
ast->ct_for_stmt.body = TRY_AST(parse_stmt(context));
|
||||
@@ -1086,7 +1086,6 @@ Ast *parse_stmt(Context *context)
|
||||
case TOKEN_CT_ENDIF:
|
||||
case TOKEN_CT_ENDSWITCH:
|
||||
case TOKEN_RBRAPIPE:
|
||||
case TOKEN_IN:
|
||||
case TOKEN_BANGBANG:
|
||||
SEMA_TOKEN_ERROR(context->tok, "Unexpected '%s' found when expecting a statement.", token_type_to_string(context->tok.type));
|
||||
advance(context);
|
||||
|
||||
@@ -120,7 +120,7 @@ static inline void parse_translation_unit(Context *context)
|
||||
while (!TOKEN_IS(TOKEN_EOF))
|
||||
{
|
||||
Decl *decl = parse_top_level_statement(context);
|
||||
assert(decl);
|
||||
if (!decl) continue;
|
||||
if (decl_ok(decl))
|
||||
{
|
||||
vec_add(context->global_decls, decl);
|
||||
|
||||
@@ -496,7 +496,7 @@ bool ixxbo(Context *context, Expr *left, Type *type)
|
||||
|
||||
/**
|
||||
* Cast comptime, signed or unsigned -> pointer.
|
||||
* @return true unless the constant value evaluates to zero.
|
||||
* @return true if the cast succeeds.
|
||||
*/
|
||||
bool xipt(Context *context, Expr *left, Type *from, Type *canonical, Type *type, CastType cast_type)
|
||||
{
|
||||
@@ -504,14 +504,11 @@ bool xipt(Context *context, Expr *left, Type *from, Type *canonical, Type *type,
|
||||
if (left->expr_kind == EXPR_CONST)
|
||||
{
|
||||
RETURN_NON_CONST_CAST(CAST_XIPTR);
|
||||
if (bigint_cmp_zero(&left->const_expr.i) != CMP_EQ)
|
||||
if (bigint_cmp_zero(&left->const_expr.i) == CMP_EQ)
|
||||
{
|
||||
SEMA_ERROR(left, "Cannot cast non zero constants into pointers.");
|
||||
return false;
|
||||
expr_const_set_null(&left->const_expr);
|
||||
left->type = type;
|
||||
}
|
||||
expr_const_set_null(&left->const_expr);
|
||||
left->type = type;
|
||||
return true;
|
||||
}
|
||||
if (type_size(from) < type_size(type_voidptr))
|
||||
{
|
||||
|
||||
@@ -106,12 +106,24 @@ static bool sema_analyse_union_members(Context *context, Decl *decl, Decl **memb
|
||||
|
||||
decl->strukt.union_rep = max_alignment_element;
|
||||
|
||||
if (!max_size)
|
||||
{
|
||||
decl->strukt.size = 0;
|
||||
decl->alignment = 1;
|
||||
return true;
|
||||
}
|
||||
|
||||
// The actual size might be larger than the max size due to alignment.
|
||||
unsigned size = aligned_offset(max_size, decl->alignment);
|
||||
|
||||
unsigned rep_size = type_size(members[max_alignment_element]->type);
|
||||
|
||||
// If the actual size is bigger than the real size, add
|
||||
// padding.
|
||||
decl->needs_additional_pad = size > max_size;
|
||||
if (size > rep_size)
|
||||
{
|
||||
decl->strukt.padding = size - rep_size;
|
||||
}
|
||||
|
||||
decl->strukt.size = size;
|
||||
|
||||
@@ -146,36 +158,56 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem
|
||||
|
||||
if (!decl_ok(decl)) return false;
|
||||
|
||||
AlignSize member_alignment = type_abi_alignment(member->type);
|
||||
AlignSize member_natural_alignment = type_abi_alignment(member->type);
|
||||
AlignSize member_alignment = is_packed ? 1 : member_natural_alignment;
|
||||
Attr **attributes = member->attributes;
|
||||
unsigned count = vec_size(attributes);
|
||||
for (unsigned j = 0; j < count; j++)
|
||||
{
|
||||
Attr *attribute = attributes[j];
|
||||
if (!sema_analyse_attribute(context, attribute, ATTR_VAR)) return false;
|
||||
if (TOKSTR(attribute->name) == kw_align)
|
||||
{
|
||||
member_alignment = attribute->alignment;
|
||||
// Update total alignment if we have a member that has bigger alignment.
|
||||
if (member_alignment > decl->alignment) decl->alignment = member_alignment;
|
||||
}
|
||||
}
|
||||
|
||||
// If the member alignment is higher than the currently detected alignment,
|
||||
// then we update the natural alignment
|
||||
if (member_alignment > natural_alignment)
|
||||
if (member_natural_alignment > natural_alignment)
|
||||
{
|
||||
natural_alignment = member_alignment;
|
||||
natural_alignment = member_natural_alignment;
|
||||
}
|
||||
|
||||
// In the case of a struct, we will align this to the next offset,
|
||||
// using the alignment of the member.
|
||||
unsigned align_offset = aligned_offset(offset, member_alignment);
|
||||
|
||||
// Usually we align things, but if this is a packed struct we don't
|
||||
// so offsets may mismatch
|
||||
if (align_offset > offset)
|
||||
unsigned natural_align_offset = aligned_offset(offset, member_natural_alignment);
|
||||
|
||||
// If the natural align is different from the aligned offset we have two cases:
|
||||
if (natural_align_offset != align_offset)
|
||||
{
|
||||
if (is_packed)
|
||||
// If the natural alignment is greater, in this case the struct is unaligned.
|
||||
if (member_natural_alignment > member_alignment)
|
||||
{
|
||||
// If it is packed, we note that the packing made it unaligned.
|
||||
assert(natural_align_offset > align_offset);
|
||||
is_unaligned = true;
|
||||
}
|
||||
else
|
||||
{
|
||||
// Otherwise we insert the (implicit) padding by moving to the aligned offset.
|
||||
offset = align_offset;
|
||||
// Otherwise we have a greater offset, and in this case
|
||||
// we add padding for the difference.
|
||||
assert(natural_align_offset < align_offset);
|
||||
member->padding = align_offset - offset;
|
||||
}
|
||||
}
|
||||
|
||||
offset = align_offset;
|
||||
member->offset = offset;
|
||||
offset += type_size(member->type);
|
||||
;
|
||||
}
|
||||
|
||||
// Set the alignment:
|
||||
@@ -194,7 +226,7 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem
|
||||
// in this case we need an additional padding
|
||||
if (size > aligned_offset(offset, natural_alignment))
|
||||
{
|
||||
decl->needs_additional_pad = true;
|
||||
decl->strukt.padding = size - offset;
|
||||
}
|
||||
|
||||
// If the size is smaller the naturally aligned struct, then it is also unaligned
|
||||
@@ -204,7 +236,8 @@ static bool sema_analyse_struct_members(Context *context, Decl *decl, Decl **mem
|
||||
}
|
||||
if (is_unaligned && size > offset)
|
||||
{
|
||||
decl->needs_additional_pad = true;
|
||||
assert(!decl->strukt.padding);
|
||||
decl->strukt.padding = size - offset;
|
||||
}
|
||||
decl->is_packed = is_unaligned;
|
||||
decl->strukt.size = size;
|
||||
@@ -535,6 +568,8 @@ static const char *attribute_domain_to_string(AttributeDomain domain)
|
||||
{
|
||||
switch (domain)
|
||||
{
|
||||
case ATTR_MEMBER:
|
||||
return "member";
|
||||
case ATTR_FUNC:
|
||||
return "function";
|
||||
case ATTR_VAR:
|
||||
@@ -564,11 +599,11 @@ static AttributeType sema_analyse_attribute(Context *context, Attr *attr, Attrib
|
||||
}
|
||||
static AttributeDomain attribute_domain[NUMBER_OF_ATTRIBUTES] = {
|
||||
[ATTRIBUTE_WEAK] = ATTR_FUNC | ATTR_CONST | ATTR_VAR,
|
||||
[ATTRIBUTE_CNAME] = 0xFF,
|
||||
[ATTRIBUTE_CNAME] = ~0,
|
||||
[ATTRIBUTE_SECTION] = ATTR_FUNC | ATTR_CONST | ATTR_VAR,
|
||||
[ATTRIBUTE_PACKED] = ATTR_STRUCT | ATTR_UNION | ATTR_ERROR,
|
||||
[ATTRIBUTE_NORETURN] = ATTR_FUNC,
|
||||
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION,
|
||||
[ATTRIBUTE_ALIGN] = ATTR_FUNC | ATTR_CONST | ATTR_VAR | ATTR_STRUCT | ATTR_UNION | ATTR_MEMBER,
|
||||
[ATTRIBUTE_INLINE] = ATTR_FUNC,
|
||||
[ATTRIBUTE_NOINLINE] = ATTR_FUNC,
|
||||
[ATTRIBUTE_OPAQUE] = ATTR_STRUCT | ATTR_UNION,
|
||||
@@ -756,14 +791,35 @@ static inline bool sema_analyse_global(Context *context, Decl *decl)
|
||||
if (!sema_resolve_type_info(context, decl->var.type_info)) return false;
|
||||
decl->type = decl->var.type_info->type;
|
||||
}
|
||||
// Check the initializer.
|
||||
if (decl->var.init_expr && decl->type)
|
||||
{
|
||||
if (!sema_analyse_expr_of_required_type(context, decl->type, decl->var.init_expr, false)) return false;
|
||||
if (!decl->var.init_expr->constant)
|
||||
Expr *init_expr = decl->var.init_expr;
|
||||
// 1. Check type.
|
||||
if (!sema_analyse_expr_of_required_type(context, decl->type, init_expr, false)) return false;
|
||||
// 2. Check const-ness
|
||||
if (!init_expr->constant)
|
||||
{
|
||||
|
||||
SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value.");
|
||||
return false;
|
||||
// 3. Special case is when the init expression is the reference
|
||||
// to a constant global structure.
|
||||
if (init_expr->expr_kind == EXPR_CONST_IDENTIFIER)
|
||||
{
|
||||
// 4. If so we copy the init expression, which should always be constant.
|
||||
*init_expr = *init_expr->identifier_expr.decl->var.init_expr;
|
||||
assert(init_expr->constant);
|
||||
}
|
||||
else
|
||||
{
|
||||
if (init_expr->expr_kind == EXPR_CAST)
|
||||
{
|
||||
SEMA_ERROR(decl->var.init_expr, "The expression may not be a non constant cast.");
|
||||
}
|
||||
else
|
||||
{
|
||||
SEMA_ERROR(decl->var.init_expr, "The expression must be a constant value.");
|
||||
}
|
||||
return false;
|
||||
}
|
||||
}
|
||||
if (!decl->type) decl->type = decl->var.init_expr->type;
|
||||
}
|
||||
@@ -838,11 +894,18 @@ static inline bool sema_analyse_define(Context *context, Decl *decl)
|
||||
static inline bool sema_analyse_error(Context *context __unused, Decl *decl)
|
||||
{
|
||||
if (!sema_analyse_struct_union(context, decl)) return false;
|
||||
if (decl->strukt.size > type_size(type_usize))
|
||||
ByteSize error_full_size = type_size(type_voidptr);
|
||||
if (decl->strukt.size > error_full_size)
|
||||
{
|
||||
SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", type_size(type_usize), decl->strukt.size);
|
||||
SEMA_ERROR(decl, "Error type may not exceed pointer size (%d bytes) it was %d bytes.", error_full_size, decl->strukt.size);
|
||||
return false;
|
||||
}
|
||||
|
||||
if (decl->strukt.size < error_full_size)
|
||||
{
|
||||
decl->strukt.padding = error_full_size - decl->strukt.size;
|
||||
decl->strukt.size = error_full_size;
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
|
||||
@@ -199,6 +199,7 @@ static inline bool sema_cast_ident_rvalue(Context *context, Type *to, Expr *expr
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_CONST:
|
||||
if (!type_is_builtin(decl->type->type_kind)) break;
|
||||
expr_replace(expr, expr_copy_from_macro(context, decl->var.init_expr));
|
||||
return sema_analyse_expr(context, to, expr);
|
||||
case VARDECL_PARAM_EXPR:
|
||||
@@ -254,9 +255,70 @@ static inline bool sema_type_error_on_binop(Context *context, Expr *expr)
|
||||
|
||||
static bool expr_cast_to_index(Context *context, Expr *index)
|
||||
{
|
||||
printf("TODO: consider changing indexing size\n");
|
||||
if (index->type->canonical->type_kind == type_usize->canonical->type_kind) return true;
|
||||
return cast_implicit(context, index, type_isize);
|
||||
switch (index->type->canonical->type_kind)
|
||||
{
|
||||
case TYPE_IXX:
|
||||
if (!bigint_fits_in_bits(&index->const_expr.i, 64, true))
|
||||
{
|
||||
SEMA_ERROR(index, "The index is out of range, it must fit in a signed 64 bit integer.");
|
||||
return false;
|
||||
}
|
||||
return cast_implicit(context, index, type_isize);
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
return cast_implicit(context, index, type_int);
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
return cast_implicit(context, index, type_uint);
|
||||
case TYPE_I32:
|
||||
case TYPE_U32:
|
||||
case TYPE_I64:
|
||||
case TYPE_U64:
|
||||
// This is fine.
|
||||
return true;
|
||||
case TYPE_U128:
|
||||
SEMA_ERROR(index, "You need to explicitly cast this to a uint or ulong.");
|
||||
return false;
|
||||
case TYPE_I128:
|
||||
SEMA_ERROR(index, "You need to explicitly cast this to a int or long.");
|
||||
return false;
|
||||
default:
|
||||
SEMA_ERROR(index, "Cannot implicitly convert '%s' to an index.", type_to_error_string(index->type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool expr_cast_to_index_size(Context *context, Expr *index)
|
||||
{
|
||||
switch (index->type->canonical->type_kind)
|
||||
{
|
||||
case TYPE_IXX:
|
||||
if (!bigint_fits_in_bits(&index->const_expr.i, 64, true))
|
||||
{
|
||||
SEMA_ERROR(index, "The index is out of range, it must fit in a signed 64 bit integer.");
|
||||
return false;
|
||||
}
|
||||
return cast_implicit(context, index, type_isize);
|
||||
case TYPE_U8:
|
||||
case TYPE_U16:
|
||||
case TYPE_U32:
|
||||
case TYPE_U64:
|
||||
return cast_implicit(context, index, type_usize);
|
||||
case TYPE_I8:
|
||||
case TYPE_I16:
|
||||
case TYPE_I32:
|
||||
case TYPE_I64:
|
||||
return cast_implicit(context, index, type_isize);
|
||||
case TYPE_U128:
|
||||
SEMA_ERROR(index, "You need to explicitly cast this to a usize.");
|
||||
return false;
|
||||
case TYPE_I128:
|
||||
SEMA_ERROR(index, "You need to explicitly cast this to a size.");
|
||||
return false;
|
||||
default:
|
||||
SEMA_ERROR(index, "Cannot implicitly convert '%s' to an index.", type_to_error_string(index->type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static inline bool sema_expr_analyse_ternary(Context *context, Type *to, Expr *expr)
|
||||
@@ -1204,6 +1266,9 @@ static inline bool sema_expr_analyse_call(Context *context, Type *to, Expr *expr
|
||||
return true;
|
||||
}
|
||||
FALLTHROUGH;
|
||||
case EXPR_TYPEINFO:
|
||||
SEMA_ERROR(expr, "A type cannot be followed by (), did you mean to use ({})?");
|
||||
return false;
|
||||
default:
|
||||
SEMA_ERROR(expr, "This value cannot be invoked, did you accidentally add ()?");
|
||||
return false;
|
||||
@@ -1358,12 +1423,12 @@ static inline bool sema_expr_analyse_subscript(Context *context, Expr *expr)
|
||||
SEMA_ERROR(subscripted, "Cannot index '%s'.", type_to_error_string(type));
|
||||
return false;
|
||||
}
|
||||
if (!sema_analyse_expr(context, type_isize, index)) return false;
|
||||
if (!sema_analyse_expr(context, type_int, index)) return false;
|
||||
|
||||
expr->constant = index->constant & subscripted->constant;
|
||||
expr->pure = index->pure & subscripted->pure;
|
||||
|
||||
// Unless we already have type_usize, cast to type_isize;
|
||||
// Cast to an appropriate type for index.
|
||||
if (!expr_cast_to_index(context, index)) return false;
|
||||
|
||||
// Check range
|
||||
@@ -1397,16 +1462,16 @@ static inline bool sema_expr_analyse_slice(Context *context, Expr *expr)
|
||||
}
|
||||
expr->slice_expr.expr = current_expr;
|
||||
|
||||
if (!sema_analyse_expr(context, type_isize, start)) return false;
|
||||
if (!sema_analyse_expr(context, type_int, start)) return false;
|
||||
expr->pure &= start->pure;
|
||||
expr->constant &= start->constant;
|
||||
if (end && !sema_analyse_expr(context, type_isize, end)) return false;
|
||||
if (end && !sema_analyse_expr(context, type_int, end)) return false;
|
||||
expr->pure &= !end || end->pure;
|
||||
expr->constant &= !end || end->constant;
|
||||
|
||||
// Unless we already have type_usize, cast to type_isize;
|
||||
if (!expr_cast_to_index(context, start)) return false;
|
||||
if (end && !expr_cast_to_index(context, end)) return false;
|
||||
// Fix index sizes
|
||||
if (!expr_cast_to_index_size(context, start)) return false;
|
||||
if (end && !expr_cast_to_index_size(context, end)) return false;
|
||||
|
||||
// Check range
|
||||
if (type->type_kind == TYPE_POINTER)
|
||||
@@ -1992,7 +2057,7 @@ static Decl *sema_resolve_element_for_name(Decl** decls, DesignatorElement **ele
|
||||
|
||||
static int64_t sema_analyse_designator_index(Context *context, Expr *index)
|
||||
{
|
||||
if (!sema_analyse_expr_value(context, type_usize, index))
|
||||
if (!sema_analyse_expr_value(context, type_uint, index))
|
||||
{
|
||||
return -1;
|
||||
}
|
||||
@@ -2093,12 +2158,13 @@ static Type *sema_expr_analyse_designator(Context *context, Type *current, Expr
|
||||
bool did_report_error = false;
|
||||
for (unsigned i = 0; i < vec_size(path); i++)
|
||||
{
|
||||
current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error);
|
||||
if (!current) break;
|
||||
}
|
||||
if (!current && !did_report_error)
|
||||
{
|
||||
SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current));
|
||||
Type *new_current = sema_find_type_of_element(context, current, path, &i, &is_constant, &did_report_error);
|
||||
if (!new_current)
|
||||
{
|
||||
if (!did_report_error) SEMA_ERROR(expr, "This is not a valid member of '%s'.", type_to_error_string(current));
|
||||
return NULL;
|
||||
}
|
||||
current = new_current;
|
||||
}
|
||||
expr->constant = is_constant;
|
||||
expr->pure = is_constant;
|
||||
@@ -2112,15 +2178,17 @@ static void sema_update_const_initializer_with_designator(ConstInitializer *cons
|
||||
|
||||
static void sema_create_const_initializer_value(ConstInitializer *const_init, Expr *value)
|
||||
{
|
||||
// Possibly this is already a const initializers, in that case
|
||||
// overwrite what is inside, eg [1] = { .a = 1 }
|
||||
if (value->expr_kind == EXPR_INITIALIZER_LIST && value->initializer_expr.init_type == INITIALIZER_CONST)
|
||||
{
|
||||
*const_init = *value->initializer_expr.initializer;
|
||||
value->initializer_expr.initializer = const_init;
|
||||
return;
|
||||
}
|
||||
const_init->init_value = value;
|
||||
const_init->type = value->type->canonical;
|
||||
const_init->kind = CONST_VALUE;
|
||||
const_init->value = value;
|
||||
const_init->kind = CONST_INIT_VALUE;
|
||||
}
|
||||
|
||||
static bool is_empty_initializer_list(Expr *value)
|
||||
@@ -2129,44 +2197,71 @@ static bool is_empty_initializer_list(Expr *value)
|
||||
}
|
||||
static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer);
|
||||
|
||||
/**
|
||||
* Update a struct element, e.g. { .a = 1 } or { .a[12] = { .b } }
|
||||
*/
|
||||
static inline void sema_update_const_initializer_with_designator_struct(ConstInitializer *const_init,
|
||||
DesignatorElement **curr,
|
||||
DesignatorElement **end,
|
||||
Expr *value)
|
||||
{
|
||||
// Get the current path element that we're processing
|
||||
DesignatorElement *element = curr[0];
|
||||
assert(element->kind == DESIGNATOR_FIELD);
|
||||
DesignatorElement **next_element = curr + 1;
|
||||
bool at_end = next_element == end;
|
||||
// Optimize in case this is a zero.
|
||||
if (at_end && is_empty_initializer_list(value))
|
||||
bool is_last_path_element = next_element == end;
|
||||
|
||||
// Optimize in case this is a zero, e.g. [12].b = {}
|
||||
if (is_last_path_element && is_empty_initializer_list(value))
|
||||
{
|
||||
const_init->kind = CONST_INIT_ZERO;
|
||||
return;
|
||||
}
|
||||
Decl **elements = const_init->type->decl->strukt.members;
|
||||
|
||||
// Convert a zero struct and expand it into all its parts.
|
||||
if (const_init->kind == CONST_INIT_ZERO)
|
||||
{
|
||||
Decl **elements = const_init->type->decl->strukt.members;
|
||||
// Allocate array containing all elements { a, b, c ... }
|
||||
ConstInitializer **const_inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements));
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
ConstInitializer *element_init = CALLOCS(ConstInitializer);
|
||||
// Create zero initializers for each of those { a: zeroinit, b: zeroinit, ... }
|
||||
ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer));
|
||||
element_init->type = elements[i]->type->canonical;
|
||||
element_init->kind = CONST_INIT_ZERO;
|
||||
const_inits[i] = element_init;
|
||||
}
|
||||
const_init->elements = const_inits;
|
||||
const_init->kind = CONST_INIT_EXPANDED;
|
||||
// Change type to CONST_INIT_STRUCT since we expanded.
|
||||
const_init->init_struct = const_inits;
|
||||
const_init->kind = CONST_INIT_STRUCT;
|
||||
}
|
||||
ConstInitializer *sub_element = const_init->elements[element->index];
|
||||
if (!at_end)
|
||||
|
||||
// We should always have expanded the struct at this point.
|
||||
assert(const_init->kind == CONST_INIT_STRUCT);
|
||||
|
||||
// Find the ConstInitializer to change
|
||||
ConstInitializer *sub_element = const_init->init_struct[element->index];
|
||||
|
||||
// If this isn't the last element, we recurse.
|
||||
if (!is_last_path_element)
|
||||
{
|
||||
sema_update_const_initializer_with_designator(sub_element, next_element, end, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise we update the value in that particular element.
|
||||
sema_create_const_initializer_value(sub_element, value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Update a union element, which is different from structs, since we're here
|
||||
* only keeping a single value.
|
||||
* Note that if we have two fields "a" and "b", then in this case { .b = 2, .a = 1 },
|
||||
* we will completely discard the information in ".b = 2", even if there had been
|
||||
* an overlap. In essence we're implicitly clearing the memory when we assign to .a, meaning
|
||||
* we are allowed to completely overwrite the "memory" of .b
|
||||
*/
|
||||
static inline void sema_update_const_initializer_with_designator_union(ConstInitializer *const_init,
|
||||
DesignatorElement **curr,
|
||||
DesignatorElement **end,
|
||||
@@ -2174,123 +2269,58 @@ static inline void sema_update_const_initializer_with_designator_union(ConstInit
|
||||
{
|
||||
DesignatorElement *element = curr[0];
|
||||
assert(element->kind == DESIGNATOR_FIELD);
|
||||
ConstInitializer *sub_element = const_init->union_const.element;
|
||||
ConstInitializer *sub_element = const_init->init_union.element;
|
||||
|
||||
// If it's an empty initializer, just clear everything back to CONST_INIT_ZERO
|
||||
// That is we get for example: { .b = { 1, 3 }, .a = { } }. This would then simply
|
||||
// become { }
|
||||
DesignatorElement **next_element = curr + 1;
|
||||
bool is_at_end = next_element == end;
|
||||
if (is_at_end && is_empty_initializer_list(value))
|
||||
bool is_at_last_path_element = next_element == end;
|
||||
if (is_at_last_path_element && is_empty_initializer_list(value))
|
||||
{
|
||||
const_init->kind = CONST_INIT_ZERO;
|
||||
return;
|
||||
}
|
||||
|
||||
// If we currently have a zero, then we create a sub element that is zero.
|
||||
if (const_init->kind == CONST_INIT_ZERO)
|
||||
{
|
||||
sub_element = const_init->union_const.element = CALLOCS(ConstInitializer);
|
||||
sub_element = const_init->init_union.element = CALLOCS(ConstInitializer);
|
||||
sub_element->kind = CONST_INIT_ZERO;
|
||||
const_init->union_const.element = sub_element;
|
||||
const_init->init_union.element = sub_element;
|
||||
}
|
||||
else if (element->index != const_init->union_const.index)
|
||||
else if (element->index != const_init->init_union.index)
|
||||
{
|
||||
// We will zero out the sub element if it wasn't a union
|
||||
sub_element->kind = CONST_INIT_ZERO;
|
||||
sub_element->type = value->type;
|
||||
}
|
||||
|
||||
// Update of the sub element.
|
||||
sub_element->type = const_init->type->decl->strukt.members[element->index]->type->canonical;
|
||||
const_init->union_const.index = element->index;
|
||||
const_init->kind = CONST_SELECTED;
|
||||
if (!is_at_end)
|
||||
|
||||
// And the index
|
||||
const_init->init_union.index = element->index;
|
||||
|
||||
// And the type
|
||||
const_init->kind = CONST_INIT_UNION;
|
||||
|
||||
// If we're not at the last element in the path, descend further.
|
||||
if (!is_at_last_path_element)
|
||||
{
|
||||
sema_update_const_initializer_with_designator(sub_element, next_element, end, value);
|
||||
return;
|
||||
}
|
||||
|
||||
// Otherwise just set the current type.
|
||||
sema_create_const_initializer_value(sub_element, value);
|
||||
}
|
||||
|
||||
|
||||
|
||||
static inline ConstInitializer *sema_find_const_init_array_slice(ConstInitializer *const_init, ArrayIndex index)
|
||||
{
|
||||
if (!const_init) return NULL;
|
||||
switch (const_init->kind)
|
||||
{
|
||||
case CONST_INIT_ARRAY_SPLIT:
|
||||
{
|
||||
ConstInitializer *found;
|
||||
if ((found = sema_find_const_init_array_slice(const_init->split_const.low, index))) return found;
|
||||
if ((found = sema_find_const_init_array_slice(const_init->split_const.mid, index))) return found;
|
||||
if ((found = sema_find_const_init_array_slice(const_init->split_const.hi, index))) return found;
|
||||
return false;
|
||||
}
|
||||
case CONST_INIT_ARRAY_RANGE_ZERO:
|
||||
if (const_init->array_range_zero.low > index || const_init->array_range_zero.high < index) return NULL;
|
||||
return const_init;
|
||||
case CONST_INIT_ARRAY_VALUE_FRAGMENT:
|
||||
return const_init->single_array_index.index == index ? const_init : NULL;
|
||||
default:
|
||||
UNREACHABLE
|
||||
}
|
||||
}
|
||||
|
||||
static inline ConstInitializer *sema_split_const_init_array_slice(ConstInitializer *const_init, ArrayIndex index)
|
||||
{
|
||||
switch (const_init->kind)
|
||||
{
|
||||
case CONST_INIT_ARRAY_SPLIT:
|
||||
UNREACHABLE
|
||||
case CONST_INIT_ARRAY_RANGE_ZERO:
|
||||
{
|
||||
// Special case: single element.
|
||||
if (const_init->array_range_zero.low == const_init->array_range_zero.high)
|
||||
{
|
||||
const_init->kind = CONST_INIT_ARRAY_VALUE_FRAGMENT;
|
||||
const_init->type = const_init->type->array.base;
|
||||
const_init->single_array_index.index = index;
|
||||
const_init->single_array_index.element = MALLOC(sizeof(ConstInitializer));
|
||||
const_init->single_array_index.element->type = const_init->type->array.base;
|
||||
const_init->single_array_index.element->kind = CONST_INIT_ZERO;
|
||||
return const_init;
|
||||
}
|
||||
ConstInitializer *low = NULL;
|
||||
ConstInitializer *hi = NULL;
|
||||
if (const_init->array_range_zero.low < index)
|
||||
{
|
||||
low = MALLOC(sizeof(ConstInitializer));
|
||||
low->kind = CONST_INIT_ARRAY_RANGE_ZERO;
|
||||
low->array_range_zero.low = const_init->array_range_zero.low;
|
||||
low->array_range_zero.high = index - 1;
|
||||
low->type = type_get_array(const_init->type->array.base, low->array_range_zero.high - low->array_range_zero.low + 1);
|
||||
}
|
||||
if (const_init->array_range_zero.high > index)
|
||||
{
|
||||
hi = MALLOC(sizeof(ConstInitializer));
|
||||
hi->kind = CONST_INIT_ARRAY_RANGE_ZERO;
|
||||
hi->array_range_zero.low = index + 1;
|
||||
hi->array_range_zero.high = const_init->array_range_zero.high;
|
||||
hi->type = type_get_array(const_init->type->array.base, hi->array_range_zero.high - hi->array_range_zero.low + 1);
|
||||
}
|
||||
ConstInitializer *mid = MALLOC(sizeof(ConstInitializer));
|
||||
mid->kind = CONST_INIT_ARRAY_VALUE_FRAGMENT;
|
||||
mid->type = const_init->type->array.base;
|
||||
mid->single_array_index.index = index;
|
||||
mid->single_array_index.element = MALLOC(sizeof(ConstInitializer));
|
||||
mid->single_array_index.element->type = const_init->type->array.base;
|
||||
mid->single_array_index.element->kind = CONST_INIT_ZERO;
|
||||
const_init->split_const.low = low;
|
||||
const_init->split_const.mid = mid;
|
||||
const_init->split_const.hi = hi;
|
||||
const_init->kind = CONST_INIT_ARRAY_SPLIT;
|
||||
return mid;
|
||||
}
|
||||
case CONST_INIT_ARRAY_VALUE_FRAGMENT:
|
||||
return const_init;
|
||||
case CONST_INIT_ZERO:
|
||||
case CONST_INIT_EXPANDED:
|
||||
case CONST_SELECTED:
|
||||
case CONST_VALUE:
|
||||
return NULL;
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
/**
|
||||
* Update an array { [2] = 1 }
|
||||
*/
|
||||
static inline void sema_update_const_initializer_with_designator_array(ConstInitializer *const_init,
|
||||
DesignatorElement **curr,
|
||||
DesignatorElement **end,
|
||||
@@ -2300,25 +2330,91 @@ static inline void sema_update_const_initializer_with_designator_array(ConstInit
|
||||
ArrayIndex low_index = element->index;
|
||||
ArrayIndex high_index = element->kind == DESIGNATOR_RANGE ? element->index_end : element->index;
|
||||
assert(element->kind == DESIGNATOR_ARRAY || element->kind == DESIGNATOR_RANGE);
|
||||
|
||||
// Expand zero into array.
|
||||
if (const_init->kind == CONST_INIT_ZERO)
|
||||
{
|
||||
const_init->kind = CONST_INIT_ARRAY_RANGE_ZERO;
|
||||
const_init->array_range_zero.low = 0;
|
||||
const_init->array_range_zero.high = const_init->type->array.len - 1;
|
||||
const_init->kind = CONST_INIT_ARRAY;
|
||||
const_init->init_array.elements = NULL;
|
||||
}
|
||||
|
||||
Type *element_type = const_init->type->array.base;
|
||||
DesignatorElement **next_element = curr + 1;
|
||||
bool is_direct_update = next_element == end;
|
||||
bool is_last_path_element = next_element == end;
|
||||
|
||||
// Get all the elements in the array
|
||||
ConstInitializer **array_elements = const_init->init_array.elements;
|
||||
|
||||
unsigned array_count = vec_size(array_elements);
|
||||
|
||||
ArrayIndex insert_index = 0;
|
||||
|
||||
for (ArrayIndex index = low_index; index <= high_index; index++)
|
||||
{
|
||||
ConstInitializer *sub_element = sema_find_const_init_array_slice(const_init, index);
|
||||
sub_element = sema_split_const_init_array_slice(sub_element, index)->single_array_index.element;
|
||||
if (!is_direct_update)
|
||||
// Walk to the insert point or until we reached the end of the array.
|
||||
while (insert_index < array_count && array_elements[insert_index]->init_array_value.index < index)
|
||||
{
|
||||
sema_update_const_initializer_with_designator(sub_element, next_element, end, value);
|
||||
insert_index++;
|
||||
}
|
||||
// Pick up the initializer at the insert point.
|
||||
ConstInitializer *initializer = insert_index < array_count ? array_elements[insert_index] : NULL;
|
||||
ConstInitializer *inner_value;
|
||||
|
||||
// If we don't have an initializer, the location needs to be at the end.
|
||||
// Create and append:
|
||||
if (!initializer)
|
||||
{
|
||||
initializer = MALLOC(sizeof(ConstInitializer));
|
||||
initializer->type = element_type;
|
||||
initializer->kind = CONST_INIT_ARRAY_VALUE;
|
||||
initializer->init_array_value.index = index;
|
||||
inner_value = MALLOC(sizeof(ConstInitializer));
|
||||
inner_value->type = element_type;
|
||||
inner_value->kind = CONST_INIT_ZERO;
|
||||
initializer->init_array_value.element = inner_value;
|
||||
vec_add(array_elements, initializer);
|
||||
array_count++;
|
||||
}
|
||||
else
|
||||
{
|
||||
// If we already have an initializer "found"
|
||||
// it might be after the index. In this case, we
|
||||
// need to do an insert.
|
||||
if (initializer->init_array_value.index != insert_index)
|
||||
{
|
||||
assert(initializer->init_array_value.index > insert_index);
|
||||
|
||||
// First we add a null at the end.
|
||||
vec_add(array_elements, NULL);
|
||||
// Shift all values one step up:
|
||||
for (unsigned i = array_count; i > insert_index; i--)
|
||||
{
|
||||
array_elements[i] = array_elements[i - 1];
|
||||
}
|
||||
// Then we create our new entry.
|
||||
initializer = MALLOC(sizeof(ConstInitializer));
|
||||
initializer->type = element_type;
|
||||
initializer->kind = CONST_INIT_ARRAY_VALUE;
|
||||
initializer->init_array_value.index = index;
|
||||
inner_value = MALLOC(sizeof(ConstInitializer));
|
||||
inner_value->type = element_type;
|
||||
inner_value->kind = CONST_INIT_ZERO;
|
||||
initializer->init_array_value.element = inner_value;
|
||||
// And assign it to the location.
|
||||
array_elements[insert_index] = initializer;
|
||||
}
|
||||
}
|
||||
|
||||
const_init->init_array.elements = array_elements;
|
||||
inner_value = initializer->init_array_value.element;
|
||||
|
||||
// Update
|
||||
if (!is_last_path_element)
|
||||
{
|
||||
sema_update_const_initializer_with_designator(inner_value, next_element, end, value);
|
||||
continue;
|
||||
}
|
||||
sema_create_const_initializer_value(sub_element, value);
|
||||
sema_create_const_initializer_value(inner_value, value);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2347,10 +2443,11 @@ static inline void sema_update_const_initializer_with_designator(
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
static void sema_create_const_initializer(ConstInitializer *const_init, Expr *initializer)
|
||||
{
|
||||
const_init->type = initializer->type->canonical;
|
||||
const_init->kind = CONST_INIT_ZERO;
|
||||
const_init->type = initializer->type->canonical;
|
||||
Expr **init_expressions = initializer->initializer_expr.initializer_expr;
|
||||
VECEACH(init_expressions, i)
|
||||
{
|
||||
@@ -2377,56 +2474,57 @@ static void debug_dump_const_initializer(ConstInitializer *init, const char *nam
|
||||
case CONST_INIT_ZERO:
|
||||
printf(" = 0\n");
|
||||
return;
|
||||
case CONST_SELECTED:
|
||||
case CONST_INIT_UNION:
|
||||
{
|
||||
printf(" ->\n");
|
||||
Decl** members = init->type->decl->strukt.members;
|
||||
debug_dump_const_initializer(init->union_const.element, members[init->union_const.index]->name, indent + 1);
|
||||
debug_dump_const_initializer(init->init_union.element, members[init->init_union.index]->name, indent + 1);
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_EXPANDED:
|
||||
case CONST_INIT_STRUCT:
|
||||
{
|
||||
printf(" ->\n");
|
||||
Decl** members = init->type->decl->strukt.members;
|
||||
unsigned member_count = vec_size(members);
|
||||
for (unsigned i = 0; i < member_count; i++)
|
||||
{
|
||||
debug_dump_const_initializer(init->elements[i], members[i]->name, indent + 1);
|
||||
debug_dump_const_initializer(init->init_struct[i], members[i]->name, indent + 1);
|
||||
}
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_ARRAY_VALUE_FRAGMENT:
|
||||
case CONST_INIT_ARRAY_VALUE:
|
||||
{
|
||||
printf(" [%llu] ->\n", (unsigned long long)init->single_array_index.index);
|
||||
debug_dump_const_initializer(init->single_array_index.element, "", indent + 1);
|
||||
printf(" [%llu] ->\n", (unsigned long long)init->init_array_value.index);
|
||||
debug_dump_const_initializer(init->init_array_value.element, "", indent + 1);
|
||||
return;
|
||||
}
|
||||
case CONST_VALUE:
|
||||
case CONST_INIT_VALUE:
|
||||
{
|
||||
assert(init->value->expr_kind == EXPR_CONST);
|
||||
assert(init->init_value->expr_kind == EXPR_CONST);
|
||||
printf(" = ");
|
||||
expr_const_fprint(stdout, &init->value->const_expr);
|
||||
expr_const_fprint(stdout, &init->init_value->const_expr);
|
||||
puts("");
|
||||
return;
|
||||
}
|
||||
case CONST_INIT_ARRAY_SPLIT:
|
||||
case CONST_INIT_ARRAY:
|
||||
printf(" [//] ->\n");
|
||||
if (init->split_const.low)
|
||||
{
|
||||
debug_dump_const_initializer(init->split_const.low, NULL, indent + 1);
|
||||
}
|
||||
if (init->split_const.mid)
|
||||
{
|
||||
debug_dump_const_initializer(init->split_const.mid, NULL, indent + 1);
|
||||
}
|
||||
if (init->split_const.hi)
|
||||
{
|
||||
debug_dump_const_initializer(init->split_const.hi, NULL, indent + 1);
|
||||
VECEACH(init->init_array.elements, i)
|
||||
{
|
||||
debug_dump_const_initializer(init->init_array.elements[i], NULL, indent + 1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
case CONST_INIT_ARRAY_RANGE_ZERO:
|
||||
printf(" [%llu .. %llu] = 0\n", (unsigned long long)init->array_range_zero.low, (unsigned long long)init->array_range_zero.high);
|
||||
case CONST_INIT_ARRAY_FULL:
|
||||
printf(" [: ->\n");
|
||||
{
|
||||
VECEACH(init->init_array_full, i)
|
||||
{
|
||||
debug_dump_const_initializer(init->init_array_full[i], NULL, indent + 1);
|
||||
}
|
||||
}
|
||||
return;
|
||||
|
||||
}
|
||||
UNREACHABLE
|
||||
}
|
||||
@@ -2456,6 +2554,7 @@ static bool sema_expr_analyse_designated_initializer(Context *context, Type *ass
|
||||
{
|
||||
ConstInitializer *const_init = MALLOC(sizeof(ConstInitializer));
|
||||
sema_create_const_initializer(const_init, initializer);
|
||||
debug_dump_const_initializer(const_init, "TOP", 0);
|
||||
initializer->initializer_expr.init_type = INITIALIZER_CONST;
|
||||
initializer->initializer_expr.initializer = const_init;
|
||||
return true;
|
||||
@@ -2523,7 +2622,8 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context,
|
||||
if (initializer->constant)
|
||||
{
|
||||
ConstInitializer *const_init = CALLOCS(ConstInitializer);
|
||||
const_init->kind = CONST_INIT_EXPANDED;
|
||||
const_init->kind = CONST_INIT_STRUCT;
|
||||
const_init->type = initializer->type->canonical;
|
||||
ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements));
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
@@ -2538,7 +2638,11 @@ static inline bool sema_expr_analyse_struct_plain_initializer(Context *context,
|
||||
sema_create_const_initializer_value(element_init, expr);
|
||||
inits[i] = element_init;
|
||||
}
|
||||
const_init->init_struct = inits;
|
||||
initializer->initializer_expr.init_type = INITIALIZER_CONST;
|
||||
initializer->initializer_expr.initializer = const_init;
|
||||
}
|
||||
|
||||
// 7. Done!
|
||||
return true;
|
||||
}
|
||||
@@ -2593,6 +2697,30 @@ static inline bool sema_expr_analyse_array_plain_initializer(Context *context, T
|
||||
return false;
|
||||
}
|
||||
|
||||
if (initializer->constant)
|
||||
{
|
||||
ConstInitializer *const_init = CALLOCS(ConstInitializer);
|
||||
const_init->kind = CONST_INIT_ARRAY_FULL;
|
||||
const_init->type = initializer->type->canonical;
|
||||
ConstInitializer **inits = MALLOC(sizeof(ConstInitializer *) * vec_size(elements));
|
||||
VECEACH(elements, i)
|
||||
{
|
||||
Expr *expr = elements[i];
|
||||
if (expr->expr_kind == EXPR_INITIALIZER_LIST)
|
||||
{
|
||||
assert(expr->constant);
|
||||
inits[i] = expr->initializer_expr.initializer;
|
||||
continue;
|
||||
}
|
||||
ConstInitializer *element_init = MALLOC(sizeof(ConstInitializer));
|
||||
sema_create_const_initializer_value(element_init, expr);
|
||||
inits[i] = element_init;
|
||||
}
|
||||
const_init->init_array_full = inits;
|
||||
initializer->initializer_expr.init_type = INITIALIZER_CONST;
|
||||
initializer->initializer_expr.initializer = const_init;
|
||||
}
|
||||
|
||||
// 7. Done!
|
||||
return true;
|
||||
}
|
||||
@@ -3116,7 +3244,7 @@ static bool sema_expr_analyse_sub(Context *context, Type *to, Expr *expr, Expr *
|
||||
// 6. No need for further casts, just it is an integer.
|
||||
if (!type_is_integer(right_type))
|
||||
{
|
||||
SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(left_type), type_to_error_string(right_type));
|
||||
SEMA_ERROR(expr, "Cannot subtract '%s' from '%s'", type_to_error_string(right_type), type_to_error_string(left_type));
|
||||
return false;
|
||||
}
|
||||
|
||||
@@ -3443,10 +3571,13 @@ static bool sema_expr_analyse_mod(Context *context, Type *to, Expr *expr, Expr *
|
||||
// 1. Analyse both sides.
|
||||
if (!sema_expr_analyse_binary_sub_expr(context, to, left, right)) return false;
|
||||
|
||||
// 2. Make sure we have some sort of integer on both sides.
|
||||
if (!type_is_any_integer(right->type->canonical) || !type_is_any_integer(left->type->canonical))
|
||||
Type *left_type = left->type->canonical;
|
||||
Type *right_type = right->type->canonical;
|
||||
|
||||
// 2. Perform promotion to a common type.
|
||||
if (!binary_arithmetic_promotion(context, left, right, left_type, right_type, expr, "Cannot divide '%s' by '%s'."))
|
||||
{
|
||||
return sema_type_error_on_binop(context, expr);
|
||||
return false;
|
||||
}
|
||||
|
||||
// 3. a % 0 is not valid, so detect it.
|
||||
@@ -3852,13 +3983,16 @@ static bool sema_expr_analyse_deref(Context *context, Expr *expr, Expr *inner)
|
||||
return true;
|
||||
}
|
||||
|
||||
static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl)
|
||||
static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl, bool *is_constant)
|
||||
{
|
||||
if (decl->decl_kind != DECL_VAR) return false;
|
||||
*is_constant = false;
|
||||
switch (decl->var.kind)
|
||||
{
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_GLOBAL:
|
||||
*is_constant = true;
|
||||
return true;
|
||||
case VARDECL_LOCAL:
|
||||
case VARDECL_PARAM:
|
||||
case VARDECL_PARAM_REF:
|
||||
return true;
|
||||
@@ -3886,7 +4020,7 @@ static inline bool sema_take_addr_of_var(Expr *expr, Decl *decl)
|
||||
UNREACHABLE
|
||||
}
|
||||
|
||||
static inline bool sema_take_addr_of_ident(Expr *inner)
|
||||
static inline bool sema_take_addr_of_ident(Expr *inner, bool *is_constant)
|
||||
{
|
||||
Decl *decl = inner->identifier_expr.decl;
|
||||
decl = decl_raw(decl);
|
||||
@@ -3894,16 +4028,17 @@ static inline bool sema_take_addr_of_ident(Expr *inner)
|
||||
{
|
||||
case DECL_ENUM_CONSTANT:
|
||||
case DECL_FUNC:
|
||||
*is_constant = true;
|
||||
return true;
|
||||
case DECL_VAR:
|
||||
return sema_take_addr_of_var(inner, decl);
|
||||
return sema_take_addr_of_var(inner, decl, is_constant);
|
||||
default:
|
||||
SEMA_ERROR(inner, "It is not possible to take the address of a '%s'.", type_to_error_string(inner->type));
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
static bool sema_take_addr_of(Expr *inner)
|
||||
static bool sema_take_addr_of(Expr *inner, bool *is_constant)
|
||||
{
|
||||
switch (inner->expr_kind)
|
||||
{
|
||||
@@ -3916,18 +4051,28 @@ static bool sema_take_addr_of(Expr *inner)
|
||||
return false;
|
||||
case EXPR_IDENTIFIER:
|
||||
case EXPR_CONST_IDENTIFIER:
|
||||
return sema_take_addr_of_ident(inner);
|
||||
return sema_take_addr_of_ident(inner, is_constant);
|
||||
case EXPR_UNARY:
|
||||
*is_constant = inner->constant;
|
||||
if (inner->unary_expr.operator == UNARYOP_DEREF) return true;
|
||||
break;
|
||||
case EXPR_ACCESS:
|
||||
return sema_take_addr_of(inner->access_expr.parent);
|
||||
return sema_take_addr_of(inner->access_expr.parent, is_constant);
|
||||
case EXPR_GROUP:
|
||||
return sema_take_addr_of(inner->group_expr);
|
||||
return sema_take_addr_of(inner->group_expr, is_constant);
|
||||
case EXPR_SUBSCRIPT:
|
||||
return sema_take_addr_of(inner->subscript_expr.expr);
|
||||
{
|
||||
bool index_was_const = false;
|
||||
if (!sema_take_addr_of(inner->subscript_expr.expr, &index_was_const)) return false;
|
||||
*is_constant = index_was_const && inner->constant;
|
||||
return true;
|
||||
}
|
||||
case EXPR_SLICE:
|
||||
return sema_take_addr_of(inner->slice_expr.expr);
|
||||
{
|
||||
*is_constant = false;
|
||||
bool dummy;
|
||||
return sema_take_addr_of(inner->slice_expr.expr, &dummy);
|
||||
}
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -3958,11 +4103,12 @@ static bool sema_expr_analyse_addr(Context *context, Type *to, Expr *expr, Expr
|
||||
}
|
||||
|
||||
// 2. Take the address.
|
||||
if (!sema_take_addr_of(inner)) return expr_poison(expr);
|
||||
bool is_constant = false;
|
||||
if (!sema_take_addr_of(inner, &is_constant)) return expr_poison(expr);
|
||||
|
||||
// 3. Get the pointer of the underlying type.
|
||||
expr->type = type_get_ptr(inner->type);
|
||||
expr->constant = inner->constant;
|
||||
expr->constant = is_constant;
|
||||
expr->pure = inner->pure;
|
||||
|
||||
return true;
|
||||
|
||||
@@ -232,8 +232,6 @@ const char *token_type_to_string(TokenType type)
|
||||
return "if";
|
||||
case TOKEN_IMPORT:
|
||||
return "import";
|
||||
case TOKEN_IN:
|
||||
return "in";
|
||||
case TOKEN_LOCAL:
|
||||
return "local";
|
||||
case TOKEN_MACRO:
|
||||
|
||||
@@ -186,7 +186,7 @@ ByteSize type_size(Type *type)
|
||||
case TYPE_ENUM:
|
||||
return type->decl->enums.type_info->type->canonical->builtin.bytesize;
|
||||
case TYPE_ERRTYPE:
|
||||
return alignment_error_code;
|
||||
return type_size(type_usize->canonical);
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
assert(type->decl->resolve_status == RESOLVE_DONE);
|
||||
@@ -607,7 +607,7 @@ AlignSize type_abi_alignment(Type *type)
|
||||
case TYPE_ENUM:
|
||||
return type->decl->enums.type_info->type->canonical->builtin.abi_alignment;
|
||||
case TYPE_ERRTYPE:
|
||||
return alignment_error_code;
|
||||
return t_usz.canonical->builtin.abi_alignment;
|
||||
case TYPE_STRUCT:
|
||||
case TYPE_UNION:
|
||||
return type->decl->alignment;
|
||||
|
||||
27
test/test_suite/arrays/array_literal.c3t
Normal file
27
test/test_suite/arrays/array_literal.c3t
Normal file
@@ -0,0 +1,27 @@
|
||||
module testing;
|
||||
|
||||
func double test(uint x)
|
||||
{
|
||||
double[30] student_t = { 0.0 , 12.706 , 4.303 , 3.182 , 2.776 , 2.571 ,
|
||||
2.447 , 2.365 , 2.306 , 2.262 , 2.228 ,
|
||||
2.201 , 2.179 , 2.160 , 2.145 , 2.131 ,
|
||||
2.120 , 2.110 , 2.101 , 2.093 , 2.086 ,
|
||||
2.080 , 2.074 , 2.069 , 2.064 , 2.060 ,
|
||||
2.056 , 2.052 , 2.048 , 2.045 };
|
||||
return student_t[x];
|
||||
}
|
||||
|
||||
// #expect: array_literal.ll
|
||||
|
||||
@0 = constant [30 x double] [double 0.000000e+00, double 1.270600e+01, double 4.303000e+00, double 3.182000e+00, double 2.776000e+00, double 2.571000e+00, double 2.447000e+00, double 2.365000e+00, double 2.306000e+00, double 2.262000e+00, double 2.228000e+00, double 2.201000e+00, double 2.179000e+00, double 2.160000e+00, double 2.145000e+00, double 2.131000e+00, double 2.120000e+00, double 2.110000e+00, double 2.101000e+00, double 2.093000e+00, double 2.086000e+00, double 2.080000e+00, double 2.074000e+00, double 2.069000e+00, double 2.064000e+00, double 2.060000e+00, double 2.056000e+00, double 2.052000e+00, double 2.048000e+00, double 2.045000e+00], align 8
|
||||
|
||||
entry:
|
||||
%x = alloca i32, align 4
|
||||
%student_t = alloca [30 x double], align 8
|
||||
store i32 %0, i32* %x, align 4
|
||||
%1 = bitcast [30 x double]* %student_t to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %1, i8* align 8 bitcast ([30 x double]* @0 to i8*), i32 240, i1 false)
|
||||
%2 = load i32, i32* %x, align 4
|
||||
%arridx = getelementptr inbounds [30 x double], [30 x double]* %student_t, i32 0, i32 %2
|
||||
%3 = load double, double* %arridx, align 8
|
||||
ret double %3
|
||||
12
test/test_suite/arrays/array_struct.c3t
Normal file
12
test/test_suite/arrays/array_struct.c3t
Normal file
@@ -0,0 +1,12 @@
|
||||
module test;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int x, y;
|
||||
}
|
||||
|
||||
Foo[10] array;
|
||||
|
||||
// #expect: array_struct.ll
|
||||
|
||||
@array = protected global [10 x %test.Foo] zeroinitializer, align 4
|
||||
20
test/test_suite/arrays/complex_array_const.c3t
Normal file
20
test/test_suite/arrays/complex_array_const.c3t
Normal file
@@ -0,0 +1,20 @@
|
||||
module test;
|
||||
|
||||
struct Connection
|
||||
{
|
||||
long to;
|
||||
char* type;
|
||||
long length;
|
||||
}
|
||||
|
||||
Connection[3] link
|
||||
= { {1, "link1", 10},
|
||||
{2, "link2", 20},
|
||||
{3, "link3", 30} };
|
||||
|
||||
// #expect: complex_array_const.ll
|
||||
|
||||
@0 = internal constant [6 x i8] c"link1\00"
|
||||
@1 = internal constant [6 x i8] c"link2\00"
|
||||
@2 = internal constant [6 x i8] c"link3\00"
|
||||
@link = protected global [3 x %test.Connection] [%test.Connection { i64 1, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @0, i32 0, i32 0), i64 10 }, %test.Connection { i64 2, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @1, i32 0, i32 0), i64 20 }, %test.Connection { i64 3, i8* getelementptr inbounds ([6 x i8], [6 x i8]* @2, i32 0, i32 0), i64 30 }], align 8
|
||||
5
test/test_suite/expressions/fail_index_usize.c3
Normal file
5
test/test_suite/expressions/fail_index_usize.c3
Normal file
@@ -0,0 +1,5 @@
|
||||
func void test(int* array, usize n)
|
||||
{
|
||||
array[n] = 33;
|
||||
n[array] = 33; // #error: Cannot index
|
||||
}
|
||||
@@ -46,32 +46,53 @@ func void testSimple()
|
||||
|
||||
entry:
|
||||
%a = alloca %pointer_access.ExtraSimple, align 8
|
||||
%0 = bitcast %pointer_access.ExtraSimple* %a to i8*
|
||||
call void @llvm.memcpy.p0i8.p0i8.i32(i8* align 8 %0, i8* align 8 bitcast (%pointer_access.ExtraSimple* @0 to i8*), i32 72, i1 false)
|
||||
%c = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%j = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c, i32 0, i32 4
|
||||
store double 3.400000e+00, double* %j, align 8
|
||||
%a1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0
|
||||
%1 = load i32, i32* %a1, align 4
|
||||
%c2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%e = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c2, i32 0, i32 0
|
||||
%2 = load double, double* %e, align 8
|
||||
%c3 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%f = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c3, i32 0, i32 3
|
||||
%3 = load double, double* %f, align 8
|
||||
%c4 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%j5 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %c4, i32 0, i32 4
|
||||
%4 = load double, double* %j5, align 8
|
||||
%g = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5
|
||||
%5 = load i32, i32* %g, align 4
|
||||
%anon = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4
|
||||
%o0 = bitcast %pointer_access.anon.0* %anon to double*
|
||||
%6 = load double, double* %o0, align 8
|
||||
%anon6 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3
|
||||
%r = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon6, i32 0, i32 0
|
||||
%7 = load i32, i32* %r, align 4
|
||||
%anon7 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3
|
||||
%s = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %anon7, i32 0, i32 1
|
||||
%8 = load i32, i32* %s, align 4
|
||||
call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @1, i32 0, i32 0), i32 %1, double %2, double %3, double %4, i32 %5, double %6, i32 %7, i32 %8)
|
||||
ret void
|
||||
%0 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0
|
||||
store i32 0, i32* %0, align 8
|
||||
%1 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 1
|
||||
store i32 0, i32* %1, align 4
|
||||
%2 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%3 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 0
|
||||
store double 0.000000e+00, double* %3, align 8
|
||||
%4 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 1
|
||||
store double 0.000000e+00, double* %4, align 8
|
||||
%5 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 2
|
||||
store double 0.000000e+00, double* %5, align 8
|
||||
%6 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 3
|
||||
store double 0.000000e+00, double* %6, align 8
|
||||
%7 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %2, i32 0, i32 4
|
||||
store double 3.300000e+00, double* %7, align 8
|
||||
%8 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3
|
||||
%9 = bitcast %pointer_access.anon* %8 to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 8 %9, i8 0, i64 8, i1 false)
|
||||
%10 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4
|
||||
%11 = bitcast %pointer_access.anon.0* %10 to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 8 %11, i8 0, i64 8, i1 false)
|
||||
%12 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5
|
||||
store i32 0, i32* %12, align 8
|
||||
%13 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%14 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %13, i32 0, i32 4
|
||||
store double 3.400000e+00, double* %14, align 8
|
||||
%15 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 0
|
||||
%16 = load i32, i32* %15, align 8
|
||||
%17 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%18 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %17, i32 0, i32 0
|
||||
%19 = load double, double* %18, align 8
|
||||
%20 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%21 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %20, i32 0, i32 3
|
||||
%22 = load double, double* %21, align 8
|
||||
%23 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 2
|
||||
%24 = getelementptr inbounds %pointer_access.c, %pointer_access.c* %23, i32 0, i32 4
|
||||
%25 = load double, double* %24, align 8
|
||||
%26 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 5
|
||||
%27 = load i32, i32* %26, align 8
|
||||
%28 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 4
|
||||
%29 = bitcast %pointer_access.anon.0* %28 to double*
|
||||
%30 = load double, double* %29, align 8
|
||||
%31 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3
|
||||
%32 = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %31, i32 0, i32 0
|
||||
%33 = load i32, i32* %32, align 8
|
||||
%34 = getelementptr inbounds %pointer_access.ExtraSimple, %pointer_access.ExtraSimple* %a, i32 0, i32 3
|
||||
%35 = getelementptr inbounds %pointer_access.anon, %pointer_access.anon* %34, i32 0, i32 1
|
||||
%36 = load i32, i32* %35, align 4
|
||||
call void (i8*, ...) @printf(i8* getelementptr inbounds ([71 x i8], [71 x i8]* @0, i32 0, i32 0), i32 %16, double %19, double %22, double %25, i32 %27, double %30, i32 %33, i32 %36)
|
||||
ret void
|
||||
|
||||
27
test/test_suite/expressions/simple_float_sub_neg.c3t
Normal file
27
test/test_suite/expressions/simple_float_sub_neg.c3t
Normal file
@@ -0,0 +1,27 @@
|
||||
module test;
|
||||
|
||||
func double test(double a, double b, double c, double d)
|
||||
{
|
||||
return -(a-b) - (c-d);
|
||||
}
|
||||
|
||||
// #expect: simple_float_sub_neg.ll
|
||||
|
||||
entry:
|
||||
%a = alloca double, align 8
|
||||
%b = alloca double, align 8
|
||||
%c = alloca double, align 8
|
||||
%d = alloca double, align 8
|
||||
store double %0, double* %a, align 8
|
||||
store double %1, double* %b, align 8
|
||||
store double %2, double* %c, align 8
|
||||
store double %3, double* %d, align 8
|
||||
%4 = load double, double* %a, align 8
|
||||
%5 = load double, double* %b, align 8
|
||||
%fsub = fsub double %4, %5
|
||||
%fneg = fneg double %fsub
|
||||
%6 = load double, double* %c, align 8
|
||||
%7 = load double, double* %d, align 8
|
||||
%fsub1 = fsub double %6, %7
|
||||
%fsub2 = fsub double %fneg, %fsub1
|
||||
ret double %fsub2
|
||||
10
test/test_suite/expressions/strings.c3t
Normal file
10
test/test_suite/expressions/strings.c3t
Normal file
@@ -0,0 +1,10 @@
|
||||
module test;
|
||||
|
||||
func char* foo()
|
||||
{
|
||||
return "*** Word \"%s\" on line %d is not";
|
||||
}
|
||||
|
||||
// #expect: strings.ll
|
||||
|
||||
@0 = internal constant [32 x i8] c"*** Word \22%s\22 on line %d is not\00"
|
||||
118
test/test_suite/functions/assorted_tests.c3t
Normal file
118
test/test_suite/functions/assorted_tests.c3t
Normal file
@@ -0,0 +1,118 @@
|
||||
module test;
|
||||
|
||||
func int foo1()
|
||||
{
|
||||
byte *pp = void;
|
||||
uint w_cnt = void;
|
||||
|
||||
w_cnt += *pp;
|
||||
|
||||
return cast(w_cnt as int);
|
||||
}
|
||||
|
||||
extern func void test2(int, double, float);
|
||||
|
||||
func void foo2(int x)
|
||||
{
|
||||
test2(x, x ? 1.0 : 12.5, 1.0);
|
||||
}
|
||||
|
||||
func int trys(char* s, int x)
|
||||
{
|
||||
int asa = void;
|
||||
double val = void;
|
||||
int lls = void;
|
||||
if (x)
|
||||
{
|
||||
asa = lls + asa;
|
||||
}
|
||||
else
|
||||
{
|
||||
}
|
||||
return asa + cast(val as int);
|
||||
}
|
||||
|
||||
struct InternalFPF
|
||||
{
|
||||
byte type;
|
||||
}
|
||||
|
||||
local func void setInternalFPFZero(InternalFPF* dest) @noinline
|
||||
{
|
||||
dest.type = 0;
|
||||
}
|
||||
|
||||
func void denormalize(InternalFPF* ptr)
|
||||
{
|
||||
setInternalFPFZero(ptr);
|
||||
}
|
||||
|
||||
|
||||
// #expect: assorted_tests.ll
|
||||
|
||||
%pp = alloca i8*, align 8
|
||||
%w_cnt = alloca i32, align 4
|
||||
%0 = load i32, i32* %w_cnt, align 4
|
||||
%1 = load i8*, i8** %pp, align 8
|
||||
%2 = load i8, i8* %1, align 8
|
||||
%uiuiext = zext i8 %2 to i32
|
||||
%uadd = add nuw i32 %0, %uiuiext
|
||||
store i32 %uadd, i32* %w_cnt, align 4
|
||||
%3 = load i32, i32* %w_cnt, align 4
|
||||
ret i32 %3
|
||||
|
||||
%x = alloca i32, align 4
|
||||
store i32 %0, i32* %x, align 4
|
||||
%1 = load i32, i32* %x, align 4
|
||||
%2 = load i32, i32* %x, align 4
|
||||
%intbool = icmp ne i32 %2, 0
|
||||
br i1 %intbool, label %cond.lhs, label %cond.rhs
|
||||
cond.lhs:
|
||||
br label %cond.phi
|
||||
cond.rhs:
|
||||
br label %cond.phi
|
||||
cond.phi:
|
||||
%val = phi double [ 1.000000e+00, %cond.lhs ], [ 1.250000e+01, %cond.rhs ]
|
||||
call void @test2(i32 %1, double %val, float 1.000000e+00)
|
||||
ret void
|
||||
|
||||
%s = alloca i8*, align 8
|
||||
%x = alloca i32, align 4
|
||||
%asa = alloca i32, align 4
|
||||
%val = alloca double, align 8
|
||||
%lls = alloca i32, align 4
|
||||
store i32 %1, i32* %x, align 4
|
||||
%2 = load i32, i32* %x, align 4
|
||||
%intbool = icmp ne i32 %2, 0
|
||||
br i1 %intbool, label %if.then, label %if.exit
|
||||
|
||||
if.then:
|
||||
%3 = load i32, i32* %lls, align 4
|
||||
%4 = load i32, i32* %asa, align 4
|
||||
%add = add nsw i32 %3, %4
|
||||
store i32 %add, i32* %asa, align 4
|
||||
br label %if.exit
|
||||
|
||||
if.exit:
|
||||
%5 = load i32, i32* %asa, align 4
|
||||
%6 = load double, double* %val, align 8
|
||||
%fpui = fptoui double %6 to i32
|
||||
%add1 = add nsw i32 %5, %fpui
|
||||
ret i32 %add1
|
||||
|
||||
|
||||
void @test.setInternalFPFZero(%test.InternalFPF* %0)
|
||||
%dest = alloca %test.InternalFPF*, align 8
|
||||
store %test.InternalFPF* %0, %test.InternalFPF** %dest, align 8
|
||||
%1 = load %test.InternalFPF*, %test.InternalFPF** %dest, align 8
|
||||
%2 = getelementptr inbounds %test.InternalFPF, %test.InternalFPF* %1, i32 0, i32 0
|
||||
store i8 0, i8* %2, align 1
|
||||
ret void
|
||||
|
||||
void @test.denormalize(%test.InternalFPF* %0)
|
||||
entry:
|
||||
%ptr = alloca %test.InternalFPF*, align 8
|
||||
store %test.InternalFPF* %0, %test.InternalFPF** %ptr, align 8
|
||||
%1 = load %test.InternalFPF*, %test.InternalFPF** %ptr, align 8
|
||||
call void @test.setInternalFPFZero(%test.InternalFPF* %1)
|
||||
ret void
|
||||
13
test/test_suite/functions/double_return.c3t
Normal file
13
test/test_suite/functions/double_return.c3t
Normal file
@@ -0,0 +1,13 @@
|
||||
module test;
|
||||
|
||||
func int test(bool pos, bool color)
|
||||
{
|
||||
return 0;
|
||||
return cast(pos && color as int);
|
||||
}
|
||||
|
||||
// #expect: double_return.ll
|
||||
|
||||
entry:
|
||||
ret i32 0
|
||||
}
|
||||
34
test/test_suite/globals/external_global.c3t
Normal file
34
test/test_suite/globals/external_global.c3t
Normal file
@@ -0,0 +1,34 @@
|
||||
module test;
|
||||
|
||||
struct MinInfo
|
||||
{
|
||||
long offset;
|
||||
uint file_attr;
|
||||
}
|
||||
|
||||
struct UzGlobs
|
||||
{
|
||||
char answerbuf;
|
||||
MinInfo[1] info;
|
||||
MinInfo* pInfo;
|
||||
}
|
||||
|
||||
extern UzGlobs g;
|
||||
|
||||
func int extract_or_test_files()
|
||||
{
|
||||
g.pInfo = &g.info;
|
||||
return 0;
|
||||
}
|
||||
|
||||
// #expect: external_global.ll
|
||||
|
||||
%test.UzGlobs = type { i8, [1 x %test.MinInfo], %test.MinInfo* }
|
||||
%test.MinInfo = type { i64, i32 }
|
||||
|
||||
@g = external global %test.UzGlobs, align 8
|
||||
|
||||
entry:
|
||||
store %test.MinInfo* getelementptr inbounds (%test.UzGlobs, %test.UzGlobs* @g, i32 0, i32 1, i32 0), %test.MinInfo** getelementptr inbounds (%test.UzGlobs, %test.UzGlobs* @g, i32 0, i32 2), align 8
|
||||
ret i32 0
|
||||
}
|
||||
14
test/test_suite/pointers/const_pointer.c3t
Normal file
14
test/test_suite/pointers/const_pointer.c3t
Normal file
@@ -0,0 +1,14 @@
|
||||
module test;
|
||||
|
||||
double foo = 17;
|
||||
double bar = 12.0;
|
||||
float xx = 12.0;
|
||||
|
||||
void*[3] data = { &foo, &bar, &xx };
|
||||
|
||||
// #expect: const_pointer.ll
|
||||
|
||||
@foo = protected global double 1.700000e+01, align 8
|
||||
@bar = protected global double 1.200000e+01, align 8
|
||||
@xx = protected global float 1.200000e+01, align 4
|
||||
@data = protected global [3 x i8*] [i8* bitcast (double* @foo to i8*), i8* bitcast (double* @bar to i8*), i8* bitcast (float* @xx to i8*)], align 8
|
||||
@@ -30,13 +30,13 @@ public func void test3(long* x)
|
||||
%c = alloca i64, align 8
|
||||
store i64* %0, i64** %x
|
||||
%1 = load i64*, i64** %x, align 8
|
||||
%ptridx = getelementptr inbounds i64, i64* %1, i64 0
|
||||
%ptridx = getelementptr inbounds i64, i64* %1, i32 0
|
||||
%2 = load i64, i64* %ptridx, align 8
|
||||
store i64 %2, i64* %a, align 8
|
||||
%3 = load i64*, i64** %x, align 8
|
||||
%4 = load i64, i64* %3, align 8
|
||||
store i64 %4, i64* %b, align 8
|
||||
%5 = load i64*, i64** %x, align 8
|
||||
%ptridx1 = getelementptr inbounds i64, i64* %5, i64 1
|
||||
%ptridx1 = getelementptr inbounds i64, i64* %5, i32 1
|
||||
%6 = load i64, i64* %ptridx1, align 8
|
||||
store i64 %6, i64* %c, align 8
|
||||
65
test/test_suite/statements/if_tests.c3t
Normal file
65
test/test_suite/statements/if_tests.c3t
Normal file
@@ -0,0 +1,65 @@
|
||||
module iftest;
|
||||
|
||||
func void test1(int x)
|
||||
{
|
||||
if (x > 0)
|
||||
{
|
||||
defer x += 1;
|
||||
}
|
||||
else
|
||||
{}
|
||||
}
|
||||
|
||||
func void test2(int x)
|
||||
{
|
||||
if (x > 0)
|
||||
{
|
||||
}
|
||||
else
|
||||
{}
|
||||
}
|
||||
|
||||
func void test3(int x)
|
||||
{
|
||||
if (x > 0)
|
||||
{}
|
||||
else { x += 1; }
|
||||
}
|
||||
|
||||
// #expect: if_tests.ll
|
||||
|
||||
%x = alloca i32, align 4
|
||||
store i32 %0, i32* %x, align 4
|
||||
%1 = load i32, i32* %x, align 4
|
||||
%gt = icmp sgt i32 %1, 0
|
||||
br i1 %gt, label %if.then, label %if.exit
|
||||
if.then:
|
||||
%2 = load i32, i32* %x, align 4
|
||||
%add = add nsw i32 %2, 1
|
||||
store i32 %add, i32* %x, align 4
|
||||
br label %exit
|
||||
exit:
|
||||
br label %if.exit
|
||||
if.exit:
|
||||
ret void
|
||||
|
||||
define void @iftest.test2(i32 %0)
|
||||
%x = alloca i32, align 4
|
||||
store i32 %0, i32* %x, align 4
|
||||
%1 = load i32, i32* %x, align 4
|
||||
%gt = icmp sgt i32 %1, 0
|
||||
ret void
|
||||
|
||||
define void @iftest.test3(i32 %0)
|
||||
%x = alloca i32, align 4
|
||||
store i32 %0, i32* %x, align 4
|
||||
%1 = load i32, i32* %x, align 4
|
||||
%gt = icmp sgt i32 %1, 0
|
||||
br i1 %gt, label %if.exit, label %if.else
|
||||
if.else:
|
||||
%2 = load i32, i32* %x, align 4
|
||||
%add = add nsw i32 %2, 1
|
||||
store i32 %add, i32* %x, align 4
|
||||
br label %if.exit
|
||||
if.exit:
|
||||
ret void
|
||||
71
test/test_suite/statements/while_switch.c3t
Normal file
71
test/test_suite/statements/while_switch.c3t
Normal file
@@ -0,0 +1,71 @@
|
||||
module test;
|
||||
|
||||
|
||||
extern func int printf(char*, ...);
|
||||
extern func int foo();
|
||||
|
||||
func int main()
|
||||
{
|
||||
while (foo())
|
||||
{
|
||||
switch (foo())
|
||||
{
|
||||
case 0:
|
||||
case 1:
|
||||
case 2:
|
||||
case 3:
|
||||
printf("3");
|
||||
nextcase;
|
||||
case 4:
|
||||
printf("4");
|
||||
nextcase;
|
||||
case 5:
|
||||
case 6:
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
// #expect: while_switch.ll
|
||||
|
||||
|
||||
@0 = internal constant [2 x i8] c"3\00"
|
||||
@1 = internal constant [2 x i8] c"4\00"
|
||||
|
||||
entry:
|
||||
%switch = alloca i32, align 4
|
||||
br label %while.begin
|
||||
while.begin:
|
||||
%0 = call i32 @foo()
|
||||
%intbool = icmp ne i32 %0, 0
|
||||
br i1 %intbool, label %while.body, label %while.exit
|
||||
while.body:
|
||||
%1 = call i32 @foo()
|
||||
store i32 %1, i32* %switch, align 4
|
||||
br label %switch.entry
|
||||
switch.entry:
|
||||
%2 = load i32, i32* %switch, align 4
|
||||
switch i32 %2, label %switch.default [
|
||||
i32 0, label %switch.case
|
||||
i32 1, label %switch.case
|
||||
i32 2, label %switch.case
|
||||
i32 3, label %switch.case
|
||||
i32 4, label %switch.case1
|
||||
i32 5, label %switch.default
|
||||
i32 6, label %switch.default
|
||||
]
|
||||
switch.case:
|
||||
%3 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @0, i32 0, i32 0))
|
||||
br label %switch.case1
|
||||
switch.case1:
|
||||
%4 = call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([2 x i8], [2 x i8]* @1, i32 0, i32 0))
|
||||
br label %switch.default
|
||||
switch.default:
|
||||
br label %switch.exit
|
||||
switch.exit:
|
||||
br label %while.begin
|
||||
while.exit:
|
||||
ret i32 0
|
||||
|
||||
12
test/test_suite/struct/func_return_struct.c3
Normal file
12
test/test_suite/struct/func_return_struct.c3
Normal file
@@ -0,0 +1,12 @@
|
||||
struct Test
|
||||
{
|
||||
int i;
|
||||
short s1, s2;
|
||||
}
|
||||
|
||||
extern func Test func_returning_struct();
|
||||
|
||||
func void loop()
|
||||
{
|
||||
func_returning_struct();
|
||||
}
|
||||
14
test/test_suite/struct/simple_struct.c3t
Normal file
14
test/test_suite/struct/simple_struct.c3t
Normal file
@@ -0,0 +1,14 @@
|
||||
module test;
|
||||
|
||||
Foo a;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int x;
|
||||
double a;
|
||||
}
|
||||
|
||||
// #expect: simple_struct.ll
|
||||
|
||||
%test.Foo = type { i32, double }
|
||||
@a = protected global %test.Foo zeroinitializer, align 8
|
||||
15
test/test_suite/struct/struct_as_value.c3t
Normal file
15
test/test_suite/struct/struct_as_value.c3t
Normal file
@@ -0,0 +1,15 @@
|
||||
module test;
|
||||
|
||||
struct Event
|
||||
{
|
||||
int op;
|
||||
}
|
||||
|
||||
func Event test(int x)
|
||||
{
|
||||
Event foo = { 1 };
|
||||
Event bar = { 2 };
|
||||
return x ? foo : bar;
|
||||
}
|
||||
|
||||
// TODO possibly look at the IR
|
||||
33
test/test_suite/struct/struct_const_construct_simple.c3t
Normal file
33
test/test_suite/struct/struct_const_construct_simple.c3t
Normal file
@@ -0,0 +1,33 @@
|
||||
module structo;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
int foo;
|
||||
long bar;
|
||||
}
|
||||
|
||||
usize x = Foo.sizeof;
|
||||
|
||||
Foo foo1 = { 1, 2 };
|
||||
Foo foo2 = { .foo = 2 };
|
||||
Foo foo3 = { .bar = 3 };
|
||||
Foo foo4 = { .bar = 4, .foo = 4, .bar = 1 };
|
||||
Foo foo5 = {};
|
||||
Foo foo6;
|
||||
const Foo FOO7 = { 1, 2 };
|
||||
Foo foo8 = FOO7;
|
||||
|
||||
// #expect: struct_const_construct_simple.ll
|
||||
|
||||
%structo.Foo = type { i32, i64 }
|
||||
|
||||
@Foo = linkonce_odr constant i8 1
|
||||
@x = protected global i64 16, align 8
|
||||
@foo1 = protected global %structo.Foo { i32 1, i64 2 }, align 8
|
||||
@foo2 = protected global %structo.Foo { i32 2, i64 0 }, align 8
|
||||
@foo3 = protected global %structo.Foo { i32 0, i64 3 }, align 8
|
||||
@foo4 = protected global %structo.Foo { i32 4, i64 1 }, align 8
|
||||
@foo5 = protected global %structo.Foo zeroinitializer, align 8
|
||||
@foo6 = protected global %structo.Foo zeroinitializer, align 8
|
||||
@FOO7 = protected constant %structo.Foo { i32 1, i64 2 }, align 8
|
||||
@foo8 = protected global %structo.Foo { i32 1, i64 2 }, align 8
|
||||
102
test/test_suite/struct/struct_pack_and_align.c3t
Normal file
102
test/test_suite/struct/struct_pack_and_align.c3t
Normal file
@@ -0,0 +1,102 @@
|
||||
module struct2;
|
||||
|
||||
// <{ i64, i8, [3 x i8] }>
|
||||
struct Foo1 @packed @align(4)
|
||||
{
|
||||
long bar;
|
||||
char foo;
|
||||
}
|
||||
|
||||
$assert(Foo1.sizeof == 12);
|
||||
public Foo1 foo1 = { 1, 2 };
|
||||
|
||||
// <{ i8, i64, [3 x i8] }>
|
||||
struct Foo2 @packed @align(4)
|
||||
{
|
||||
char foo;
|
||||
long bar;
|
||||
}
|
||||
|
||||
$assert(Foo2.sizeof == 12);
|
||||
public Foo2 foo2 = { 1, 2 };
|
||||
|
||||
// <{ i8, i64, [7 x i8] }>
|
||||
struct Foo3 @packed @align(8)
|
||||
{
|
||||
char foo;
|
||||
long bar;
|
||||
}
|
||||
|
||||
public Foo3 foo3 = { 1, 2 };
|
||||
$assert(Foo3.sizeof == 16);
|
||||
|
||||
// <{ i8, i64 }>
|
||||
struct Foo4 @packed
|
||||
{
|
||||
char foo;
|
||||
long bar;
|
||||
}
|
||||
|
||||
$assert(Foo4.sizeof == 9);
|
||||
public Foo4 foo4 = { 1, 2 };
|
||||
|
||||
// { i32, [12 x i8], i8, [15 x i8] }
|
||||
struct Foo5
|
||||
{
|
||||
int bar @align(16);
|
||||
char foo @align(16);
|
||||
}
|
||||
|
||||
$assert(Foo5.sizeof == 32);
|
||||
public Foo5 foo5 = { 1, 2 };
|
||||
|
||||
func int test5(char x)
|
||||
{
|
||||
Foo5 y = { .foo = x };
|
||||
return y.foo + y.bar;
|
||||
}
|
||||
|
||||
// { i32, i16, i16 }
|
||||
struct Foo6 @packed
|
||||
{
|
||||
int a;
|
||||
short b;
|
||||
short c;
|
||||
}
|
||||
|
||||
$assert(Foo6.sizeof == 8);
|
||||
public Foo6 foo6 = { 1, 2, 3 };
|
||||
|
||||
// #expect: struct_pack_and_align.ll
|
||||
|
||||
%struct2.Foo1 = type <{ i64, i8, [3 x i8] }>
|
||||
%struct2.Foo2 = type <{ i8, i64, [3 x i8] }>
|
||||
%struct2.Foo3 = type <{ i8, i64, [7 x i8] }>
|
||||
%struct2.Foo4 = type <{ i8, i64 }>
|
||||
%struct2.Foo5 = type { i32, [12 x i8], i8, [15 x i8] }
|
||||
%struct2.Foo6 = type { i32, i16, i16 }
|
||||
|
||||
@foo1 = global %struct2.Foo1 <{ i64 1, i8 2, [3 x i8] undef }>, align 4
|
||||
@foo2 = global %struct2.Foo2 <{ i8 1, i64 2, [3 x i8] undef }>, align 4
|
||||
@foo3 = global %struct2.Foo3 <{ i8 1, i64 2, [7 x i8] undef }>, align 8
|
||||
@foo4 = global %struct2.Foo4 <{ i8 1, i64 2 }>, align 1
|
||||
@foo5 = global %struct2.Foo5 { i32 1, [12 x i8] undef, i8 2, [15 x i8] undef }, align 16
|
||||
@foo6 = global %struct2.Foo6 { i32 1, i16 2, i16 3 }, align 1
|
||||
|
||||
@struct2.test5
|
||||
entry:
|
||||
%x = alloca i8, align 1
|
||||
%y = alloca %struct2.Foo5, align 16
|
||||
store i8 %0, i8* %x, align 1
|
||||
%1 = bitcast %struct2.Foo5* %y to i8*
|
||||
call void @llvm.memset.p0i8.i64(i8* align 16 %1, i8 0, i64 32, i1 false)
|
||||
%2 = getelementptr inbounds %struct2.Foo5, %struct2.Foo5* %y, i32 0, i32 2
|
||||
%3 = load i8, i8* %x, align 1
|
||||
store i8 %3, i8* %2, align 16
|
||||
%4 = getelementptr inbounds %struct2.Foo5, %struct2.Foo5* %y, i32 0, i32 2
|
||||
%5 = load i8, i8* %4, align 16
|
||||
%sisiext = sext i8 %5 to i32
|
||||
%6 = getelementptr inbounds %struct2.Foo5, %struct2.Foo5* %y, i32 0, i32 0
|
||||
%7 = load i32, i32* %6, align 16
|
||||
%add = add nsw i32 %sisiext, %7
|
||||
ret i32 %add
|
||||
27
test/test_suite/struct/struct_params.c3
Normal file
27
test/test_suite/struct/struct_params.c3
Normal file
@@ -0,0 +1,27 @@
|
||||
module test;
|
||||
|
||||
struct Foo
|
||||
{
|
||||
char p;
|
||||
short q;
|
||||
char r;
|
||||
int x;
|
||||
short y, z;
|
||||
int q2;
|
||||
}
|
||||
|
||||
extern func int test(Foo, float);
|
||||
extern func int testE(char,short,char,int,int,float);
|
||||
|
||||
func void test3(Foo *x)
|
||||
{
|
||||
x.q = 1;
|
||||
}
|
||||
|
||||
func void test2(Foo y)
|
||||
{
|
||||
testE(y.p, y.q, y.r, y.x, y.y, 0.1);
|
||||
test(y, 0.1);
|
||||
test2(y);
|
||||
test3(&y);
|
||||
}
|
||||
15
test/test_suite/symbols/shadow_struct.c3
Normal file
15
test/test_suite/symbols/shadow_struct.c3
Normal file
@@ -0,0 +1,15 @@
|
||||
struct Foo
|
||||
{
|
||||
Foo *x;
|
||||
int y;
|
||||
}
|
||||
|
||||
typedef float as Foo; // #error: shadow a previous declaration
|
||||
|
||||
enum Bar
|
||||
{
|
||||
TEST1,
|
||||
TEST2
|
||||
}
|
||||
|
||||
typedef float as Bar; // #error: shadow a previous declaration
|
||||
@@ -196,3 +196,4 @@ func void test26()
|
||||
}
|
||||
|
||||
|
||||
|
||||
|
||||
30
test/test_suite/union/empty_unions.c3
Normal file
30
test/test_suite/union/empty_unions.c3
Normal file
@@ -0,0 +1,30 @@
|
||||
module test;
|
||||
|
||||
struct Empty {}
|
||||
union Foo {}
|
||||
union Qu
|
||||
{
|
||||
Qu *x;
|
||||
}
|
||||
|
||||
union Xe
|
||||
{
|
||||
char c;
|
||||
int a, z;
|
||||
long b;
|
||||
void *b1;
|
||||
struct qu
|
||||
{
|
||||
int a;
|
||||
long z;
|
||||
}
|
||||
}
|
||||
|
||||
func Xe foo(Xe a)
|
||||
{
|
||||
a.c = 123;
|
||||
a.a = 39249;
|
||||
a.b = 12301230123123;
|
||||
a.z = 1;
|
||||
return a;
|
||||
}
|
||||
29
test/test_suite/union/union_in_struct.c3t
Normal file
29
test/test_suite/union/union_in_struct.c3t
Normal file
@@ -0,0 +1,29 @@
|
||||
module test;
|
||||
|
||||
struct Blend_Map_Entry
|
||||
{
|
||||
union vals {
|
||||
float[5] colour;
|
||||
double[2] point_Slope;
|
||||
}
|
||||
}
|
||||
|
||||
Blend_Map_Entry a = { .vals = { .colour = { 1, 2, 3, 4, 5 } } };
|
||||
Blend_Map_Entry b = { .vals = { .point_Slope = { 6, 7 } } };
|
||||
Blend_Map_Entry c = { .vals.colour[2] = 1 };
|
||||
Blend_Map_Entry d = { .vals.colour = { 1, 2, 3, 4, 5 } };
|
||||
|
||||
func void test(Blend_Map_Entry* foo)
|
||||
{
|
||||
}
|
||||
|
||||
// #expect: union_in_struct.ll
|
||||
|
||||
|
||||
%test.Blend_Map_Entry = type { %test.vals }
|
||||
%test.vals = type { [2 x double], [8 x i8] }
|
||||
|
||||
@a = protected global { { [5 x float], [4 x i8] } } { { [5 x float], [4 x i8] } { [5 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00], [4 x i8] undef } }, align 8
|
||||
@b = protected global %test.Blend_Map_Entry { %test.vals { [2 x double] [double 6.000000e+00, double 7.000000e+00], [8 x i8] undef } }, align 8
|
||||
@c = protected global { { { [2 x float], float, [2 x float] }, [4 x i8] } } { { { [2 x float], float, [2 x float] }, [4 x i8] } { { [2 x float], float, [2 x float] } { [2 x float] zeroinitializer, float 1.000000e+00, [2 x float] zeroinitializer }, [4 x i8] undef } }, align 8
|
||||
@d = protected global { { [5 x float], [4 x i8] } } { { [5 x float], [4 x i8] } { [5 x float] [float 1.000000e+00, float 2.000000e+00, float 3.000000e+00, float 4.000000e+00, float 5.000000e+00], [4 x i8] undef } }, align 8
|
||||
13
test/test_suite/union/union_member_voidcast.c3
Normal file
13
test/test_suite/union/union_member_voidcast.c3
Normal file
@@ -0,0 +1,13 @@
|
||||
module test;
|
||||
|
||||
union Xu
|
||||
{
|
||||
void *b;
|
||||
}
|
||||
|
||||
func Xu foo()
|
||||
{
|
||||
Xu a;
|
||||
a.b = cast(123 as void*);
|
||||
return a;
|
||||
}
|
||||
31
test/test_suite/visibility/local_global_func.c3t
Normal file
31
test/test_suite/visibility/local_global_func.c3t
Normal file
@@ -0,0 +1,31 @@
|
||||
module test;
|
||||
|
||||
int x;
|
||||
local int y = 12;
|
||||
|
||||
local func void foo(int z) @noinline
|
||||
{
|
||||
y = z;
|
||||
}
|
||||
|
||||
func void* test()
|
||||
{
|
||||
foo(12);
|
||||
return &y;
|
||||
}
|
||||
|
||||
// #expect: local_global_func.ll
|
||||
|
||||
@x = protected global i32 0, align 4
|
||||
@y = hidden global i32 12, align 4
|
||||
|
||||
define internal void @test.foo(i32 %0)
|
||||
|
||||
%z = alloca i32, align 4
|
||||
store i32 %0, i32* %z, align 4
|
||||
%1 = load i32, i32* %z, align 4
|
||||
store i32 %1, i32* @y, align 4
|
||||
ret void
|
||||
define i8* @test.test()
|
||||
call void @test.foo(i32 12)
|
||||
ret i8* bitcast (i32* @y to i8*)
|
||||
Reference in New Issue
Block a user