Одну LLVM скомпилировал при наличии реальных данных от профилировщика, другую - без.
Они не идентичные, но разница что-то не ощущается совершенно.
Чем отличаются эти две функции?
Модератор: Andy
-
- Сообщения: 1213
- Зарегистрирован: 26 фев 2004, 13:24
- Откуда: Pietari, Venäjä
- Контактная информация:
С профилировщиком
Код: Выделить всё
.text
.def jit_fn3;
.scl 2;
.type 32;
.endef
.globl jit_fn3
.align 16, 0x90
jit_fn3: # @jit_fn3
.Ltmp0:
.seh_proc jit_fn3
# BB#0: # %Block_200.split
pushq %r15
.Ltmp1:
.seh_pushreg 15
pushq %r14
.Ltmp2:
.seh_pushreg 14
pushq %r13
.Ltmp3:
.seh_pushreg 13
pushq %r12
.Ltmp4:
.seh_pushreg 12
pushq %rsi
.Ltmp5:
.seh_pushreg 6
pushq %rdi
.Ltmp6:
.seh_pushreg 7
pushq %rbp
.Ltmp7:
.seh_pushreg 5
pushq %rbx
.Ltmp8:
.seh_pushreg 3
subq $40, %rsp
.Ltmp9:
.seh_stackalloc 40
.Ltmp10:
.seh_endprologue
movq %rcx, %r15
xorl %edx, %edx
callq _readRegUint
movq %rax, %rbx
movl $4, %edx
movq %r15, %rcx
callq _readRegUint
movl $3, %edx
movq %r15, %rcx
callq _readRegUint
movl $7, %edx
movq %r15, %rcx
callq _readRegUint
movl $2, %edx
movq %r15, %rcx
callq _readRegUint
movl $1, %edx
movq %r15, %rcx
callq _readRegUint
movq %rax, %r14
movl $5, %edx
movq %r15, %rcx
callq _readRegUint
movl $6, %edx
movq %r15, %rcx
callq _readRegUint
cmpq $99999999, %rbx # imm = 0x5F5E0FF
seta %al
movzbl %al, %eax
sbbq $0, %rax
testq %rax, %rax
jle .LBB0_1
# BB#14: # %Block_21c
movl $100000000, %r8d # imm = 0x5F5E100
movq %r15, %rcx
movq %rbx, %rdx
callq _udiv128
movq %rax, %rdx
cmpq $9999, %rdx # imm = 0x270F
seta %al
movzbl %al, %eax
sbbq $0, %rax
movl $9, %edi
testq %rax, %rax
jg .LBB0_2
jmp .LBB0_3
.LBB0_1: # %Block_214
cmpq $9999, %rbx # imm = 0x270F
seta %al
movzbl %al, %eax
sbbq $0, %rax
movl $1, %edi
testq %rax, %rax
movq %rbx, %rdx
jle .LBB0_3
.LBB0_2: # %Block_228
movl $10000, %r8d # imm = 0x2710
movq %r15, %rcx
callq _udiv128
movq %rax, %rdx
addq $4, %rdi
.LBB0_3: # %Block_234
cmpq $99, %rdx
seta %al
movzbl %al, %eax
sbbq $0, %rax
testq %rax, %rax
jle .LBB0_5
# BB#4: # %Block_23c
movl $100, %r8d
movq %r15, %rcx
callq _udiv128
addq $2, %rdi
cmpq $9, %rax
.LBB0_6: # %Block_244
seta %al
movzbl %al, %eax
sbbq $0, %rax
testq %rax, %rax
jle .LBB0_9
# BB#7: # %Block_24c
leaq 1(%rdi), %rsi
movl %esi, %edx
xorl %r9d, %r9d
movq %r15, %rcx
movq %r14, %r8
callq _storeMemUint8
testq %rdi, %rdi
js .LBB0_13
# BB#8:
movq %rdi, %rbp
movq %rdi, %r13
.LBB0_10: # %Block_270
leaq (%r14,%rbp), %rdi
subl %r13d, %esi
addl $-2, %esi
movslq %esi, %r12
movl $10, %r8d
movq %r15, %rcx
movq %rbx, %rdx
callq _udiv128
movq %rax, %rbx
movl $6, %edx
movq %r15, %rcx
callq _readSpRegUint
leal 48(%rax), %r9d
xorl %r8d, %r8d
movq %r15, %rcx
movq %rdi, %rdx
callq _storeMemUint8
leaq -1(%r13), %rsi
movslq %esi, %rax
cmpq %r12, %rax
setg %cl
movzbl %cl, %ecx
cmpq %rax, %r12
setg %al
movzbl %al, %eax
cmpq %rax, %rcx
je .LBB0_13
# BB#11: # %Block_288.preheader
leaq -1(%r14,%rbp), %rdi
shlq $32, %r13
movabsq $-8589934592, %rbp # imm = 0xFFFFFFFE00000000
addq %r13, %rbp
movabsq $-4294967296, %r14 # imm = 0xFFFFFFFF00000000
.align 16, 0x90
.LBB0_12: # %Block_288
# =>This Inner Loop Header: Depth=1
movl $10, %r8d
movq %r15, %rcx
movq %rbx, %rdx
callq _udiv128
movq %rax, %rbx
movl $6, %edx
movq %r15, %rcx
callq _readSpRegUint
leal 48(%rax), %r9d
xorl %r8d, %r8d
movq %r15, %rcx
movq %rdi, %rdx
callq _storeMemUint8
decq %rdi
movq %rbp, %rax
sarq $32, %rax
cmpq %r12, %rax
setg %cl
movzbl %cl, %ecx
cmpq %rax, %r12
setg %al
movzbl %al, %eax
addq %r14, %rbp
decq %rsi
cmpq %rax, %rcx
jne .LBB0_12
.LBB0_13: # %Block_2b8
xorl %edx, %edx
movq %r15, %rcx
callq _pop
nop
addq $40, %rsp
popq %rbx
popq %rbp
popq %rdi
popq %rsi
popq %r12
popq %r13
popq %r14
popq %r15
retq
.LBB0_9: # %Block_250
movl %edi, %edx
xorl %r9d, %r9d
movq %r15, %rcx
movq %r14, %r8
callq _storeMemUint8
leaq -1(%rdi), %r13
movslq %r13d, %rbp
testq %rbp, %rbp
movq %rdi, %rsi
jns .LBB0_10
jmp .LBB0_13
.LBB0_5: # %Block_244
cmpq $9, %rdx
jmp .LBB0_6
.seh_handlerdata
.text
.Ltmp11:
.seh_endproc
2B OR NOT(2B) = FF
-
- Сообщения: 1213
- Зарегистрирован: 26 фев 2004, 13:24
- Откуда: Pietari, Venäjä
- Контактная информация:
Без данных профиля
Код: Выделить всё
.text
.def jit_fn3;
.scl 2;
.type 32;
.endef
.globl jit_fn3
.align 16, 0x90
jit_fn3: # @jit_fn3
.Ltmp0:
.seh_proc jit_fn3
# BB#0: # %Block_200.split
pushq %r15
.Ltmp1:
.seh_pushreg 15
pushq %r14
.Ltmp2:
.seh_pushreg 14
pushq %r13
.Ltmp3:
.seh_pushreg 13
pushq %r12
.Ltmp4:
.seh_pushreg 12
pushq %rsi
.Ltmp5:
.seh_pushreg 6
pushq %rdi
.Ltmp6:
.seh_pushreg 7
pushq %rbp
.Ltmp7:
.seh_pushreg 5
pushq %rbx
.Ltmp8:
.seh_pushreg 3
subq $40, %rsp
.Ltmp9:
.seh_stackalloc 40
.Ltmp10:
.seh_endprologue
movq %rcx, %r15
xorl %edx, %edx
callq _readRegUint
movq %rax, %rbx
movl $4, %edx
movq %r15, %rcx
callq _readRegUint
movl $3, %edx
movq %r15, %rcx
callq _readRegUint
movl $7, %edx
movq %r15, %rcx
callq _readRegUint
movl $2, %edx
movq %r15, %rcx
callq _readRegUint
movl $1, %edx
movq %r15, %rcx
callq _readRegUint
movq %rax, %r14
movl $5, %edx
movq %r15, %rcx
callq _readRegUint
movl $6, %edx
movq %r15, %rcx
callq _readRegUint
cmpq $99999999, %rbx # imm = 0x5F5E0FF
seta %al
movzbl %al, %eax
sbbq $0, %rax
testq %rax, %rax
jle .LBB0_1
# BB#13: # %Block_21c
movl $100000000, %r8d # imm = 0x5F5E100
movq %r15, %rcx
movq %rbx, %rdx
callq _udiv128
cmpq $9999, %rax # imm = 0x270F
seta %cl
movzbl %cl, %ecx
sbbq $0, %rcx
movl $9, %edi
testq %rcx, %rcx
jg .LBB0_2
jmp .LBB0_3
.LBB0_1: # %Block_214
cmpq $9999, %rbx # imm = 0x270F
seta %al
movzbl %al, %eax
sbbq $0, %rax
movl $1, %edi
testq %rax, %rax
movq %rbx, %rax
jle .LBB0_3
.LBB0_2: # %Block_228
movl $10000, %r8d # imm = 0x2710
movq %r15, %rcx
movq %rax, %rdx
callq _udiv128
addq $4, %rdi
.LBB0_3: # %Block_234
cmpq $99, %rax
seta %cl
movzbl %cl, %ecx
sbbq $0, %rcx
testq %rcx, %rcx
jle .LBB0_5
# BB#4: # %Block_23c
movl $100, %r8d
movq %r15, %rcx
movq %rax, %rdx
callq _udiv128
addq $2, %rdi
.LBB0_5: # %Block_244
cmpq $9, %rax
seta %al
movzbl %al, %eax
sbbq $0, %rax
testq %rax, %rax
jg .LBB0_6
# BB#8: # %Block_250
movl %edi, %edx
xorl %r9d, %r9d
movq %r15, %rcx
movq %r14, %r8
callq _storeMemUint8
leaq -1(%rdi), %r13
movslq %r13d, %rbp
testq %rbp, %rbp
movq %rdi, %rsi
jns .LBB0_9
jmp .LBB0_12
.LBB0_6: # %Block_24c
leaq 1(%rdi), %rsi
movl %esi, %edx
xorl %r9d, %r9d
movq %r15, %rcx
movq %r14, %r8
callq _storeMemUint8
testq %rdi, %rdi
js .LBB0_12
# BB#7:
movq %rdi, %rbp
movq %rdi, %r13
.LBB0_9: # %Block_270
leaq (%r14,%rbp), %rdi
subl %r13d, %esi
addl $-2, %esi
movslq %esi, %r12
movl $10, %r8d
movq %r15, %rcx
movq %rbx, %rdx
callq _udiv128
movq %rax, %rbx
movl $6, %edx
movq %r15, %rcx
callq _readSpRegUint
leal 48(%rax), %r9d
xorl %r8d, %r8d
movq %r15, %rcx
movq %rdi, %rdx
callq _storeMemUint8
leaq -1(%r13), %rsi
movslq %esi, %rax
cmpq %r12, %rax
setg %cl
movzbl %cl, %ecx
cmpq %rax, %r12
setg %al
movzbl %al, %eax
cmpq %rax, %rcx
je .LBB0_12
# BB#10: # %Block_288.preheader
leaq -1(%r14,%rbp), %rdi
shlq $32, %r13
movabsq $-8589934592, %rbp # imm = 0xFFFFFFFE00000000
addq %r13, %rbp
movabsq $-4294967296, %r14 # imm = 0xFFFFFFFF00000000
.align 16, 0x90
.LBB0_11: # %Block_288
# =>This Inner Loop Header: Depth=1
movl $10, %r8d
movq %r15, %rcx
movq %rbx, %rdx
callq _udiv128
movq %rax, %rbx
movl $6, %edx
movq %r15, %rcx
callq _readSpRegUint
leal 48(%rax), %r9d
xorl %r8d, %r8d
movq %r15, %rcx
movq %rdi, %rdx
callq _storeMemUint8
decq %rdi
movq %rbp, %rax
sarq $32, %rax
cmpq %r12, %rax
setg %cl
movzbl %cl, %ecx
cmpq %rax, %r12
setg %al
movzbl %al, %eax
addq %r14, %rbp
decq %rsi
cmpq %rax, %rcx
jne .LBB0_11
.LBB0_12: # %Block_2b8
xorl %edx, %edx
movq %r15, %rcx
callq _pop
nop
addq $40, %rsp
popq %rbx
popq %rbp
popq %rdi
popq %rsi
popq %r12
popq %r13
popq %r14
popq %r15
retq
.seh_handlerdata
.text
.Ltmp11:
.seh_endproc
2B OR NOT(2B) = FF
Синтаксис непривычный, сложный =)
Код практически идентичный, только разная интерпретация результата, возвращаемого процедурой _udiv128. В одном случае он в RAX, а в другом в RDX. Соответственно и перестраиваются инструкции, которые от них зависят (или влияют)
Без профилировщика понятно, что основные потери идут на CALL (_storeMemUint8, _udiv128, etc.) - это как я понял, обертка для безопасного деления, записи в память и т.д.
Основная цель - оптимизация?
Код практически идентичный, только разная интерпретация результата, возвращаемого процедурой _udiv128. В одном случае он в RAX, а в другом в RDX. Соответственно и перестраиваются инструкции, которые от них зависят (или влияют)
Без профилировщика понятно, что основные потери идут на CALL (_storeMemUint8, _udiv128, etc.) - это как я понял, обертка для безопасного деления, записи в память и т.д.
Основная цель - оптимизация?
It's a long way to the top if you wanna rock'n'roll
-
- Сообщения: 1213
- Зарегистрирован: 26 фев 2004, 13:24
- Откуда: Pietari, Venäjä
- Контактная информация:
Меня сейчас интересует на что повлияли данные профилировщика, почему LLVM переставил инструкции именно таким образом когда достоверно узнал склько раз реально срабатывает каждый бранч. Две недели отлаживал профилировщик чтобы он вносил минимум дополнительного оверхеда и при этом экспортировал удобный весовой граф для последующей jit-компиляции, а выхлоп получился нулевой.
От _storeMem не избавиться похоже никак так как там эмуляция виртуальной памяти другой архитектуры, _udiv128 надо заменить на нативную инструкцию которая в x86_64 есть. Но в первую очередь - выкинуть эти идиотские _readRegUint, результат которых отбрасывается и не используется. Начну этим заниматься уже завтра.
_readSpReg, кстати, это чтение остатка от деления который на эмулируемой архитектуре идет в спецрегистр. Ее надо слить с _udiv128 таким образом чтобы перед выходом из процедуры срабатывал некий триггер и последний остаток всеже записывался в спецрегистр для прозрачной эмуляции, это будет отдельный большой кариес.
Возможен также вариант что оптимизированный вариант у меня не запускался никогда, так как по дефолту встраиваемый LLVM JIT компилирует код проще чем это делает полный стационарный LLVM компилятор чей асмовый аутпут я запостил, это тоже надо проверить.
От _storeMem не избавиться похоже никак так как там эмуляция виртуальной памяти другой архитектуры, _udiv128 надо заменить на нативную инструкцию которая в x86_64 есть. Но в первую очередь - выкинуть эти идиотские _readRegUint, результат которых отбрасывается и не используется. Начну этим заниматься уже завтра.
_readSpReg, кстати, это чтение остатка от деления который на эмулируемой архитектуре идет в спецрегистр. Ее надо слить с _udiv128 таким образом чтобы перед выходом из процедуры срабатывал некий триггер и последний остаток всеже записывался в спецрегистр для прозрачной эмуляции, это будет отдельный большой кариес.
Возможен также вариант что оптимизированный вариант у меня не запускался никогда, так как по дефолту встраиваемый LLVM JIT компилирует код проще чем это делает полный стационарный LLVM компилятор чей асмовый аутпут я запостил, это тоже надо проверить.
2B OR NOT(2B) = FF