diff --git a/kai.xcodeproj/project.pbxproj b/kai.xcodeproj/project.pbxproj index fd1c32b..4910752 100644 --- a/kai.xcodeproj/project.pbxproj +++ b/kai.xcodeproj/project.pbxproj @@ -32,6 +32,8 @@ 5D41B52C20CE32E8005E9A8E /* gen_xcode_tests.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = gen_xcode_tests.cpp; sourceTree = ""; }; 5D41B53420CE393F005E9A8E /* kai-xctests.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "kai-xctests.xctest"; sourceTree = BUILT_PRODUCTS_DIR; }; 5D41B53820CE393F005E9A8E /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = ""; }; + 5D50634C21BF57AF0054B90E /* ast_descriptions.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = ast_descriptions.c; path = src/ast_descriptions.c; sourceTree = ""; }; + 5D50634E21C08B920054B90E /* targets.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; name = targets.h; path = src/targets.h; sourceTree = ""; }; 5D5612AD20ABA02E00E2F4E8 /* targets.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = targets.c; path = src/targets.c; sourceTree = ""; }; 5D5612AE20ABAD5B00E2F4E8 /* os.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = os.c; path = src/os.c; sourceTree = ""; }; 5D9C729920A5E7C400755A1B /* flags.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; name = flags.c; path = src/flags.c; sourceTree = ""; }; @@ -145,6 +147,7 @@ 5DBC259A20BC133A0086904A /* tools */, B25AB22520B987D0002E1440 /* array.h */, B25AB22620B987D0002E1440 /* ast.h */, + 5D50634E21C08B920054B90E /* targets.h */, B25AB22B20B987D1002E1440 /* checker.h */, 5D405841212E4EF3007A7E8F /* string.h */, B25AB22720B987D0002E1440 /* common.h */, @@ -170,6 +173,7 @@ B249A258209B80E00016B49E /* lexer.c */, B249A25A209B80E00016B49E /* map.c */, 5DA7CA2720A9653F00E4640D /* ast.c */, + 5D50634C21BF57AF0054B90E /* ast_descriptions.c */, B249A259209B80E00016B49E /* parser.c */, B249A25E209B80E00016B49E /* string.c */, B249A25F209B80E00016B49E /* utf.c */, @@ -471,7 +475,7 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = c11; GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_CFLAGS = ( "-Wno-c99-extensions", "-Wno-c11-extensions", @@ -501,7 +505,7 @@ CODE_SIGN_STYLE = Automatic; GCC_C_LANGUAGE_STANDARD = c11; GCC_WARN_CHECK_SWITCH_STATEMENTS = YES; - MACOSX_DEPLOYMENT_TARGET = 10.13; + MACOSX_DEPLOYMENT_TARGET = 10.14; OTHER_CFLAGS = ( "-Wno-c99-extensions", "-Wno-c11-extensions", diff --git a/src/ast.c b/src/ast.c index 7215a64..5b0331b 100644 --- a/src/ast.c +++ b/src/ast.c @@ -52,19 +52,7 @@ b32 isDecl(Stmt *stmt) { return stmt->kind > _DeclKind_Start && stmt->kind < _DeclKind_End; } -// @ErrorQuality -// FIXME: Better description -const char *DescribeStmt(Stmt *stmt) { - return AstDescriptions[stmt->kind]; -} - -const char *DescribeExpr(Expr *expr) { - return AstDescriptions[expr->kind]; -} - -const char *DescribeDecl(Decl *decl) { - return AstDescriptions[decl->kind]; -} +#include "ast_descriptions.c" void *AllocAst(Package *package, size_t size) { ASSERT(size != 0); diff --git a/src/ast_descriptions.c b/src/ast_descriptions.c new file mode 100644 index 0000000..642cbac --- /dev/null +++ b/src/ast_descriptions.c @@ -0,0 +1,26 @@ + +// @ErrorQuality +// FIXME: Better description +const char *DescribeStmt(Stmt *stmt) { + return AstDescriptions[stmt->kind]; +} + +const char *DescribeExpr(Expr *expr) { + char buf[1024]; + + switch (expr->kind) { + case ExprKind_Ident: + return expr->Ident.name; + + case ExprKind_LitInt: + sprintf(buf, "%llu", expr->LitInt.val); + return StrIntern(buf); + + default: + return AstDescriptions[expr->kind]; + } +} + +const char *DescribeDecl(Decl *decl) { + return AstDescriptions[decl->kind]; +} diff --git a/src/checker.c b/src/checker.c index d36ba4f..caecb6c 100644 --- a/src/checker.c +++ b/src/checker.c @@ -223,6 +223,10 @@ b32 IsFloat(Type *type) { return type->kind == TypeKind_Float; } +b32 IsPointer(Type *type) { + return (type->kind == TypeKind_Pointer); +} + b32 isFunction(Type *type) { return type->kind == TypeKind_Function; } @@ -1626,6 +1630,31 @@ Type *checkExprSelector(Expr *expr, CheckerContext *ctx, Package *pkg) { break; } + case TypeKind_Slice: { + SelectorValue val; + if (expr->Selector.name == internRaw) { + type = NewTypePointer(TypeFlag_None, base->Slice.elementType); + val.Struct.index = 0; + val.Struct.offset = TargetPointer->Width; + } else if (expr->Selector.name == internLen) { + type = U64Type; + val.Struct.index = 1; + val.Struct.offset = TargetPointer->Width * 2; + } else if (expr->Selector.name == internCap) { + type = U64Type; + val.Struct.index = 2; + val.Struct.offset = TargetPointer->Width * 3; + } else { + ReportError(pkg, TODOError, expr->Selector.start, "Slice %s has no member %s", + DescribeExpr(expr->Selector.expr), expr->Selector.name); + goto error; + } + storeInfoSelector(pkg, expr, type, SelectorKind_Slice, val, ctx); + ctx->mode = ExprMode_Addressable; + ctx->flags &= ~CheckerContextFlag_Constant; + break; + } + TypeKind_File: { Symbol *file = pkg->checkerInfo[expr->Selector.expr->id].Ident.symbol; Package *import = (Package *) file->backendUserdata; @@ -1664,7 +1693,8 @@ Type *checkExprSelector(Expr *expr, CheckerContext *ctx, Package *pkg) { } default: { - ReportError(pkg, TODOError, expr->start, "%s has no member '%s'", DescribeExpr(expr->Selector.expr), expr->Selector.name); + ReportError(pkg, TODOError, expr->start, "%s (type %s) has no member '%s'", + DescribeExpr(expr->Selector.expr), DescribeType(base), expr->Selector.name); goto error; } } @@ -2022,6 +2052,9 @@ void checkDeclVariable(Decl *decl, CheckerContext *ctx, Package *pkg) { if (exprCtx.mode == ExprMode_Invalid) { // This may not best handle users declaring from mixed calls & not calls markSymbolInvalid(symbols[lhsIndex]); + + values += 1; + lhsIndex += 1; continue; } @@ -2185,7 +2218,11 @@ void checkStmtAssign(Stmt *stmt, CheckerContext *ctx, Package *pkg) { ctx->desiredType = lhsTypes[lhsIndex]; Type *type = checkExpr(expr, ctx, pkg); - if (ctx->mode == ExprMode_Invalid) continue; + if (ctx->mode == ExprMode_Invalid) { + values += 1; + lhsIndex += 1; + continue; + } if (ctx->mode == ExprMode_Unresolved) goto unresolved; if (type->kind == TypeKind_Tuple) { diff --git a/src/checker.h b/src/checker.h index 2fd1a0e..4b36d1c 100644 --- a/src/checker.h +++ b/src/checker.h @@ -61,6 +61,7 @@ typedef u8 SelectorKind; #define SelectorKind_None 0x0 #define SelectorKind_Struct 0x1 #define SelectorKind_Import 0x2 +#define SelectorKind_Slice 0x4 typedef struct Selector_Struct Selector_Struct; struct Selector_Struct { @@ -158,6 +159,7 @@ Type *TypeFromCheckerInfo(CheckerInfo info); b32 IsInteger(Type *type); b32 IsSigned(Type *type); b32 IsFloat(Type *type); +b32 IsPointer(Type *type); #ifdef __cplusplus } #endif diff --git a/src/lexer.c b/src/lexer.c index 0605ace..f06cdb6 100644 --- a/src/lexer.c +++ b/src/lexer.c @@ -30,6 +30,10 @@ const char *internSemicolon; const char *internUnderscore; const char *internIn; +const char *internRaw; +const char *internLen; +const char *internCap; + // Directive names const char *internLine; const char *internFile; @@ -84,6 +88,10 @@ void InitKeywords() { internUnderscore = StrIntern("_"); internIn = StrIntern("in"); + internRaw = StrIntern("raw"); + internLen = StrIntern("len"); + internCap = StrIntern("cap"); + internLine = StrIntern("line"); internFile = StrIntern("file"); internAssert = StrIntern("assert"); diff --git a/src/llvm.cpp b/src/llvm.cpp index c9eb127..6f52331 100644 --- a/src/llvm.cpp +++ b/src/llvm.cpp @@ -7,6 +7,7 @@ #include "types.h" #include "checker.h" #include "llvm.h" +#include "targets.h" // Save our definition of DEBUG and undefine it to avoid conflict with LLVM's // definition @@ -66,6 +67,13 @@ typedef struct DebugTypes { llvm::DIType *u32; llvm::DIType *u64; + llvm::DIType *int_; + llvm::DIType *uint; + llvm::DIType *intptr; + llvm::DIType *uintptr; + + llvm::DIType *rawptr; + llvm::DIType *f32; llvm::DIType *f64; } DebugTypes; @@ -78,6 +86,16 @@ typedef struct Debug { llvm::DIScope *scope; } Debug; +typedef struct Types Types; +struct Types { + llvm::IntegerType *i8; + llvm::IntegerType *i16; + llvm::IntegerType *i32; + llvm::IntegerType *i64; + llvm::IntegerType *intptr; + llvm::PointerType *rawptr; +}; + typedef struct Context Context; struct Context { DynamicArray(CheckerInfo) checkerInfo; @@ -92,6 +110,7 @@ struct Context { llvm::Value *retValue; bool returnAddress; + Types types; Debug d; Arena arena; @@ -171,6 +190,16 @@ llvm::Type *canonicalize(Context *ctx, Type *type) { return llvm::FunctionType::get(returnType, params, (type->Function.Flags & TypeFlag_CVargs) != 0); } break; + case TypeKind_Slice: { + std::vector elements; + llvm::Type *tys[3] = { + llvm::PointerType::getUnqual(canonicalize(ctx, type->Slice.elementType)), + ctx->types.intptr, + ctx->types.intptr, + }; + return llvm::StructType::create(tys); + } break; + case TypeKind_Struct: { if (type->Symbol && type->Symbol->backendUserdata) { BackendStructUserdata *userdata = (BackendStructUserdata *) type->Symbol->backendUserdata; @@ -248,6 +277,68 @@ llvm::DIType *debugCanonicalize(Context *ctx, Type *type) { return ctx->d.builder->createArrayType(type->Width, type->Align, debugCanonicalize(ctx, elementType), subscriptsArray); } + if (type->kind == TypeKind_Slice) { + // TODO: Work out how to take advantage of debug info for C VLA's so that we can make this appear better + if (type->Symbol && type->Symbol->backendUserdata) { + BackendStructUserdata *userdata = (BackendStructUserdata *) type->Symbol->backendUserdata; + if (userdata->debugType) return userdata->debugType; + } + + std::vector elementTypes; + + auto ptr = ctx->d.builder->createMemberType( + ctx->d.scope, + "raw", + ctx->d.file, + 0, + TargetPointer->Width, + TargetPointer->Align, + 0, + llvm::DINode::DIFlags::FlagZero, + debugCanonicalize(ctx, type->Slice.elementType) + ); + + auto len = ctx->d.builder->createMemberType( + ctx->d.scope, + "len", + ctx->d.file, + 0, + TargetPointer->Width, + TargetPointer->Align, + 0, + llvm::DINode::DIFlags::FlagZero, + ctx->d.types.uint + ); + + auto cap = ctx->d.builder->createMemberType( + ctx->d.scope, + "cap", + ctx->d.file, + 0, + TargetPointer->Width, + TargetPointer->Align, + 0, + llvm::DINode::DIFlags::FlagZero, + ctx->d.types.uint + ); + + char buf[1024]; + sprintf(buf, "Slice(%s)", DescribeType(type->Slice.elementType)); + + auto elements = ctx->d.builder->getOrCreateArray({ptr, len, cap}); + return ctx->d.builder->createStructType( + ctx->d.scope, + buf, + ctx->d.file, + 0, + type->Width, + type->Align, + llvm::DINode::DIFlags::FlagZero, + NULL, // DerivedFrom + elements + ); + } + if (type->kind == TypeKind_Function) { // NOTE: Clang just uses a derived type that is a pointer // !44 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !9, size: 64) @@ -318,6 +409,13 @@ void initDebugTypes(llvm::DIBuilder *b, DebugTypes *types) { types->u32 = b->createBasicType("u32", 32, DW_ATE_unsigned); types->u64 = b->createBasicType("u64", 64, DW_ATE_unsigned); + types->int_ = b->createBasicType("int", TargetPointer->Width, DW_ATE_signed); + types->uint = b->createBasicType("uint", TargetPointer->Width, DW_ATE_unsigned); + types->intptr = b->createBasicType("intptr", TargetPointer->Width, DW_ATE_signed); + types->uintptr = b->createBasicType("uintptr", TargetPointer->Width, DW_ATE_unsigned); + + types->rawptr = b->createPointerType(types->u8, TargetPointer->Width); + types->f32 = b->createBasicType("f32", 32, DW_ATE_float); types->f64 = b->createBasicType("f64", 64, DW_ATE_float); } @@ -623,7 +721,34 @@ llvm::Value *emitExprLitCompound(Context *ctx, Expr *expr) { return value; } - case TypeKind_Union: case TypeKind_Enum: case TypeKind_Slice: + case TypeKind_Slice: { + llvm::StructType *type = (llvm::StructType *) canonicalize(ctx, info.type); + llvm::Type *elementType = canonicalize(ctx, info.type->Slice.elementType); + llvm::Value *value; + if (!expr->LitCompound.elements) { + value = llvm::Constant::getNullValue(type); + } else { + value = llvm::UndefValue::get(type); // {*Element, u64, u64} + ForEach(expr->LitCompound.elements, Expr_KeyValue *) { + // For both Array and Slice type Compound Literals the info on KeyValue is the constant index + u64 index = (u64) it->info; + llvm::Value *el = emitExpr(ctx, it->value, elementType); + value = ctx->b.CreateInsertValue(value, el, (u32) index); + } + + u64 length = ArrayLen(expr->LitCompound.elements); + value = ctx->b.CreateInsertValue(value, llvm::ConstantInt::get(ctx->types.intptr, length), 1); + + // Capacity = 0 indicates that the slice recides on the stack, any operation to resize the slice must + // create a new heap allocation + value = ctx->b.CreateInsertValue(value, llvm::ConstantInt::getNullValue(ctx->types.intptr), 2); + } + + if (ctx->returnAddress) UNIMPLEMENTED(); + return value; + } + + case TypeKind_Union: case TypeKind_Enum: UNIMPLEMENTED(); break; } @@ -642,6 +767,7 @@ llvm::Value *emitExprSelector(Context *ctx, Expr *expr) { break; } + case SelectorKind_Slice: case SelectorKind_Struct: { bool previousReturnAddress = ctx->returnAddress; ctx->returnAddress = true; @@ -798,8 +924,13 @@ llvm::Value *emitExprUnary(Context *ctx, Expr *expr) { case TK_Sub: return ctx->b.CreateNeg(val); case TK_Not: - case TK_BNot: + case TK_BNot: { + if (val->getType()->isPointerTy()) { + llvm::PointerType *ty = (llvm::PointerType *) val->getType(); + return ctx->b.CreateICmpEQ(val, llvm::ConstantPointerNull::get(ty)); + } return ctx->b.CreateNot(val); + } case TK_Lss: { if (ctx->returnAddress) @@ -931,7 +1062,7 @@ llvm::Value *emitExprSubscript(Context *ctx, Expr *expr) { // of an unsigned integer would get wrapped and become negative. We can // prevent this by ZExt-ing the index if (indexType->Width < 64 && !IsSigned(indexType)) { - index = ctx->b.CreateZExt(index, canonicalize(ctx, I64Type)); + index = ctx->b.CreateZExt(index, ctx->types.i64); } Type *recvType = TypeFromCheckerInfo(recvInfo); @@ -942,7 +1073,7 @@ llvm::Value *emitExprSubscript(Context *ctx, Expr *expr) { ctx->returnAddress = true; aggregate = emitExpr(ctx, expr->Subscript.expr); ctx->returnAddress = previousReturnAddress; - indicies.push_back(llvm::ConstantInt::get(canonicalize(ctx, I64Type), 0)); + indicies.push_back(llvm::ConstantInt::get(ctx->types.i64, 0)); indicies.push_back(index); resultType = TypeFromCheckerInfo(recvInfo)->Array.elementType; } break; @@ -1902,6 +2033,14 @@ b32 CodegenLLVM(Package *p) { .dataLayout = dataLayout, .b = b, .fn = nullptr, + .types = { + .i8 = llvm::IntegerType::get(context, 8), + .i16 = llvm::IntegerType::get(context, 16), + .i32 = llvm::IntegerType::get(context, 32), + .i64 = llvm::IntegerType::get(context, 64), + .intptr = llvm::IntegerType::get(context, TargetPointer->Width), + .rawptr = llvm::PointerType::getInt8PtrTy(context), + }, .d = debug, }; diff --git a/src/targets.c b/src/targets.c index 57bddfa..e2ea28d 100644 --- a/src/targets.c +++ b/src/targets.c @@ -1,13 +1,5 @@ -typedef enum Os Os; -enum Os { - Os_Current, - Os_Linux, - Os_Darwin, - Os_Windows, - - NUM_OSES, -}; +#include "targets.h" const char *OsNames[NUM_OSES] = { [Os_Current] = "current", @@ -16,16 +8,6 @@ const char *OsNames[NUM_OSES] = { [Os_Windows] = "Windows" }; -typedef enum Arch Arch; -enum Arch { - Arch_Current, - Arch_x86_64, - Arch_x86, - Arch_arm, - Arch_arm64, - NUM_ARCHES, -}; - const char *ArchNames[NUM_ARCHES] = { [Arch_Current] = "current", [Arch_x86_64] = "x86_64", @@ -49,34 +31,18 @@ Arch ArchForName(const char *name) { } -// Type details - -typedef struct TargetMetrics { - u32 Width; - u32 Align; -} TargetMetrics; - -enum Enum_TargetMetrics { - TargetMetrics_Pointer, -}; - -TargetMetrics metrics_32bit[2] = { - [TargetMetrics_Pointer] = { .Width = 32, .Align = 32 }, -}; - -TargetMetrics metrics_64bit[2] = { - [TargetMetrics_Pointer] = { .Width = 64, .Align = 64 }, -}; +TargetPointerMetrics pointers_32bit = { .Width = 32, .Align = 32 }; +TargetPointerMetrics pointers_64bit = { .Width = 64, .Align = 64 }; -TargetMetrics *Os_Linux_ArchSupport[NUM_ARCHES] = { - [Arch_x86_64] = metrics_64bit, +TargetPointerMetrics *Os_Linux_ArchSupport[NUM_ARCHES] = { + [Arch_x86_64] = &pointers_64bit, }; -TargetMetrics *Os_Darwin_ArchSupport[NUM_ARCHES] = { - [Arch_x86_64] = metrics_64bit, +TargetPointerMetrics *Os_Darwin_ArchSupport[NUM_ARCHES] = { + [Arch_x86_64] = &pointers_64bit, }; -TargetMetrics *Os_Windows_ArchSupport[NUM_ARCHES] = { - [Arch_x86_64] = metrics_64bit, - [Arch_x86] = metrics_32bit, +TargetPointerMetrics *Os_Windows_ArchSupport[NUM_ARCHES] = { + [Arch_x86_64] = &pointers_64bit, + [Arch_x86] = &pointers_32bit, }; diff --git a/src/targets.h b/src/targets.h new file mode 100644 index 0000000..8c0a367 --- /dev/null +++ b/src/targets.h @@ -0,0 +1,23 @@ + +typedef enum Os { + Os_Current, + Os_Linux, + Os_Darwin, + Os_Windows, + + NUM_OSES, +} Os; + +typedef enum Arch { + Arch_Current, + Arch_x86_64, + Arch_x86, + Arch_arm, + Arch_arm64, + NUM_ARCHES, +} Arch; + +typedef struct TargetPointerMetrics { + u32 Width; + u32 Align; +} TargetPointerMetrics; diff --git a/src/types.c b/src/types.c index 817fc75..8d2701e 100644 --- a/src/types.c +++ b/src/types.c @@ -1,7 +1,8 @@ #include "types.h" -TargetMetrics *TargetTypeMetrics = NULL; +// Metrics (width & alignment) of pointers on the target platform +TargetPointerMetrics *TargetPointer = NULL; Type *InvalidType; Type *FileType; @@ -251,8 +252,8 @@ Type *NewTypeFunction(TypeFlag flags, DynamicArray(Type *) params, DynamicArray( } } Type *type = AllocType(TypeKind_Function); - type->Width = TargetTypeMetrics[TargetMetrics_Pointer].Width; - type->Align = TargetTypeMetrics[TargetMetrics_Pointer].Align; + type->Width = TargetPointer->Width; + type->Align = TargetPointer->Align; type->Flags = flags; Type **p = Alloc(DefaultAllocator, numParams * sizeof *p); @@ -342,19 +343,19 @@ void InitBuiltins() { switch (TargetOs) { case Os_Linux: - TargetTypeMetrics = Os_Linux_ArchSupport[TargetArch]; + TargetPointer = Os_Linux_ArchSupport[TargetArch]; break; case Os_Darwin: - TargetTypeMetrics = Os_Darwin_ArchSupport[TargetArch]; + TargetPointer = Os_Darwin_ArchSupport[TargetArch]; break; case Os_Windows: - TargetTypeMetrics = Os_Windows_ArchSupport[TargetArch]; + TargetPointer = Os_Windows_ArchSupport[TargetArch]; break; default: break; } - if (!TargetTypeMetrics) { + if (!TargetPointer) { printf("Unsupported os & arch combination: %s/%s\n", OsNames[TargetOs], ArchNames[TargetArch]); exit(1); } @@ -406,7 +407,7 @@ void InitBuiltins() { TYPE(StringType, "string", Struct, 128, TypeFlag_None); - switch (TargetTypeMetrics[TargetMetrics_Pointer].Width) { + switch (TargetPointer->Width) { case 32: TYPEALIAS(IntptrType, "intptr", I32Type); TYPEALIAS(UintptrType, "uintptr", U32Type); @@ -425,10 +426,10 @@ void InitBuiltins() { RawptrType->Pointer.pointeeType = U8Type; - AnyType->Align = TargetTypeMetrics[TargetMetrics_Pointer].Align; - AnyType->Width = TargetTypeMetrics[TargetMetrics_Pointer].Width * 2; + AnyType->Align = TargetPointer->Align; + AnyType->Width = TargetPointer->Width * 2; - RawptrType->Align = RawptrType->Width = TargetTypeMetrics[TargetMetrics_Pointer].Width; + RawptrType->Align = RawptrType->Width = TargetPointer->Width; #undef TYPE } diff --git a/src/types.h b/src/types.h index 64d70f2..259b661 100644 --- a/src/types.h +++ b/src/types.h @@ -1,5 +1,5 @@ -extern struct TargetMetrics *TargetTypeMetrics; +extern struct TargetPointerMetrics *TargetPointer; extern Type *InvalidType; extern Type *FileType; diff --git a/test/libc.kai b/test/libc.kai index 076d998..1d055be 100644 --- a/test/libc.kai +++ b/test/libc.kai @@ -1,5 +1,7 @@ #foreign llvm #callconv "c" { - printf :: fn(rawptr, u64) -> i32 + printf :: fn(rawptr, #cvargs ..any) -> i32 + strlen :: fn(*u8) -> u64 + malloc :: fn(size: u64) -> rawptr }