1#!/usr/bin/env perl
2#
3# ====================================================================
4# Written by Andy Polyakov, @dot-asm, initially for use with OpenSSL.
5# ====================================================================
6#
7# ChaCha20 for Itanium.
8#
9# March 2019
10#
11# Itanium 9xxx, which has pair of shifters, manages to process one byte
12# in 9.3 cycles. This aligns perfectly with theoretical estimate.
13# On the other hand, pre-9000 CPU has single shifter and each extr/dep
14# pairs below takes additional cycle. Then final input->xor->output
15# pass runs slower than expected... Overall result is 15.6 cpb, two
16# cycles more than theoretical estimate.
17
18$output = pop and open STDOUT, ">$output";
19
20my @k = map("r$_",(16..31));
21my @x = map("r$_",(38..53));
22my @y = map("r$_",(8..11));
23my @z = map("r$_",(15,35..37));
24my ($out,$inp,$len,$key,$counter) = map("r$_",(32..36));
25
26$code.=<<___;
27#if defined(_HPUX_SOURCE)
28# if !defined(_LP64)
29#  define ADDP  addp4
30# else
31#  define ADDP  add
32# endif
33#else
34# define ADDP   add
35#endif
36
37.text
38
39.global	ChaCha20_ctr32#
40.proc	ChaCha20_ctr32#
41.align	32
42ChaCha20_ctr32:
43	.prologue
44	.save		ar.pfs,r2
45{ .mmi;	alloc		r2=ar.pfs,5,17,0,0
46	ADDP		@k[11]=4,$key
47	.save		ar.lc,r3
48	mov		r3=ar.lc		}
49{ .mmi;	ADDP		$out=0,$out
50	ADDP		$inp=0,$inp		}
51{ .mmi;	ADDP		$key=0,$key
52	ADDP		$counter=0,$counter
53	.save		pr,r14
54	mov		r14=pr			};;
55
56	.body
57{ .mlx;	ld4		@k[4]=[$key],8
58	movl		@k[0]=0x61707865	}
59{ .mlx;	ld4		@k[5]=[@k[11]],8
60	movl		@k[1]=0x3320646e	};;
61{ .mlx;	ld4		@k[6]=[$key],8
62	movl		@k[2]=0x79622d32	}
63{ .mlx;	ld4		@k[7]=[@k[11]],8
64	movl		@k[3]=0x6b206574	};;
65{ .mmi;	ld4		@k[8]=[$key],8
66	ld4		@k[9]=[@k[11]],8
67	add		@k[15]=4,$counter	};;
68{ .mmi;	ld4		@k[10]=[$key]
69	ld4		@k[11]=[@k[11]]
70	mov		@x[0]=@k[0]		};;
71{ .mmi;	ld4		@k[12]=[$counter],8
72	ld4		@k[13]=[@k[15]],8
73	mov		@x[1]=@k[1]		};;
74{ .mmi;	ld4		@k[14]=[$counter]
75	ld4		@k[15]=[@k[15]]
76	mov		@x[2]=@k[2]		}
77{ .mmi;	mov		@x[3]=@k[3]
78	mov		@x[4]=@k[4]
79	mov		@x[5]=@k[5]		};;
80{ .mmi;	mov		@x[6]=@k[6]
81	mov		@x[7]=@k[7]
82	mov		@x[8]=@k[8]		}
83{ .mmi;	mov		@x[9]=@k[9]
84	mov		@x[10]=@k[10]
85	mov		@x[11]=@k[11]		}
86{ .mmi;	mov		@x[12]=@k[12]
87	mov		@x[13]=@k[13]
88	mov		@x[14]=@k[14]		};;
89
90.Loop_outer:
91{ .mii;	mov		@x[15]=@k[15]
92	mov		ar.lc=9
93	mov		ar.ec=1			}
94{ .mmb;	cmp.geu		p6,p0=64,$len
95	sub		@z[1]=64,$len
96	brp.loop.imp	.Loop_top,.Loop_end-16	};;
97
98.Loop_top:
99___
100sub ROUND {
101my ($a0,$b0,$c0,$d0)=@_;
102my ($a1,$b1,$c1,$d1)=map(($_&~3)+(($_+1)&3),($a0,$b0,$c0,$d0));
103my ($a2,$b2,$c2,$d2)=map(($_&~3)+(($_+1)&3),($a1,$b1,$c1,$d1));
104my ($a3,$b3,$c3,$d3)=map(($_&~3)+(($_+1)&3),($a2,$b2,$c2,$d2));
105
106$code.=<<___;
107{ .mmi;	add		@x[$a0]=@x[$a0],@x[$b0]
108	add		@x[$a1]=@x[$a1],@x[$b1]
109	add		@x[$a2]=@x[$a2],@x[$b2]		};;
110{ .mmi;	add		@x[$a3]=@x[$a3],@x[$b3]
111	xor		@x[$d0]=@x[$d0],@x[$a0]
112	xor		@x[$d1]=@x[$d1],@x[$a1]		};;
113{ .mmi;	xor		@x[$d2]=@x[$d2],@x[$a2]
114	xor		@x[$d3]=@x[$d3],@x[$a3]
115	extr.u		@y[0]=@x[$d0],16,16		};;
116{ .mii;	extr.u		@y[1]=@x[$d1],16,16
117	dep		@x[$d0]=@x[$d0],@y[0],16,16	};;
118{ .mii;	 add		@x[$c0]=@x[$c0],@x[$d0]
119	extr.u		@y[2]=@x[$d2],16,16
120	dep		@x[$d1]=@x[$d1],@y[1],16,16	};;
121{ .mii;	 add		@x[$c1]=@x[$c1],@x[$d1]
122	 xor		@x[$b0]=@x[$b0],@x[$c0]
123	extr.u		@y[3]=@x[$d3],16,16		};;
124{ .mii;	 xor		@x[$b1]=@x[$b1],@x[$c1]
125	dep		@x[$d2]=@x[$d2],@y[2],16,16
126	dep		@x[$d3]=@x[$d3],@y[3],16,16	};;
127{ .mmi;	 add		@x[$c2]=@x[$c2],@x[$d2]
128	 add		@x[$c3]=@x[$c3],@x[$d3]
129	 extr.u		@y[0]=@x[$b0],20,12		};;
130{ .mmi;	 xor		@x[$b2]=@x[$b2],@x[$c2]
131	 xor		@x[$b3]=@x[$b3],@x[$c3]
132	 dep.z		@x[$b0]=@x[$b0],12,20		};;
133{ .mii;	 or		@x[$b0]=@x[$b0],@y[0]
134	 extr.u		@y[1]=@x[$b1],20,12
135	 dep.z		@x[$b1]=@x[$b1],12,20		};;
136{ .mii;	add		@x[$a0]=@x[$a0],@x[$b0]
137	 extr.u		@y[2]=@x[$b2],20,12
138	 extr.u		@y[3]=@x[$b3],20,12		}
139{ .mii;	 or		@x[$b1]=@x[$b1],@y[1]
140	 dep.z		@x[$b2]=@x[$b2],12,20
141	 dep.z		@x[$b3]=@x[$b3],12,20		};;
142{ .mmi;	 or		@x[$b2]=@x[$b2],@y[2]
143	 or		@x[$b3]=@x[$b3],@y[3]
144	add		@x[$a1]=@x[$a1],@x[$b1]		};;
145{ .mmi;	add		@x[$a2]=@x[$a2],@x[$b2]
146	add		@x[$a3]=@x[$a3],@x[$b3]
147	xor		@x[$d0]=@x[$d0],@x[$a0]		};;
148{ .mii;	xor		@x[$d1]=@x[$d1],@x[$a1]
149	extr.u		@y[0]=@x[$d0],24,8
150	dep.z		@x[$d0]=@x[$d0],8,24		};;
151{ .mii;	or		@x[$d0]=@x[$d0],@y[0]
152	extr.u		@y[1]=@x[$d1],24,8
153	dep.z		@x[$d1]=@x[$d1],8,24		};;
154{ .mmi;	or		@x[$d1]=@x[$d1],@y[1]
155	xor		@x[$d2]=@x[$d2],@x[$a2]
156	xor		@x[$d3]=@x[$d3],@x[$a3]		};;
157{ .mii;	 add		@x[$c0]=@x[$c0],@x[$d0]
158	extr.u		@y[2]=@x[$d2],24,8
159	dep.z		@x[$d2]=@x[$d2],8,24		};;
160{ .mii;	 xor		@x[$b0]=@x[$b0],@x[$c0]
161	extr.u		@y[3]=@x[$d3],24,8
162	dep.z		@x[$d3]=@x[$d3],8,24		};;
163{ .mmi;	or		@x[$d2]=@x[$d2],@y[2]
164	or		@x[$d3]=@x[$d3],@y[3]
165	 extr.u		@y[0]=@x[$b0],25,7		};;
166{ .mmi;	 add		@x[$c1]=@x[$c1],@x[$d1]
167	 add		@x[$c2]=@x[$c2],@x[$d2]
168	 dep.z		@x[$b0]=@x[$b0],7,25		};;
169{ .mmi;	 xor		@x[$b1]=@x[$b1],@x[$c1]
170	 xor		@x[$b2]=@x[$b2],@x[$c2]
171	 add		@x[$c3]=@x[$c3],@x[$d3]		};;
172{ .mii;	 xor		@x[$b3]=@x[$b3],@x[$c3]
173	 extr.u		@y[1]=@x[$b1],25,7
174	 dep.z		@x[$b1]=@x[$b1],7,25		};;
175{ .mii;	 or		@x[$b0]=@x[$b0],@y[0]
176	 extr.u		@y[2]=@x[$b2],25,7
177	 dep.z		@x[$b2]=@x[$b2],7,25		};;
178{ .mii;	 or		@x[$b1]=@x[$b1],@y[1]
179	 extr.u		@y[3]=@x[$b3],25,7
180	 dep.z		@x[$b3]=@x[$b3],7,25		};;
181___
182$code.=<<___		if ($d0 == 12);
183{ .mmi;	 or		@x[$b2]=@x[$b2],@y[2]
184	 or		@x[$b3]=@x[$b3],@y[3]
185	mov		@z[0]=-1			};;
186___
187$code.=<<___		if ($d0 == 15);
188{ .mmb;	 or		@x[$b2]=@x[$b2],@y[2]
189	 or		@x[$b3]=@x[$b3],@y[3]
190	br.ctop.sptk	.Loop_top			};;
191___
192}
193	&ROUND(0, 4, 8, 12);
194	&ROUND(0, 5, 10, 15);
195$code.=<<___;
196.Loop_end:
197
198{ .mmi;	add		@x[0]=@x[0],@k[0]
199	add		@x[1]=@x[1],@k[1]
200(p6)	shr.u		@z[0]=@z[0],@z[1]		}
201{ .mmb;	add		@x[2]=@x[2],@k[2]
202	add		@x[3]=@x[3],@k[3]
203	clrrrb.pr					};;
204{ .mmi;	add		@x[4]=@x[4],@k[4]
205	add		@x[5]=@x[5],@k[5]
206	add		@x[6]=@x[6],@k[6]		}
207{ .mmi;	add		@x[7]=@x[7],@k[7]
208	add		@x[8]=@x[8],@k[8]
209	add		@x[9]=@x[9],@k[9]		}
210{ .mmi;	add		@x[10]=@x[10],@k[10]
211	add		@x[11]=@x[11],@k[11]
212	add		@x[12]=@x[12],@k[12]		}
213{ .mmi;	add		@x[13]=@x[13],@k[13]
214	add		@x[14]=@x[14],@k[14]
215	add		@x[15]=@x[15],@k[15]		}
216{ .mmi;	add		@k[12]=1,@k[12]			// next counter
217	mov		pr=@z[0],0x1ffff		};;
218
219//////////////////////////////////////////////////////////////////
220// Each predicate bit corresponds to byte to be processed. Note
221// that p0 is wired to 1, but it works out, because there always
222// is at least one byte to process...
223{ .mmi;	(p0)	ld1		@z[0]=[$inp],1
224		shr.u		@y[1]=@x[0],8		};;
225{ .mmi;	(p1)	ld1		@z[1]=[$inp],1
226	(p2)	shr.u		@y[2]=@x[0],16		};;
227{ .mmi;	(p2)	ld1		@z[2]=[$inp],1
228	(p0)	xor		@z[0]=@z[0],@x[0]
229	(p3)	shr.u		@y[3]=@x[0],24		};;
230___
231for(my $i0=0; $i0<60; $i0+=4) {
232my ($i1, $i2, $i3, $i4, $i5, $i6, $i7) = map($i0+$_,(1..7));
233my $k = $i0/4+1;
234
235$code.=<<___;
236{ .mmi;	(p$i3)	ld1		@z[3]=[$inp],1
237	(p$i0)	st1		[$out]=@z[0],1
238	(p$i1)	xor		@z[1]=@z[1],@y[1]	};;
239{ .mmi;	(p$i4)	ld1		@z[0]=[$inp],1
240	(p$i5)	shr.u		@y[1]=@x[$k],8		}
241{ .mmi;	(p$i1)	st1		[$out]=@z[1],1
242	(p$i2)	xor		@z[2]=@z[2],@y[2]
243	(p1)	mov		@x[$k-1]=@k[$k-1]	};;
244{ .mfi;	(p$i5)	ld1		@z[1]=[$inp],1
245	(p$i6)	shr.u		@y[2]=@x[$k],16		}
246{ .mfi;	(p$i2)	st1		[$out]=@z[2],1
247	(p$i3)	xor		@z[3]=@z[3],@y[3]	};;
248{ .mfi;	(p$i6)	ld1		@z[2]=[$inp],1
249	(p$i7)	shr.u		@y[3]=@x[$k],24		}
250___
251$code.=<<___	if ($i0==0);	# p1,p2 are available for reuse in first round
252{ .mmi;	(p$i3)	st1		[$out]=@z[3],1
253	(p$i4)	xor		@z[0]=@z[0],@x[$k]
254		cmp.ltu		p1,p2=64,$len		};;
255___
256$code.=<<___	if ($i0>0);
257{ .mfi;	(p$i3)	st1		[$out]=@z[3],1
258	(p$i4)	xor		@z[0]=@z[0],@x[$k]	};;
259___
260}
261$code.=<<___;
262{ .mmi;	(p63)	ld1		@z[3]=[$inp],1
263	(p60)	st1		[$out]=@z[0],1
264	(p61)	xor		@z[1]=@z[1],@y[1]	};;
265{ .mmi;	(p61)	st1		[$out]=@z[1],1
266	(p62)	xor		@z[2]=@z[2],@y[2]	};;
267{ .mmi;	(p62)	st1		[$out]=@z[2],1
268	(p63)	xor		@z[3]=@z[3],@y[3]
269	(p2)	mov		ar.lc=r3		};;
270{ .mib;	(p63)	st1		[$out]=@z[3],1
271	(p1)	add		$len=-64,$len
272(p1)	br.dptk.many		.Loop_outer		};;
273
274{ .mmi;	mov			@k[4]=0			// wipe key material
275	mov			@k[5]=0
276	mov			@k[6]=0			}
277{ .mmi;	mov			@k[7]=0
278	mov			@k[8]=0
279	mov			@k[9]=0			}
280{ .mmi;	mov			@k[10]=0
281	mov			@k[11]=0
282	mov			@k[12]=0		}
283{ .mmi;	mov			@k[13]=0
284	mov			@k[14]=0
285	mov			@k[15]=0		}
286{ .mib;	mov			pr=r14,0x1ffff
287	br.ret.sptk.many	b0			};;
288.endp	ChaCha20_ctr32#
289stringz "ChaCha20 for IA64, CRYPTOGAMS by \@dot-asm"
290___
291
292print $code;
293close STDOUT or die "error closing STDOUT: $!";
294