Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add casts: pointer <--> long #471

Merged
merged 4 commits into from
Dec 19, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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 @@ -543,6 +543,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 @@ -579,7 +579,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_with_error(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