Added 'alignof' unary operator and alignment checking on structs
authorBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 26 Jul 2020 16:51:35 +0000 (11:51 -0500)
committerBrendan Hansen <brendan.f.hansen@gmail.com>
Sun, 26 Jul 2020 16:51:35 +0000 (11:51 -0500)
14 files changed:
include/onyxastnodes.h
include/onyxlex.h
include/onyxtypes.h
misc/onyx.sublime-syntax
misc/onyx.vim
onyx
progs/ez.onyx
src/onyxchecker.c
src/onyxlex.c
src/onyxparser.c
src/onyxsymres.c
src/onyxtypes.c
src/onyxutils.c
src/onyxwasm.c

index 1beab6f640e78240e3bd6137e77399e413b88767..1393417b8e40b3bb841429e0ea674fa3cc397d4b 100644 (file)
@@ -21,6 +21,7 @@ typedef struct AstDereference AstDereference;
 typedef struct AstArrayAccess AstArrayAccess;
 typedef struct AstFieldAccess AstFieldAccess;
 typedef struct AstSizeOf AstSizeOf;
+typedef struct AstAlignOf AstAlignOf;
 
 typedef struct AstReturn AstReturn;
 typedef struct AstBreak AstBreak;
@@ -103,6 +104,7 @@ typedef enum AstKind {
     Ast_Kind_Array_Access,
     Ast_Kind_Field_Access,
     Ast_Kind_Size_Of,
+    Ast_Kind_Align_Of,
 
     Ast_Kind_If,
     Ast_Kind_For,
@@ -253,6 +255,7 @@ struct AstDereference   { AstTyped_base; AstTyped *expr; };
 struct AstArrayAccess   { AstTyped_base; AstTyped *addr; AstTyped *expr; u64 elem_size; };
 struct AstFieldAccess   { AstTyped_base; AstTyped *expr; u64 offset; };
 struct AstSizeOf        { AstTyped_base; AstType *so_type; u64 size; };
+struct AstAlignOf       { AstTyped_base; AstType *ao_type; u64 alignment; };
 
 // Intruction Node
 struct AstReturn        { AstNode_base; AstTyped* expr; };
index c81ab3995d1b3c233a0446608654e36508866e3c..be2f15103958c8ccd63eaed78c569d07b697b2b4 100644 (file)
@@ -26,6 +26,7 @@ typedef enum TokenType {
     Token_Type_Keyword_Break,
     Token_Type_Keyword_Continue,
     Token_Type_Keyword_Sizeof,
+    Token_Type_Keyword_Alignof,
 
     Token_Type_Right_Arrow,
     Token_Type_Left_Arrow,
index b87f91e288d4924c38543d8fe6f7b0b92392961a..12d06a7546936cdf7e1b4be8eb52854a6b3a92cd 100644 (file)
@@ -39,7 +39,7 @@ enum BasicFlag {
 typedef struct TypeBasic {
     enum BasicKind kind;
     u32 flags;
-    i64 size; // NOTE: In bytes
+    u32 size, alignment; // NOTE: In bytes
     const char* name;
 } TypeBasic;
 
@@ -62,7 +62,7 @@ typedef struct StructMember {
     TYPE_KIND(Struct, struct {                                    \
         char* name;                                               \
         u32 size;                                                 \
-        u32 mem_count;                                            \
+        u16 aligment, mem_count;                                  \
         bh_table(StructMember) members;                           \
         bh_arr(StructMember *) memarr;                            \
     })                                                            \
@@ -106,6 +106,7 @@ struct AstFunction;
 
 b32 types_are_compatible(Type* t1, Type* t2);
 u32 type_size_of(Type* type);
+u32 type_alignment_of(Type* type);
 Type* type_build_from_ast(bh_allocator alloc, struct AstType* type_node);
 
 Type* type_build_function_type(bh_allocator alloc, struct AstFunction* func, struct AstType* return_type);
index acc867e7887a6b7be9d316b825f9d3fe304116ca..457791a6c928645ef31c109b8206d85a029d02e7 100644 (file)
@@ -23,7 +23,7 @@ contexts:
     # strings in YAML. When using single quoted strings, only single quotes
     # need to be escaped: this is done by using two single quotes next to each
     # other.
-    - match: '\b(package|struct|proc|use|global|enum|if|elseif|else|for|while|do|break|continue|return|as|sizeof)\b'
+    - match: '\b(package|struct|proc|use|global|enum|if|elseif|else|for|while|do|break|continue|return|as|sizeof|alignof)\b'
       scope: keyword.control.onyx
 
     - match: '\b(bool|void|i8|u8|i16|u16|i32|u32|i64|u64|f32|f64|rawptr)\b'
index 95e53595656a73c3dd8759bbab63c0eab04edef8..5a6bfbf112d118a1aa7a141f0420dbe8e65d0cda 100644 (file)
@@ -14,7 +14,7 @@ syn keyword onyxKeyword package struct proc use global
 syn keyword onyxKeyword if elseif else
 syn keyword onyxKeyword for while do
 syn keyword onyxKeyword break continue return
-syn keyword onyxKeyword as sizeof
+syn keyword onyxKeyword as sizeof alignof
 
 syn keyword onyxType bool void
 syn keyword onyxType i8 u8
diff --git a/onyx b/onyx
index a46eafd47af0b8a0a1c4b36ca32ac2aa77522e87..5aad47df32e260995fada08dd0ac63d7f215c535 100755 (executable)
Binary files a/onyx and b/onyx differ
index 5df021ff5aa95de602f1ca928cfe179bbfff5583..47784d99ffb734486bbefcd4b0285949d6257cfd 100644 (file)
@@ -37,6 +37,7 @@ proc #export "main" {
     foo.x = 123;
     foo.y = 321;
     foo.st = st;
+    print(alignof Foo);
     print(sizeof Foo);
     print(foo.foo_sum());
     print_st(foo.st);
index cf6960b1bd49f7108bdbfdc5729513edf9283564..eb943630614286bf2e635b3c3c5a62be83d448f6 100644 (file)
@@ -20,6 +20,7 @@ CHECK(dereference, AstDereference* deref);
 CHECK(array_access, AstArrayAccess* expr);
 CHECK(field_access, AstFieldAccess** pfield);
 CHECK(size_of, AstSizeOf* so);
+CHECK(align_of, AstAlignOf* ao);
 CHECK(global, AstGlobal* global);
 CHECK(function, AstFunction* func);
 CHECK(overloaded_function, AstOverloadedFunction* func);
@@ -574,6 +575,12 @@ CHECK(size_of, AstSizeOf* so) {
     return 0;
 }
 
+CHECK(align_of, AstAlignOf* ao) {
+    ao->alignment = type_alignment_of(type_build_from_ast(semstate.allocator, ao->ao_type));
+
+    return 0;
+}
+
 CHECK(expression, AstTyped** pexpr) {
     AstTyped* expr = *pexpr;
     if (expr->kind > Ast_Kind_Type_Start && expr->kind < Ast_Kind_Type_End) {
@@ -623,6 +630,7 @@ CHECK(expression, AstTyped** pexpr) {
         case Ast_Kind_Array_Access: retval = check_array_access((AstArrayAccess *) expr); break;
         case Ast_Kind_Field_Access: retval = check_field_access((AstFieldAccess **) pexpr); break;
         case Ast_Kind_Size_Of:      retval = check_size_of((AstSizeOf *) expr); break;
+        case Ast_Kind_Align_Of:     retval = check_align_of((AstAlignOf *) expr); break;
 
         case Ast_Kind_Global:
             if (expr->type == NULL) {
index 01db3fee540334ebaabcc66c3e9f8fb6459f2e83..8012a6ff028951a4ae44a0c387b20b6e7bb25de5 100644 (file)
@@ -24,6 +24,7 @@ static const char* token_type_names[] = {
     "break",
     "continue",
     "sizeof",
+    "alignof",
 
     "->",
     "<-",
@@ -158,6 +159,7 @@ OnyxToken* onyx_get_token(OnyxTokenizer* tokenizer) {
     LITERAL_TOKEN("break",      1, Token_Type_Keyword_Break);
     LITERAL_TOKEN("continue",   1, Token_Type_Keyword_Continue);
     LITERAL_TOKEN("sizeof",     1, Token_Type_Keyword_Sizeof);
+    LITERAL_TOKEN("alignof",    1, Token_Type_Keyword_Alignof);
     LITERAL_TOKEN("true",       1, Token_Type_Literal_True);
     LITERAL_TOKEN("false",      1, Token_Type_Literal_False);
     LITERAL_TOKEN("->",         0, Token_Type_Right_Arrow);
index 608e3f2aed84835a83db700e888770b294e3721b..10b93d0ed1e28f19252b15738b11776c30db5aa5 100644 (file)
@@ -198,6 +198,16 @@ static AstTyped* parse_factor(OnyxParser* parser) {
             break;
         }
 
+        case Token_Type_Keyword_Alignof: {
+            AstAlignOf* ao_node = make_node(AstAlignOf, Ast_Kind_Align_Of);
+            ao_node->token = expect_token(parser, Token_Type_Keyword_Alignof);
+            ao_node->ao_type = (AstType *) parse_type(parser);
+            ao_node->type_node = (AstType *) &basic_type_i32;
+
+            retval = (AstTyped *) ao_node;
+            break;
+        }
+
         case Token_Type_Symbol: {
             OnyxToken* sym_token = expect_token(parser, Token_Type_Symbol);
             AstTyped* sym_node = make_node(AstTyped, Ast_Kind_Symbol);
index a17ad421310135d3145c918f25919fcce57935c9..c7a04c41aff1039a330cf96246c4cb4e42c419d3 100644 (file)
@@ -48,6 +48,7 @@ static AstType* symres_type(AstType* type);
 static void symres_local(AstLocal** local);
 static void symres_call(AstCall* call);
 static void symres_size_of(AstSizeOf* so);
+static void symres_align_of(AstAlignOf* so);
 static void symres_field_access(AstFieldAccess** fa);
 static void symres_expression(AstTyped** expr);
 static void symres_return(AstReturn* ret);
@@ -174,6 +175,11 @@ static void symres_size_of(AstSizeOf* so) {
     so->so_type = symres_type(so->so_type);
 }
 
+static void symres_align_of(AstAlignOf* ao) {
+    ao->type_node = symres_type(ao->type_node);
+    ao->ao_type = symres_type(ao->ao_type);
+}
+
 static void symres_field_access(AstFieldAccess** fa) {
     if ((*fa)->expr == NULL) return;
     symres_expression(&(*fa)->expr);
@@ -234,6 +240,7 @@ static void symres_expression(AstTyped** expr) {
         case Ast_Kind_Dereference:  symres_expression(&((AstDereference *)(*expr))->expr); break;
         case Ast_Kind_Field_Access: symres_field_access((AstFieldAccess **) expr); break;
         case Ast_Kind_Size_Of:      symres_size_of((AstSizeOf *)*expr); break;
+        case Ast_Kind_Align_Of:     symres_align_of((AstAlignOf *)*expr); break;
 
         case Ast_Kind_Array_Access:
             symres_expression(&((AstArrayAccess *)(*expr))->addr);
index 37271b48d1fabd7549ed9ee9d17a6e870a40afa7..85664e2cc7ce1cb83a7e079663c7208397693da2 100644 (file)
@@ -4,23 +4,23 @@
 
 // NOTE: These have to be in the same order as Basic
 Type basic_types[] = {
-    { Type_Kind_Basic, 0, { Basic_Kind_Void,                    0,                       0, "void"   } },
+    { Type_Kind_Basic, 0, { Basic_Kind_Void,                    0,                       0, 1, "void"   } },
 
-    { Type_Kind_Basic, 0, { Basic_Kind_Bool,   Basic_Flag_Boolean,                       1, "bool"   } },
+    { Type_Kind_Basic, 0, { Basic_Kind_Bool,   Basic_Flag_Boolean,                       1, 1, "bool"   } },
 
-    { Type_Kind_Basic, 0, { Basic_Kind_I8,     Basic_Flag_Integer,                       1, "i8"     } },
-    { Type_Kind_Basic, 0, { Basic_Kind_U8,     Basic_Flag_Integer | Basic_Flag_Unsigned, 1, "u8"     } },
-    { Type_Kind_Basic, 0, { Basic_Kind_I16,    Basic_Flag_Integer,                       2, "i16"    } },
-    { Type_Kind_Basic, 0, { Basic_Kind_U16,    Basic_Flag_Integer | Basic_Flag_Unsigned, 2, "u16"    } },
-    { Type_Kind_Basic, 0, { Basic_Kind_I32,    Basic_Flag_Integer,                       4, "i32"    } },
-    { Type_Kind_Basic, 0, { Basic_Kind_U32,    Basic_Flag_Integer | Basic_Flag_Unsigned, 4, "u32"    } },
-    { Type_Kind_Basic, 0, { Basic_Kind_I64,    Basic_Flag_Integer,                       8, "i64"    } },
-    { Type_Kind_Basic, 0, { Basic_Kind_U64,    Basic_Flag_Integer | Basic_Flag_Unsigned, 8, "u64"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_I8,     Basic_Flag_Integer,                       1, 1, "i8"     } },
+    { Type_Kind_Basic, 0, { Basic_Kind_U8,     Basic_Flag_Integer | Basic_Flag_Unsigned, 1, 1, "u8"     } },
+    { Type_Kind_Basic, 0, { Basic_Kind_I16,    Basic_Flag_Integer,                       2, 2, "i16"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_U16,    Basic_Flag_Integer | Basic_Flag_Unsigned, 2, 2, "u16"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_I32,    Basic_Flag_Integer,                       4, 4, "i32"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_U32,    Basic_Flag_Integer | Basic_Flag_Unsigned, 4, 4, "u32"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_I64,    Basic_Flag_Integer,                       8, 8, "i64"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_U64,    Basic_Flag_Integer | Basic_Flag_Unsigned, 8, 8, "u64"    } },
 
-    { Type_Kind_Basic, 0, { Basic_Kind_F32,    Basic_Flag_Float,                         4, "f32"    } },
-    { Type_Kind_Basic, 0, { Basic_Kind_F64,    Basic_Flag_Float,                         8, "f64"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_F32,    Basic_Flag_Float,                         4, 4, "f32"    } },
+    { Type_Kind_Basic, 0, { Basic_Kind_F64,    Basic_Flag_Float,                         8, 4, "f64"    } },
 
-    { Type_Kind_Basic, 0, { Basic_Kind_Rawptr, Basic_Flag_Pointer,                       4, "rawptr" } },
+    { Type_Kind_Basic, 0, { Basic_Kind_Rawptr, Basic_Flag_Pointer,                       4, 4, "rawptr" } },
 };
 
 b32 types_are_surface_compatible(Type* t1, Type* t2) {
@@ -170,6 +170,20 @@ u32 type_size_of(Type* type) {
     }
 }
 
+u32 type_alignment_of(Type* type) {
+    if (type == NULL) return 1;
+
+    switch (type->kind) {
+        case Type_Kind_Basic:    return type->Basic.alignment;
+        case Type_Kind_Pointer:  return 4;
+        case Type_Kind_Function: return 1;
+        case Type_Kind_Array:    return type_alignment_of(type->Array.elem);
+        case Type_Kind_Struct:   return type->Struct.aligment;
+        case Type_Kind_Enum:     return type_alignment_of(type->Enum.backing);
+        default: return 1;
+    }
+}
+
 Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
     if (type_node == NULL) return NULL;
 
@@ -238,12 +252,17 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
             bh_arr_new(global_heap_allocator, s_type->Struct.memarr, s_type->Struct.mem_count);
 
             u32 offset = 0;
-            u32 min_alignment = 1;
+            u32 alignment = 1, mem_alignment;
             u32 idx = 0;
             bh_arr_each(AstStructMember *, member, s_node->members) {
                 (*member)->type = type_build_from_ast(alloc, (*member)->type_node);
 
                 // TODO: Add alignment checking here
+                mem_alignment = type_alignment_of((*member)->type);
+                if (mem_alignment > alignment) alignment = mem_alignment;
+                if (offset % alignment != 0) {
+                    offset += alignment - (offset % alignment);
+                }
 
                 StructMember smem = {
                     .offset = offset,
@@ -260,7 +279,11 @@ Type* type_build_from_ast(bh_allocator alloc, AstType* type_node) {
                 idx++;
             }
 
-            // TODO: Again, add alignment
+            s_type->Struct.aligment = alignment;
+            
+            if (offset % alignment != 0) {
+                offset += alignment - (offset % alignment);
+            }
             s_type->Struct.size = offset;
 
             return s_type;
index 14e35c5a6760e991127d06c119482379f620c24d..8c53165b9b2828c93c35b24b495d7450b6138b65 100644 (file)
@@ -50,6 +50,7 @@ static const char* ast_node_names[] = {
     "ARRAY_ACCESS",
     "FIELD_ACCESS",
     "SIZE OF",
+    "ALIGN OF"
 
     "IF",
     "FOR",
index bc29901d320243fa8b8725448c1b01bd4109f821..1a0f5e98e1a23b2baae5b2947025a7ab81a3acab 100644 (file)
@@ -425,7 +425,7 @@ COMPILE_FUNC(store_instruction, Type* type, u32 alignment, u32 offset) {
 
 COMPILE_FUNC(load_instruction, Type* type, u32 offset) {
     bh_arr(WasmInstruction) code = *pcode;
-    
+
     if (type->kind == Type_Kind_Enum) {
         type = type->Enum.backing;
     }
@@ -969,6 +969,12 @@ COMPILE_FUNC(expression, AstTyped* expr) {
             break;
         }
 
+        case Ast_Kind_Align_Of: {
+            AstAlignOf* ao = (AstAlignOf *) expr;
+            WID(WI_I32_CONST, ao->alignment);
+            break;
+        }
+
         case Ast_Kind_Enum_Value: {
             AstEnumValue* ev = (AstEnumValue *) expr;
             WasmType backing_type = onyx_type_to_wasm_type(ev->type);