From fadffd3580b0d0ec494e068a5f75cf4e9533150c Mon Sep 17 00:00:00 2001 From: Akuli Date: Tue, 19 Dec 2023 14:21:49 +0200 Subject: [PATCH] Add casts: pointer <--> long (#471) --- doc/tutorial.md | 22 +++++-------------- self_hosted/create_llvm_ir.jou | 5 +++++ self_hosted/llvm.jou | 1 + self_hosted/typecheck.jou | 2 ++ src/build_cfg.c | 6 +++++ src/codegen.c | 3 ++- src/jou_compiler.h | 1 + src/print.c | 3 +++ src/typecheck.c | 3 ++- .../cast_between_ptr_and_long.jou | 7 ++++++ tests/wrong_type/pointer_to_int.jou | 4 ++++ 11 files changed, 39 insertions(+), 18 deletions(-) create mode 100644 tests/should_succeed/cast_between_ptr_and_long.jou create mode 100644 tests/wrong_type/pointer_to_int.jou diff --git a/doc/tutorial.md b/doc/tutorial.md index 0d944d52..c96660c8 100644 --- a/doc/tutorial.md +++ b/doc/tutorial.md @@ -219,21 +219,11 @@ import "stdlib/io.jou" def main() -> int: b = 123 as byte - printf("%p\n", &b) + printf("%lld\n", &b as long) return 0 ``` -Here the `p` of `%p` is short for "pointer". - -This prints something like `0x7ffd85fd3db7`. -This is a number written in hexadecimal, -and it means `140726851419575`. -Hexadecimal basically means that instead of representing numbers with 10 digits (`0`-`9`), -we use 16 "digits" (`0`-`9` and then `a`-`f`). -The prefix `0x` is a convention to indicate that the number is in heXadecimal. -How exactly hexadecimal works is not really relevant here, -but what matters is that we got some number. -So: +This prints something like `140726851419575`, so: ``` memory_of_the_computer[140726851419575] == 123 @@ -269,13 +259,13 @@ because the program essentially loads into whatever memory location is available ``` $ ./jou asd.jou -0x7ffe7e1ded17 +140731014311191 $ ./jou asd.jou -0x7ffff24bec87 +140737258450055 $ ./jou asd.jou -0x7fff356b6dd7 +140734089620951 $ ./jou asd.jou -0x7ffeabcfe7f7 +140731780950007 ``` In Jou, memory addresses are represented as **pointers**. diff --git a/self_hosted/create_llvm_ir.jou b/self_hosted/create_llvm_ir.jou index dd5e7b92..4c6f6a93 100644 --- a/self_hosted/create_llvm_ir.jou +++ b/self_hosted/create_llvm_ir.jou @@ -205,6 +205,11 @@ class AstToIR: # unsigned 8-bit 0xFF (255) --> 16-bit 0x00FF (255) return LLVMBuildIntCast2(self->builder, obj, type_to_llvm(to), (from->kind == TypeKind::SignedInteger) as int, "cast_int") + if from->is_integer_type() and to->is_pointer_type(): + return LLVMBuildIntToPtr(self->builder, obj, type_to_llvm(to), "long_as_ptr") + if from->is_pointer_type() and to->is_integer_type(): + return LLVMBuildPtrToInt(self->builder, obj, type_to_llvm(to), "ptr_as_long") + if from->kind == TypeKind::SignedInteger and to->kind == TypeKind::FloatingPoint: return LLVMBuildSIToFP(self->builder, obj, type_to_llvm(to), "cast_signed_to_float") if from->kind == TypeKind::UnsignedInteger and to->kind == TypeKind::FloatingPoint: diff --git a/self_hosted/llvm.jou b/self_hosted/llvm.jou index 8001c04d..56817086 100644 --- a/self_hosted/llvm.jou +++ b/self_hosted/llvm.jou @@ -263,6 +263,7 @@ declare LLVMBuildFPToSI(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType declare LLVMBuildUIToFP(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, Name: byte*) -> LLVMValue* declare LLVMBuildSIToFP(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, Name: byte*) -> LLVMValue* declare LLVMBuildPtrToInt(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, Name: byte*) -> LLVMValue* +declare LLVMBuildIntToPtr(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, Name: byte*) -> LLVMValue* declare LLVMBuildBitCast(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, Name: byte*) -> LLVMValue* declare LLVMBuildIntCast2(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, IsSigned: int, Name: byte*) -> LLVMValue* declare LLVMBuildFPCast(Builder: LLVMBuilder*, Val: LLVMValue*, DestTy: LLVMType*, Name: byte*) -> LLVMValue* diff --git a/self_hosted/typecheck.jou b/self_hosted/typecheck.jou index 2b9d18f5..c59f1f7c 100644 --- a/self_hosted/typecheck.jou +++ b/self_hosted/typecheck.jou @@ -51,6 +51,8 @@ def can_cast_explicitly(from: Type*, to: Type*) -> bool: or (from->is_integer_type() and to->kind == TypeKind::Enum) or (from->kind == TypeKind::Enum and to->is_integer_type()) or (from == &bool_type and to->is_integer_type()) + or (from->is_pointer_type() and to == long_type) + or (from == long_type and to->is_pointer_type()) ) # Implicit casts are used in many places, e.g. function arguments. diff --git a/src/build_cfg.c b/src/build_cfg.c index 160d39a0..2619e8a1 100644 --- a/src/build_cfg.c +++ b/src/build_cfg.c @@ -133,6 +133,12 @@ static const LocalVariable *build_cast( return result; } + if (is_number_type(obj->type) && obj->type->data.width_in_bits == 64 && is_pointer_type(to)) { + const LocalVariable *result = add_local_var(st, to); + add_unary_op(st, location, CF_INT64_TO_PTR, obj, result); + return result; + } + if (is_integer_type(obj->type) || to->kind == TYPE_ENUM) { const LocalVariable *i32var = add_local_var(st, intType); const LocalVariable *result = add_local_var(st, to); diff --git a/src/codegen.c b/src/codegen.c index 313b5de4..96fccb99 100644 --- a/src/codegen.c +++ b/src/codegen.c @@ -313,7 +313,8 @@ static void codegen_instruction(const struct State *st, const CfInstruction *ins case CF_ADDRESS_OF_GLOBAL_VAR: setdest(LLVMGetNamedGlobal(st->module, ins->data.globalname)); break; case CF_PTR_LOAD: setdest(LLVMBuildLoad(st->builder, getop(0), "ptr_load")); break; case CF_PTR_STORE: LLVMBuildStore(st->builder, getop(1), getop(0)); break; - case CF_PTR_TO_INT64: setdest(LLVMBuildPtrToInt(st->builder, getop(0), LLVMInt64Type(), "ptr_as_int")); break; + case CF_PTR_TO_INT64: setdest(LLVMBuildPtrToInt(st->builder, getop(0), LLVMInt64Type(), "ptr_as_long")); break; + case CF_INT64_TO_PTR: setdest(LLVMBuildIntToPtr(st->builder, getop(0), codegen_type(ins->destvar->type), "long_as_ptr")); break; case CF_PTR_CLASS_FIELD: { const Type *classtype = ins->operands[0]->type->data.valuetype; diff --git a/src/jou_compiler.h b/src/jou_compiler.h index a8bae5fd..c505e4a4 100644 --- a/src/jou_compiler.h +++ b/src/jou_compiler.h @@ -540,6 +540,7 @@ struct CfInstruction { CF_PTR_STORE, // *op1 = op2 (does not use destvar, takes 2 operands) CF_PTR_LOAD, // aka dereference CF_PTR_TO_INT64, + CF_INT64_TO_PTR, CF_PTR_CLASS_FIELD, // takes 1 operand (pointer), sets destvar to &op->fieldname CF_PTR_CAST, CF_PTR_ADD_INT, diff --git a/src/print.c b/src/print.c index a74db610..9d081d22 100644 --- a/src/print.c +++ b/src/print.c @@ -549,6 +549,9 @@ static void print_cf_instruction(const CfInstruction *ins) case CF_PTR_TO_INT64: printf("cast %s to 64-bit integer", varname(ins->operands[0])); break; + case CF_INT64_TO_PTR: + printf("cast %s from 64-bit integer to pointer", varname(ins->operands[0])); + break; case CF_CONSTANT: print_constant(&ins->data.constant); break; diff --git a/src/typecheck.c b/src/typecheck.c index ffdc4979..2ecd1586 100644 --- a/src/typecheck.c +++ b/src/typecheck.c @@ -583,7 +583,8 @@ static void do_explicit_cast(ExpressionTypes *types, const Type *to, Location lo && !(is_integer_type(from) && to->kind == TYPE_ENUM) && !(from->kind == TYPE_ENUM && is_integer_type(to)) && !(from->kind == TYPE_BOOL && is_integer_type(to)) - // TODO: pointer-to-int, int-to-pointer + && !(is_pointer_type(from) && to == longType) + && !(from == longType && is_pointer_type(to)) ) { fail(location, "cannot cast from type %s to %s", from->name, to->name); diff --git a/tests/should_succeed/cast_between_ptr_and_long.jou b/tests/should_succeed/cast_between_ptr_and_long.jou new file mode 100644 index 00000000..94db0cf9 --- /dev/null +++ b/tests/should_succeed/cast_between_ptr_and_long.jou @@ -0,0 +1,7 @@ +import "stdlib/io.jou" + +def main() -> int: + nums = [1, 2, 3] + p = ((&nums[0] as long) + 8) as int* + printf("%d\n", *p) # Output: 3 + return 0 diff --git a/tests/wrong_type/pointer_to_int.jou b/tests/wrong_type/pointer_to_int.jou new file mode 100644 index 00000000..8b8a943f --- /dev/null +++ b/tests/wrong_type/pointer_to_int.jou @@ -0,0 +1,4 @@ +def foo() -> None: + # Pointers (64-bit) do not fit in int (32-bit), so compiler errors + x = 1 + y = &x as int # Error: cannot cast from type int* to int