(学习笔记)3.11 浮点代码(3.11.2 过程中的浮点代码3.11.3 浮点运算操作)

张开发
2026/4/9 17:26:10 15 分钟阅读

分享文章

(学习笔记)3.11 浮点代码(3.11.2 过程中的浮点代码3.11.3 浮点运算操作)
文章目录线索栏笔记栏1.浮点过程调用约定1参数传递规则2寄存器保存3混合类型参数映射原则4练习题3.52 解答 (参数寄存器分配)2.浮点运算操作1指令格式2后缀3操作数规则参考图3-49表格4运算类型3.综合示例与逆向工程1示例函数 funct分析2练习题3.53 解答思路 (推断参数类型)3练习题3.54 解答 (从汇编还原C代码)总结栏线索栏x86-64中浮点参数和返回值通过哪个寄存器组传递规则是什么当一个函数的参数包含指针、整数和浮点数的混合类型时到寄存器的映射遵循什么原则XMM寄存器的“调用者保存”或“被调用者保存”责任是如何划分的标量浮点算术指令的通用格式是什么源操作数和目的操作数有哪些可能的位置如何从指令后缀判断其操作的是单精度还是双精度给定一个函数的C语言原型和其GCC生成的汇编代码如何根据使用的寄存器和指令来验证或推断参数类型及计算逻辑笔记栏1.浮点过程调用约定1参数传递规则1寄存器最多8个浮点参数按顺序使用 %xmm0~ %xmm7。2额外参数超出部分通过栈传递。3返回值通过 %xmm0​ 返回。2寄存器保存所有XMM寄存器都是调用者保存的。被调用函数可以自由覆盖它们调用者需负责保存重要值。3混合类型参数映射原则映射完全取决于参数的类型和声明顺序互不干扰的两个队列1整数/指针​ → 通用寄存器队列 (%rdi, %rsi, %rdx, %rcx, %r8, %r9)。2浮点数​ → XMM寄存器队列 (%xmm0~%xmm7)。4练习题3.52 解答 (参数寄存器分配)A. double g1(double a, long b, float c, int d);a(double) → %xmm0b(long) → %rsic(float) → %xmm1d(int) → %edxB. double g2(int a, double *b, float *c, long d);a(int) → %edib(double*) → %rsic(float*) → %rdxd(long) → %rcxC. double g3(double *a, double b, int c, float d);a(double*) → %rdib(double) → %xmm0c(int) → %esid(float) → %xmm1D. double g4(float a, int *b, float c, double d);a(float) → %xmm0b(int*) → %rdic(float) → %xmm1d(double) → %xmm22.浮点运算操作1指令格式vOPss或 vOPsd其中 OP是操作如 add, mul。2后缀ss表示单精度标量 (float)sd表示双精度标量 (double)。3操作数规则参考图3-49表格1目的操作数 D必须是XMM寄存器。2源操作数 S1可以是XMM寄存器或内存地址。3源操作数 S2必须是XMM寄存器。4效果D ← S2 OP S14运算类型包括加(vadd)、减(vsub)、乘(vmul)、除(vdiv)、最大值(vmax)、最小值(vmin)、平方根(vsqrt)等。3.综合示例与逆向工程1示例函数 funct分析1C代码return a*x - b/i;(参数double a, float x, double b, int i)2汇编关键步骤①(第2-3行) 将 x(float) 转换为 double使用vunpcklpsvcvtps2pd序列。②(第4行) vmulsd %xmm0, %xmm1, %xmm0计算 a * x。③(第5行) vcvtsi2sd %edi, %xmm1, %xmm1将整数 i转换为 double。④(第6行) vdivsd %xmm1, %xmm2, %xmm2计算 b / i。⑤(第7行) vsubsd %xmm2, %xmm0, %xmm0计算 (a*x) - (b/i)并作为返回值。2练习题3.53 解答思路 (推断参数类型)1已知汇编函数 funct1使用了 vcvtsi2ssq(q表示long-float), vaddss(单精度加), vcvtsi2ss(int-float), vdivss(单精度除), vunpcklpsvcvtps2pd(单转双序列), vsubsd(双精度减)。2逆向推导①vaddss操作 %xmm0和 %xmm2说明 q和 r中至少有一个是 float且已存在于XMM寄存器。②vcvtsi2ssq将 %rsi(存q或r) 转为 float说明该参数是 long型。③vcvtsi2ss将 %edi(存p) 转为 float说明 p是 int型。④vdivss后进行了单转双然后 vsubsd用到了 %xmm1(存s)说明最终是双精度减法因此 s是 double型。3一种可能的组合arg1_t int, arg2_t long, arg3_t float, arg4_t double。 即double funct1(int p, long q, float r, double s);3练习题3.54 解答 (从汇编还原C代码)1给定原型double funct2(double w, int x, float y, long z);2汇编分析①(第2行) 将 x(int) 转为 float- (float)x。②(第3行) 单精度乘法(float)x * y- 结果在 %xmm1(单精度)。③(第4-5行) 将上一步单精度结果转为双精度 - (double)((float)x * y)。④(第6行) 将 z(long) 转为 double- (double)z。⑤(第7行) 双精度除法w / (double)z。⑥(第8行) 双精度减法(double)((float)x * y) - (w / (double)z)。3还原的C代码doublefunct2(doublew,intx,floaty,longz){doublet1(double)((float)x*y);doublet2w/(double)z;returnt1-t2;}// 或更简洁地 return (double)(x * y) - (w / (double)z);// 注意x * y 是整数与浮点数相乘结果为 float然后被强制转换为 double。总结栏本节将浮点操作的讨论从基础数据表示和指令扩展到过程调用和混合类型计算的完整上下文是理解真实世界浮点代码的关键。独立的调用约定浮点参数/返回值使用独立的XMM寄存器组和传递规则与整数/指针体系并行不悖。在分析函数时必须同时追踪通用寄存器和XMM寄存器的分配情况。类型驱动的指令选择浮点运算和转换指令的后缀(ss/sd)严格对应操作数的精度。在混合精度表达式中编译器会插入显式的类型转换指令如vcvtsi2sd,vcvtps2pd来满足指令的输入要求这些指令是理解表达式求值顺序和中间类型的关键标记。逆向工程的系统性方法练习题3.53和3.54展示了从汇编反推高级语言逻辑的通用方法1识别寄存器用途根据调用约定确定参数位置。2解析指令序列将转换(vcvt)、运算(vadd等)指令映射为类型转换和算术操作。3重建数据流追踪值如何在寄存器间流动、转换和组合最终重建出表达式。性能提示混合精度计算如int与float相乘会引入额外的转换指令。在性能敏感代码中保持数据类型一致如都使用double有时能减少转换开销但也需权衡精度和内存带宽。最终启示浮点代码的生成是C语言类型系统和x86-64浮点指令集的精确对接。理解这套规则不仅能读懂编译器输出更能预判混合类型运算的性能开销并能在调试时通过检查XMM寄存器和相关指令快速定位浮点相关的逻辑错误或精度异常。

更多文章