Tea-BC-规范
TeaBC 概述(TeaBC Summary)
TeaBC(Tea byte code) 是一个类似汇编的底层编程语言。它具有以下特性:
- 可以转换为 LLVM(Low level virtual machine) 字节码,并继续转为原生的汇编指令。
- 支持面向对象,相比 LLVM,更接近高级语言。
- 语法简单,比 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)