빠른 포화 상태로 팔 ASM의 두 개의 반 단어를 이동합니다
-
18-09-2019 - |
문제
32 비트 단어로 2 개의 서명 된 16 비트 값이 있으며, 일정한 값 (1에서 6까지)으로 올바른 (나누기)를 바꾸고 바이트 (0..0xff)로 포화해야합니다.
예를 들어,
- 0x FFE1 00AA ~와 함께 시프트 = 5 해야합니다 0x 0000 0005;
- 0x 2345 1234 해야합니다 0x 00ff 0091
이 의사 코드와 같은 값을 동시에 포화하려고합니다.
AND RT, R0, 0x80008000; - mask high bits to get negatives
ORR RT, RT, LSR #1
ORR RT, RT, LSR #2
ORR RT, RT, LSR #4
ORR RT, RT, LSR #8; - now its expanded signs in each halfword
MVN RT, RT
AND R0, RT; now negative values are zero
; here something to saturate high overflow and shift after
그러나 내가 얻는 코드는 매우 추악하고 느립니다. :) 내가 지금 가지고있는 가장 좋은 (가장 빠른) 것은 다음과 같이 각 절반의 별도의 채도입니다.
MOV RT, R0, LSL #16
MOVS RT, RT, ASR #16+5
MOVMI RT, #0
CMP RT, RT, #256
MOVCS RT, #255
MOVS R0, R0, ASR #16+5
MOVMI R0, #0
CMP R0, R0, #256
MOVCS R0, #255
ORR R0, RT, R0, LSL #16
그러나 그것은 10 사이클입니다. :( 더 빠를 수 있습니까?
추신 : 나중에 나는 이것에 대한 USAT16 명령을 발견했지만 ARMV6에만 해당됩니다. ARMV5TE 및 ARMV4에서 작업하려면 코드가 필요합니다.
편집하다: 이제 첫 번째 코드를 다시 작성합니다.
ANDS RT, 0x10000, R0 << 1; // 0x10000 is in register. Sign (HI) moves to C flag, Sign (LO) is masked
SUBNE RT, RT, 1; // Mask LO with 0xFFFF if it's negative
SUBCS RT, RT, 0x10000; // Mask HI with 0xFFFF if it's negative
BIC R0, R0, RT; // Negatives are 0 now. The mask can be used as XOR too
TST R0, 0xE0000000; // check HI overflow
ORRNE R0, R0, 0x1FE00000 // set HI to 0xFF (shifted) if so
TST R0, 0x0000E000 // check LO overflow
ORRNE R0, R0, 0x00001FE0 // set LO to 0xFF if so
AND R0, 0x00FF00FF, R0 >> 5; // 0x00FF00FF is in register
그러나 그것은 아름답 지 않습니다.
해결책 2
하나의 수표를 사용하여 두 개의 작업에 플래그를 설정하는 것이 좋습니다. 그러나 나는 두 번째 부분을 위해 그것을 할 수 없습니다. 나는 다른 일을 할 수 있습니다 :) 여기에 1에서 6까지의 전환으로 사용할 보편적 변형이 있습니다.
;prepare:
MOV RMask, ((0xFF00 << shift) & 0xFF00) << 16; Mask overflow bits
MOV R_0xFF00FF, 0xFF;
ORR R_0xFF00FF, 0xFF000000;
;...
; innerloop:
;....
TST R0, RMask, R0 << 16; Set flags for LO half
ORRNE R0, R0, 0xFF << shift; It is overflow. First try positive
BICMI R0, R0, 0xFF << shift; Fix it if negative. LO half is ready
TST R0, RMask, R0; Set flags for HI half. Can TST R0, R0, #Mask also
ORRNE R0, R0, 0xFF << (shift+16)
BICNE R0, R0, 0xFF << (shift+16)
AND R0, R_0xFF00FF, R0 >> shift; Shift and mask
이제 7주기입니다. :)
더 나을 수 있습니까?
편집하다: 오버 플로우가 충분히 드물기 때문에 다음과 같은 것을 추가하는 것이 좋습니다.
TST R0, 0xE000E000
BEQ no_saturation_needed
... ; saturation ops here
다른 팁
당신이 가진 것은 당신이 언급 한대로 문제를 위해 할 수있는 것만 큼 좋은 것입니다. 단단한 루프로 많은 데이터를 위해이 작업을 수행하고 마스크를 보유 할 몇 가지 레지스터를 감당할 수 있다면, 사이클을 저장할 수 있지만 크게 개선되지는 않을 것입니다. V6 아키텍처 전에 ARM의 이러한 유형의 "소규모 벡터"포화 작업에 대한 지원은 없습니다.
기본적으로 이것이 프로그램에서 유일한 병목 현상이 아니라면, 이것을 버리고 다음 핫스팟으로 넘어갈 때입니다.