常数,变量和运算
一、常数
int f() { return 0x123; /* 291 */ }
int g() { return -1; }
int h() { return 0x1234; /* 4660 */ }
int i() { return 0xbb8; /* 3000 */ }
在C中看这些常数如何被装入计算机中,首先看0x123,十进制表示为291,正好可以被addi的立即数表示,反汇编结果如下:
而return -1也是如此,1也可以用立即数表示,0xfff表示-1,可以通过一条addi指令表示出来。
0x1234超过12位立即数表示范围了。所以通过lui和addi一起来操作。lui装入0x1,放在高20为,然后再用addi装载剩余的234,
0xbb8也是要分成前后两端分别装入。这里要注意,我们的addi立即数并没办法满足0xbb8的大小,而lui的0x1(4096)又大于0xbb8,所以我们只能进行负数操作,先用lui加载一个高20位的1(4096),而后用addi加载个负数,最后得到0xbb8.
结论:
而对于64位的常数在RV64装入来说:
long long j() { return 0x1234567800000000; }
long long k() { return 0x1234567887654321; }
对于long j()来说:
通过li addi
先把先前非0的装载到a0中,a0 = (0x12345678 >> 3)
而后通过后续指令 -> a0 = a0 << 35;
对于long k()来说:
对于一个复杂的64为常数,编译器直接把它放到内存中,其中LC0就是0x123456...的十进制表示。 ld a0,LC0 就是直接加载LC0寄存器的值。
在64位常数在RV32中的装入,只需要用两个32位寄存器联合存放一个64位数据。
变量的大小与对齐的具体规定实在RISCV的两套整数ABI中,其中RV32是在ILP32 ABI中定义,64位是在LP64 ABI定义的。
在变量分配过程中,把常用的放在寄存器中,寄存器在RV32中只有32个,其中内存的访问速度比较慢,但是容量非常大(8~32G)
所有变量都分配在内存空间,需要访问在读入寄存器。
对于程序的内存分布来说,分为静态数据取,堆区和栈区。
堆和栈是动态区,随着变量的增加而增加。其
其中全局变量分在data区,例如
#include <stdio.h>
#include <stdlib.h>
int g;
void f(int n) {
static int sl;
int l, *h = malloc(4);
printf("n = %2d, &g = %p, &sl = %p, &l = %p, h = %p\n", n, &g, &sl, &l, h);
if (n < 5) f(n + 1);
}
int main() { f(1); printf("===\n"); f(1); return 0; }
中的static int s1;就放在data区,int g也是全部变量,也放在data区。
非静态局部变量,也就是int l,就放在stack区。最后一类是动态变量,需要malloc就是动态分配的,也就是在堆区。
关于RV变量的访问,示例代码如下:
#define def(type, name) \
volatile type name ## _a; \
volatile type name ## _b; \
void f_##name () { name ## _a = name ## _b; }
def(_Bool, _Bool)
def(char, char)
def(signed char, signed_char)
def(short, short)
def(unsigned short, unsigned_short)
def(int, int)
def(unsigned int, unsigned_int)
def(long, long)
def(long long, long_long)
def(void *, void_)
def(float, float)
def(double, double)
rv32gcc -O2 -S a.c
rv64gcc -O2 -S a.c
发现不同类型的变量访问指令:
对于变量分配不对齐,会降低访问效率:
不对齐即addr(n) % align(n) != 0, 如int变量分配在地址0x13
硬件支持不对齐访存: 电路更复杂, 且需要两个周期以上
软件支持不对齐访存: 抛异常, 效率很低
运算和指令
对于C运算符,RV指令有对应关系
+, -, *, /, % add, sub, mul, div, rem
= mv, 访存指令
&, |, ^ and, or, xor
~ xori r, r, -1
<<, >> sll, srl, sra
! sltiu r, r, 1
<, > slt
而对于编译优化来说,之前我们在编译过程中常用的 -O
-O0 - 每次计算前先从内存读出变量, 每次计算后马上写回内存
-O1 - 开始计算前从内存读出变量, 计算过程在寄存器中进行, 计算全部结束后再写回内存
-O2 - 编译器直接把结果算好了
标签:表示,int32,机器,int,程序,long,return,uint32,def
From: https://www.cnblogs.com/ink-bai/p/18156210