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 `<expr>` with `()`. So instead of `*(<expre>)` we got `*<expr>` 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
This commit is contained in:
Tristan B. Velloza Kildaire 2023-04-17 16:50:11 +02:00 committed by GitHub
parent fca2bac20e
commit 493da1a4e7
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
8 changed files with 169 additions and 4 deletions

View File

@ -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 #######
##################################

13
malloc_test.sh Executable file
View File

@ -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

View File

@ -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<stdio.h>
#include<assert.h>
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<stdio.h>
#include<assert.h>
int main()
{
test();
// TODO: Test the value
return 0;
}`);
}

View File

@ -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

View File

@ -0,0 +1,13 @@
#include<stdlib.h>
int ctr = 2;
unsigned long* memAlloc(unsigned long sizeAlloc)
{
return malloc(sizeAlloc);
}
void memFree(unsigned long* memory)
{
free(memory);
}

View File

@ -4,7 +4,7 @@ int j;
int function(int* ptr)
{
*ptr = 2+2;
*(ptr+0) = 2+2;
return (*ptr)+1*2;
}

View File

@ -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;
}

View File

@ -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);
}