程序的机器级表示-常数、变量、运算

news/2025/11/29 22:55:48/文章来源:https://www.cnblogs.com/mo686/p/19287695

📚 使用须知

  • 本博客内容仅供学习参考
  • 建议理解思路后独立实现
  • 欢迎交流讨论

task : 常数、变量、运算

32位常数的装入

int f() { return 0x123; /* 291 */ }
int g() { return -1; }
int h() { return 0x1234; /* 4660 */ }
int i() { return 0xbb8; /* 3000 */ }
rv32gcc -O2 -c a.c
rvobjdump -M no-aliases -d a.o
lui  r,(imm[31:12]+imm[11])  # 若结果为0, 可省略
addi r,zero,imm[11:0]

理解lui中的imm[11]: 一大步(lui) + 一小步(addi)

________________________________________________|  |   |         |      |     |-1  |  291      3000     |    46600                   4096

根据addi的语义,imm[11:0]需要符号扩展
相当于对lui结果进行±2048范围内的调整(可仔细推敲边界情况)
lui需要先装入一个距离imm不超过±2048范围的数
如果imm[11:0]为负, 则lui需先右移4096, 再让addi左移

64位常数在RV64中的装入

long long j() { return 0x1234567800000000; }
long long k() { return 0x1234567887654321; }
rv64gcc -O2 -S a.c

多一条移位指令slli
如果再复杂一点, gcc干脆把常数放到只读数据段, 用ld读内存
用访问数据的延迟换取更短的指令序列

指令太多并不好: 乱序执行的高性能CPU中, 取指令的代价大于取数据
在流水线中, 指令是数据的上游
若从内存取数据, 只需让取数逻辑等待, 可先执行其他无关指令
若从内存取指令, 则整条流水线都要等待

伪指令的定义可参考《RISC-V汇编语言编程手册》

long long j() { return 0x1234567800000000; }
long long k() { return 0x1234567887654321; }
rv32gcc -O2 -S a.c

用两个32位寄存器联合存放一个64位数据

在这个版本中, clang生成的代码比gcc更好一些 😂

clang --target=riscv32 -O2 -S a.c

变量的大小和对齐

RISC-V的两套整数ABI

I = Integer, L = Long, P = Pointer

ILP32 ABI (RV32)

大小 对齐
bool/_Bool 1 1
char 1 1
short 2 2
int 4 4
long 4 4
long long 8 8
void * 4 4
float 4 4
double 8 8

LP64 ABI (RV64)

大小 对齐
bool/_Bool 1 1
char 1 1
short 2 2
int 4 4
long 8 8
long long 8 8
void * 8 8
float 4 4
double 8 8

变量的分配

{v1,v2,…}→{r1,r2,…,M}

在实际的电路中

R访问速度较快(当前周期可读出), 但容量有限(RISC-V中只有32个)
M访问速度较慢(需要上百个周期读出), 但容量几乎无限(现代内存条8~32GB)
DDR颗粒中的存储单元由一个晶体管+一个电容组成
电容需要充放电, 延迟普遍比晶体管大, 故通常M的访问速度较慢

C程序中的变量远多于寄存器, 该如何分配?

直观的分配策略: 将常用的变量分配在R, 将不常用的变量分配在M,但编译器很难分析哪些变量更常用

实际的分配策略: 所有变量先分配在M, 需要访问时读入R

程序的内存布局

与变量相关的三个内存区域: 静态数据区(data), 堆区(heap), 栈区(stack)
静态 = 不动态增长和变化, 编译时确定

四种需要分配的C变量
全局变量 -> data区
静态局部变量 -> data区
非静态局部变量 -> stack区
动态变量 -> heap区

  +----------+|          |+----------+|   stack  |+----------+|    |     ||    v     ||          ||    ^     ||    |     |+----------+|   heap   |+----------+|   data   |+----------+|   text   |+----------+|          |
0 +----------+
#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; }

变量的访问

#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

一个例外: unsigned int在RV64中使用lw而不是lwu
具体可参考RISC-V指令集手册和RISC-V ABI手册

变量的访问(2)

ILP32 ABI (RV32)

大小 指令
bool/_Bool 1 lbu/sb
char 1 lbu/sb
signed char 1 lb/sb
short 2 lh/sh
unsigned short 2 lhu/sh
int 4 lw/sw
unsigned int 4 lw/sw
long 4 lw/sw
long long 8 lw+lw/sw+sw
void * 4 lw/sw
float 4 flw/fsw
double 8 fld/fsd

LP64 ABI (RV64)

大小 指令
bool/_Bool 1 lbu/sb
char 1 lbu/sb
signed char 1 lb/sb
short 2 lh/sh
unsigned short 2 lhu/sh
int 4 lw/sw
unsigned int 4 lw/sw
long 8 ld/sd
long long 8 ld/sd
void * 8 ld/sd
float 4 flw/fsw
double 8 fld/fsd

变量分配不对齐会降低访问效率

不对齐即addr(n) % align(n) != 0, 如int变量分配在地址0x13

硬件支持不对齐访存: 电路更复杂, 且需要两个周期以上
软件支持不对齐访存: 抛异常, 效率很低

在x86上实测不对齐访存的性能

#include <stdlib.h>
#define LOOP 2000000
#define SIZE 10000
char buf[SIZE + 64] __attribute((aligned(64))); // 64 = 缓存块的大小(字节)
int main(int argc, char *argv[]) {int offset = atoi(argv[1]);for (int n = LOOP; n != 0; n --) {for (char *p = buf + offset; p < buf + SIZE; p += 64) { *(long *)p = 1; }}return 0;
}
gcc -O2 a.c && for i in `seq 1 64`; do TIME="$i: %E" /usr/bin/time ./a.out $i; done

在x86上, 当不对齐的数据跨越64字节边界时, 可观察到约2x的性能下降

运算和指令

C运算符 RISC-V指令
+, -, *, /, % add, sub, mul, div, rem
= mv, 访存指令
&, |, ^ and, or, xor
~ xori r, r, -1
<<, >> sll, srl, sra
! sltiu r, r, 1
<, > slt
long f1(long a, long b) { return a + b; }
long f2(long a, long b) { return a - b; }
long f3(long a, long b) { return a * b; }
long f4(long a, long b) { return a / b; }
// ...

通过-O1编译成汇编文件, 即可了解二者的联系

编译优化 - 尽可能在寄存器中进行运算

int sum = 0;
void f() {int i;for (i = 1; i <= 100; i ++) {sum += i;}
}

-O0 - 每次计算前先从内存读出变量, 每次计算后马上写回内存
-O1 - 开始计算前从内存读出变量, 计算过程在寄存器中进行, 计算全部结束后再写回内存
-O2 - 编译器直接把结果算好了

RV32进行64位加法

long long f1(long long a, long long b) { return a + b; }
rv32gcc -O2 -S a.c
(a1, a0) <= (a1, a0) + (a3, a2)mva1 a0 ===> a5a3 a2      |vcarry <--- sltu+           ^---------     |a1 a0 -----+

RV64进行128位加法, 过程类似

__int128 f1(__int128 a, __int128 b) { return a + b; }

有符号数和无符号数

#include <stdint.h>int32_t add1( int32_t a,  int32_t b) { return a + b; }
uint32_t add2(uint32_t a, uint32_t b) { return a + b; }int32_t cmp1( int32_t a,  int32_t b) { return a < b; }int32_t cmp2(uint32_t a, uint32_t b) { return a < b; }int32_t shr1( int32_t a,  int32_t b) { return a >> b; }
uint32_t shr2(uint32_t a,  int32_t b) { return a >> b; }int64_t zext1( int32_t a) { return a; }
uint64_t zext2(uint32_t a) { return a; }

add1add2的代码完全一样
结论: 在RISC-V硬件看来, 有符号加法和无符号加法的行为完全一致
可以使用同一个加法器模块进行计算

比较 - slt/sltu
右移 - sra/srl
扩展到64位 - 符号扩展/零扩展
乘, 除, 取余均有两种符号的指令

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/981277.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

香橙派上进行 Livox Mid-360 激光雷达开发(一)基本环节配置

香橙派上进行 Livox Mid-360 激光雷达开发(一)基本环节配置基本信息 主机:Orangepi5max 16G (ARM64 架构) + 64G tf卡 系统:Orangepi5max_1.0.0_ubuntu_jammy_desktop_xfce_linux5.10.160(ubuntu 22.04) 系统配…

2025 年杭州拱墅区摄影培训人像摄影培训推荐榜:路人贾摄影讲堂 全杭州10个区覆盖、人像摄影十杰创办

随着新媒体内容创作与商业摄影市场的持续火热,对于高品质人像摄影技能的需求激增。无论是希望成为职业摄影师,还是追求个人兴趣精进的摄影爱好者,选择一家师资力量雄厚、课程体系科学、实战经验丰富的培训机构,已成…

深入解析:打造高清3D虚拟世界|零基础学习Unity HDRP高清渲染管线(第十一天)

深入解析:打造高清3D虚拟世界|零基础学习Unity HDRP高清渲染管线(第十一天)2025-11-29 22:43 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow…

npm1300 如何导入模型

生成或下载模型文件 使用 nPM PowerUP 软件对电池进行建模后,会生成电池模型文件(通常为 .inc 或 .json 格式)。你也可以在 nPM PowerUP 的 Profiles 选项卡中,直接下载 Nordic 提供的预配置模型 保存模型文件 在 …

2025最新小程序开发服务商推荐!云南地区权威榜单发布,技术与服务双优助力企业数字化转型

随着移动互联网的深度普及,小程序已成为企业连接用户、拓展业务的核心载体。本榜单基于技术实力、行业适配性、服务效能三大维度,结合云南本土市场需求与企业服务案例,综合评选出2025年云南地区五大小程序开发优质服…

npm1300 的建模目的

nPM1300 的 Fuel Gauge(电量计)功能需要针对具体电池进行建模,以便更准确地估算电池的荷电状态(SoC)。建模过程会生成一个电池模型,结合电池的温度、电压和电流,由主控 MCU 上的算法进行 SoC 估算; 如果需要no…

iapp逆向,lib.so第一层密钥计算工具

iapp逆向,lib.so第一层密钥计算工具https://wwbme.lanzouu.com/iRLJp3ch6cqh 原地址 https://www.zxiyun.com/15369.html

npm1300 建模工具与流程

官方推荐使用 nPM Fuel Gauge board(nPM FG)配合兼容的 Nordic 评估板进行电池建模实验; 通过 nPM PowerUP 桌面应用(集成在 nRF Connect for Desktop 中)可以选择已有的电池模型,或对新电池进行建模和分析; 建模…

MATLAB实现基于GRNN-LSTM广义回归神经网络(GRNN)结合长短期记忆网络(LSTM)进行时间序列预测的详细项目实例 - 实践

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

基于反馈循环的自我进化AI智能体:原理、架构与代码实现

传统AI智能体有个老问题:部署之后就"定住了"。工程师手工打磨的提示词和规则,遇到新场景就容易失灵,性能曲线到达某个点后趋于平缓。而自我进化智能体(Self-Evolving Agent)的思路就是打破这种静态模式,让…

CVE-2016-9177

CVE-2016-9177复现首先扫描目录发现存在/..................\etc\passwd目录,直接访问发现访问不了在云服务器使用curl访问,发现可以访问访问/..................\flag,得到flag

2025最新AI数字人服务商推荐!云南本土AI数字人技术领航者权威榜单发布

随着AI数字人技术在各行各业的广泛应用,云南市场对优质AI数字人服务商的需求日益增长。本榜单基于技术实力、服务案例、创新能力和本地化服务优势四大维度,结合行业发展趋势与客户反馈,权威解析2025年云南五大AI数字…

2025最新云南短视频制作运营服务商权威推荐!资质服务双优企业榜单发布贤邦科技/云南爱冰等专业公司上榜

引言 后短视频时代,企业数字化营销需求呈现爆发式增长,短视频作为品牌传播与流量转化的核心载体,其制作专业性、运营精准度与技术适配性成为竞争关键。据中国传媒大学《2025短视频行业白皮书》数据显示,云南地区短…

npm1300 LDO 说明;

nPM1300 的 LDO(低压差线性稳压器)功能说明如下: 基本特性 nPM1300 集成了两个可配置为 LDO 或负载开关(Load Switch)的通道,每个 LDO 最大输出电流为 50 mA(VOUT > 1.2V 时),输出电压可在 1.0 V 至 3.3 V…

深入解析:使用 TransGPTex 将 LaTeX 英文论文翻译成中文:完整实战教程

pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family: "Consolas", "Monaco", "Courier New", …

npm1300 的BUCK 说明

npm1300 的BUCK 说明 nPM1300 的 BUCK(降压型稳压器)功能说明如下: 基本特性 nPM1300 集成了两个独立的降压型稳压器(BUCK1 和 BUCK2),每路最大输出电流为 200 mA。 输出电压可配置,范围为 1.0 V 至 3.3 V,步进…

npm1300 软启动与放电说明

软启动与放电 LDO 支持软启动功能,默认启用,可通过 LDSWCONFIG 寄存器设置软启动电流限制。 LDO 支持主动放电(active discharge),即在关闭时通过下拉电阻快速放电输出端口,同样通过 LDSWCONFIG 寄存器配置。软启…

npm1300 工作模式介绍

自动模式:默认,BUCK 会根据负载自动在 Hysteretic(低负载高效率)和 PWM(高负载低纹波)间切换。 强制 PWM/Hysteretic:可通过寄存器(BUCK1PWMSET/BUCK2PWMSET/BUCKCTRL0)或 GPIO(BUCKPWMCTRL)强制进入某一模…

跨进程通信之QLocalSocket

跨进程通信之QLocalSocket苏轼 - 《临江仙送钱穆父》 一别都门三改火,天涯踏尽红尘。 依然一笑作春温。 无波真古井,有节是秋筠。 惆怅孤帆连夜发,送行淡月微云。 尊前不用翠眉颦。 人生如逆旅,我亦是行人。QLocal…

2025最新云南短视频制作公司及运营服务商推荐!技术实力与效果双优企业榜单发布

随着短视频成为企业品牌传播和流量获取的核心阵地,市场对专业服务商的需求持续攀升。本榜单基于技术创新能力、内容制作实力、运营效果表现和客户服务质量四大维度,结合行业权威数据及客户反馈,综合评选出2025年云南…