# 都有 Python 了，还要什么编译器！

1. 汇编代码不可移植；

2. 虽然在现代工具的辅助下变得容易了些，但 Assembly 编程仍然需要大量繁琐的工作。

1. 使用 Clang 为基准生成 LLVM 中间代码，该基准用于测量 solve_5，一个不存在的函数；

2. 使 Python 在 LLVM 中生成线性求解器（linear solver）代码；

3. 使用 Python 脚本测试基准，用生成求解器替换 solve_5 调用；

4. 使用 LLVM 静态编译器将中间代码转换为机器代码；

5. 使用 GNU 汇编器和 Clang 的链接器将机器代码转换为可执行的二进制文件。

Python 部分

# this generates n-solver in LLVM code with LLVMCode objects.

# No LLVM stuff yet, just completely Pythonic solution

defsolve_linear_system(a_array, b_array, x_array, n_value):

defa(i, j, n):

ifn == n_value:

returna_array[i * n_value + j]

returna(i, j, n+1)*a(n, n, n+1) - a(i, n, n+1)*a(n, j, n+1)

defb(i, n):

ifn == n_value:

returnb_array[i]

returna(n, n, n+1)*b(i, n+1) - a(i, n, n+1)*b(n, n+1)

defx(i):

d = b(i,i+1)

forjinrange(i):

d -= a(i, j, i+1) * x_array[j]

returnd / a(i, i, i+1)

forkinrange(n_value):

x_array[k] = x(k)

returnx_array

# this is basically the whole LLVM layer

I =

STACK = []

classLLVMCode:

# the only constructor for now is by double* instruction

def__init__(self, io, code =''):

self.io = io

self.code = code

def__getitem__(self, i):

global I, STACK

copy_code ="%"+ str(I+1)

copy_code +=" = getelementptr inbounds double, double* "

copy_code +=self.io +", i64 "+ str(i) +"\n"

copy_code +="%"+ str(I+2)

copy_code +=" = load double, double* %"+ str(I+1)

copy_code +=", align 8\n"

I +=2

STACK += [I]

returnLLVMCode(self.io, copy_code)

def__setitem__(self, i, other_llvcode):

global I, STACK

self.code += other_llvcode.code

self.code +="%"+ str(I+1)

self.code +=" = getelementptr inbounds double, double* "

self.code +=self.io +", i64 "+ str(i) +"\n"

self.code +="store double %"+ str(I)

self.code +=", double* %"+ str(I+1) +", align 8\n"

I +=1

STACK = STACK[:-1]

returnself

defgeneral_arithmetics(self, operator, other_llvcode):

global I, STACK

self.code += other_llvcode.code;

self.code +="%"+ str(I+1) +" = f"+ operator

self.code +=" double %"+ str(STACK[-2]) +", %"

self.code += str(STACK[-1]) +"\n";

I +=1

STACK = STACK[:-2] + [I]

returnself

def__sub__(self, other_llvcode):

returnself.general_arithmetics('sub', other_llvcode)

def__mul__(self, other_llvcode):

returnself.general_arithmetics('mul', other_llvcode)

def__div__(self, other_llvcode):

returnself.general_arithmetics('div', other_llvcode)

LLVM 中的指令有编号，我们希望保存枚举，因此将代码插入到基准测试中的函数很重要，但也不是很复杂。

# this replaces the function call

# and updates all the instructions' indices

defreplace_call(text, line, params):

globalI, STACK

# '%12 ' -> 12

I = int(''.join(

[xiforxiinparams[2]ifxi.isdigit()]

))

first_instruction_to_replace = I +1

STACK = []

replacement = solve_linear_system(

LLVMCode(params[]),

LLVMCode(params[1]),

LLVMCode(params[2]),5).code

delta_instruction = I - first_instruction_to_replace +1

foriinxrange(first_instruction_to_replace, sys.maxint):

not_found = sum(

[text.find('%'+ str(i) + c) ==-1

forcinPOSSIBLE_CHARS_NUMBER_FOLLOWS_WITH]

)

ifnot_found ==4:

# the last instruction has already been substituted

break

new_i = i + delta_instruction

forcinPOSSIBLE_CHARS_NUMBER_FOLLOWS_WITH:

# substitute instruction number

text = text.replace('%'+ str(i) + c,'%'+ str(new_i) + c)

returntext.replace(line, replacement)

Step 1. Benchmark C source code

Step 2. LLVM 汇编语言

Step 3. 调用替换后的 LLVM

Step 4. 本地优化装配

1. C 的技巧对 Clang 来说并不适用，因此测量 GCC 版本，其平均运行大约 70 毫秒；

2. C++ 版本是用 Clang 构建的，运行时间为 60 毫秒；

3. Python 版本（此处描述的版本）仅运行 55 毫秒。

• 发表于:
• 原文链接：https://kuaibao.qq.com/s/20190217A0DWCW00?refer=cp_1026
• 腾讯「云+社区」是腾讯内容开放平台帐号（企鹅号）传播渠道之一，根据《腾讯内容开放平台服务协议》转载发布内容。

2019-09-15

2019-09-15

2019-09-15

2019-09-15

2019-09-15

2019-09-15