Skip to content

Commit 15d5245

Browse files
committed
avr: implement __[u]divmod(h|q)i4
GCC uses a special calling convention for integer multiplication and devision. ref: https://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention Unless we teach the compiler about this convention we're limited to implementing these functions via naked_asm!. The implementations provided here derive from libcc. ref: https://github.com/gcc-mirror/gcc/blob/95f10974a9190e345776604480a2df0191104308/libgcc/config/avr/lib1funcs.S
1 parent 9978a8b commit 15d5245

File tree

1 file changed

+147
-0
lines changed

1 file changed

+147
-0
lines changed

compiler-builtins/src/int/udiv.rs

Lines changed: 147 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -43,6 +43,153 @@ intrinsics! {
4343

4444
((rem as u64) << 32) | (div as u64)
4545
}
46+
47+
#[naked]
48+
pub unsafe extern "C" fn __udivmodqi4() {
49+
// Returns unsigned 8-bit `n / d` and `n % d`.
50+
//
51+
// Note: GCC implements a [non-standard calling convention](https://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention) for this function.
52+
// Derived from: https://github.com/gcc-mirror/gcc/blob/95f10974a9190e345776604480a2df0191104308/libgcc/config/avr/lib1funcs.S#L1365
53+
54+
// r25: remainder
55+
// r24: dividend, quotient
56+
// r22: divisor
57+
// r23: loop counter
58+
core::arch::naked_asm!(
59+
"clr r25", // clear remainder
60+
"ldi r23, 8", // init loop counter
61+
"lsl r24", // shift dividend
62+
"1:",
63+
"rol r25", // shift dividend into remainder
64+
"cp r25, r22", // compare remainder & divisor
65+
"brcs 2f", // REMAINder <= divisor
66+
"sub r25, r22", // restore remainder
67+
"2:",
68+
"rol r24", // shift dividend (with CARRY)
69+
"dec r23", // decrement loop counter
70+
"brne 1b",
71+
"com r24", // complement result (C flag was complemented in loop)
72+
"ret",
73+
);
74+
}
75+
76+
#[naked]
77+
pub unsafe extern "C" fn __divmodqi4() {
78+
// Returns signed 8-bit `n / d` and `n % d`.
79+
//
80+
// Note: GCC implements a [non-standard calling convention](https://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention) for this function.
81+
// Derived from: https://github.com/gcc-mirror/gcc/blob/95f10974a9190e345776604480a2df0191104308/libgcc/config/avr/lib1funcs.S#L1385
82+
83+
// r25: remainder
84+
// r24: dividend, quotient
85+
// r22: divisor
86+
// r23: loop counter
87+
core::arch::naked_asm!(
88+
"bst r24, 7", // store sign of dividend
89+
"mov r0, r24",
90+
"eor r0, r22", // r0.7 is sign of result
91+
"sbrc r24, 7",
92+
"neg r24", // dividend negative : negate
93+
"sbrc r22, 7",
94+
"neg r22", // divisor negative : negate
95+
// TODO: "call" => instruction requires a CPU feature not currently enabled
96+
// TODO: gcc checks for __AVR_HAVE_JMP_CALL__
97+
"rcall __udivmodqi4", // do the unsigned div/mod
98+
"brtc 1f",
99+
"neg r25", // correct remainder sign
100+
"1:",
101+
"sbrc r0, 7",
102+
"neg r24", // correct result sign
103+
"ret",
104+
);
105+
}
106+
107+
#[naked]
108+
pub unsafe extern "C" fn __udivmodhi4() {
109+
// Returns unsigned 16-bit `n / d` and `n % d`.
110+
//
111+
// Note: GCC implements a [non-standard calling convention](https://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention) for this function.
112+
// Derived from: https://github.com/gcc-mirror/gcc/blob/95f10974a9190e345776604480a2df0191104308/libgcc/config/avr/lib1funcs.S#L1427
113+
114+
// r26: remainder (low)
115+
// r27: remainder (high)
116+
// r24: dividend (low)
117+
// r25: dividend (high)
118+
// r22: divisor (low)
119+
// r23: divisor (high)
120+
// r21: loop counter
121+
core::arch::naked_asm!(
122+
"sub r26, r26",
123+
"sub r27, r27", // clear remainder and carry
124+
"ldi r21, 17", // init loop counter
125+
"rjmp 2f", // jump to entry point
126+
"1:",
127+
"rol r26", // shift dividend into remainder
128+
"rol r27",
129+
"cp r26, r22", // compare remainder & divisor
130+
"cpc r27, r23",
131+
"brcs 2f", // remainder < divisor
132+
"sub r26, r22", // restore remainder
133+
"sbc r27, r23",
134+
"2:",
135+
"rol r24", // shift dividend (with CARRY)
136+
"rol r25",
137+
"dec r21", // decrement loop counter
138+
"brne 1b",
139+
"com r24",
140+
"com r25",
141+
// TODO: "movw" => instruction requires a CPU feature not currently enabled
142+
// TODO: gcc checks for __AVR_HAVE_MOVW__
143+
"mov r22, r24",
144+
"mov r23, r25",
145+
"mov r24, r26",
146+
"mov r25, r27",
147+
"ret",
148+
);
149+
}
150+
151+
#[naked]
152+
pub unsafe extern "C" fn __divmodhi4() {
153+
// Returns signed 16-bit `n / d` and `n % d`.
154+
//
155+
// Note: GCC implements a [non-standard calling convention](https://gcc.gnu.org/wiki/avr-gcc#Exceptions_to_the_Calling_Convention) for this function.
156+
// Derived from: https://github.com/gcc-mirror/gcc/blob/95f10974a9190e345776604480a2df0191104308/libgcc/config/avr/lib1funcs.S#L1457
157+
158+
// r26: remainder (low)
159+
// r27: remainder (high)
160+
// r24: dividend (low)
161+
// r25: dividend (high)
162+
// r22: divisor (low)
163+
// r23: divisor (high)
164+
// r21: loop counter
165+
core::arch::naked_asm!(
166+
"bst r25, 7", // store sign of dividend
167+
"mov r0, r23",
168+
"brtc 0f",
169+
"com r0", // r0.7 is sign of result
170+
"rcall 1f", // dividend negative : negate
171+
"0:",
172+
"sbrc r23, 7",
173+
"rcall 2f", // divisor negative : negate
174+
// TODO: "call" => instruction requires a CPU feature not currently enabled
175+
// TODO: gcc checks for __AVR_HAVE_JMP_CALL__
176+
"rcall __udivmodhi4", // do the unsigned div/mod
177+
"sbrc r0, 7",
178+
"rcall 2f", // correct remainder sign
179+
"brtc 3f",
180+
"1:",
181+
"com r25", // correct dividend/remainder sign
182+
"neg r24",
183+
"sbci r25, 0xFF",
184+
"ret",
185+
"2:",
186+
"com r23", // correct divisor/result sign
187+
"neg r22",
188+
"sbci r23, 0xFF",
189+
"3:",
190+
"ret",
191+
);
192+
}
46193
}
47194

48195
intrinsics! {

0 commit comments

Comments
 (0)