Skip to content

Commit

Permalink
Add casts: pointer <--> long (#471)
Browse files Browse the repository at this point in the history
  • Loading branch information
Akuli authored Dec 19, 2023
1 parent ac2d401 commit fadffd3
Show file tree
Hide file tree
Showing 11 changed files with 39 additions and 18 deletions.
22 changes: 6 additions & 16 deletions doc/tutorial.md
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down Expand Up @@ -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**.
Expand Down
5 changes: 5 additions & 0 deletions self_hosted/create_llvm_ir.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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:
Expand Down
1 change: 1 addition & 0 deletions self_hosted/llvm.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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*
Expand Down
2 changes: 2 additions & 0 deletions self_hosted/typecheck.jou
Original file line number Diff line number Diff line change
Expand Up @@ -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.
Expand Down
6 changes: 6 additions & 0 deletions src/build_cfg.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
3 changes: 2 additions & 1 deletion src/codegen.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
1 change: 1 addition & 0 deletions src/jou_compiler.h
Original file line number Diff line number Diff line change
Expand Up @@ -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,
Expand Down
3 changes: 3 additions & 0 deletions src/print.c
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
3 changes: 2 additions & 1 deletion src/typecheck.c
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
7 changes: 7 additions & 0 deletions tests/should_succeed/cast_between_ptr_and_long.jou
Original file line number Diff line number Diff line change
@@ -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
4 changes: 4 additions & 0 deletions tests/wrong_type/pointer_to_int.jou
Original file line number Diff line number Diff line change
@@ -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

0 comments on commit fadffd3

Please sign in to comment.