Skip to content

Commit 892b179

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 892b179

File tree

1 file changed

+138
-0
lines changed

1 file changed

+138
-0
lines changed

compiler-builtins/src/int/udiv.rs

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

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

48186
intrinsics! {

0 commit comments

Comments
 (0)