chsasank / llama.lisp

Lisp dialect designed for HPC and AI
GNU Lesser General Public License v2.1
15 stars 6 forks source link

Casting from int to float and float to int #67

Closed adi-lb-phoenix closed 4 months ago

adi-lb-phoenix commented 4 months ago

We should be able to cast floating point values from integer values . We do not have equivalent C code and its LLVM IR issue. for ex :

(declare a int)
(set a 10)
(declare b float)
(set b 10.0)
(declare c float)
(set c (fadd a b ))

We cannot add 'a' and 'b' as both of them are in different formats. We should be able to change the format of 'a' to float before adding it.

We should be able to do something similar to the below :

(c-lisp

    (define ((fprint float) (n  float) ))

    (define ((main void))
    (declare a int)
    (declare b float)
    (declare c float)
    (set a 10)
    (set b 10.0)
    (declare a float)
    (set c (fadd a b))
    (call fprint c)

    )
)

The output when the above is executed executed :

guile ../../utils/sexp-json.scm < test.sexp  | python ../../c-lisp.py | python ../../brilisp.py | python ../../llvm.py | bash ../brilisp/run.sh {args}
Error in statement: ['declare', 'a', 'float']
Traceback (most recent call last):
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 473, in <module>
    json.dump(brilisp_code_generator.c_lisp(json.loads(sys.stdin.read())), sys.stdout)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 59, in c_lisp
    return ["brilisp"] + [self.gen_function(fn) for fn in prog[1:]]
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 59, in <listcomp>
    return ["brilisp"] + [self.gen_function(fn) for fn in prog[1:]]
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 125, in gen_function
    *self.gen_compound_stmt(
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 303, in gen_compound_stmt
    instr_list += self.gen_stmt(s)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 163, in gen_stmt
    raise e
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 150, in gen_stmt
    return self.gen_decl_stmt(stmt)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../c-lisp.py", line 271, in gen_decl_stmt
    raise CodegenError(f"Re-declaration of variable {name}")
__main__.CodegenError: Re-declaration of variable a
Traceback (most recent call last):
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../brilisp.py", line 174, in <module>
    main()
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../brilisp.py", line 169, in main
    expr = json.load(sys.stdin)
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/__init__.py", line 293, in load
    return loads(fp.read(),
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
Traceback (most recent call last):
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 359, in <module>
    main()
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 352, in main
    bril_prog = munch.munchify(json.load(sys.stdin))
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/__init__.py", line 293, in load
    return loads(fp.read(),
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/__init__.py", line 346, in loads
    return _default_decoder.decode(s)
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/decoder.py", line 337, in decode
    obj, end = self.raw_decode(s, idx=_w(s, 0).end())
  File "/gnu/store/kgxbvcr9ks2k8lbm8fla7zvp63dbagrc-python-3.10.7/lib/python3.10/json/decoder.py", line 355, in raw_decode
    raise JSONDecodeError("Expecting value", s, err.value) from None
json.decoder.JSONDecodeError: Expecting value: line 1 column 1 (char 0)
/usr/bin/ld: /gnu/store/ln6hxqjvz6m9gdd9s97pivlqck7hzs99-glibc-2.35/lib/Scrt1.o: in function `_start':
(.text+0x17): undefined reference to `main'
clang: error: linker command failed with exit code 1 (use -v to see invocation)
../brilisp/run.sh: line 9: /tmp/tmp.InjTJQHqDK.out: No such file or directory
rm: cannot remove '/tmp/tmp.InjTJQHqDK.out': No such file or directory
chsasank commented 4 months ago

Can you write some C code and show generated LLVM IR with clang?

LLVM has many casting ops, we need to decide on which ones to implement: https://llvm.org/docs/LangRef.html#conversion-operations

adi-lb-phoenix commented 4 months ago

My C code :

#include<stdio.h>
float int_to_float(int x){
    float y ;
    y = x ;
    return y ;
}

int main(){
int a = 10;
printf("%f",int_to_float(a));

}

LLVM IR of the above code generated using : clang -S -emit-llvm <filename>

; ModuleID = 'I_2_F.c'
source_filename = "I_2_F.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-i128:128-f80:128-n8:16:32:64-S128"
target triple = "x86_64-unknown-linux-gnu"

@.str = private unnamed_addr constant [3 x i8] c"%f\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local float @int_to_float(i32 noundef %0) #0 {
  %2 = alloca i32, align 4
  %3 = alloca float, align 4
  store i32 %0, ptr %2, align 4
  %4 = load i32, ptr %2, align 4
  %5 = sitofp i32 %4 to float
  store float %5, ptr %3, align 4
  %6 = load float, ptr %3, align 4
  ret float %6
}

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
  %1 = alloca i32, align 4
  store i32 10, ptr %1, align 4
  %2 = load i32, ptr %1, align 4
  %3 = call float @int_to_float(i32 noundef %2)
  %4 = fpext float %3 to double
  %5 = call i32 (ptr, ...) @printf(ptr noundef @.str, double noundef %4)
  ret i32 0
}

declare i32 @printf(ptr noundef, ...) #1

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cmov,+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 8, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 2}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"clang version 18.1.7"}
GlowingScrewdriver commented 4 months ago

We should be able to do something similar to the below :

(define ((main void))
(declare a int)
(declare b float)
(declare c float)
(set a 10)
(set b 10.0)
(declare a float)
(set c (fadd a b))
(call fprint c)

I don't think this should be the syntax. Semantically, a is redeclared so that it is used as a float instead of an int.

I think we should implement casting as an operator, like how C does it. So in this example, a should not be modified, and the result of the casting expression will be a new floating-point value.

Explicit casts in C work like this:

int val;
val = 10; // val is integer 10
float fval;
fval = (float) val; // fval is float 10, and val is still integer 10

Following the same pattern in C-Lisp, we would end up with something like:

(declare val int)
(set val 10) ; val is integer 10
(declare fval float)
(set fval (sitofp val)) ; fval is float 10, and fval is still integer 10

sitofp (or whatever name we decide on) would be the casting operator.

adi-lb-phoenix commented 4 months ago

I agree with your point, we should not redeclare it, rather use it as a casting operator. Regarding naming conventions. I do not think it best to use any keywords that are implemented in any other language, as I believe it might lead to issues while debugging. Although C-lisp and llvm.py are apart by one compiler (brilisp.py).

adi-lb-phoenix commented 4 months ago

(c-lisp

(define ((fprint float) (n  float) ))

(define ((main void))
(declare a int)
(declare b float)
(declare c float)
(set a 10)
(set b 10.0)
(declare a float)
(set c (fadd a b))
(call fprint c)

)

)

The error occurred when it was passed to llvm.py command : guile ../../utils/sexp-json.scm < test.sexp | python ../../c-lisp.py | python ../../brilisp.py | python ../../llvm.py Output:

Exception: Operands must be the same type, got (i32, float); in instruction Munch({'op': 'fadd', 'type': 'float', 'dest': 'tmp_clisp-ftucgfgr', 'args': ['tmp_clisp.inp_0-bvlsnznb', 'tmp_clisp.inp_1-pgvljqyz']}):
Traceback (most recent call last):
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 359, in <module>
    main()
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 354, in main
    code_gen.generate(bril_prog)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 44, in generate
    self.gen_function(fn)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 345, in gen_function
    self.gen_instructions(fn_instrs)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 261, in gen_instructions
    raise e
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 252, in gen_instructions
    gen_value(instr)
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/../../llvm.py", line 163, in gen_value
    llvm_instr(*[self.gen_var(arg) for arg in instr.args], name=instr.dest),
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/builder.py", line 34, in wrapped
    raise ValueError("Operands must be the same type, got (%s, %s)"
ValueError: Operands must be the same type, got (i32, float)

So we need to make the change before we Exception: Operands must be the same type, got (i32, float); in instruction Munch({'op': 'fadd', 'type': 'float', 'dest': 'tmp_clisp-ftucgfgr', 'args': ['tmp_clisp.inp_0-bvlsnznb', 'tmp_clisp.inp_1-pgvljqyz']}): . So when this error is discovered . Can we look at the function return type ? Based on the function return type convert that data type for which the error was raised. Ex In this above case when the the variable was discovered to not be int. Could we do sitofp i32 'tmp_clisp.inp_0-bvlsnznb' to float ?

adi-lb-phoenix commented 4 months ago

How did the function traversal happen : main()-> code_gen.generate(bril_prog) -> self.gen_function(fn) -> self.gen_instructions(fn_instrs)-> self.gen_instructions(fn_instrs) -> raise e in gen_instructions -> gen_value(instr) in gen_instructions ->

check the below line in the code : line 163, in gen_value llvm_instr(*[self.gen_var(arg) for arg in instr.args], name=instr.dest), File "home/.local/lib/python3.10/site-packages/llvmlite/ir/builder.py", line 34, in wrapped raise ValueError("Operands must be the same type, got (%s, %s)"

adi-lb-phoenix commented 4 months ago

On inspecting gen_value function. The error seems to be returning from llvm_instr(*[self.gen_var(arg) for arg in instr.args], name=instr.dest). I printed the contents of the list fed into llvm_instr() function. print([self.gen_var(arg) for arg in instr.args] ,"REPL") Contents : [<ir.LoadInstr '.12' of type 'i32', opname 'load', operands [<ir.AllocaInstr 'tmp_clisp.inp_0-gjhylhwu' of type 'i32*', opname 'alloca', operands ()>]>, <ir.LoadInstr '.13' of type 'float', opname 'load', operands [<ir.AllocaInstr 'tmp_clisp.inp_1-ffnjzeqi' of type 'float*', opname 'alloca', operands ()>]>] REPL REPL is just to recognize the printed contents

adi-lb-phoenix commented 4 months ago

Above 3 comments are to be ignored for now.

adi-lb-phoenix commented 4 months ago

` (c-lisp

(define ((fprint float) (n  float) ))

(define ((main void))
    (declare a int)
    (declare b float)
    (declare c float)
    (set a 10)
    (set b 10.0)
    (declare a float)
    (set c (fadd a b))
    (call fprint c)

)

)Error in statement: ['declare', 'a', 'float'] Traceback (most recent call last): File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 473, in json.dump(brilisp_code_generator.c_lisp(json.loads(sys.stdin.read())), sys.stdout) File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 59, in c_lisp return ["brilisp"] + [self.gen_function(fn) for fn in prog[1:]] File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 59, in return ["brilisp"] + [self.gen_function(fn) for fn in prog[1:]] File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 125, in gen_function *self.gen_compound_stmt( File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 303, in gen_compound_stmt instr_list += self.gen_stmt(s) File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 163, in gen_stmt raise e File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 150, in gen_stmt return self.gen_decl_stmt(stmt) File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 271, in gen_decl_stmt raise CodegenError(f"Re-declaration of variable {name}") main.CodegenError: Re-declaration of variable a `

adi-lb-phoenix commented 4 months ago

(c-lisp

(define ((fprint float) (n  float) ))

(define ((main void))
    (declare a int)
    (declare b float)
    (declare c float)
    (set a 10)
    (set b 10.0)
    (sitofp a)
    (set c (fadd a b))
    (call fprint c)

)

`

exp-json.scm < test.sexp | python ../../../../c-lisp.py Error in statement: ['sitofp', 'a'] Traceback (most recent call last): File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 473, in json.dump(brilisp_code_generator.c_lisp(json.loads(sys.stdin.read())), sys.stdout) File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 59, in c_lisp return ["brilisp"] + [self.gen_function(fn) for fn in prog[1:]] File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 59, in return ["brilisp"] + [self.gen_function(fn) for fn in prog[1:]] File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 125, in gen_function *self.gen_compound_stmt( File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 303, in gen_compound_stmt instr_list += self.gen_stmt(s) File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 163, in gen_stmt raise e File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 158, in gen_stmt return self.gen_expr(stmt) File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/c-lisp/llama2.c/int_to_float/../../../../c-lisp.py", line 468, in gen_expr raise CodegenError(f"Bad expression: {expr}") main.CodegenError: Bad expression: ['sitofp', 'a'] `

adi-lb-phoenix commented 4 months ago

We will not try to solve this in C-lisp first, but we will do it from brilisp first.

(brilisp 
    (define ((print int) (n int)))

    (define ((add5 float) (n int))
        (set (five float) (const 5.0))
        (sitofp n)
        (set (sum float) (add n five))
        (ret sum)
    )

    (define (( main float)) 
        (set (a int) (const 9))
        (set (b int) (call add5 a))
        (set (tmp int)(call print b))
        (ret b)

    )

command executed : guile ../../../utils/sexp-json.scm < I_2_F.sexp | python ../../../brilisp_1.py Output :

Traceback (most recent call last):
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 174, in <module>
    main()
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 170, in main
    print(json.dumps(brilisp(expr)))
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 165, in brilisp
    return {"functions": [gen_function(x) for x in body]}
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 165, in <listcomp>
    return {"functions": [gen_function(x) for x in body]}
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 23, in gen_function
    "instrs": [gen_instr(x) for x in instrs],
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 23, in <listcomp>
    "instrs": [gen_instr(x) for x in instrs],
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../brilisp_1.py", line 157, in gen_instr
    raise SyntaxError(f"Unknown instruction {instr}")
SyntaxError: Unknown instruction ['sitofp', 'n']
GlowingScrewdriver commented 4 months ago

Here is how I'd go about implementing cast in Brilisp:

Some operations you can look at for reference:

adi-lb-phoenix commented 4 months ago

Defined the below functions in gen_instr(instr): of brilisp_1.py. brilisp_1py is a copy of brilisp.py.

def is_sitofp(instr):
        return instr[0] == "sitofp"

    def gen_sitofp_instr(instr):
        return {"op":"sitofp", "type":instr[1][1], "args": instr[1][0]}

    def is_fptosi(instr):
        return instr[0] == "fptosi"

    def gen_fptosi_instr(instr):
        return {"op":"fptosi", "type":instr[1][1], "args": instr[1][0]}

Why did I do it ? I observed that in this function, each instruction was being checked if the 0th position of that list matched a instruction name and if true a a new function in the name of "gen_instructionNAME_instr" was called to generate instructions for the matched instruction from one form to brilisp format. Next step is to observe th e functions calling functions such as the above and add conditions/statements in that calling function that lets the above defined functions be called to verify and generate instructions when instructions such as "sitofp" and "fitosi" is encountered.

adi-lb-phoenix commented 4 months ago

@GlowingScrewdriver

Here is how I'd go about implementing cast in Brilisp:

* Create a new operator in Brilisp. Call it by the same name as LLVM's int-to-float cast: `sitofp`. In terms of the Brilisp compiler architecture, this will be defined alongside other operations like the arithmetic, pointer, etc.

* LLVM `sitofp` takes the result type as an argument (the result is a floating-point, but the argument specifies precision -- `float`/`double`). For now, we will hardcode that argument since we have only one floating-point type.

* Do not insert implicit casts anywhere. That is a conscious design decision adopted in both Brilisp and C-Lisp. If a cast is needed, the programmer will write one.

Some operations you can look at for reference:

* `id`: Possibly the simplest instruction.

* Any "value" instruction, like `add`: Direct mapping to LLVM instructions.

I have taken your valuable suggestions and the directions into account 💯 👍

adi-lb-phoenix commented 4 months ago

I have added the below lines of code :

elif is_sitofp(instr):
            return gen_sitofp_instr(instr)
elif is_fptosi(instr):
            return (gen_fptosi_instr)

along with these :

if is_const(instr):
        return gen_constr_instr(instr)
    elif is_value(instr):
        return gen_value_instr(instr)
    elif is_ret(instr):
        return gen_ret_instr(instr)
    elif is_call(instr):
        return gen_call_instr(instr)
    elif is_jmp(instr):
        return gen_jmp_instr(instr)
    elif is_label(instr):
        return gen_label_instr(instr)
    elif is_br(instr):
        return gen_br_instr(instr)
    elif is_nop(instr):
        return gen_nop_instr(instr)
    elif is_store(instr):
        return gen_store_instr(instr)
    elif is_sitofp(instr):
            return gen_sitofp_instr(instr)
    elif is_fptosi(instr):
            return (gen_fptosi_instr)
    else:
        raise SyntaxError(f"Unknown instruction {instr}")

All the above code is present in gen_instr(instr) function

adi-lb-phoenix commented 4 months ago

Executed the below program

(brilisp 
    (define ((print int) (n int)))

    (define ((add5 float) (n int))
        (set (five float) (const 5.0))
        (sitofp n)
        (set (sum float) (add n five))
        (ret sum)
    )

    (define (( main float)) 
        (set (a int) (const 9))
        (set (b float) (call add5 a))
        (set (tmp int)(call print b))
        (ret b)
    )

Output when excuted command guile ../../../utils/sexp-json.scm < I_2_F.sexp | python ../../../brilisp_1.py. Output : {"functions": [{"name": "print", "type": "int", "args": [{"name": "n", "type": "int"}], "instrs": []}, {"name": "add5", "type": "float", "args": [{"name": "n", "type": "int"}], "instrs": [{"op": "const", "type": "float", "dest": "five", "value": 5.0}, {"op": "sitofp", "args": "n"}, {"op": "add", "type": "float", "dest": "sum", "args": ["n", "five"]}, {"op": "ret", "args": ["sum"]}]}, {"name": "main", "type": "float", "args": [], "instrs": [{"op": "const", "type": "int", "dest": "a", "value": 9}, {"op": "call", "type": "float", "dest": "b", "funcs": ["add5"], "args": ["a"]}, {"op": "call", "type": "int", "dest": "tmp", "funcs": ["print"], "args": ["b"]}, {"op": "ret", "args": ["b"]}]}]}

GlowingScrewdriver commented 4 months ago

We don't want to change the original variable while performing a cast. The cast operator takes an integer as an operand, and produces a new floating-point value. So add5 in your program should look something like:

    (define ((add5 float) (n int))
        (set (five float) (const 5.0))
        (set (f_n float) (sitofp n))
        (set (sum float) (add f_n five))
        (ret sum)
    )
adi-lb-phoenix commented 4 months ago

Why don't you want to change the original variable ? is it To maintain scope ?

GlowingScrewdriver commented 4 months ago

Because that's not how casts work in LLVM, (or C, for that matter). And we are saving ourselves much of the trouble of language design by adopting the semantics of LLVM and C.

GlowingScrewdriver commented 4 months ago

Thing is, we can't think of all the caveats of a design decision when we make it. But the C language has been around for decades, and has undergone many improvements. LLVM too is quite mature. So following their footsteps definitely can't go wrong.

adi-lb-phoenix commented 4 months ago
(brilisp 
    (define ((print int) (n int)))

    (define ((add5 float) (n int))
        (set (five float) (const 5.0))
        (set (f_n float) (sitofp n))
        (set (sum float) (add n five))
        (ret sum)
    )

    (define (( main float)) 
        (set (a int) (const 9))
        (set (b float) (call add5 a))
        (set (tmp int)(call print b))
        (ret b)
    )
)

In brilisp_1.py added sitofp

    def is_value(instr):
        value_op = {
            "add",
            "mul",
            "sub",
            "div",
            "eq",
            "ne",
            "lt",
            "gt",
            "le",
            "ge",
            "not",
            "and",
            "or",
            "alloc",
            "load",
            "ptradd",
            "id",
            "fadd",
            "fsub",
            "fmul",
            "fdiv",
            "feq",
            "fne",
            "flt",
            "fgt",
            "fle",
            "fge",
            "sitofp",
            "fptosi"
        }

made Changes to the function defined https://github.com/chsasank/llama.lisp/issues/67#issuecomment-2211671406 as below

    def is_sitofp(instr):
        print(instr,"REPL")
        if instr[1][1] not in ["float"]:
            raise TypeError("type does not match float")
        return instr[0] == "sitofp"

    def gen_sitofp_instr(instr):
        return {"op":"sitofp","type":instr[1][1],"args": instr[1][0]}

    def is_fptosi(instr):
        print(instr,"REPL")
        if instr[1][1] not in ["int"]:
            raise TypeError("type does not match int")
        return instr[0] == "fptosi"

    def gen_fptosi_instr(instr):
        return {"op":"fptosi", "type":instr[1][1], "args": instr[1][0]}
guile ../../../utils/sexp-json.scm < I_2_F.sexp | python ../../../brilisp_1.py 
{"functions": [{"name": "print", "type": "int", "args": [{"name": "n", "type": "int"}], "instrs": []}, {"name": "add5", "type": "float", "args": [{"name": "n", "type": "int"}], "instrs": [{"op": "const", "type": "float", "dest": "five", "value": 5.0}, {"op": "sitofp", "type": "float", "dest": "f_n", "args": ["n"]}, {"op": "add", "type": "float", "dest": "sum", "args": ["n", "five"]}, {"op": "ret", "args": ["sum"]}]}, {"name": "main", "type": "float", "args": [], "instrs": [{"op": "const", "type": "int", "dest": "a", "value": 9}, {"op": "call", "type": "float", "dest": "b", "funcs": ["add5"], "args": ["a"]}, {"op": "call", "type": "int", "dest": "tmp", "funcs": ["print"], "args": ["b"]}, {"op": "ret", "args": ["b"]}]}]}

{"op": "sitofp", "type": "float", "dest": "f_n", "args": ["n"]} . Next step will be to build IR for the sitofp and fptosi in gen_instructions function in llvm.py. The call to build IR from llvm.py will begin from

                elif instr.op == "sitofp":
                    gen_sitofp(instr)
                elif instr.op =="fptosi":
                    gen_fptosi(instr)

I will use this https://llvmlite.readthedocs.io/en/latest/user-guide/ir/ir-builder.html#llvmlite.ir.IRBuilder.sitofp to create IR for sitofp

adi-lb-phoenix commented 4 months ago

in llvm.py , the function which will build the IR for sitofp :

def gen_sitofp(instr):
            # IRBuilder.sitofp(value, typ, name='')
            self.declare_var(self.gen_type(instr.type), instr.dest)
            self.gen_symbol_store(instr.dest,
                self.builder.sitofp(
                    self.gen_var(instr.args[0]),self.gen_type(instr.type),name=self.gen_var(instr.dest))

Command : guile ../../../utils/sexp-json.scm < I_2_F.sexp | python ../../../brilisp_1.py | python ../../../llvm.py

Output :

Traceback (most recent call last):
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/_utils.py", line 46, in __str__
    return self.__cached_str
AttributeError: 'CastInstr' object has no attribute '_StrCaching__cached_str'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/_utils.py", line 56, in get_reference
    return self.__cached_refstr
AttributeError: 'CastInstr' object has no attribute '_StringReferenceCaching__cached_refstr'

During handling of the above exception, another exception occurred:

Traceback (most recent call last):
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../llvm.py", line 379, in <module>
    main()
  File "/home/aadithya.bhat/c_lisp/bril/bril-txt/llama.lisp/src/backend/tests/brilisp/int_to_float/../../../llvm.py", line 375, in main
    print(code_gen.module)
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/module.py", line 242, in __repr__
    lines += self._get_body_lines()
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/module.py", line 212, in _get_body_lines
    lines += [str(v) for v in self.globals.values()]
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/module.py", line 212, in <listcomp>
    lines += [str(v) for v in self.globals.values()]
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 1015, in __str__
    self.descr(buf)
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 1010, in descr
    self.descr_body(buf)
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 1004, in descr_body
    blk.descr(buf)
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 1174, in descr
    buf += ["  {0}\n".format(instr) for instr in self.instructions]
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 1174, in <listcomp>
    buf += ["  {0}\n".format(instr) for instr in self.instructions]
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/_utils.py", line 48, in __str__
    s = self.__cached_str = self._to_string()
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 547, in _to_string
    buf.append("{0} = ".format(self.get_reference()))
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/_utils.py", line 58, in get_reference
    s = self.__cached_refstr = self._get_reference()
  File "/home/aadithya.bhat/.local/lib/python3.10/site-packages/llvmlite/ir/values.py", line 567, in _get_reference
    if '\\' in name or '"' in name:
TypeError: argument of type 'LoadInstr' is not iterable
adi-lb-phoenix commented 4 months ago

in llvm.py , the function which will build the IR for sitofp :

def gen_sitofp(instr):
            # IRBuilder.sitofp(value, typ, name='')
            self.declare_var(self.gen_type(instr.type), instr.dest)
            self.gen_symbol_store(instr.dest,
                self.builder.sitofp(
                    self.gen_var(instr.args[0]),self.gen_type(instr.type),name=self.gen_var(instr.dest))

Command : guile ../../../utils/sexp-json.scm < I_2_F.sexp | python ../../../brilisp_1.py | python ../../../llvm.py Updated an instruction ingen_sitofpfunction : self.gen_var(instr.args[0]),self.gen_type(instr.type),name=instr.dest` Output :


%".5" = load float, float* %"f_n" REPL
; ModuleID = ""
target triple = "unknown-unknown-unknown"
target datalayout = ""

declare float @"fprint"(float %".1")

define float @"add5"(i32 %"n") { alloca-bncsohpf: %"n.1" = alloca i32 %"five" = alloca float %"f_n" = alloca float %"sum" = alloca float br label %"entry-toewuyga" entry-toewuyga: store i32 %"n", i32 %"n.1" store float 0x4014000000000000, float %"five" %".5" = load float, float %"f_n" %".6" = load i32, i32 %"n.1" %"f_n.1" = sitofp i32 %".6" to float store float %"f_n.1", float %"f_n" %".8" = load float, float %"f_n" %".9" = load float, float %"five" %"sum.1" = fadd float %".8", %".9" store float %"sum.1", float %"sum" %".11" = load float, float* %"sum" ret float %".11" }

define float @"main"() { alloca-npgxravk: %"a" = alloca i32 %"b" = alloca float %"tmp" = alloca float br label %"entry-yuytbslm" entry-yuytbslm: store i32 9, i32 %"a" %".3" = load i32, i32 %"a" %"b.1" = call float @"add5"(i32 %".3") store float %"b.1", float %"b" %".5" = load float, float %"b" %"tmp.1" = call float @"fprint"(float %".5") store float %"tmp.1", float %"tmp" %".7" = load float, float %"b" ret float %".7" }

adi-lb-phoenix commented 4 months ago

Brilisp file :

(brilisp 
    (define ((fprint float) (n float)))

    (define ((add5 float) (n int))
        (set (five float) (const 5.0))
        (set (f_n float) (sitofp n))
        (set (sum float) (fadd f_n five))
        (ret sum)
    )

    (define (( main float)) 
        (set (a int) (const 9))
        (set (b float) (call add5 a))
        (set (tmp float) (call fprint b))
        (ret b)
    )

)

Command : guile ../../../utils/sexp-json.scm < I_2_F.sexp | python ../../../brilisp_1.py | python ../../../llvm.py | bash ../run.sh

Output :

14.000000
adi-lb-phoenix commented 4 months ago

For float to int in brilisp.py we had already added the functions is_fptosi(instr) and gen_fptosi_instr(instr) which would verify and generate the required library for us.

in llvm.py

We add the below function in gen_instructions

 def gen_fptosi(instr):
            self.declare_var(self.gen_type(instr.type), instr.dest)
            self.gen_symbol_store(instr.dest,
                    self.builder.fptosi(
                        self.gen_var(instr.args[0]),self.gen_type(instr.type),name=instr.dest
                                        )
                                )

And the below lines of code

elif instr.op == "sitofp":
                    gen_sitofp(instr)
                elif instr.op =="fptosi":
                    gen_fptosi(instr)

in gen_instructions(self, instrs):

The below brilisp code does conversion from float to int and performs addition operation on int and a value which has been converted from float to int.

(brilisp 
    (define ((print int) (n int)))

    (define ((add5 int) (n float))
        (set (five int) (const 5))
        (set (f_n int) (fptosi n))
        (set (sum int) (add f_n five))
        (ret sum)
    )

    (define (( main int)) 
        (set (a float) (const 9.0))
        (set (b int) (call add5 a))
        (set (tmp int) (call print b))
        (ret b)
    )

)

Command : guile ../../../utils/sexp-json.scm < F_2_I.sexp | python ../../../brilisp.py | python ../../../llvm.py | bash ../run.sh

Output :

14
GlowingScrewdriver commented 4 months ago

Don't use this issue to post progress on the code. That's what you should use a PR for.