From 493da1a4e766448908ad0035de063086d38a4912 Mon Sep 17 00:00:00 2001 From: "Tristan B. Velloza Kildaire" Date: Mon, 17 Apr 2023 16:50:11 +0200 Subject: [PATCH] Pointer support (#2) * Make branches not identical * Removed temporary file * Typecheck - Added `attemptPointerAriehmeticCoercion(Value, Value)` * Typechecker - Moved `attemptPointerAriehmeticCoercion(Value, Value)` to class-level and made privately accessible * Test cases - Added pointer arithmetic in the form of `*(ptr+0)` to `simple_pointer.t` to start testing it out * Typechecker - When handling `BinaryOperatorExpression` call `attemptPointerAriehmeticCoercion(Value, Value)` with both `(vLhsInstr, vRhsInstr)` before we call `vLhsInstr.getInstrType()` and `vRhsInstr.getInstrType()` before `isSameType(vLhsType, vRhsType)`. By doing so we attempt to coerce the types of both instructions if one is a pointer and another is an integer, else do nothing * DGen - When emitting for `PointerDereferenceAssignmentInstruction` we didn't wrap `` with `()`. So instead of `*()` we got `*` which doesn't work if you're doing pointer arithmetic * Test cases - Added `simple_pointer_cast.t` to test casting (currently broken parsing-wise) DGen - Added a todo for semantic tests for the `simple_pointer_cast.t` test case * Parser - Added a TODO - we need a `parseType()` * Test cases - Removed `simple_cast_complex_type.t` as it is wrong, syntax wise * Test cases - Removed coercion usage, I am only testing the casting here (explicit) * Test cases - Removed `simple_pointer_cast.t` and replace it with `simple_pointer_cast_le.t` which casts the integer pointer to a byte pointer and sets the lowest significant byte (little endian hence at base of integer) to `2+2` DGen - Added semantic test for `simple_pointer_cast_le.t` * Test cases - Update `simple_pointer_cast_le.t` to do some pointer airthmetic at the byte-level of the 32-bit integer DGen - Updated the semantic test code generation for `simple_pointer_cast_le.t` to check for new values * Added 'simple_pointer_cast_le.t' to Emit tests * TypeChecker - Update `isSameType(Type t1, Type t2)` to now handle pointers explicitly and in a recursive manner based on their referred types - This check occurs before the `Integer` type check therefore following the rule of most specific types to least * Test cases - Added new test case `simple_pointer_malloc.t` - Added semantic code test generation for `simple_pointer_malloc.t` - Added `malloc_test.sh` to compile and generate `mem.o` from `mem.c` to link it then with `simple_pointer_malloc.t` - Added `mem.c` external C file to generate the required `mem.o` for linking against `simple_pointer_malloc.t` * Test cases - Updated `malloc_test.sh` to look for any `brk()` system calls and fixed its interpreter path --- .github/workflows/d.yml | 9 ++- malloc_test.sh | 13 ++++ source/tlang/compiler/codegen/emit/dgen.d | 30 ++++++++- source/tlang/compiler/typecheck/core.d | 65 ++++++++++++++++++- source/tlang/testing/mem.c | 13 ++++ source/tlang/testing/simple_pointer.t | 2 +- source/tlang/testing/simple_pointer_cast_le.t | 25 +++++++ source/tlang/testing/simple_pointer_malloc.t | 16 +++++ 8 files changed, 169 insertions(+), 4 deletions(-) create mode 100755 malloc_test.sh create mode 100644 source/tlang/testing/mem.c create mode 100644 source/tlang/testing/simple_pointer_cast_le.t create mode 100644 source/tlang/testing/simple_pointer_malloc.t diff --git a/.github/workflows/d.yml b/.github/workflows/d.yml index 34eaec9..9638613 100644 --- a/.github/workflows/d.yml +++ b/.github/workflows/d.yml @@ -305,11 +305,18 @@ jobs: run: | ./tlang compile source/tlang/testing/simple_pointer.t ./tlang.out + - name: Simple pointer cast (little endian) + run: | + ././tlang compile source/tlang/testing/simple_pointer_cast_le.t + ./tlang.out - name: Simple extern run: | chmod +x extern_test.sh ./extern_test.sh - + - name: Simple pointer (malloc and free) + run: | + chmod +x malloc_test.sh + ./malloc_test.sh ################################## ####### Deployment section ####### ################################## diff --git a/malloc_test.sh b/malloc_test.sh new file mode 100755 index 0000000..dc5932d --- /dev/null +++ b/malloc_test.sh @@ -0,0 +1,13 @@ +#!/bin/sh + +# Compile C to object file as library to link in +gcc source/tlang/testing/mem.c -c -o mem.o + +# Compile T to C, then compile C and link with other object file into a final object file +./tlang compile source/tlang/testing/simple_pointer_malloc.t -sm HASHMAPPER -et true -pg true -ll mem.o + +# Run the tlang file +./tlang.out + +# Run (with strace) to see it +strace -e brk ./tlang.out diff --git a/source/tlang/compiler/codegen/emit/dgen.d b/source/tlang/compiler/codegen/emit/dgen.d index a5cdc58..6959bde 100644 --- a/source/tlang/compiler/codegen/emit/dgen.d +++ b/source/tlang/compiler/codegen/emit/dgen.d @@ -445,7 +445,7 @@ public final class DCodeEmitter : CodeEmitter { starsOfLiberty ~= "*"; } - emit ~= starsOfLiberty~transform(lhsPtrAddrExprInstr); + emit ~= starsOfLiberty~"("~transform(lhsPtrAddrExprInstr)~")"; /* Assignment operator follows */ emit ~= " = "; @@ -815,6 +815,34 @@ int main() assert(t_87bc875d0b65f741b69fb100a0edebc7 == 4); assert(retValue == 6); + return 0; +}`); + } + else if(cmp(typeChecker.getModule().getName(), "simple_pointer_cast_le") == 0) + { + file.writeln(` +#include +#include +int main() +{ + int retValue = thing(); + assert(t_e159019f766be1a175186a13f16bcfb7 == 256+4); + assert(retValue == 256+4+2); + + return 0; +}`); + } + else if(cmp(typeChecker.getModule().getName(), "simple_pointer_malloc") == 0) + { + file.writeln(` +#include +#include +int main() +{ + test(); + + // TODO: Test the value + return 0; }`); } diff --git a/source/tlang/compiler/typecheck/core.d b/source/tlang/compiler/typecheck/core.d index 814112b..e78863d 100644 --- a/source/tlang/compiler/typecheck/core.d +++ b/source/tlang/compiler/typecheck/core.d @@ -316,8 +316,16 @@ public final class TypeChecker { bool same = false; + /* Handling for pointers */ + if(typeid(type1) == typeid(type2) && cast(Pointer)type1 !is null) + { + Pointer p1 = cast(Pointer)type1, p2 = cast(Pointer)type2; + + /* Now check that both of their referred types are the same */ + return isSameType(p1.getReferredType(), p2.getReferredType()); + } /* Handling for Integers */ - if(typeid(type1) == typeid(type2) && cast(Integer)type1 !is null) + else if(typeid(type1) == typeid(type2) && cast(Integer)type1 !is null) { Integer i1 = cast(Integer)type1, i2 = cast(Integer)type2; @@ -682,6 +690,52 @@ public final class TypeChecker } } + /** + * Given two Value-based instructions this will firstly check if + * at least one of the two is of type Pointer, then checks if the + * remaining instruction is an of type Integer - the remaining instruction + * will then be coerced into a pointer. + * + * If both are Pointers, neither are pointers or one or the other is + * a Pointer and another is non-Integer then nothing will be coerced. + * and this function is effectively a no-op. + * + * Params: + * vInstr1 = the first instruction + * vInstr2 = the second instruction + */ + private void attemptPointerAriehmeticCoercion(Value vInstr1, Value vInstr2) + { + // Get the types of `vInstr1` and `vInstr2` respectively + Type t1 = vInstr1.getInstrType(); + Type t2 = vInstr2.getInstrType(); + + // TODO: Check if T1 is a pointer and then if T2 is an integer make it a pointer + if(cast(Pointer)t1 && cast(Integer)t2) + { + Pointer t1Ptr = cast(Pointer)t1; + + Type coercedType = new Pointer(t1Ptr.getReferredType()); + vInstr2.setInstrType(coercedType); + } + // TODO: Else check if T2 is a pointer and then if T1 is an integer and make it a pointer + else if(cast(Pointer)t2 && cast(Integer)t2) + { + Pointer t2Ptr = cast(Pointer)t2; + + Type coercedType = new Pointer(t2Ptr.getReferredType()); + vInstr1.setInstrType(coercedType); + } + else if(cast(Pointer)t1 && cast(Pointer)t2) + { + // Do nothing + // TODO: Remove this branch + } + else + { + // Do nothing + } + } public void typeCheckThing(DNode dnode) @@ -853,9 +907,18 @@ public final class TypeChecker Value vRhsInstr = cast(Value)popInstr(); Value vLhsInstr = cast(Value)popInstr(); + /** + * Attempt to coerce the types of both instructions if one is + * a pointer and another is an integer, else do nothing + */ + attemptPointerAriehmeticCoercion(vLhsInstr, vRhsInstr); + + Type vRhsType = vRhsInstr.getInstrType(); Type vLhsType = vLhsInstr.getInstrType(); + + /** * TODO * Types must either BE THE SAME or BE COMPATIBLE diff --git a/source/tlang/testing/mem.c b/source/tlang/testing/mem.c new file mode 100644 index 0000000..06309fe --- /dev/null +++ b/source/tlang/testing/mem.c @@ -0,0 +1,13 @@ +#include + +int ctr = 2; + +unsigned long* memAlloc(unsigned long sizeAlloc) +{ + return malloc(sizeAlloc); +} + +void memFree(unsigned long* memory) +{ + free(memory); +} diff --git a/source/tlang/testing/simple_pointer.t b/source/tlang/testing/simple_pointer.t index 69db5af..6e3c511 100644 --- a/source/tlang/testing/simple_pointer.t +++ b/source/tlang/testing/simple_pointer.t @@ -4,7 +4,7 @@ int j; int function(int* ptr) { - *ptr = 2+2; + *(ptr+0) = 2+2; return (*ptr)+1*2; } diff --git a/source/tlang/testing/simple_pointer_cast_le.t b/source/tlang/testing/simple_pointer_cast_le.t new file mode 100644 index 0000000..f6f0d93 --- /dev/null +++ b/source/tlang/testing/simple_pointer_cast_le.t @@ -0,0 +1,25 @@ +module simple_pointer_cast_le; + +int j; + +int ret() +{ + return 0; +} + +int function(int* ptr) +{ + byte* bytePtr = cast(byte*)ptr; + *bytePtr = 2+2; + *(bytePtr+1) = 1; + + return (*ptr)+1*2; +} + +int thing() +{ + int discardExpr = function(&j); + int** l; + + return discardExpr; +} \ No newline at end of file diff --git a/source/tlang/testing/simple_pointer_malloc.t b/source/tlang/testing/simple_pointer_malloc.t new file mode 100644 index 0000000..1958678 --- /dev/null +++ b/source/tlang/testing/simple_pointer_malloc.t @@ -0,0 +1,16 @@ +module simple_pointer_malloc; + +extern efunc ubyte* memAlloc(ulong size); +extern efunc void memFree(ubyte* ptr); + +void test() +{ + ubyte* memory = memAlloc(10UL); + + for(int i = 0; i < 10; i = i + 1) + { + *(memory+i) = 65+i; + } + + discard memFree(memory); +} \ No newline at end of file