ppc.pl revision 9dc0df1bac950d6e491f9a7c7e4888f2b301cb15
#
# Implemented as a Perl wrapper as we want to support several different
# architectures with single file. We pick up the target based on the
# file name we are asked to generate.
#
# It should be noted though that this perl code is nothing like
# as pre-processor to cover for platform differences in name decoration,
# linker tables, 32-/64-bit instruction sets...
#
# As you might know there're several PowerPC ABI in use. Most notably
# Linux and AIX use different 32-bit ABIs. Good news are that these ABIs
# are similar enough to implement leaf(!) functions, which would be ABI
# neutral. And that's what you find here: ABI neutral leaf functions.
# In case you wonder what that is...
#
# AIX performance
#
# MEASUREMENTS WITH cc ON a 200 MhZ PowerPC 604e.
#
# The following is the performance of 32-bit compiler
# generated code:
#
# OpenSSL 0.9.6c 21 dec 2001
# built on: Tue Jun 11 11:06:51 EDT 2002
# options:bn(64,32) ...
#compiler: cc -DTHREADS -DAIX -DB_ENDIAN -DBN_LLONG -O3
# sign verify sign/s verify/s
#rsa 512 bits 0.0098s 0.0009s 102.0 1170.6
#rsa 1024 bits 0.0507s 0.0026s 19.7 387.5
#rsa 2048 bits 0.3036s 0.0085s 3.3 117.1
#rsa 4096 bits 2.0040s 0.0299s 0.5 33.4
#dsa 512 bits 0.0087s 0.0106s 114.3 94.5
#dsa 1024 bits 0.0256s 0.0313s 39.0 32.0
#
# Same bechmark with this assembler code:
#
#rsa 512 bits 0.0056s 0.0005s 178.6 2049.2
#rsa 1024 bits 0.0283s 0.0015s 35.3 674.1
#rsa 2048 bits 0.1744s 0.0050s 5.7 201.2
#rsa 4096 bits 1.1644s 0.0179s 0.9 55.7
#dsa 512 bits 0.0052s 0.0062s 191.6 162.0
#dsa 1024 bits 0.0149s 0.0180s 67.0 55.5
#
# Number of operations increases by at almost 75%
#
# Here are performance numbers for 64-bit compiler
# generated code:
#
# OpenSSL 0.9.6g [engine] 9 Aug 2002
# built on: Fri Apr 18 16:59:20 EDT 2003
# options:bn(64,64) ...
# compiler: cc -DTHREADS -D_REENTRANT -q64 -DB_ENDIAN -O3
# sign verify sign/s verify/s
#rsa 512 bits 0.0028s 0.0003s 357.1 3844.4
#rsa 1024 bits 0.0148s 0.0008s 67.5 1239.7
#rsa 2048 bits 0.0963s 0.0028s 10.4 353.0
#rsa 4096 bits 0.6538s 0.0102s 1.5 98.1
#dsa 512 bits 0.0026s 0.0032s 382.5 313.7
#dsa 1024 bits 0.0081s 0.0099s 122.8 100.6
#
# Same benchmark with this assembler code:
#
#rsa 512 bits 0.0020s 0.0002s 510.4 6273.7
#rsa 1024 bits 0.0088s 0.0005s 114.1 2128.3
#rsa 2048 bits 0.0540s 0.0016s 18.5 622.5
#rsa 4096 bits 0.3700s 0.0058s 2.7 171.0
#dsa 512 bits 0.0016s 0.0020s 610.7 507.1
#dsa 1024 bits 0.0047s 0.0058s 212.5 173.2
#
# Again, performance increases by at about 75%
#
# Mac OS X, Apple G5 1.8GHz (Note this is 32 bit code)
# OpenSSL 0.9.7c 30 Sep 2003
#
# Original code.
#
#rsa 512 bits 0.0011s 0.0001s 906.1 11012.5
#rsa 1024 bits 0.0060s 0.0003s 166.6 3363.1
#rsa 2048 bits 0.0370s 0.0010s 27.1 982.4
#rsa 4096 bits 0.2426s 0.0036s 4.1 280.4
#dsa 512 bits 0.0010s 0.0012s 1038.1 841.5
#dsa 1024 bits 0.0030s 0.0037s 329.6 269.7
#dsa 2048 bits 0.0101s 0.0127s 98.9 78.6
#
# Same benchmark with this assembler code:
#
#rsa 512 bits 0.0007s 0.0001s 1416.2 16645.9
#rsa 1024 bits 0.0036s 0.0002s 274.4 5380.6
#rsa 2048 bits 0.0222s 0.0006s 45.1 1589.5
#rsa 4096 bits 0.1469s 0.0022s 6.8 449.6
#dsa 512 bits 0.0006s 0.0007s 1664.2 1376.2
#dsa 1024 bits 0.0018s 0.0023s 545.0 442.2
#dsa 2048 bits 0.0061s 0.0075s 163.5 132.8
#
# Performance increase of ~60%
#
# If you have comments or suggestions to improve code send
# me a note at schari@us.ibm.com
#
$opf = shift;
if ($opf =~ /32\.s/) {
$BITS= 32;
$ISA= "\"ppc\"";
} elsif ($opf =~ /64\.s/) {
$BITS= 64;
$ISA= "\"ppc64\"";
# same as above, but 64-bit mnemonics...
} else { die "nonsense $opf"; }
# function entry points from the AIX code
#
# There are other, more elegant, ways to handle this. We (IBM) chose
# this approach as it plays well with scripts we run to 'namespace'
# OpenSSL .i.e. we add a prefix to all the public symbols so we can
# co-exist in the same process with other implementations of OpenSSL.
# 'cleverer' ways of doing these substitutions tend to hide data we
# need to be obvious.
#
my @items = ("bn_sqr_comba4",
"bn_sqr_comba8",
"bn_mul_comba4",
"bn_mul_comba8",
"bn_sub_words",
"bn_add_words",
"bn_div_words",
"bn_sqr_words",
"bn_mul_words",
"bn_mul_add_words");
else { do_bsd(); }
sub do_linux {
$d=&data();
if ($BITS==64) {
foreach $t (@items) {
$d =~ s/\.$t:/\
\t.globl\t$t\
$t:\
\t.size\t$t,24\
\t.previous\n\
\t.globl\t.$t\
.$t:/g;
}
}
else {
foreach $t (@items) {
$d=~s/\.$t/$t/g;
}
}
# hide internal labels to avoid pollution of name table...
print $d;
}
sub do_aix {
# AIX assembler is smart enough to please the linker without
# making us do something special...
print &data();
}
# MacOSX 32 bit
sub do_osx {
$d=&data();
# Change the bn symbol prefix from '.' to '_'
foreach $t (@items) {
$d=~s/\.$t/_$t/g;
}
# Change .machine to something OS X asm will accept
$d=~s/\#/;/g; # change comment from '#' to ';'
print $d;
}
# BSD (Untested)
sub do_bsd {
$d=&data();
foreach $t (@items) {
$d=~s/\.$t/_$t/g;
}
print $d;
}
sub data {
#--------------------------------------------------------------------
#
#
#
#
# File: ppc32.s
#
# Created by: Suresh Chari
# IBM Thomas J. Watson Research Library
# Hawthorne, NY
#
#
# Description: Optimized assembly routines for OpenSSL crypto
# on the 32 bitPowerPC platform.
#
#
# Version History
#
# 2. Fixed bn_add,bn_sub and bn_div_words, added comments,
# cleaned up code. Also made a single version which can
# be used for both the AIX and Linux compilers. See NOTE
# below.
# 12/05/03 Suresh Chari
# (with lots of help from) Andy Polyakov
##
# 1. Initial version 10/20/02 Suresh Chari
#
#
# The following file works for the xlc,cc
# and gcc compilers.
#
# NOTE: To get the file to link correctly with the gcc compiler
# you have to change the names of the routines and remove
# the first .(dot) character. This should automatically
# be done in the build process.
#
# Hand optimized assembly code for the following routines
#
# bn_sqr_comba4
# bn_sqr_comba8
# bn_mul_comba4
# bn_mul_comba8
# bn_sub_words
# bn_add_words
# bn_div_words
# bn_sqr_words
# bn_mul_words
# bn_mul_add_words
#
# NOTE: It is possible to optimize this code more for
# specific PowerPC or Power architectures. On the Northstar
# architecture the optimizations in this file do
# NOT provide much improvement.
#
# If you have comments or suggestions to improve code send
# me a note at schari\@us.ibm.com
#
#--------------------------------------------------------------------------
#
# Defines to be used in the assembly code.
#
# Declare function names to be global
# NOTE: For gcc these names MUST be changed to remove
# the first . i.e. for example change ".bn_sqr_comba4"
# to "bn_sqr_comba4". This should be automatically done
# in the build.
# .text section
#
# NOTE: The following label name should be changed to
# "bn_sqr_comba4" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# Optimized version of bn_sqr_comba4.
#
# void bn_sqr_comba4(BN_ULONG *r, BN_ULONG *a)
# r3 contains r
# r4 contains a
#
# Freely use registers r5,r6,r7,r8,r9,r10,r11 as follows:
#
# r5,r6 are the two BN_ULONGs being multiplied.
# r7,r8 are the results of the 32x32 giving 64 bit multiply.
# r9,r10, r11 are the equivalents of c1,c2, c3.
# Here's the assembly
#
#
# instructions below
#sqr_add_c(a,0,c1,c2,c3)
#to add since c1=c2=c3=0.
# Note c3(r11) is NOT set to 0
# but will be.
# sqr_add_c2(a,1,0,c2,c3,c1);
# r9= r0(=0) and carry
#sqr_add_c(a,1,c3,c1,c2)
#sqr_add_c2(a,2,0,c3,c1,c2)
#sqr_add_c2(a,3,0,c1,c2,c3);
#sqr_add_c2(a,2,1,c1,c2,c3);
#sqr_add_c(a,2,c2,c3,c1);
#sqr_add_c2(a,3,1,c2,c3,c1);
#sqr_add_c2(a,3,2,c3,c1,c2);
#sqr_add_c(a,3,c1,c2,c3);
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_sqr_comba8" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# This is an optimized version of the bn_sqr_comba8 routine.
# Tightly uses the adde instruction
#
#
# void bn_sqr_comba8(BN_ULONG *r, BN_ULONG *a)
# r3 contains r
# r4 contains a
#
# Freely use registers r5,r6,r7,r8,r9,r10,r11 as follows:
#
# r5,r6 are the two BN_ULONGs being multiplied.
# r7,r8 are the results of the 32x32 giving 64 bit multiply.
# r9,r10, r11 are the equivalents of c1,c2, c3.
#
# Possible optimization of loading all 8 longs of a into registers
# doesnt provide any speedup
#
#instructions below.
#sqr_add_c(a,0,c1,c2,c3);
#sqr_add_c2(a,1,0,c2,c3,c1);
#sqr_add_c(a,1,c3,c1,c2);
#sqr_add_c2(a,2,0,c3,c1,c2);
#sqr_add_c2(a,3,0,c1,c2,c3);
#sqr_add_c2(a,2,1,c1,c2,c3);
#sqr_add_c(a,2,c2,c3,c1);
#sqr_add_c2(a,3,1,c2,c3,c1);
#sqr_add_c2(a,4,0,c2,c3,c1);
#sqr_add_c2(a,5,0,c3,c1,c2);
#sqr_add_c2(a,4,1,c3,c1,c2);
#sqr_add_c2(a,3,2,c3,c1,c2);
#sqr_add_c(a,3,c1,c2,c3);
#sqr_add_c2(a,4,2,c1,c2,c3);
#sqr_add_c2(a,5,1,c1,c2,c3);
#sqr_add_c2(a,6,0,c1,c2,c3);
#sqr_add_c2(a,7,0,c2,c3,c1);
#sqr_add_c2(a,6,1,c2,c3,c1);
#sqr_add_c2(a,5,2,c2,c3,c1);
#sqr_add_c2(a,4,3,c2,c3,c1);
#sqr_add_c(a,4,c3,c1,c2);
#sqr_add_c2(a,5,3,c3,c1,c2);
#sqr_add_c2(a,6,2,c3,c1,c2);
#sqr_add_c2(a,7,1,c3,c1,c2);
#sqr_add_c2(a,7,2,c1,c2,c3);
#sqr_add_c2(a,6,3,c1,c2,c3);
#sqr_add_c2(a,5,4,c1,c2,c3);
#sqr_add_c(a,5,c2,c3,c1);
#sqr_add_c2(a,6,4,c2,c3,c1);
#sqr_add_c2(a,7,3,c2,c3,c1);
#sqr_add_c2(a,7,4,c3,c1,c2);
#sqr_add_c2(a,6,5,c3,c1,c2);
#sqr_add_c(a,6,c1,c2,c3);
#sqr_add_c2(a,7,5,c1,c2,c3)
#sqr_add_c2(a,7,6,c2,c3,c1)
#sqr_add_c(a,7,c3,c1,c2);
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_mul_comba4" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# This is an optimized version of the bn_mul_comba4 routine.
#
# void bn_mul_comba4(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b)
# r3 contains r
# r4 contains a
# r5 contains b
# r6, r7 are the 2 BN_ULONGs being multiplied.
# r8, r9 are the results of the 32x32 giving 64 multiply.
# r10, r11, r12 are the equivalents of c1, c2, and c3.
#
#mul_add_c(a[0],b[0],c1,c2,c3);
#mul_add_c(a[0],b[1],c2,c3,c1);
#mul_add_c(a[1],b[0],c2,c3,c1);
#mul_add_c(a[2],b[0],c3,c1,c2);
#mul_add_c(a[1],b[1],c3,c1,c2);
#mul_add_c(a[0],b[2],c3,c1,c2);
#mul_add_c(a[0],b[3],c1,c2,c3);
#mul_add_c(a[1],b[2],c1,c2,c3);
#mul_add_c(a[2],b[1],c1,c2,c3);
#mul_add_c(a[3],b[0],c1,c2,c3);
#mul_add_c(a[3],b[1],c2,c3,c1);
#mul_add_c(a[2],b[2],c2,c3,c1);
#mul_add_c(a[1],b[3],c2,c3,c1);
#mul_add_c(a[2],b[3],c3,c1,c2);
#mul_add_c(a[3],b[2],c3,c1,c2);
#mul_add_c(a[3],b[3],c1,c2,c3);
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_mul_comba8" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# Optimized version of the bn_mul_comba8 routine.
#
# void bn_mul_comba8(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b)
# r3 contains r
# r4 contains a
# r5 contains b
# r6, r7 are the 2 BN_ULONGs being multiplied.
# r8, r9 are the results of the 32x32 giving 64 multiply.
# r10, r11, r12 are the equivalents of c1, c2, and c3.
#
#mul_add_c(a[0],b[0],c1,c2,c3);
#mul_add_c(a[0],b[1],c2,c3,c1);
#mul_add_c(a[1],b[0],c2,c3,c1);
#mul_add_c(a[2],b[0],c3,c1,c2);
#mul_add_c(a[1],b[1],c3,c1,c2);
#mul_add_c(a[0],b[2],c3,c1,c2);
#mul_add_c(a[0],b[3],c1,c2,c3);
#mul_add_c(a[1],b[2],c1,c2,c3);
#mul_add_c(a[2],b[1],c1,c2,c3);
#mul_add_c(a[3],b[0],c1,c2,c3);
#mul_add_c(a[4],b[0],c2,c3,c1);
#mul_add_c(a[3],b[1],c2,c3,c1);
#mul_add_c(a[2],b[2],c2,c3,c1);
#mul_add_c(a[1],b[3],c2,c3,c1);
#mul_add_c(a[0],b[4],c2,c3,c1);
#mul_add_c(a[0],b[5],c3,c1,c2);
#mul_add_c(a[1],b[4],c3,c1,c2);
#mul_add_c(a[2],b[3],c3,c1,c2);
#mul_add_c(a[3],b[2],c3,c1,c2);
#mul_add_c(a[4],b[1],c3,c1,c2);
#mul_add_c(a[5],b[0],c3,c1,c2);
#mul_add_c(a[6],b[0],c1,c2,c3);
#mul_add_c(a[5],b[1],c1,c2,c3);
#mul_add_c(a[4],b[2],c1,c2,c3);
#mul_add_c(a[3],b[3],c1,c2,c3);
#mul_add_c(a[2],b[4],c1,c2,c3);
#mul_add_c(a[1],b[5],c1,c2,c3);
#mul_add_c(a[0],b[6],c1,c2,c3);
#mul_add_c(a[0],b[7],c2,c3,c1);
#mul_add_c(a[1],b[6],c2,c3,c1);
#mul_add_c(a[2],b[5],c2,c3,c1);
#mul_add_c(a[3],b[4],c2,c3,c1);
#mul_add_c(a[4],b[3],c2,c3,c1);
#mul_add_c(a[5],b[2],c2,c3,c1);
#mul_add_c(a[6],b[1],c2,c3,c1);
#mul_add_c(a[7],b[0],c2,c3,c1);
#mul_add_c(a[7],b[1],c3,c1,c2);
#mul_add_c(a[6],b[2],c3,c1,c2);
#mul_add_c(a[5],b[3],c3,c1,c2);
#mul_add_c(a[4],b[4],c3,c1,c2);
#mul_add_c(a[3],b[5],c3,c1,c2);
#mul_add_c(a[2],b[6],c3,c1,c2);
#mul_add_c(a[1],b[7],c3,c1,c2);
#mul_add_c(a[2],b[7],c1,c2,c3);
#mul_add_c(a[3],b[6],c1,c2,c3);
#mul_add_c(a[4],b[5],c1,c2,c3);
#mul_add_c(a[5],b[4],c1,c2,c3);
#mul_add_c(a[6],b[3],c1,c2,c3);
#mul_add_c(a[7],b[2],c1,c2,c3);
#mul_add_c(a[7],b[3],c2,c3,c1);
#mul_add_c(a[6],b[4],c2,c3,c1);
#mul_add_c(a[5],b[5],c2,c3,c1);
#mul_add_c(a[4],b[6],c2,c3,c1);
#mul_add_c(a[3],b[7],c2,c3,c1);
#mul_add_c(a[4],b[7],c3,c1,c2);
#mul_add_c(a[5],b[6],c3,c1,c2);
#mul_add_c(a[6],b[5],c3,c1,c2);
#mul_add_c(a[7],b[4],c3,c1,c2);
#mul_add_c(a[7],b[5],c1,c2,c3);
#mul_add_c(a[6],b[6],c1,c2,c3);
#mul_add_c(a[5],b[7],c1,c2,c3);
#mul_add_c(a[6],b[7],c2,c3,c1);
#mul_add_c(a[7],b[6],c2,c3,c1);
#mul_add_c(a[7],b[7],c3,c1,c2);
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_sub_words" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
#
.align 4
#
# Handcoded version of bn_sub_words
#
#BN_ULONG bn_sub_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
#
# r3 = r
# r4 = a
# r5 = b
# r6 = n
#
# Note: No loop unrolling done since this is not a performance
# critical loop.
#
# check for r6 = 0 AND set carry bit.
#
# if r6 > 0 then result !=0
# In either case carry bit is set.
# if carry = 1 this is r7-r8. Else it
# is r7-r8 -1 as we need.
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_add_words" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# Handcoded version of bn_add_words
#
#BN_ULONG bn_add_words(BN_ULONG *r, BN_ULONG *a, BN_ULONG *b, int n)
#
# r3 = r
# r4 = a
# r5 = b
# r6 = n
#
# Note: No loop unrolling done since this is not a performance
# critical loop.
#
# check for r6 = 0. Is this needed?
#
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_div_words" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# This is a cleaned up version of code generated by
# the AIX compiler. The only optimization is to use
# the PPC instruction to count leading zeros instead
# of call to num_bits_word. Since this was compiled
# only at level -O2 we can possibly squeeze it more?
#
# r3 = h
# r4 = l
# r5 = d
Lppcasm_div3: #r7 = BN_BITS2-i. so r7=i
# dl will be computed when needed
# as it saves registers.
# compute here for innerloop.
# now want to compute
# r7 = (t<<BN_BITS4)|((l&BN_MASK2h)>>BN_BITS4)
# the following 2 instructions do that
# want to compute
# h = ((h<<BN_BITS4)|(l>>BN_BITS4))&BN_MASK2
# the following 2 instructions will do this.
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_sqr_words" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# Optimized version of bn_sqr_words
#
# void bn_sqr_words(BN_ULONG *r, BN_ULONG *a, int n)
#
# r3 = r
# r4 = a
# r5 = n
#
# r6 = a[i].
# r7,r8 = product.
#
# No unrolling done here. Not performance critical.
#sqr(r[0],r[1],a[0]);
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_mul_words" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# BN_ULONG bn_mul_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w)
#
# r3 = rp
# r4 = ap
# r5 = num
# r6 = w
#mul(rp[0],ap[0],w,c1);
#addze r10,r10 #carry is NOT ignored.
#will be taken care of
#in second spin below
#using adde.
#mul(rp[1],ap[1],w,c1);
#addze r12,r12
#mul(rp[2],ap[2],w,c1);
#addze r10,r10
#mul_add(rp[3],ap[3],w,c1);
#r12
#mul(rp[0],ap[0],w,c1);
#mul(rp[1],ap[1],w,c1);
#mul_add(rp[2],ap[2],w,c1);
.long 0x00000000
#
# NOTE: The following label name should be changed to
# "bn_mul_add_words" i.e. remove the first dot
# for the gcc compiler. This should be automatically
# done in the build
#
.align 4
#
# BN_ULONG bn_mul_add_words(BN_ULONG *rp, BN_ULONG *ap, int num, BN_ULONG w)
#
# r3 = rp
# r4 = ap
# r5 = num
# r6 = w
#
# empirical evidence suggests that unrolled version performs best!!
#
#mul_add(rp[0],ap[0],w,c1);
#addze r10,r10
#the above instruction addze
#is NOT needed. Carry will NOT
#be ignored. It's not affected
#by multiply and will be collected
#in the next spin
#mul_add(rp[1],ap[1],w,c1);
#addze r12,r12
#mul_add(rp[2],ap[2],w,c1);
#addze r10,r10
#mul_add(rp[3],ap[3],w,c1);
#mul_add(rp[0],ap[0],w,c1);
#mul_add(rp[1],ap[1],w,c1);
#mul_add(rp[2],ap[2],w,c1);
.long 0x00000000
.align 4
# if some assembler chokes on some simplified mnemonic,
# this is the spot to fix it up, e.g.:
# GNU as doesn't seem to accept cmplw, 32-bit unsigned compare
# assembler X doesn't accept li, load immediate value
#$data =~ s/^(\s*)li(\s+)([^,]+),(.*)/$1addi$2$3,0,$4/gm;
return($data);
}