Tea-BC-规范

优质
小牛编辑
134浏览
2023-12-01

TeaBC 概述(TeaBC Summary)

TeaBC(Tea byte code) 是一个类似汇编的底层编程语言。它具有以下特性:

  1. 可以转换为 LLVM(Low level virtual machine) 字节码,并继续转为原生的汇编指令。
  2. 支持面向对象,相比 LLVM,更接近高级语言。
  3. 语法简单,比 LLVM 更容易上手。但是功能不如 LLVM 完整。

快速上手(Get Startted)

hello world

field const int8* val { "hello world" }

method extern void printf(int8*, ...)

method void main() {
    ld.fld val                 // 载入全局 val 到栈顶
    call printf(char32*, ...)     // 调用函数
}

类示例

class public A
    extends Object {

    method public void fn(int32 a)
        local int32 b             // 局部变量 b
        local int8 c {           

        ld.arg a                  // 载入参数 a 到栈顶
        ld.const int32 1          // 载入常数 1 到栈顶
        add                       // 计算 a + 1
        st.loc b                  // 存入 a + 1 到 b

        ld.arg a
        br.true if.end            // 如果栈顶是 true,则跳转 if.end

        st.loc c int8 "g"       

    :if.end
        ret

    }

}

成员标记

一个 TeaBC 是由多个成员定义组成的。每个成员定义都由一个成员标记开始。下文具体列出有哪些成员标记。

模块(module)

module <模块修饰符> <模块名> <属性列表>

支持的属性:

  • src <字符串> 模块的源码
  • time <字符串> 模块生成的时间
  • target <字符串> 模块对应的平台

如: module Hi src "hi.src" time "2014-12-12"

类定义(class)

class <类修饰符> <类名> <属性列表>

支持的属性:

  • extends <类型> 指示类型继承类。

结构(struct)

struct <结构修饰符> <结构名> <属性列表> { <结构主体> }

接口(interface)

    interface <接口修饰符> <接口名> <属性列表> { <结构主体> }

方法(method)

method <函数修饰符> <函数返回类型> <函数名> ( <类型> [<参数名>], ... ) <属性列表> { <函数主体> }

字段(field)

field <字段修饰符> <字段类型> <字段名> [ { <默认值> } ]

属性(property)

    property <属性修饰符> <属性类型> <属性名> { get <属性修饰符> <属性列表> { <函数主体> }  set <属性修饰符> <属性列表> { <函数主体> } }

指令参考(Instruction Reference)

终止指令(Terminator Instructions)

ret

从当前方法返回,并将返回值(如果存在)从调用方的计算堆栈推送到被调用方的计算堆栈上。

ret

> ret void
> ret i32 1

br

无条件地将控制转移到目标指令。

br label

:label

>br label %label

br.true

如果值为真、非空或非零,则将控制转移到目标指令。

br.true label

>br i1 %cond label %label

br.false $label

如果值为假、空或零,则将控制转移到目标指令。

switch

实现跳转表。

switch switch.end
switch.case int32 1 switch.case1
switch case int32 2 switch.case2

:switch.case1
:switch.case2
:switch.end

>switch i32 %val, label %otherwise [ i32 0, label %onzero i32 1, label %onone i32 2, label %ontwo ]

switch.case $type $value $label

实现跳转表的一项。

call

调用由传递的方法说明符指示的方法。

ld.const int32 1
call system.Console.writeLine(int32)

call.virt

对对象调用后期绑定方法,并且将返回值推送到计算堆栈上。

异常处理指令(Exception Handling Instructions)

try

表示进入一个需要检测异常语句块。

try
// try 语句
br.catch try.end
// catch 语句
:try.end

br.catch $catchFailLabel

表示检测到任何异常时执行的语句块。

throw

引发当前位于计算堆栈上的异常对象。

throw.r

再次引发当前异常。

算数操作符(Arthematic Operations)

add

将两个值相加并将结果推送到计算堆栈上。

ld.const int32 1
ld.const int32 1
add

>int32 + int32 -> add nsw i32 %0 %1
>uint32 + uint32 -> add i32 %0 %1
>uint32 + int32 -> add i32 %0 %1
>long + long -> add nsw i64 %0 %1
>ulong + ulong -> add i64 %0 %1
>float + float -> fadd i32 %0 %1
>double + double -> fadd i64 %0 %1

add.ovf

将两个整数相加,执行溢出检查,并且将结果推送到计算堆栈上。

ld.const int32 1
ld.const int32 1
add.ovf

int32 + int32 -> call {i32, i1} @llvm.sadd.with.overflow.i32(i32 %0, i32 %1)
uint32 + uint32 -> call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %0, i32 %1)
uint32 + int32 -> call {i32, i1} @llvm.uadd.with.overflow.i32(i32 %0, i32 %1)
long + long -> call {i64, i1} @llvm.sadd.with.overflow.i64(i64 %0, i64 %1)
ulong + ulong -> call {i64, i1} @llvm.uadd.with.overflow.i64(i64 %0, i64 %1)

http://llvm.org/docs/LangRef.html#llvm-sadd-with-overflow-intrinsics

declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)

declare {i32, i1} @llvm.sadd.with.overflow.i32(i32 %a, i32 %b)
%res = call {i32, i1} @llvm.sadd.with.overflow.i32(i32 2147483647, i32 2147483647)
%obit = extractvalue {i32, i1} %res, 1
br i1 %obit, label %overflow, label %hidden

sub

从其他值中减去一个值并将结果推送到计算堆栈上。

sub.ovf

从另一值中减去一个整数值,执行溢出检查,并且将结果推送到计算堆栈上。

mul

将两个值相乘并将结果推送到计算堆栈上。

mul.ovf

将两个整数值相乘,执行溢出检查,并将结果推送到计算堆栈上。

div

将两个值相除并将结果作为浮点(F 类型)或商(int32 类型)推送到计算堆栈上。

rem

将两个值相除并将余数推送到计算堆栈上。

neg

对一个值执行求反并将结果推送到计算堆栈上。

位运算操作符(Bitwise Operations)

shl

将整数值左移(用零填充)指定的位数,并将结果推送到计算堆栈上。

shr

将整数值右移(保留符号)指定的位数,并将结果推送到计算堆栈上。

and

计算两个值的按位“与”并将结果推送到计算堆栈上。

>and i32 %0 %1
>and i64 %0 %1

or

计算位于堆栈顶部的两个整数值的按位求补并将结果推送到计算堆栈上。

xor

计算位于计算堆栈顶部的两个值的按位异或,并且将结果推送到计算堆栈上。

not

计算堆栈顶部整数值的按位求补并将结果作为相同的类型推送到计算堆栈上。

比较操作符(Comparison Operations)

cmp.eq

比较两个值。如果这两个值相等,则将整数值 1 (int32) 推送到计算堆栈上;否则,将 0 (int32) 推送到计算堆栈上。

cmp.gt

比较两个值。如果第一个值大于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。

cmp.lt

比较两个值。如果第一个值小于第二个值,则将整数值 1 (int32) 推送到计算堆栈上;反之,将 0 (int32) 推送到计算堆栈上。

转换操作符(Convertion Operations)

conv

将位于计算堆栈顶部的值转换为 natural int。

cast

尝试将引用传递的对象转换为指定的类。

载入和写入(Load And Store)

ld.arg

将参数(由指定索引值引用)加载到堆栈上。

ld.const

将所提供的 int32 类型的值作为 int32 推送到计算堆栈上。

ld.fld

查找对象中其引用当前位于计算堆栈的字段的值。

ld.flda

查找对象中其引用当前位于计算堆栈的字段的地址。

ld.fna

将指向实现特定方法的本机代码的非托管指针(natural int 类型)推送到计算堆栈上。

ld.vfna

将指向实现与指定对象关联的特定虚方法的本机代码的非托管指针(natural int 类型)推送到计算堆栈上。

ld.loc

将指定索引处的局部变量加载到计算堆栈上。

ld.ptr

将地址指向的值类型对象复制到计算堆栈的顶部。

st.arg

将位于计算堆栈顶部的值存储到位于指定索引的参数槽中。

st.fld

用新值替换在对象引用或指针的字段中存储的值。

st.loc

从计算堆栈的顶部弹出当前值并将其存储到指定索引处的局部变量列表中。

st.ptr

将指定类型的值从计算堆栈复制到所提供的内存地址中。

其它操作符(Other Operations)

dup

复制计算堆栈上当前最顶端的值,然后将副本推送到计算堆栈上。

sizeof

将提供的值类型的大小(以字节为单位)推送到计算堆栈上。

break

发出信号以通知调试器已撞上了一个断点。

nop

如果修补操作码,则填充空间。尽管可能消耗处理周期,但未执行任何有意义的操作。

new.cls

创建一个值类型的新对象或新实例,并将对象引用(O 类型)推送到计算堆栈上。

new.strc

将位于指定地址的对象的所有字段初始化为空引用或适当的基元类型的 0。

pop

移除当前位于计算堆栈顶部的值。

alloca

malloc

free

select

变量参数操作符

va.start

返回指向当前方法的参数列表的指针。

>declare void @llvm.va_start(i8*)

>%ap = alloca i8*
>%ap2 = bitcast i8** %ap to i8*
>call void @llvm.va_start(i8* %ap2)

va.arg

返回当前参数指针对应的值,并放入栈顶。

>%tmp = va_arg i8** %ap, i32

va.copy

复制当前参数列表到指定的指针,并放入栈顶。

>declare void @llvm.va_copy(i8*, i8*)

>%aq = alloca i8*
>%aq2 = bitcast i8** %aq to i8*
>call void @llvm.va_copy(i8* %aq2, i8* %ap2)

va.end

关闭读取参数列表的指针。

>declare void @llvm.va_end(i8*)    

>call void @llvm.va_end(i8* %aq2)