岁月流转,往昔空明

C++博客 首页 新随笔 联系 聚合 管理
  118 Posts :: 3 Stories :: 413 Comments :: 0 Trackbacks

表达式值的存储

LLVM中基本数据类型及存储类型

值是编译器所需要处理的基本数据。它出现在各个角落,条件分支、表达式、返回语句。甚至是函数地址也可以被视作是值类型。

对于编译器而言,最基本的值是整数和浮点。其他的值都可以用这两者来表达,例如布尔和指针。如果你是从一个最基本的指令集开始写起,那么整数和浮点的数值运算、转换、基于整数寄存器的跳转和地址取值是一个寄存器机的最基本操作。所有更加高级的操作,例如数组、结构体、指针、函数、对象等,都可以建立在这一基础上。

如果一个指令系统在整数和浮点数之外,额外提供了布尔、分支、函数调用和结构体的支持,那么它与高级语言将会贴近更多,生成代码的方式也更加简单。

在高级语义的数据结构上,LLVM提供了相当良好的支持。它支持的原生类型(First class)包括: 各种精度的整型和浮点数,指针、向量,结构体和数组。这些类型的数据存取和运算都是有指令直接支撑,而不需要自行计算并生成更加原始的指令。

在存储类型上,LLVM提供了Value, Argument, Alloca, GlobalVariable, Pointer五种存储类型。Value是右值,它不可取引用,不可更改。Argument表示了函数实参,它是Value的一个派生类。所以对参数的任何更改行为实际上都是不被允许的。Alloca保存了栈地址,GlobalVariable保存了全局变量的地址,Pointer则是一般意义上的指针。

除了存储指令,LLVM所有的指令都是针对Value的操作,并返回一个Value。所以

Var a = Alloca int
Var b = Alloca int
Var c = Alloca int
c = ADD a, b

这样的操作,在LLVM中实际上是将a和b的地址相加,并把C从变量替换成一个左值(注意,是替换,变量的值没有任何变化)。

在LLVM中,正确的做法应当类似于下面这样:

a = Alloca int
b = Alloca int
c = Alloca int
a_v = load a
b_v = load b
c_v = ADD a, b
store c, c_v

要先将值从变量中读出,进行操作,再保存到另外一个变量中。

表达式值的数据结构

一个的表达式参数或结果可能是左值或右值。例如++x输入一个左值返回一个左值,而x++就返回一个右值。A+B则是需要两个右值并返回一个右值。

一个左值可以很方便的转化为右值,但是右值转化成左值通常是很困难的。地址信息被丢弃了,或者它根本就是一个字面常量,都会导致一个右值将永远是右值。将右值构造成左值的唯一办法,就是构造临时对象并将右值赋予左值。当这个左值被读取时,如果临时对象除了初始化之外从未被写过,并且它关联的右值依然有效,那么这个操作会被优化成直接返回那个原始的右值,从而避免临时左值的读写操作。

在Clang(一个C++编译器的前端)中对左值和右值进行了严格的区分。这是由于C++需要额外的处理临时对象。临时对象意味着尽管它有右值的语义,但是实际上是左值的存储。这是需要将真正的左值和临时的左值区分开,并提供特定语境下的转化。

SASL没有处理复杂的临时对象问题,因此它使用了一个相对简单的办法来解决左右值的判定和存储。

我们设计了一个数据结构,用于保存任何可能的值。

struct Data{
    bool isRef;
    Value* rval;
    Alloca* local;
    GlobalVariable* global;
    struct Aggregated{
        Data* parent;
        int index;
    } agg;
};

rval用于处理Argument和右值时的情况。Local意味着它是一个局部变量,global说明它是一个全局变量,agg则用于处理structure member。Parent指向包含当前变量的聚合变量,index则指明了当前变量在聚合变量中的位次。

SASL提供了load, load_ptr 和 store 来数据的存取,而不要关心它的具体存储类型。

左值/右值语义

在Data这个结构中,rval, local, global和agg四个值是互斥的。当然这里的我们也可以选择union+enum的方式来表达。

首先来看,这个结构如何表达左值/右值语义。

来看isRef,这是一个标记位。它表示了data存储的值究竟是值本身还是地址。如果是isref为真,那么data便可以被认为是一个左值。Isref为假,那么当它是rval的时候,它就是一个真正的右值了。如果是Alloca或者GlobalVariable,因为它们本身就代表了地址,那么它仍然是一个右值。如果是agg,那么要取决于它的聚合量是左值还是右值。

如果参数需要左值,那么可以直接从data拷贝,或者使用load_ptr + isRef创建一个新的右值Data。如果参数需要右值,那么可以通过load的方式获取一个右值。

数据存取的实现

llvm::Value* load( cgllvm_sctxt* data ){
  assert(data);
  Value* val = data->val;
  do{
    if( val ){ break; }
    if( data->local ){ val = builder()->CreateLoad( data->local );
      break;
    }
    if( data->global ){
      val = builder()->CreateLoad( data->global );
      break;
    }
    if( data.agg.parent ){
      val = load( data->agg.parent );
      val = builder()->CreateExtractValue( val, data->agg.index );
      break;
    }
  } while(0);

  if( data->is_ref ){val = builder()->CreateLoad( val );}
  return val;
}


llvm::Value* load_ptr( cgllvm_sctxt* data ){

  Value* addr = NULL;
  if( data->val ){ addr = NULL; }
  if( data->local ){
    addr = data->local;
  }
  if( data->global ){
    addr = data->global;
  }
  if( data->agg.parent ){
    addr = builder()->CreateGEP( load_ptr(data->agg.parent), 0, data->arg.index );
  }

  if( data->is_ref ){
    if( !addr ){
      addr = data->val;
    } else {
      addr = builder()->CreateLoad( addr );
    }
  }

  return addr;
}

void store( llvm::Value* v, cgllvm_sctxt* data ){
  Value* addr = load_ptr( data );
  builder()->CreateStore( v, addr );
}
posted on 2011-04-13 11:04 空明流转 阅读(1650) 评论(1)  编辑 收藏 引用

评论

# re: SALVIA Shading Language中的表达式值的表示与Graphics Pipeline语义的表达(上) 2011-04-13 14:20 千暮(zblc)
我擦 这代码清晰  回复  更多评论
  


只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   知识库   博问   管理