c++示例

#include <stdio.h>
#include <math.h>

#define PI 3.14159265358979323846

const int maxValue = 100;

int globalVar = 10;

int square(int x) {
return x * x;
}

void printResult(double radius) {
double area = PI * square(radius);
printf("Area of the circle with radius %.2f is %.2f\n", radius, area);
}

int main() {
double radius = 5.0;
printResult(radius);

for (int i = 0; i < maxValue; i++) {
globalVar += i;
}
printf("Final globalVar value: %d\n", globalVar);

return 0;
}

1预处理

主要涉及到:1.头文件展开,基本上就是copy,头文件中包含了外部函数的声明,和一些结构体的定义。

2.宏替换(copy)

sh:

cpp ex.c -o example.i
## 扩展
cpp -DPI=3.14 example.c -o example.i
cpp -nostdinc example.c -o example.i
cpp -dM example.c

处理结果

/*头文件部分
*预处理器在处理头文件和代码时生成的注释,用来跟踪源代码中每个部分的来源。
*/
# 0 "ex.c"
....................................................................................
# 1 "/usr/include/x86_64-linux-gnu/gnu/stubs-64.h" 1 3 4
# 11 "/usr/include/x86_64-linux-gnu/gnu/stubs.h" 2 3 4
# 511 "/usr/include/features.h" 2 3 4
# 34 "/usr/include/x86_64-linux-gnu/bits/libc-header-start.h" 2 3 4
# 28 "/usr/include/stdio.h" 2 3 4

....................................................................................
/*头文件部分
*库结构体定义
*以iofile为例,这个就是库中的输入输出的底层实现的结构体
*其中包含这缓冲区指针等等。
*/

struct _IO_FILE;
typedef struct _IO_FILE __FILE;
# 42 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types/FILE.h" 1 3 4
struct _IO_FILE
typedef struct _IO_FILE FILE;
# 43 "/usr/include/stdio.h" 2 3 4
# 1 "/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h" 1 3 4
# 35 "/usr/include/x86_64-linux-gnu/bits/types/struct_FILE.h" 3 4
struct _IO_FILE;
struct _IO_marker;
struct _IO_codecvt;
struct _IO_wide_data;
typedef void _IO_lock_t;
struct _IO_FILE
{
int _flags;


char *_IO_read_ptr;
char *_IO_read_end;
char *_IO_read_base;
char *_IO_write_base;
char *_IO_write_ptr;
char *_IO_write_end;
char *_IO_buf_base;
char *_IO_buf_end;


char *_IO_save_base;
char *_IO_backup_base;
char *_IO_save_end;

struct _IO_marker *_markers;

struct _IO_FILE *_chain;

int _fileno;
int _flags2;
__off_t _old_offset;


unsigned short _cur_column;
signed char _vtable_offset;
char _shortbuf[1];

_IO_lock_t *_lock;
__off64_t _offset;

struct _IO_codecvt *_codecvt;
struct _IO_wide_data *_wide_data;
struct _IO_FILE *_freeres_list;
void *_freeres_buf;
size_t __pad5;
int _mode;

char _unused2[15 * sizeof (int) - 4 * sizeof (void *) - sizeof (size_t)];
};
....................................................................................
/*一些函数声明*/
extern int remove (const char *__filename) __attribute__ ((__nothrow__ , __leaf__));

extern int rename (const char *__old, const char *__new) __attribute__ ((__nothrow__ , __leaf__));
extern int renameat (int __oldfd, const char *__old, int __newfd,
const char *__new) __attribute__ ((__nothrow__ , __leaf__));
# 178 "/usr/include/stdio.h" 3 4
extern int fclose (FILE *__stream);
# 188 "/usr/include/stdio.h" 3 4
extern FILE *tmpfile (void)
__attribute__ ((__malloc__)) __attribute__ ((__malloc__ (fclose, 1))) ;
# 205 "/usr/include/stdio.h" 3 4
extern char *tmpnam (char[20]) __attribute__ ((__nothrow__ , __leaf__)) ;
extern char *tmpnam_r (char __s[20]) __attribute__ ((__nothrow__ , __leaf__)) ;
# 222 "/usr/include/stdio.h" 3 4
extern char *tempnam (const char *__dir, const char *__pfx)
__attribute__ ((__nothrow__ , __leaf__)) __attribute__ ((__malloc__)) __attribute__ ((__malloc__ (__builtin_free, 1)));
....................................................................................
/*主程序逻辑*/
# 6 "ex.c"
const int maxValue = 100;

int globalVar = 10;

int square(int x) {
return x * x;
}

void printResult(double radius) {
double area = 3.14159265358979323846 * square(radius);
printf("Area of the circle with radius %.2f is %.2f\n", radius, area);
}

int main() {
double radius = 5.0;
printResult(radius);

for (int i = 0; i < maxValue; i++) {
globalVar += i;
}
printf("Final globalVar value: %d\n", globalVar);

return 0;
}

2词法分析

主要是用于token流的生成,为了后续的ast的实现

[StartOfLine] 和 **[LeadingSpace]**:

  • 这些标签表示该 token 是否位于行首,或者其前面是否有空格。

Loc

  • 表示 token 的来源文件和具体的行、列位置,帮助追踪每个 token 在源文件中的位置,方便调试或分析。

Spelling

  • Spelling=<built-in> 表示该 token 是来自于编译器的内置定义,而不是用户代码中的内容。例如,size_t__builtin_va_list 都是标准库或内置类型。
//clang -Xclang -dump-tokens -fsyntax-only ex.c > mytoken 2>&1
头文件的一些词法
.....................
const 'const' [StartOfLine] Loc=<ex.c:6:1>
int 'int' [LeadingSpace] Loc=<ex.c:6:7>
identifier 'maxValue' [LeadingSpace] Loc=<ex.c:6:11>
equal '=' [LeadingSpace] Loc=<ex.c:6:20>
numeric_constant '100' [LeadingSpace] Loc=<ex.c:6:22>
semi ';' Loc=<ex.c:6:25>
int 'int' [StartOfLine] Loc=<ex.c:8:1>
identifier 'globalVar' [LeadingSpace] Loc=<ex.c:8:5>
equal '=' [LeadingSpace] Loc=<ex.c:8:15>
numeric_constant '10' [LeadingSpace] Loc=<ex.c:8:17>
semi ';' Loc=<ex.c:8:19>
int 'int' [StartOfLine] Loc=<ex.c:10:1>
identifier 'square' [LeadingSpace] Loc=<ex.c:10:5>
l_paren '(' Loc=<ex.c:10:11>
int 'int' Loc=<ex.c:10:12>
identifier 'x' [LeadingSpace] Loc=<ex.c:10:16>
r_paren ')' Loc=<ex.c:10:17>
l_brace '{' [LeadingSpace] Loc=<ex.c:10:19>
return 'return' [StartOfLine] [LeadingSpace] Loc=<ex.c:11:5>
identifier 'x' [LeadingSpace] Loc=<ex.c:11:12>
star '*' [LeadingSpace] Loc=<ex.c:11:14>
identifier 'x' [LeadingSpace] Loc=<ex.c:11:16>
semi ';' Loc=<ex.c:11:17>
r_brace '}' [StartOfLine] Loc=<ex.c:12:1>
void 'void' [StartOfLine] Loc=<ex.c:14:1>
identifier 'printResult' [LeadingSpace] Loc=<ex.c:14:6>
l_paren '(' Loc=<ex.c:14:17>
double 'double' Loc=<ex.c:14:18>
identifier 'radius' [LeadingSpace] Loc=<ex.c:14:25>
r_paren ')' Loc=<ex.c:14:31>
l_brace '{' [LeadingSpace] Loc=<ex.c:14:33>
double 'double' [StartOfLine] [LeadingSpace] Loc=<ex.c:15:5>
identifier 'area' [LeadingSpace] Loc=<ex.c:15:12>
equal '=' [LeadingSpace] Loc=<ex.c:15:17>
numeric_constant '3.14159265358979323846' [LeadingSpace] Loc=<ex.c:15:19 <Spelling=ex.c:4:12>>
star '*' [LeadingSpace] Loc=<ex.c:15:22>
identifier 'square' [LeadingSpace] Loc=<ex.c:15:24>
l_paren '(' Loc=<ex.c:15:30>
identifier 'radius' Loc=<ex.c:15:31>
r_paren ')' Loc=<ex.c:15:37>
semi ';' Loc=<ex.c:15:38>
identifier 'printf' [StartOfLine] [LeadingSpace] Loc=<ex.c:16:5>
l_paren '(' Loc=<ex.c:16:11>
string_literal '"Area of the circle with radius %.2f is %.2f\n"' Loc=<ex.c:16:12>
comma ',' Loc=<ex.c:16:59>
identifier 'radius' [LeadingSpace] Loc=<ex.c:16:61>
comma ',' Loc=<ex.c:16:67>
identifier 'area' [LeadingSpace] Loc=<ex.c:16:69>
r_paren ')' Loc=<ex.c:16:73>
semi ';' Loc=<ex.c:16:74>
r_brace '}' [StartOfLine] Loc=<ex.c:17:1>
int 'int' [StartOfLine] Loc=<ex.c:19:1>
identifier 'main' [LeadingSpace] Loc=<ex.c:19:5>
l_paren '(' Loc=<ex.c:19:9>
r_paren ')' Loc=<ex.c:19:10>
l_brace '{' [LeadingSpace] Loc=<ex.c:19:12>
double 'double' [StartOfLine] [LeadingSpace] Loc=<ex.c:20:5>
identifier 'radius' [LeadingSpace] Loc=<ex.c:20:12>
equal '=' [LeadingSpace] Loc=<ex.c:20:19>
numeric_constant '5.0' [LeadingSpace] Loc=<ex.c:20:21>
semi ';' Loc=<ex.c:20:24>
identifier 'printResult' [StartOfLine] [LeadingSpace] Loc=<ex.c:21:5>
l_paren '(' Loc=<ex.c:21:16>
identifier 'radius' Loc=<ex.c:21:17>
r_paren ')' Loc=<ex.c:21:23>
semi ';' Loc=<ex.c:21:24>
for 'for' [StartOfLine] [LeadingSpace] Loc=<ex.c:23:5>
l_paren '(' [LeadingSpace] Loc=<ex.c:23:9>
int 'int' Loc=<ex.c:23:10>
identifier 'i' [LeadingSpace] Loc=<ex.c:23:14>
equal '=' [LeadingSpace] Loc=<ex.c:23:16>
numeric_constant '0' [LeadingSpace] Loc=<ex.c:23:18>
semi ';' Loc=<ex.c:23:19>
identifier 'i' [LeadingSpace] Loc=<ex.c:23:21>
less '<' [LeadingSpace] Loc=<ex.c:23:23>
identifier 'maxValue' [LeadingSpace] Loc=<ex.c:23:25>
semi ';' Loc=<ex.c:23:33>
identifier 'i' [LeadingSpace] Loc=<ex.c:23:35>
plusplus '++' Loc=<ex.c:23:36>
r_paren ')' Loc=<ex.c:23:38>
l_brace '{' [LeadingSpace] Loc=<ex.c:23:40>
identifier 'globalVar' [StartOfLine] [LeadingSpace] Loc=<ex.c:24:9>
plusequal '+=' [LeadingSpace] Loc=<ex.c:24:19>
identifier 'i' [LeadingSpace] Loc=<ex.c:24:22>
semi ';' Loc=<ex.c:24:23>
r_brace '}' [StartOfLine] [LeadingSpace] Loc=<ex.c:25:5>
identifier 'printf' [StartOfLine] [LeadingSpace] Loc=<ex.c:26:5>
l_paren '(' Loc=<ex.c:26:11>
string_literal '"Final globalVar value: %d\n"' Loc=<ex.c:26:12>
comma ',' Loc=<ex.c:26:41>
identifier 'globalVar' [LeadingSpace] Loc=<ex.c:26:43>
r_paren ')' Loc=<ex.c:26:52>
semi ';' Loc=<ex.c:26:53>
return 'return' [StartOfLine] [LeadingSpace] Loc=<ex.c:28:5>
numeric_constant '0' [LeadingSpace] Loc=<ex.c:28:12>
semi ';' Loc=<ex.c:28:13>
r_brace '}' [StartOfLine] Loc=<ex.c:29:1>
eof '' Loc=<ex.c:29:2>

3语法分析(AST生成)

忽略了头文件的ast部分,可以看出主函数由几个声明组成,TranslationUnitDecl作为树根。

clang -E -Xclang -ast-dump ex.c > myparse 2>&1

TranslationUnitDecl 0xa5e388 <<invalid sloc>> <invalid sloc>
|-TypedefDecl 0xa5ebb0 <<invalid sloc>> <invalid sloc> implicit __int128_t '__int128'
| `-BuiltinType 0xa5e950 '__int128'
|-TypedefDecl 0xa5ec20 <<invalid sloc>> <invalid sloc> implicit __uint128_t 'unsigned __int128'
| `-BuiltinType 0xa5e970 'unsigned __int128'
|-TypedefDecl 0xa5ef28 <<invalid sloc>> <invalid sloc> implicit __NSConstantString 'struct __NSConstantString_tag'
| `-RecordType 0xa5ed00 'struct __NSConstantString_tag'
| `-Record 0xa5ec78 '__NSConstantString_tag'
|-TypedefDecl 0xa5efc0 <<invalid sloc>> <invalid sloc> implicit __builtin_ms_va_list 'char *'
.................................


|-VarDecl 0xbad2f8 <ex.c:6:1, col:22> col:11 used maxValue 'const int' cinit
| `-IntegerLiteral 0xbad360 <col:22> 'int' 100
|-VarDecl 0xbad398 <line:8:1, col:17> col:5 used globalVar 'int' cinit
| `-IntegerLiteral 0xbad400 <col:17> 'int' 10
|-FunctionDecl 0xbb4018 <line:10:1, line:12:1> line:10:5 used square 'int (int)'
| |-ParmVarDecl 0xbb3f80 <col:12, col:16> col:16 used x 'int'
| `-CompoundStmt 0xbb4160 <col:19, line:12:1>
| `-ReturnStmt 0xbb4150 <line:11:5, col:16>
| `-BinaryOperator 0xbb4130 <col:12, col:16> 'int' '*'
| |-ImplicitCastExpr 0xbb4100 <col:12> 'int' <LValueToRValue>
| | `-DeclRefExpr 0xbb40c0 <col:12> 'int' lvalue ParmVar 0xbb3f80 'x' 'int'
| `-ImplicitCastExpr 0xbb4118 <col:16> 'int' <LValueToRValue>
| `-DeclRefExpr 0xbb40e0 <col:16> 'int' lvalue ParmVar 0xbb3f80 'x' 'int'
|-FunctionDecl 0xbb4258 <line:14:1, line:17:1> line:14:6 used printResult 'void (double)'
| |-ParmVarDecl 0xbb4190 <col:18, col:25> col:25 used radius 'double'
| `-CompoundStmt 0xbb46a8 <col:33, line:17:1>
| |-DeclStmt 0xbb44b0 <line:15:5, col:38>
| | `-VarDecl 0xbb4318 <col:5, col:37> col:12 used area 'double' cinit
| | `-BinaryOperator 0xbb4490 <line:4:12, line:15:37> 'double' '*'
| | |-FloatingLiteral 0xbb4380 <line:4:12> 'double' 3.141593e+00
| | `-ImplicitCastExpr 0xbb4478 <line:15:24, col:37> 'double' <IntegralToFloating>
| | `-CallExpr 0xbb4420 <col:24, col:37> 'int'
| | |-ImplicitCastExpr 0xbb4408 <col:24> 'int (*)(int)' <FunctionToPointerDecay>
| | | `-DeclRefExpr 0xbb43a0 <col:24> 'int (int)' Function 0xbb4018 'square' 'int (int)'
| | `-ImplicitCastExpr 0xbb4460 <col:31> 'int' <FloatingToIntegral>
| | `-ImplicitCastExpr 0xbb4448 <col:31> 'double' <LValueToRValue>
| | `-DeclRefExpr 0xbb43c0 <col:31> 'double' lvalue ParmVar 0xbb4190 'radius' 'double'
| `-CallExpr 0xbb4610 <line:16:5, col:73> 'int'
| |-ImplicitCastExpr 0xbb45f8 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0xbb44c8 <col:5> 'int (const char *, ...)' Function 0xb083e8 'printf' 'int (const char *, ...)'
| |-ImplicitCastExpr 0xbb4660 <col:12> 'const char *' <NoOp>
| | `-ImplicitCastExpr 0xbb4648 <col:12> 'char *' <ArrayToPointerDecay>
| | `-StringLiteral 0xbb4528 <col:12> 'char[45]' lvalue "Area of the circle with radius %.2f is %.2f\n"
| |-ImplicitCastExpr 0xbb4678 <col:61> 'double' <LValueToRValue>
| | `-DeclRefExpr 0xbb4570 <col:61> 'double' lvalue ParmVar 0xbb4190 'radius' 'double'
| `-ImplicitCastExpr 0xbb4690 <col:69> 'double' <LValueToRValue>
| `-DeclRefExpr 0xbb4590 <col:69> 'double' lvalue Var 0xbb4318 'area' 'double'
`-FunctionDecl 0xbb4720 <line:19:1, line:29:1> line:19:5 main 'int ()'
`-CompoundStmt 0xbb4d78 <col:12, line:29:1>
|-DeclStmt 0xbb4860 <line:20:5, col:24>
| `-VarDecl 0xbb47d8 <col:5, col:21> col:12 used radius 'double' cinit
| `-FloatingLiteral 0xbb4840 <col:21> 'double' 5.000000e+00
|-CallExpr 0xbb4900 <line:21:5, col:23> 'void'
| |-ImplicitCastExpr 0xbb48e8 <col:5> 'void (*)(double)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0xbb4878 <col:5> 'void (double)' Function 0xbb4258 'printResult' 'void (double)'
| `-ImplicitCastExpr 0xbb4928 <col:17> 'double' <LValueToRValue>
| `-DeclRefExpr 0xbb4898 <col:17> 'double' lvalue Var 0xbb47d8 'radius' 'double'
|-ForStmt 0xbb4bb8 <line:23:5, line:25:5>
| |-DeclStmt 0xbb49e0 <line:23:10, col:19>
| | `-VarDecl 0xbb4958 <col:10, col:18> col:14 used i 'int' cinit
| | `-IntegerLiteral 0xbb49c0 <col:18> 'int' 0
| |-<<<NULL>>>
| |-BinaryOperator 0xbb4a68 <col:21, col:25> 'int' '<'
| | |-ImplicitCastExpr 0xbb4a38 <col:21> 'int' <LValueToRValue>
| | | `-DeclRefExpr 0xbb49f8 <col:21> 'int' lvalue Var 0xbb4958 'i' 'int'
| | `-ImplicitCastExpr 0xbb4a50 <col:25> 'int' <LValueToRValue>
| | `-DeclRefExpr 0xbb4a18 <col:25> 'const int' lvalue Var 0xbad2f8 'maxValue' 'const int'
| |-UnaryOperator 0xbb4b00 <col:35, col:36> 'int' postfix '++'
| | `-DeclRefExpr 0xbb4ae0 <col:35> 'int' lvalue Var 0xbb4958 'i' 'int'
| `-CompoundStmt 0xbb4ba0 <col:40, line:25:5>
| `-CompoundAssignOperator 0xbb4b70 <line:24:9, col:22> 'int' '+=' ComputeLHSTy='int' ComputeResultTy='int'
| |-DeclRefExpr 0xbb4b18 <col:9> 'int' lvalue Var 0xbad398 'globalVar' 'int'
| `-ImplicitCastExpr 0xbb4b58 <col:22> 'int' <LValueToRValue>
| `-DeclRefExpr 0xbb4b38 <col:22> 'int' lvalue Var 0xbb4958 'i' 'int'
|-CallExpr 0xbb4cd0 <line:26:5, col:52> 'int'
| |-ImplicitCastExpr 0xbb4cb8 <col:5> 'int (*)(const char *, ...)' <FunctionToPointerDecay>
| | `-DeclRefExpr 0xbb4bf0 <col:5> 'int (const char *, ...)' Function 0xb083e8 'printf' 'int (const char *, ...)'
| |-ImplicitCastExpr 0xbb4d18 <col:12> 'const char *' <NoOp>
| | `-ImplicitCastExpr 0xbb4d00 <col:12> 'char *' <ArrayToPointerDecay>
| | `-StringLiteral 0xbb4c48 <col:12> 'char[27]' lvalue "Final globalVar value: %d\n"
| `-ImplicitCastExpr 0xbb4d30 <col:43> 'int' <LValueToRValue>
| `-DeclRefExpr 0xbb4c80 <col:43> 'int' lvalue Var 0xbad398 'globalVar' 'int'
`-ReturnStmt 0xbb4d68 <line:28:5, col:12>
`-IntegerLiteral 0xbb4d48 <col:12> 'int' 0

4中间代码生成

ir是后端的开始,可以进行代码的优化,最终生成最终的汇编代码。

有了ir,那么为每一种os设计语言就不用考虑后端了,就直接涉及前端即可,只要能生成一样的ir格式。

无优化

clang -S -emit-llvm -fno-discard-value-names ex.c -o ex.ll -O0 -g0

格式和汇编差不多,但是寄存器数量是不受限制的,采用三地址码的格式,但是同一条指令相同寄存器,不能又做源又做汇编。

其中一开始是全局变量的定义,之后分别是函数声明和定义,文件最开头是架构信息为可选项,文件末尾则是一些个函数属性,函数利用#0,#1来选择是哪一个

; ModuleID = 'ex.c'
source_filename = "ex.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@maxValue = dso_local constant i32 100, align 4
@globalVar = dso_local global i32 10, align 4
@.str = private unnamed_addr constant [45 x i8] c"Area of the circle with radius %.2f is %.2f\0A\00", align 1
@.str.1 = private unnamed_addr constant [27 x i8] c"Final globalVar value: %d\0A\00", align 1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @square(i32 noundef %x) #0 {
entry:
%x.addr = alloca i32, align 4
store i32 %x, i32* %x.addr, align 4
%0 = load i32, i32* %x.addr, align 4
%1 = load i32, i32* %x.addr, align 4
%mul = mul nsw i32 %0, %1
ret i32 %mul
}

; Function Attrs: noinline nounwind optnone uwtable
define dso_local void @printResult(double noundef %radius) #0 {
entry:
%radius.addr = alloca double, align 8
%area = alloca double, align 8
store double %radius, double* %radius.addr, align 8
%0 = load double, double* %radius.addr, align 8
%conv = fptosi double %0 to i32
%call = call i32 @square(i32 noundef %conv)
%conv1 = sitofp i32 %call to double
%mul = fmul double 0x400921FB54442D18, %conv1
store double %mul, double* %area, align 8
%1 = load double, double* %radius.addr, align 8
%2 = load double, double* %area, align 8
%call2 = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([45 x i8], [45 x i8]* @.str, i64 0, i64 0), double noundef %1, double noundef %2)
ret void
}

declare i32 @printf(i8* noundef, ...) #1

; Function Attrs: noinline nounwind optnone uwtable
define dso_local i32 @main() #0 {
entry:
%retval = alloca i32, align 4
%radius = alloca double, align 8
%i = alloca i32, align 4
store i32 0, i32* %retval, align 4
store double 5.000000e+00, double* %radius, align 8
%0 = load double, double* %radius, align 8
call void @printResult(double noundef %0)
store i32 0, i32* %i, align 4
br label %for.cond

for.cond: ; preds = %for.inc, %entry
%1 = load i32, i32* %i, align 4
%cmp = icmp slt i32 %1, 100
br i1 %cmp, label %for.body, label %for.end

for.body: ; preds = %for.cond
%2 = load i32, i32* %i, align 4
%3 = load i32, i32* @globalVar, align 4
%add = add nsw i32 %3, %2
store i32 %add, i32* @globalVar, align 4
br label %for.inc

for.inc: ; preds = %for.body
%4 = load i32, i32* %i, align 4
%inc = add nsw i32 %4, 1
store i32 %inc, i32* %i, align 4
br label %for.cond, !llvm.loop !6

for.end: ; preds = %for.cond
%5 = load i32, i32* @globalVar, align 4
%call = call i32 (i8*, ...) @printf(i8* noundef getelementptr inbounds ([27 x i8], [27 x i8]* @.str.1, i64 0, i64 0), i32 noundef %5)
ret i32 0
}

attributes #0 = { noinline nounwind optnone uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { "frame-pointer"="all" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }

!llvm.module.flags = !{!0, !1, !2, !3, !4}
!llvm.ident = !{!5}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{i32 7, !"frame-pointer", i32 2}
!5 = !{!"Ubuntu clang version 14.0.0-1ubuntu1.1"}
!6 = distinct !{!6, !7}
!7 = !{!"llvm.loop.mustprogress"}

O1优化

可以看出代码少了很多,主要优化部分在main以及print函数,优化点在参数的存储那一部分,无优化的时候每次调用函数的时候都会把参数存放在寄存器中

; ModuleID = 'ex.c'
source_filename = "ex.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@maxValue = dso_local local_unnamed_addr constant i32 100, align 4
@globalVar = dso_local local_unnamed_addr global i32 10, align 4
@.str = private unnamed_addr constant [45 x i8] c"Area of the circle with radius %.2f is %.2f\0A\00", align 1
@.str.1 = private unnamed_addr constant [27 x i8] c"Final globalVar value: %d\0A\00", align 1

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local i32 @square(i32 noundef %x) local_unnamed_addr #0 {
entry:
%mul = mul nsw i32 %x, %x
ret i32 %mul
}

; Function Attrs: nofree nounwind uwtable
define dso_local void @printResult(double noundef %radius) local_unnamed_addr #1 {
entry:
%conv = fptosi double %radius to i32
%mul.i = mul nsw i32 %conv, %conv
%conv1 = sitofp i32 %mul.i to double
%mul = fmul double %conv1, 0x400921FB54442D18
%call2 = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([45 x i8], [45 x i8]* @.str, i64 0, i64 0), double noundef %radius, double noundef %mul)
ret void
}

; Function Attrs: nofree nounwind
declare noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #2

; Function Attrs: nofree nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #1 {
entry:
%call2.i = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([45 x i8], [45 x i8]* @.str, i64 0, i64 0), double noundef 5.000000e+00, double noundef 0x4053A28C59D5433B) #3
%globalVar.promoted = load i32, i32* @globalVar, align 4, !tbaa !5
%0 = add i32 %globalVar.promoted, 4950
store i32 %0, i32* @globalVar, align 4, !tbaa !5
%call = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([27 x i8], [27 x i8]* @.str.1, i64 0, i64 0), i32 noundef %0)
ret i32 0
}

attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nofree nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { nofree nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #3 = { nounwind }

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{!"Ubuntu clang version 14.0.0-1ubuntu1.1"}
!5 = !{!6, !6, i64 0}
!6 = !{!"int", !7, i64 0}
!7 = !{!"omnipotent char", !8, i64 0}
!8 = !{!"Simple C/C++ TBAA"}

O2优化

感觉已经和O1没什么区别了,应该优化到尽头了

; ModuleID = 'ex.c'
source_filename = "ex.c"
target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128"
target triple = "x86_64-pc-linux-gnu"

@maxValue = dso_local local_unnamed_addr constant i32 100, align 4
@globalVar = dso_local local_unnamed_addr global i32 10, align 4
@.str = private unnamed_addr constant [45 x i8] c"Area of the circle with radius %.2f is %.2f\0A\00", align 1
@.str.1 = private unnamed_addr constant [27 x i8] c"Final globalVar value: %d\0A\00", align 1

; Function Attrs: mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn
define dso_local i32 @square(i32 noundef %x) local_unnamed_addr #0 {
entry:
%mul = mul nsw i32 %x, %x
ret i32 %mul
}

; Function Attrs: nofree nounwind uwtable
define dso_local void @printResult(double noundef %radius) local_unnamed_addr #1 {
entry:
%conv = fptosi double %radius to i32
%mul.i = mul nsw i32 %conv, %conv
%conv1 = sitofp i32 %mul.i to double
%mul = fmul double %conv1, 0x400921FB54442D18
%call2 = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([45 x i8], [45 x i8]* @.str, i64 0, i64 0), double noundef %radius, double noundef %mul)
ret void
}

; Function Attrs: nofree nounwind
declare noundef i32 @printf(i8* nocapture noundef readonly, ...) local_unnamed_addr #2

; Function Attrs: nofree nounwind uwtable
define dso_local i32 @main() local_unnamed_addr #1 {
entry:
%call2.i = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([45 x i8], [45 x i8]* @.str, i64 0, i64 0), double noundef 5.000000e+00, double noundef 0x4053A28C59D5433B) #3
%globalVar.promoted = load i32, i32* @globalVar, align 4, !tbaa !5
%0 = add i32 %globalVar.promoted, 4950
store i32 %0, i32* @globalVar, align 4, !tbaa !5
%call = call i32 (i8*, ...) @printf(i8* noundef nonnull dereferenceable(1) getelementptr inbounds ([27 x i8], [27 x i8]* @.str.1, i64 0, i64 0), i32 noundef %0)
ret i32 0
}

attributes #0 = { mustprogress nofree norecurse nosync nounwind readnone uwtable willreturn "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #1 = { nofree nounwind uwtable "frame-pointer"="none" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #2 = { nofree nounwind "frame-pointer"="none" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" }
attributes #3 = { nounwind }

!llvm.module.flags = !{!0, !1, !2, !3}
!llvm.ident = !{!4}

!0 = !{i32 1, !"wchar_size", i32 4}
!1 = !{i32 7, !"PIC Level", i32 2}
!2 = !{i32 7, !"PIE Level", i32 2}
!3 = !{i32 7, !"uwtable", i32 1}
!4 = !{!"Ubuntu clang version 14.0.0-1ubuntu1.1"}
!5 = !{!6, !6, i64 0}
!6 = !{!"int", !7, i64 0}
!7 = !{!"omnipotent char", !8, i64 0}
!8 = !{!"Simple C/C++ TBAA"}

5汇编代码生成

无优化

非常正常的汇编代码,其中定义了每个section,其符合pic的规范,生成的目标文件需要经过got表的重定位(主要涉及到printf函数callq printf@PLT)。

PLT会经过got表的重定位,来装载符号地址。

	.text
.file "ex.c"
.globl square # -- Begin function square
.p2align 4, 0x90
.type square,@function
square: # @square
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
movl %edi, -4(%rbp)
movl -4(%rbp), %eax
imull -4(%rbp), %eax
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end0:
.size square, .Lfunc_end0-square
.cfi_endproc
# -- End function
.section .rodata.cst8,"aM",@progbits,8
.p2align 3 # -- Begin function printResult
.LCPI1_0:
.quad 0x400921fb54442d18 # double 3.1415926535897931
.text
.globl printResult
.p2align 4, 0x90
.type printResult,@function
printResult: # @printResult
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $16, %rsp
movsd %xmm0, -8(%rbp)
cvttsd2si -8(%rbp), %edi
callq square
cvtsi2sd %eax, %xmm0
movsd .LCPI1_0(%rip), %xmm1 # xmm1 = mem[0],zero
mulsd %xmm0, %xmm1
movsd %xmm1, -16(%rbp)
movsd -8(%rbp), %xmm0 # xmm0 = mem[0],zero
movsd -16(%rbp), %xmm1 # xmm1 = mem[0],zero
movabsq $.L.str, %rdi
movb $2, %al
callq printf@PLT
addq $16, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end1:
.size printResult, .Lfunc_end1-printResult
.cfi_endproc
# -- End function
.section .rodata.cst8,"aM",@progbits,8
.p2align 3 # -- Begin function main
.LCPI2_0:
.quad 0x4014000000000000 # double 5
.text
.globl main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# %bb.0: # %entry
pushq %rbp
.cfi_def_cfa_offset 16
.cfi_offset %rbp, -16
movq %rsp, %rbp
.cfi_def_cfa_register %rbp
subq $32, %rsp
movl $0, -20(%rbp)
movsd .LCPI2_0(%rip), %xmm0 # xmm0 = mem[0],zero
movsd %xmm0, -16(%rbp)
movsd -16(%rbp), %xmm0 # xmm0 = mem[0],zero
callq printResult
movl $0, -4(%rbp)
.LBB2_1: # %for.cond
# =>This Inner Loop Header: Depth=1
cmpl $100, -4(%rbp)
jge .LBB2_4
# %bb.2: # %for.body
# in Loop: Header=BB2_1 Depth=1
movl -4(%rbp), %eax
addl globalVar, %eax
movl %eax, globalVar
# %bb.3: # %for.inc
# in Loop: Header=BB2_1 Depth=1
movl -4(%rbp), %eax
addl $1, %eax
movl %eax, -4(%rbp)
jmp .LBB2_1
.LBB2_4: # %for.end
movl globalVar, %esi
movabsq $.L.str.1, %rdi
movb $0, %al
callq printf@PLT
xorl %eax, %eax
addq $32, %rsp
popq %rbp
.cfi_def_cfa %rsp, 8
retq
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
# -- End function
.type maxValue,@object # @maxValue
.section .rodata,"a",@progbits
.globl maxValue
.p2align 2
maxValue:
.long 100 # 0x64
.size maxValue, 4

.type globalVar,@object # @globalVar
.data
.globl globalVar
.p2align 2
globalVar:
.long 10 # 0xa
.size globalVar, 4

.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Area of the circle with radius %.2f is %.2f\n"
.size .L.str, 45

.type .L.str.1,@object # @.str.1
.L.str.1:
.asciz "Final globalVar value: %d\n"
.size .L.str.1, 27

.ident "Ubuntu clang version 14.0.0-1ubuntu1.1"
.section ".note.GNU-stack","",@progbits

O2优化

O1和O2的ir是差不多的,因此这里只显示了其中之一

其中的.标号则是为了pic定位设置的,访问全局变量,访问标号偏移的行对地址,再在相应的地方(got表)进行重定位。

	.text
.file "ex.c"
.globl square # -- Begin function square
.p2align 4, 0x90
.type square,@function
square: # @square
.cfi_startproc
# %bb.0: # %entry
movl %edi, %eax
imull %edi, %eax
retq
.Lfunc_end0:
.size square, .Lfunc_end0-square
.cfi_endproc
# -- End function
.section .rodata.cst8,"aM",@progbits,8
.p2align 3 # -- Begin function printResult
.LCPI1_0:
.quad 0x400921fb54442d18 # double 3.1415926535897931
.text
.globl printResult
.p2align 4, 0x90
.type printResult,@function
printResult: # @printResult
.cfi_startproc
# %bb.0: # %entry
pushq %rax
.cfi_def_cfa_offset 16
cvttsd2si %xmm0, %eax
imull %eax, %eax
cvtsi2sd %eax, %xmm1
mulsd .LCPI1_0(%rip), %xmm1
movl $.L.str, %edi
movb $2, %al
callq printf@PLT
popq %rax
.cfi_def_cfa_offset 8
retq
.Lfunc_end1:
.size printResult, .Lfunc_end1-printResult
.cfi_endproc
# -- End function
.section .rodata.cst8,"aM",@progbits,8
.p2align 3 # -- Begin function main
.LCPI2_0:
.quad 0x4014000000000000 # double 5
.LCPI2_1:
.quad 0x4053a28c59d5433b # double 78.539816339744831
.text
.globl main
.p2align 4, 0x90
.type main,@function
main: # @main
.cfi_startproc
# %bb.0: # %entry
pushq %rax
.cfi_def_cfa_offset 16
movsd .LCPI2_0(%rip), %xmm0 # xmm0 = mem[0],zero
movsd .LCPI2_1(%rip), %xmm1 # xmm1 = mem[0],zero
movl $.L.str, %edi
movb $2, %al
callq printf@PLT
movl $4950, %esi # imm = 0x1356
addl globalVar(%rip), %esi
movl %esi, globalVar(%rip)
movl $.L.str.1, %edi
xorl %eax, %eax
callq printf@PLT
xorl %eax, %eax
popq %rcx
.cfi_def_cfa_offset 8
retq
.Lfunc_end2:
.size main, .Lfunc_end2-main
.cfi_endproc
# -- End function
.type maxValue,@object # @maxValue
.section .rodata,"a",@progbits
.globl maxValue
.p2align 2
maxValue:
.long 100 # 0x64
.size maxValue, 4

.type globalVar,@object # @globalVar
.data
.globl globalVar
.p2align 2
globalVar:
.long 10 # 0xa
.size globalVar, 4

.type .L.str,@object # @.str
.section .rodata.str1.1,"aMS",@progbits,1
.L.str:
.asciz "Area of the circle with radius %.2f is %.2f\n"
.size .L.str, 45

.type .L.str.1,@object # @.str.1
.L.str.1:
.asciz "Final globalVar value: %d\n"
.size .L.str.1, 27

.ident "Ubuntu clang version 14.0.0-1ubuntu1.1"
.section ".note.GNU-stack","",@progbits

6 可执行文件生成

这里细分可以分为两个步骤,一个是可重定位文件的生成,然后是可执行文件的生成,后者则需要链接器的参与。

前者只是扫描本汇编文件,生成符号表,但是外部符号,也就是定义在其他模块的符号,编译器并不认识,因此需要链接操作。

而链接又分为动态链接,以及静态链接,所谓pic就是相对于动态链接而言的(一个库代码copy处处运行,但是每个程序的虚拟地址映射都是不同的,因此需要运行时装载,应该是一个dlresolve来完成,记不清了),静态链接会一股脑的把目标文件和库文件有用的没用的放到一起,但是动态链接则是用哪个拿那个,因此需要重定位,运行时解析(plt的懒加载),但是一般都是在程序开始时加载got表。(这块很复杂,也不是一两句说的清的)。以下可以看出重定位文件和可执行文件的关系,静态链接没什么可说的,就拿动态链接举例了。

可以观察section表,注意和可执行文件的差异,可以看出可执行文件多了plt和got表

gcc -c ex.S -o  ex.o
root@L:/home/l/homework/compiler/lab1# readelf -S ex.o
There are 15 section headers, starting at offset 0x558:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .text PROGBITS 0000000000000000 00000040
00000000000000cf 0000000000000000 AX 0 0 16
[ 2] .rela.text RELA 0000000000000000 00000388
0000000000000108 0000000000000018 I 12 1 8
[ 3] .data PROGBITS 0000000000000000 00000110
0000000000000004 0000000000000000 WA 0 0 4
[ 4] .bss NOBITS 0000000000000000 00000114
0000000000000000 0000000000000000 WA 0 0 1
[ 5] .rodata.cst8 PROGBITS 0000000000000000 00000118
0000000000000010 0000000000000008 AM 0 0 8
[ 6] .rodata PROGBITS 0000000000000000 00000128
0000000000000004 0000000000000000 A 0 0 4
[ 7] .rodata.str1.1 PROGBITS 0000000000000000 0000012c
0000000000000048 0000000000000001 AMS 0 0 1
[ 8] .comment PROGBITS 0000000000000000 00000174
0000000000000028 0000000000000001 MS 0 0 1
[ 9] .note.GNU-stack PROGBITS 0000000000000000 0000019c
0000000000000000 0000000000000000 0 0 1
[10] .eh_frame PROGBITS 0000000000000000 000001a0
0000000000000078 0000000000000000 A 0 0 8
[11] .rela.eh_frame RELA 0000000000000000 00000490
0000000000000048 0000000000000018 I 12 10 8
[12] .symtab SYMTAB 0000000000000000 00000218
0000000000000120 0000000000000018 13 6 8
[13] .strtab STRTAB 0000000000000000 00000338
000000000000004a 0000000000000000 0 0 1
[14] .shstrtab STRTAB 0000000000000000 000004d8
000000000000007d 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)


ld -PIE ex.o /usr/lib/x86_64-linux-gnu/crt1.o /usr/lib/x86_64-linux-gnu/crti.o /usr/lib/x86_64-linux-gnu/crtn.o -lc -o ex
root@L:/home/l/homework/compiler/lab1# readelf -S ex
There are 26 section headers, starting at offset 0x3478:

Section Headers:
[Nr] Name Type Address Offset
Size EntSize Flags Link Info Align
[ 0] NULL 0000000000000000 00000000
0000000000000000 0000000000000000 0 0 0
[ 1] .interp PROGBITS 00000000004002e0 000002e0
000000000000000f 0000000000000000 A 0 0 1
[ 2] .note.gnu.pr[...] NOTE 00000000004002f0 000002f0
0000000000000020 0000000000000000 A 0 0 8
[ 3] .note.ABI-tag NOTE 0000000000400310 00000310
0000000000000020 0000000000000000 A 0 0 4
[ 4] .hash HASH 0000000000400330 00000330
0000000000000024 0000000000000004 A 6 0 8
[ 5] .gnu.hash GNU_HASH 0000000000400358 00000358
000000000000001c 0000000000000000 A 6 0 8
[ 6] .dynsym DYNSYM 0000000000400378 00000378
0000000000000060 0000000000000018 A 7 1 8
[ 7] .dynstr STRTAB 00000000004003d8 000003d8
000000000000004d 0000000000000000 A 0 0 1
[ 8] .gnu.version VERSYM 0000000000400426 00000426
0000000000000008 0000000000000002 A 6 0 2
[ 9] .gnu.version_r VERNEED 0000000000400430 00000430
0000000000000030 0000000000000000 A 7 1 8
[10] .rela.dyn RELA 0000000000400460 00000460
0000000000000030 0000000000000018 A 6 0 8
[11] .rela.plt RELA 0000000000400490 00000490
0000000000000018 0000000000000018 AI 6 20 8
[12] .init PROGBITS 0000000000401000 00001000
000000000000001b 0000000000000000 AX 0 0 4
[13] .plt PROGBITS 0000000000401020 00001020
0000000000000020 0000000000000010 AX 0 0 16
[14] .text PROGBITS 0000000000401040 00001040
0000000000000105 0000000000000000 AX 0 0 16
[15] .fini PROGBITS 0000000000401148 00001148
000000000000000d 0000000000000000 AX 0 0 4
[16] .rodata PROGBITS 0000000000402000 00002000
0000000000000060 0000000000000000 A 0 0 8
[17] .eh_frame PROGBITS 0000000000402060 00002060
00000000000000c8 0000000000000000 A 0 0 8
[18] .dynamic DYNAMIC 0000000000403e40 00002e40
00000000000001b0 0000000000000010 WA 7 0 8
[19] .got PROGBITS 0000000000403ff0 00002ff0
0000000000000010 0000000000000008 WA 0 0 8
[20] .got.plt PROGBITS 0000000000404000 00003000
0000000000000020 0000000000000008 WA 0 0 8
[21] .data PROGBITS 0000000000404020 00003020
0000000000000008 0000000000000000 WA 0 0 4
[22] .comment PROGBITS 0000000000000000 00003028
0000000000000027 0000000000000001 MS 0 0 1
[23] .symtab SYMTAB 0000000000000000 00003050
0000000000000258 0000000000000018 24 7 8
[24] .strtab STRTAB 0000000000000000 000032a8
00000000000000f8 0000000000000000 0 0 1
[25] .shstrtab STRTAB 0000000000000000 000033a0
00000000000000d8 0000000000000000 0 0 1
Key to Flags:
W (write), A (alloc), X (execute), M (merge), S (strings), I (info),
L (link order), O (extra OS processing required), G (group), T (TLS),
C (compressed), x (unknown), o (OS specific), E (exclude),
D (mbind), l (large), p (processor specific)

二 IR编程

示例程序

main() {
// 定义变量
int a, b, i, t, n;

// 初始化变量
a = 0; // 斐波那契数列的第一个数
b = 1; // 斐波那契数列的第二个数
i = 1; // 计数器

// 输入要生成的斐波那契数列长度
cin >> n;

// 输出斐波那契数列的前两个数字
cout << a << endl;
cout << b << endl;

// 生成斐波那契数列
while (i < n) {
t = b; // 保存当前的 b 值
b = a + b; // b 变成下一个斐波那契数
cout << b << endl; // 输出当前的 b 值
a = t; // 更新 a 为上一个的 b 值
i = i + 1; // 计数器加 1
}
}

相应ir代码

SH:

llc -relocation-model=pic 2.ll -o main.S

gcc main.S -o two

; 模块ID = 'fib.ll'
; 声明外部函数
declare i32 @scanf(i8*, ...)
declare i32 @printf(i8*, ...)

; 定义格式化字符串
@.str_in = private unnamed_addr constant [3 x i8] c"%d\00", align 1 ; 用于输入整数
@.str_out = private unnamed_addr constant [4 x i8] c"%d\0A\00", align 1 ; 用于输出整数并换行

; 主函数
define i32 @main() {
entry:
; 分配变量的栈空间
%a =
i32, align 4 ; int a;
%b = alloca i32, align 4 ; int b;
%i = alloca i32, align 4 ; int i;
%t = alloca i32, align 4 ; int t;
%n = alloca i32, align 4 ; int n;

; 初始化变量
store i32 0, i32* %a, align 4 ; a = 0;
store i32 1, i32* %b, align 4 ; b = 1;
store i32 1, i32* %i, align 4 ; i = 1;

; 输入要生成的斐波那契数列长度 n
call i32 (i8*, ...) @scanf(i8* getelementptr inbounds ([3 x i8], [3 x i8]* @.str_in, i32 0, i32 0), i32* %n)

; 输出斐波那契数列的前两个数字
%a_val = load i32, i32* %a, align 4
call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str_out, i32 0, i32 0), i32 %a_val)
%b_val = load i32, i32* %b, align 4
call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str_out, i32 0, i32 0), i32 %b_val)

; 进入循环
br label %loop

loop:
; 加载当前的 i 和 n 的值
%i_val = load i32, i32* %i, align 4
%n_val = load i32, i32* %n, align 4
; 判断 i 是否小于 n
%cmp = icmp slt i32 %i_val, %n_val
; 根据比较结果跳转
br i1 %cmp, label %loop_body, label %end

loop_body:
; t = b;
%b_val2 = load i32, i32* %b, align 4
store i32 %b_val2, i32* %t, align 4
; b = a + b;
%a_val2 = load i32, i32* %a, align 4
%sum = add i32 %a_val2, %b_val2
store i32 %sum, i32* %b, align 4
; 输出当前的 b 值
%b_val3 = load i32, i32* %b, align 4
call i32 (i8*, ...) @printf(i8* getelementptr inbounds ([4 x i8], [4 x i8]* @.str_out, i32 0, i32 0), i32 %b_val3)
; a = t;
%t_val = load i32, i32* %t, align 4
store i32 %t_val, i32* %a, align 4
; i = i + 1;
%i_val2 = load i32, i32* %i, align 4
%inc = add i32 %i_val2, 1
store i32 %inc, i32* %i, align 4
; 返回循环判断
br label %loop

end:
; 函数返回
ret i32 0
}

输出示例:

image-20240917191630045

三 汇编编程

SH:

riscv64-unknown-linux-gnu-gcc -o three three.S -static

qemu-riscv64 ./three


.data
# scanf 和 printf 的格式字符串
input_fmt: .asciz "%d" # 读取整数的格式字符串
output_fmt: .asciz "%d\n" # 输出带换行符的整数的格式字符串

.text
.global main
main:
# 设置栈帧
addi sp, sp, -32 # 分配 32 字节的栈空间用于变量和保存的寄存器
sw ra, 28(sp) # 保存返回地址
sw s0, 24(sp) # 保存帧指针
addi s0, sp, 32 # 设置帧指针 (s0) 为栈帧的顶部

# 为变量 a, b, i, t, n 分配空间
# 通过从 s0 的偏移量访问变量:
# a: -20(s0)
# b: -16(s0)
# i: -12(s0)
# t: -8(s0)
# n: -4(s0)

# 初始化变量
li t0, 0 # 将立即数 0 加载到 t0 中
sw t0, -20(s0) # 将 t0 存储到 a 中 (a = 0)
li t0, 1 # 将立即数 1 加载到 t0 中
sw t0, -16(s0) # 将 t0 存储到 b 中 (b = 1)
sw t0, -12(s0) # 将 t0 存储到 i 中 (i = 1)

# 从输入中读取 n
la a0, input_fmt # 将 input_fmt 的地址加载到 a0 中
addi a1, s0, -4 # 计算 n 的地址并存入 a1
call scanf # 调用 scanf 读取 n

# 输出前两个斐波那契数:a 和 b
lw t0, -20(s0) # 将 a 加载到 t0 中
mv a1, t0 # 将 t0 移动到 a1(printf 的第一个参数)
la a0, output_fmt # 将 output_fmt 的地址加载到 a0 中
call printf # 调用 printf 打印 a

lw t0, -16(s0) # 将 b 加载到 t0 中
mv a1, t0 # 将 t0 移动到 a1
la a0, output_fmt
call printf # 打印 b

# 循环开始
loop_start:
lw t0, -12(s0) # 将 i 加载到 t0 中
lw t1, -4(s0) # 将 n 加载到 t1 中
blt t0, t1, loop_body # 如果 i < n,跳转到 loop_body
j loop_end # 否则,跳转到 loop_end

loop_body:
# t = b
lw t0, -16(s0) # 将 b 加载到 t0 中
sw t0, -8(s0) # 将 t0 存储到 t 中

# b = a + b
lw t0, -20(s0) # 将 a 加载到 t0 中
lw t1, -16(s0) # 将 b 加载到 t1 中
add t0, t0, t1 # t0 = a + b
sw t0, -16(s0) # 将 t0 存储到 b 中 (b = a + b)

# 输出新的 b
mv a1, t0 # 将新的 b 移动到 a1
la a0, output_fmt
call printf # 打印 b

# a = t
lw t0, -8(s0) # 将 t 加载到 t0 中
sw t0, -20(s0) # 将 t0 存储到 a 中

# i = i + 1
lw t0, -12(s0) # 将 i 加载到 t0 中
addi t0, t0, 1 # 增加 t0 (t0 = i + 1)
sw t0, -12(s0) # 将 t0 存储回 i

j loop_start # 跳回到循环的开始

loop_end:
# 清理栈帧并返回(因无start会发生段错误)
lw ra, 28(sp) # 恢复返回地址
lw s0, 24(sp) # 恢复帧指针
addi sp, sp, 32 # 释放栈空间
ret # 从 main 返回

结果:

因为没有exit系统调用,因此会直接段错误,但是不影响程序功能。

image-20240917193239718