diff options
author | Andrey Ponomarenko <aponomarenko@rosalab.ru> | 2012-07-19 18:57:20 +0400 |
---|---|---|
committer | Andrey Ponomarenko <aponomarenko@rosalab.ru> | 2012-07-19 18:57:20 +0400 |
commit | f48ec93de57523ec5eef23a60d3b50c71c106983 (patch) | |
tree | 52d123e31e0d37fe479f8819cb0cd7e72d272732 /modules/Internals | |
parent | 01117f17f0064f91d93bfd2dafe8fcd36e636f33 (diff) | |
download | abi-compliance-checker-f48ec93de57523ec5eef23a60d3b50c71c106983.tar.gz |
ABI Compliance Checker 1.98.3
Diffstat (limited to 'modules/Internals')
-rw-r--r-- | modules/Internals/CallConv.pm | 1219 | ||||
-rw-r--r-- | modules/Internals/RegTests.pm | 168 | ||||
-rw-r--r-- | modules/Internals/XmlDump.pm | 6 |
3 files changed, 1369 insertions, 24 deletions
diff --git a/modules/Internals/CallConv.pm b/modules/Internals/CallConv.pm new file mode 100644 index 0000000..6a65d1b --- /dev/null +++ b/modules/Internals/CallConv.pm @@ -0,0 +1,1219 @@ +########################################################################### +# Module for ACC tool to create a model of calling conventions +# +# Copyright (C) 2009-2010 The Linux Foundation +# Copyright (C) 2009-2011 Institute for System Programming, RAS +# Copyright (C) 2011-2012 Nokia Corporation and/or its subsidiary(-ies) +# Copyright (C) 2011-2012 ROSA Laboratory +# +# Written by Andrey Ponomarenko +# +# PLATFORMS +# ========= +# Linux, FreeBSD and Mac OS X +# x86 - System V ABI Intel386 Architecture Processor Supplement +# x86_64 - System V ABI AMD64 Architecture Processor Supplement +# +# MS Windows +# x86 - MSDN Argument Passing and Naming Conventions +# x86_64 - MSDN x64 Software Conventions +# +# This program is free software: you can redistribute it and/or modify +# it under the terms of the GNU General Public License or the GNU Lesser +# General Public License as published by the Free Software Foundation. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# and the GNU Lesser General Public License along with this program. +# If not, see <http://www.gnu.org/licenses/>. +########################################################################### +use strict; + +my $BYTE = 8; + +my %UsedReg = (); +my %UsedStack = (); + +sub classifyType($$$$$) +{ + my ($Tid, $TInfo, $Arch, $System, $Word) = @_; + my %Type = get_PureType($Tid, $TInfo); + my %Classes = (); + if($Type{"Name"} eq "void") + { + $Classes{0}{"Class"} = "VOID"; + return %Classes; + } + if($System=~/\A(linux|macos|freebsd)\Z/) + { # GCC + if($Arch eq "x86") + { + if(isFloat($Type{"Name"})) { + $Classes{0}{"Class"} = "FLOAT"; + } + elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { + $Classes{0}{"Class"} = "INTEGRAL"; + } + else { # Struct, Class, Union + $Classes{0}{"Class"} = "MEMORY"; + } + } + elsif($Arch eq "x86_64") + { + if($Type{"Type"}=~/Enum|Pointer|Ptr/ + or isScalar($Type{"Name"}) + or $Type{"Name"}=~/\A(_Bool|bool)\Z/) { + $Classes{0}{"Class"} = "INTEGER"; + } + elsif($Type{"Name"} eq "__int128" + or $Type{"Name"} eq "unsigned __int128") + { + $Classes{0}{"Class"} = "INTEGER"; + $Classes{1}{"Class"} = "INTEGER"; + } + elsif($Type{"Name"}=~/\A(float|double|_Decimal32|_Decimal64|__m64)\Z/) { + $Classes{0}{"Class"} = "SSE"; + } + elsif($Type{"Name"}=~/\A(__float128|_Decimal128|__m128)\Z/) + { + $Classes{0}{"Class"} = "SSE"; + $Classes{8}{"Class"} = "SSEUP"; + } + elsif($Type{"Name"} eq "__m256") + { + $Classes{0}{"Class"} = "SSE"; + $Classes{24}{"Class"} = "SSEUP"; + } + elsif($Type{"Name"} eq "long double") + { + $Classes{0}{"Class"} = "X87"; + $Classes{8}{"Class"} = "X87UP"; + } + elsif($Type{"Name"}=~/\Acomplex (float|double)\Z/) { + $Classes{0}{"Class"} = "MEMORY"; + } + elsif($Type{"Name"} eq "complex long double") { + $Classes{0}{"Class"} = "COMPLEX_X87"; + } + elsif($Type{"Type"}=~/Struct|Class|Union|Array/) + { + if($Type{"Size"}>4*8) { + $Classes{0}{"Class"} = "MEMORY"; + } + else { + %Classes = classifyAggregate($Tid, $TInfo, $Arch, $System, $Word); + } + } + else { + $Classes{0}{"Class"} = "MEMORY"; + } + } + elsif($Arch eq "arm") + { + } + } + elsif($System eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") + { + if(isFloat($Type{"Name"})) { + $Classes{0}{"Class"} = "FLOAT"; + } + elsif($Type{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) { + $Classes{0}{"Class"} = "INTEGRAL"; + } + elsif($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) { + $Classes{0}{"Class"} = "POD"; + } + else { # Struct, Class, Union + $Classes{0}{"Class"} = "MEMORY"; + } + } + elsif($Arch eq "x86_64") + { + if($Type{"Name"}=~/\A(float|double|long double)\Z/) { + $Classes{0}{"Class"} = "FLOAT"; + } + elsif($Type{"Name"}=~/\A__m128(|i|d)\Z/) { + $Classes{0}{"Class"} = "M128"; + } + elsif(isScalar($Type{"Name"}) + or $Type{"Type"}=~/Enum|Pointer|Ptr/ + or $Type{"Name"}=~/\A(_Bool|bool)\Z/ + or ($Type{"Type"}=~/\A(Struct|Union)\Z/ and $Type{"Size"}<=8) + or $Type{"Name"} eq "__m64") { + $Classes{0}{"Class"} = "INTEGRAL"; + } + else { + $Classes{0}{"Class"} = "MEMORY"; + } + } + } + return %Classes; +} + +sub classifyAggregate($$$$$) +{ + my ($Tid, $TInfo, $Arch, $System, $Word) = @_; + my %Type = get_PureType($Tid, $TInfo); + my %Group = (); + my $GroupID = 0; + my %Classes = (); + my %Offsets = (); + if($Type{"Type"} eq "Array") + { + my %Base = get_OneStep_BaseType($Tid, $TInfo); + my %BaseType = get_PureType($Base{"Tid"}, $TInfo); + my $Pos = 0; + my $Max = 0; + if(my $BSize = $BaseType{"Size"}) { + $Max = ($Type{"Size"}/$BSize) - 1; + } + foreach my $Pos (0 .. $Max) + { + $Type{"Memb"}{$Pos}{"algn"} = getTypeAlgn($BaseType{"Tid"}, $TInfo); + $Type{"Memb"}{$Pos}{"type"} = $BaseType{"Tid"}; + $Type{"Memb"}{$Pos}{"name"} = "[$Pos]"; + } + } + if($Type{"Type"} eq "Union") + { + foreach my $Pos (keys(%{$Type{"Memb"}})) + { + $Offsets{$Pos} = $Pos; + $Group{0}{$Pos} = 1; + } + } + else + { # Struct, Class + foreach my $Pos (keys(%{$Type{"Memb"}})) + { + my $Offset = getOffset($Pos, \%Type, $TInfo, $Word)/$BYTE; + $Offsets{$Pos} = $Offset; + my $GroupOffset = int($Offset/$Word)*$Word; + $Group{$GroupOffset}{$Pos} = 1; + } + } + foreach my $GroupOffset (sort {int($a)<=>int($b)} (keys(%Group))) + { + my %GroupClasses = (); + foreach my $Pos (sort {int($a)<=>int($b)} (keys(%{$Group{$GroupOffset}}))) + { # split the field into the classes + my $MTid = $Type{"Memb"}{$Pos}{"type"}; + my $MName = $Type{"Memb"}{$Pos}{"name"}; + my %SubClasses = classifyType($MTid, $TInfo, $Arch, $System, $Word); + foreach my $Offset (sort {int($a)<=>int($b)} keys(%SubClasses)) + { + if(defined $SubClasses{$Offset}{"Elems"}) + { + foreach (keys(%{$SubClasses{$Offset}{"Elems"}})) { + $SubClasses{$Offset}{"Elems"}{$_} = joinFields($MName, $SubClasses{$Offset}{"Elems"}{$_}); + } + } + else { + $SubClasses{$Offset}{"Elems"}{0} = $MName; + } + } + + # add to the group + foreach my $Offset (sort {int($a)<=>int($b)} keys(%SubClasses)) { + $GroupClasses{$Offsets{$Pos}+$Offset} = $SubClasses{$Offset}; + } + } + + # merge classes in the group + my %MergeGroup = (); + + foreach my $Offset (sort {int($a)<=>int($b)} keys(%GroupClasses)) { + $MergeGroup{int($Offset/$Word)}{$Offset} = $GroupClasses{$Offset}; + } + + foreach my $Offset (sort {int($a)<=>int($b)} keys(%MergeGroup)) { + while(postMerger($Arch, $System, $MergeGroup{$Offset})) { }; + } + + %GroupClasses = (); + foreach my $M_Offset (sort {int($a)<=>int($b)} keys(%MergeGroup)) + { + foreach my $Offset (sort {int($a)<=>int($b)} keys(%{$MergeGroup{$M_Offset}})) + { + $GroupClasses{$Offset} = $MergeGroup{$M_Offset}{$Offset}; + } + } + + # add to the result list of classes + foreach my $Offset (sort {int($a)<=>int($b)} keys(%GroupClasses)) + { + if($Type{"Type"} eq "Union") + { + foreach my $P (keys(%{$GroupClasses{$Offset}{"Elems"}})) + { + if($P!=0) { + delete($GroupClasses{$Offset}{"Elems"}{$P}); + } + } + } + $Classes{$Offset} = $GroupClasses{$Offset}; + } + } + + return %Classes; +} + +sub postMerger($$$) +{ + my ($Arch, $System, $PreClasses) = @_; + my @Offsets = sort {int($a)<=>int($b)} keys(%{$PreClasses}); + if($#Offsets==0) { + return 0; + } + my %PostClasses = (); + my $Num = 0; + my $Merged = 0; + while($Num<=$#Offsets-1) + { + my $Offset1 = $Offsets[$Num]; + my $Offset2 = $Offsets[$Num+1]; + my $Class1 = $PreClasses->{$Offset1}{"Class"}; + my $Class2 = $PreClasses->{$Offset2}{"Class"}; + my $ResClass = ""; + if($System=~/\A(linux|macos|freebsd)\Z/) + { # GCC + if($Arch eq "x86_64") + { + if($Class1 eq $Class2) { + $ResClass = $Class1; + } + elsif($Class1 eq "MEMORY" + or $Class2 eq "MEMORY") { + $ResClass = "MEMORY"; + } + elsif($Class1 eq "INTEGER" + or $Class2 eq "INTEGER") { + $ResClass = "INTEGER"; + } + elsif($Class1=~/X87/ + or $Class2=~/X87/) { + $ResClass = "MEMORY"; + } + else { + $ResClass = "SSE"; + } + } + } + if($ResClass) + { # combine + $PostClasses{$Offset1}{"Class"} = $ResClass; + foreach (keys(%{$PreClasses->{$Offset1}{"Elems"}})) { + $PostClasses{$Offset1}{"Elems"}{$Offset1+$_} = $PreClasses->{$Offset1}{"Elems"}{$_}; + } + foreach (keys(%{$PreClasses->{$Offset2}{"Elems"}})) { + $PostClasses{$Offset1}{"Elems"}{$Offset2+$_} = $PreClasses->{$Offset2}{"Elems"}{$_}; + } + $Merged = 1; + } + else + { # save unchanged + $PostClasses{$Offset1} = $PreClasses->{$Offset1}; + $PostClasses{$Offset2} = $PreClasses->{$Offset2}; + } + $Num += 2; + } + if($Num==$#Offsets) { + $PostClasses{$Offsets[$Num]} = $PreClasses->{$Offsets[$Num]}; + } + %{$PreClasses} = %PostClasses; + return $Merged; +} + +sub callingConvention_R_Model($$$$$$) { + return callingConvention_R_I_Model(@_, 1); +} + +sub joinFields($$) +{ + my ($F1, $F2) = @_; + if(substr($F2, 0, 1) eq "[") + { # array elements + return $F1.$F2; + } + else { # fields + return $F1.".".$F2; + } +} + +sub callingConvention_R_I_Model($$$$$$) +{ + my ($SInfo, $TInfo, $Arch, $System, $Word, $Target) = @_; + my %Conv = (); + my $RTid = $SInfo->{"Return"}; + my %Type = get_PureType($RTid, $TInfo); + + if($Target) { + %UsedReg = (); + } + + my %UsedReg_Copy = %UsedReg; + + my %Classes = classifyType($RTid, $TInfo, $Arch, $System, $Word); + + foreach my $Offset (sort {int($a)<=>int($b)} keys(%Classes)) + { + my $Elems = undef; + if(defined $Classes{$Offset}{"Elems"}) + { + foreach (keys(%{$Classes{$Offset}{"Elems"}})) { + $Classes{$Offset}{"Elems"}{$_} = joinFields(".result", $Classes{$Offset}{"Elems"}{$_}); + } + $Elems = $Classes{$Offset}{"Elems"}; + } + else { + $Elems = { 0 => ".result" }; + } + + my $CName = $Classes{$Offset}{"Class"}; + + if($CName eq "VOID") { + next; + } + + if($System=~/\A(linux|macos|freebsd)\Z/) + { # GCC + if($Arch eq "x86") + { + if($CName eq "FLOAT") + { # x87 register + useRegister("st0", "f", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + useRegister("eax", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY") { + pushStack_R($SInfo, $Word); + } + } + elsif($Arch eq "x86_64") + { + my @INT = ("rax", "rdx"); + my @SSE = ("xmm0", "xmm1"); + if($CName eq "INTEGER") + { + if(my $R = getLastAvailable($SInfo, "f", @INT)) + { + useRegister($R, "f", $Elems, $SInfo); + } + else + { # revert registers + # pass as MEMORY + %UsedReg = %UsedReg_Copy; + useHidden($SInfo, $Arch, $System, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($CName eq "SSE") + { + if(my $R = getLastAvailable($SInfo, "8l", @SSE)) + { + useRegister($R, "8l", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + useHidden($SInfo, $Arch, $System, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($CName eq "SSEUP") + { + if(my $R = getLastUsed($SInfo, "xmm0", "xmm1")) + { + useRegister($R, "8h", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + useHidden($SInfo, $Arch, $System, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($CName eq "X87") + { + useRegister("st0", "8l", $Elems, $SInfo); + } + elsif($CName eq "X87UP") + { + useRegister("st0", "8h", $Elems, $SInfo); + } + elsif($CName eq "COMPLEX_X87") + { + useRegister("st0", "f", $Elems, $SInfo); + useRegister("st1", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY") + { + useHidden($SInfo, $Arch, $System, $Word); + $Conv{"Hidden"} = 1; + last; + } + } + elsif($Arch eq "arm") + { # TODO + } + } + elsif($System eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") + { + if($CName eq "FLOAT") + { + useRegister("fp0", "f", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + useRegister("eax", "f", $Elems, $SInfo); + } + elsif($CName eq "POD") + { + useRegister("eax", "f", $Elems, $SInfo); + useRegister("edx", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY" or $CName eq "M128") + { + useHidden($SInfo, $Arch, $System, $Word); + $Conv{"Hidden"} = 1; + } + } + elsif($Arch eq "x86_64") + { + if($CName eq "FLOAT" or $CName eq "M128") + { + useRegister("xmm0", "f", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + useRegister("eax", "f", $Elems, $SInfo); + } + elsif($CName eq "MEMORY") + { + useHidden($SInfo, $Arch, $System, $Word); + $Conv{"Hidden"} = 1; + } + } + } + } + + + if(my %Regs = usedBy(".result", $SInfo)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", sort(keys(%Regs))); + } + elsif(my %Regs = usedBy(".result_ptr", $SInfo)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", sort(keys(%Regs))); + } + + if(not $Conv{"Method"}) + { # unknown + if($Type{"Name"} ne "void") + { + $Conv{"Method"} = "stack"; + $Conv{"Hidden"} = 1; + } + } + + return %Conv; +} + +sub usedBy($$) +{ + my ($Name, $SInfo) = @_; + my %Regs = (); + foreach my $Reg (sort keys(%{$UsedReg{$SInfo}})) + { + foreach my $Size (sort keys(%{$UsedReg{$SInfo}{$Reg}})) + { + foreach my $Offset (sort keys(%{$UsedReg{$SInfo}{$Reg}{$Size}})) + { + if($UsedReg{$SInfo}{$Reg}{$Size}{$Offset}=~/\A\Q$Name\E(\.|\Z)/) { + $Regs{$Reg} = 1; + } + } + } + } + return %Regs; +} + +sub useHidden($$$$) +{ + my ($SInfo, $Arch, $System, $Word) = @_; + if($System=~/\A(linux|macos|freebsd)\Z/) + { # GCC + if($Arch eq "x86") { + pushStack_R($SInfo, $Word); + } + elsif($Arch eq "x86_64") + { + my $Elems = { 0 => ".result_ptr" }; + useRegister("rdi", "f", $Elems, $SInfo); + } + } + elsif($System eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") { + pushStack_R($SInfo, $Word); + } + elsif($Arch eq "x86_64") + { + my $Elems = { 0 => ".result_ptr" }; + useRegister("rcx", "f", $Elems, $SInfo); + } + } +} + +sub pushStack_P($$$$) +{ + my ($SInfo, $Pos, $TInfo, $StackAlgn) = @_; + my $PTid = $SInfo->{"Param"}{$Pos}{"type"}; + my $PName = $SInfo->{"Param"}{$Pos}{"name"}; + my $Alignment = $SInfo->{"Param"}{$Pos}{"algn"}; + if($Alignment<$StackAlgn) { + $Alignment = $StackAlgn; + } + return pushStack($SInfo, $Alignment, $TInfo->{$PTid}{"Size"}, { 0 => $PName }); +} + +sub pushStack_R($$) +{ + my ($SInfo, $Word) = @_; + return pushStack($SInfo, $Word, $Word, { 0 => ".result_ptr" }); +} + +sub pushStack_C($$$) +{ + my ($SInfo, $Class, $TInfo) = @_; + return pushStack($SInfo, $Class->{"Algn"}, $Class->{"Size"}, $Class->{"Elems"}); +} + +sub pushStack($$$$) +{ + my ($SInfo, $Algn, $Size, $Elem) = @_; + my $Offset = 0; + if(my @Offsets = sort {int($a)<=>int($b)} keys(%{$UsedStack{$SInfo}})) + { + $Offset = $Offsets[$#Offsets]; + $Offset += $UsedStack{$SInfo}{$Offset}{"Size"}; + $Offset += getPadding($Offset, $Algn); + } + my %Info = ( + "Size" => $Size, + "Elem" => $Elem + ); + $UsedStack{$SInfo}{$Offset} = \%Info; + return $Offset; +} + +sub useRegister($$$$) +{ + my ($R, $Offset, $Elems, $SInfo) = @_; + if(defined $UsedReg{$SInfo}{$R}) + { + if(defined $UsedReg{$SInfo}{$R}{$Offset}) + { # busy + return 0; + } + } + $UsedReg{$SInfo}{$R}{$Offset}=$Elems; + return $R; +} + +sub getLastAvailable(@) +{ + my $SInfo = shift(@_); + my $Offset = shift(@_); + my $Pos = 0; + foreach (@_) + { + if(not defined $UsedReg{$SInfo}{$_}) { + return $_; + } + elsif(not defined $UsedReg{$SInfo}{$_}{$Offset}) { + return $_; + } + } + return undef; +} + +sub getLastUsed(@) +{ + my $SInfo = shift(@_); + my $Pos = 0; + foreach (@_) + { + if(not defined $UsedReg{$SInfo}{$_}) + { + if($Pos>0) { + return @_[$Pos-1]; + } + else { + return @_[0]; + } + } + $Pos+=1; + } + return undef; +} + +sub callingConvention_P_Model($$$$$$) { + return callingConvention_P_I_Model(@_, 1); +} + +sub callingConvention_P_I_Model($$$$$$$) +{ # calling conventions for different compilers and operating systems + my ($SInfo, $Pos, $TInfo, $Arch, $System, $Word, $Target) = @_; + my %Conv = (); + my $ParamTypeId = $SInfo->{"Param"}{$Pos}{"type"}; + my $PName = $SInfo->{"Param"}{$Pos}{"name"}; + my %Type = get_PureType($ParamTypeId, $TInfo); + + if($Target) + { + %UsedReg = (); + + # distribute return value + if(my $RTid = $SInfo->{"Return"}) { + callingConvention_R_I_Model($SInfo, $TInfo, $Arch, $System, $Word, 0); + } + # distribute other parameters + if($Pos>0) + { + my %PConv = (); + my $PPos = 0; + while($PConv{"Next"} ne $Pos) + { + %PConv = callingConvention_P_I_Model($SInfo, $PPos++, $TInfo, $Arch, $System, $Word, 0); + if(not $PConv{"Next"}) { + last; + } + } + } + } + + my %UsedReg_Copy = %UsedReg; + + my %Classes = classifyType($ParamTypeId, $TInfo, $Arch, $System, $Word); + + my $Error = 0; + foreach my $Offset (sort {int($a)<=>int($b)} keys(%Classes)) + { + my $Elems = undef; + if(defined $Classes{$Offset}{"Elems"}) + { + foreach (keys(%{$Classes{$Offset}{"Elems"}})) { + $Classes{$Offset}{"Elems"}{$_} = joinFields($PName, $Classes{$Offset}{"Elems"}{$_}); + } + $Elems = $Classes{$Offset}{"Elems"}; + } + else { + $Elems = { 0 => $PName }; + } + + my $CName = $Classes{$Offset}{"Class"}; + + if($CName eq "VOID") { + next; + } + + if($System=~/\A(linux|macos|freebsd)\Z/) + { # GCC + if($Arch eq "x86") + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + elsif($Arch eq "x86_64") + { + my @INT = ("rdi", "rsi", "rdx", "rcx", "r8", "r9"); + my @SSE = ("xmm0", "xmm1", "xmm2", "xmm3", "xmm4", "xmm5", "xmm6", "xmm7"); + + if($CName eq "INTEGER") + { + if(my $R = getLastAvailable($SInfo, "f", @INT)) { + useRegister($R, "f", $Elems, $SInfo); + } + else + { # revert registers and + # push the argument on the stack + %UsedReg = %UsedReg_Copy; + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($CName eq "SSE") + { + if(my $R = getLastAvailable($SInfo, "8l", @SSE)) { + useRegister($R, "8l", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($CName eq "SSEUP") + { + if(my $R = getLastUsed($SInfo, @SSE)) { + useRegister($R, "8h", $Elems, $SInfo); + } + else + { + %UsedReg = %UsedReg_Copy; + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($CName=~/X87|MEMORY/) + { # MEMORY, X87, X87UP, COMPLEX_X87 + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($Arch eq "arm") + { # Procedure Call Standard for the ARM Architecture + # TODO + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + else + { # TODO + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + elsif($System eq "windows") + { # MS C++ Compiler + if($Arch eq "x86") + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + elsif($Arch eq "x86_64") + { + if($Pos<=3) + { + if($CName eq "FLOAT") + { + useRegister("xmm".$Pos, "8l", $Elems, $SInfo); + } + elsif($CName eq "INTEGRAL") + { + if($Pos==0) { + useRegister("rcx", "f", $Elems, $SInfo); + } + elsif($Pos==1) { + useRegister("rdx", "f", $Elems, $SInfo); + } + elsif($Pos==2) { + useRegister("r8", "f", $Elems, $SInfo); + } + elsif($Pos==3) { + useRegister("r9", "f", $Elems, $SInfo); + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + else + { + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + } + else + { # TODO + pushStack_P($SInfo, $Pos, $TInfo, $Word); + last; + } + } + + if(my %Regs = usedBy($PName, $SInfo)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", sort(keys(%Regs))); + } + else + { + if($Type{"Name"} ne "void") { + $Conv{"Method"} = "stack"; + } + } + + if(defined $SInfo->{"Param"}{$Pos+1}) + { # TODO + $Conv{"Next"} = $Pos+1; + } + + return %Conv; +} + +sub getTypeAlgn($$) +{ + my ($Tid, $TInfo) = @_; + if(defined $TInfo->{$Tid}{"Algn"}) { + return $TInfo->{$Tid}{"Algn"}; + } + else + { # support for old ABI dumps + if($TInfo->{$Tid}{"Type"}=~/Struct|Class|Union/) + { + if(defined $TInfo->{$Tid}{"Memb"}) + { + my $Max = 0; + foreach my $Pos (keys(%{$TInfo->{$Tid}{"Memb"}})) + { + my $Algn = $TInfo->{$Tid}{"Memb"}{$Pos}{"algn"}; + if($Algn>$Algn) { + $Max = $Algn; + } + } + return $Max; + } + return 0; + } + elsif($TInfo->{$Tid}{"Type"} eq "Array") + { + my %Base = get_OneStep_BaseType($Tid, $TInfo); + return getTypeAlgn($Base{"Tid"}, $TInfo); + } + elsif($TInfo->{$Tid}{"Type"}=~/Intrinsic|Enum|Pointer|Ptr/) + { # model + return $TInfo->{$Tid}{"Size"}; + } + else + { + my %PureType = get_PureType($Tid, $TInfo); + return getTypeAlgn($PureType{"Tid"}, $TInfo); + } + } +} + +sub getAlignment($$$$) +{ + my ($Pos, $TypePtr, $TInfo, $Word) = @_; + my $Tid = $TypePtr->{"Memb"}{$Pos}{"type"}; + my %Type = get_PureType($Tid, $TInfo); + my $Computed = $TypePtr->{"Memb"}{$Pos}{"algn"}; + my $Alignment = 0; + + if(my $BSize = $TypePtr->{"Memb"}{$Pos}{"bitfield"}) + { # bitfields + if($Computed) + { # real in bits + $Alignment = $Computed; + } + return ($Alignment, $BSize); + } + else + { # other fields + if($Computed) + { # real in bytes + $Alignment = $Computed*$BYTE; + } + else + { # model + $Alignment = $Type{"Size"}*$BYTE; + } + return ($Alignment, $Type{"Size"}*$BYTE); + } +} + +sub getOffset($$$$) +{ # offset of the field including padding + my ($FieldPos, $TypePtr, $TInfo, $Word) = @_; + my $Offset = 0; + + my $Buffer=0; + + foreach my $Pos (0 .. keys(%{$TypePtr->{"Memb"}})-1) + { + my ($Alignment, $MSize) = getAlignment($Pos, $TypePtr, $TInfo, $Word); + + if(not $Alignment) + { # support for old ABI dumps + if($MSize=~/\A(8|16|32|64)\Z/) + { + if($Buffer+$MSize<$Word*$BYTE) + { + $Alignment = 1; + $Buffer += $MSize; + } + else + { + $Alignment = $MSize; + $Buffer = 0; + } + } + else + { + $Alignment = 1; + $Buffer += $MSize; + } + } + + # padding + $Offset += getPadding($Offset, $Alignment); + if($Pos==$FieldPos) + { # after the padding + # before the field + return $Offset; + } + $Offset += $MSize; + } + return $FieldPos;# if something is going wrong +} + +sub getPadding($$) +{ + my ($Offset, $Alignment) = @_; + my $Padding = 0; + if($Offset % $Alignment!=0) + { # not aligned, add padding + $Padding = $Alignment - $Offset % $Alignment; + } + return $Padding; +} + +sub isMemPadded($$$$$$) +{ # check if the target field can be added/removed/changed + # without shifting other fields because of padding bits + my ($FieldPos, $Size, $TypePtr, $Skip, $TInfo, $Word) = @_; + return 0 if($FieldPos==0); + delete($TypePtr->{"Memb"}{""}); + my $Offset = 0; + my (%Alignment, %MSize) = (); + my $MaxAlgn = 0; + my $End = keys(%{$TypePtr->{"Memb"}})-1; + my $NextField = $FieldPos+1; + foreach my $Pos (0 .. $End) + { + if($Skip and $Skip->{$Pos}) + { # skip removed/added fields + if($Pos > $FieldPos) + { # after the target + $NextField += 1; + next; + } + } + ($Alignment{$Pos}, $MSize{$Pos}) = getAlignment($Pos, $TypePtr, $TInfo, $Word); + if($Alignment{$Pos}>$MaxAlgn) { + $MaxAlgn = $Alignment{$Pos}; + } + if($Pos==$FieldPos) + { + if($Size==-1) + { # added/removed fields + if($Pos!=$End) + { # skip target field and see + # if enough padding will be + # created on the next step + # to include this field + next; + } + } + } + # padding + my $Padding = 0; + if($Offset % $Alignment{$Pos}!=0) + { # not aligned, add padding + $Padding = $Alignment{$Pos} - $Offset % $Alignment{$Pos}; + } + if($Pos==$NextField) + { # try to place target field in the padding + if($Size==-1) + { # added/removed fields + my $TPadding = 0; + if($Offset % $Alignment{$FieldPos}!=0) + {# padding of the target field + $TPadding = $Alignment{$FieldPos} - $Offset % $Alignment{$FieldPos}; + } + if($TPadding+$MSize{$FieldPos}<=$Padding) + { # enough padding to place target field + return 1; + } + else { + return 0; + } + } + else + { # changed fields + my $Delta = $Size-$MSize{$FieldPos}; + if($Delta>=0) + { # increased + if($Size-$MSize{$FieldPos}<=$Padding) + { # enough padding to change target field + return 1; + } + else { + return 0; + } + } + else + { # decreased + $Delta = abs($Delta); + if($Delta+$Padding>=$MSize{$Pos}) + { # try to place the next field + if(($Offset-$Delta) % $Alignment{$Pos} != 0) + { # padding of the next field in new place + my $NPadding = $Alignment{$Pos} - ($Offset-$Delta) % $Alignment{$Pos}; + if($NPadding+$MSize{$Pos}<=$Delta+$Padding) + { # enough delta+padding to store next field + return 0; + } + } + else + { + return 0; + } + } + return 1; + } + } + } + elsif($Pos==$End) + { # target field is the last field + if($Size==-1) + { # added/removed fields + if($Offset % $MaxAlgn!=0) + { # tail padding + my $TailPadding = $MaxAlgn - $Offset % $MaxAlgn; + if($Padding+$MSize{$Pos}<=$TailPadding) + { # enough tail padding to place the last field + return 1; + } + } + return 0; + } + else + { # changed fields + # scenario #1 + my $Offset1 = $Offset+$Padding+$MSize{$Pos}; + if($Offset1 % $MaxAlgn != 0) + { # tail padding + $Offset1 += $MaxAlgn - $Offset1 % $MaxAlgn; + } + # scenario #2 + my $Offset2 = $Offset+$Padding+$Size; + if($Offset2 % $MaxAlgn != 0) + { # tail padding + $Offset2 += $MaxAlgn - $Offset2 % $MaxAlgn; + } + if($Offset1!=$Offset2) + { # different sizes of structure + return 0; + } + return 1; + } + } + $Offset += $Padding+$MSize{$Pos}; + } + return 0; +} + +sub isScalar($) { + return ($_[0]=~/\A(unsigned |)(char|short|int|long|long long)\Z/); +} + +sub isFloat($) { + return ($_[0]=~/\A(float|double|long double)\Z/); +} + +sub callingConvention_R_Real($) +{ + my $SInfo = $_[0]; + my %Conv = (); + my %Regs = (); + my $Hidden = 0; + foreach my $Reg (keys(%{$SInfo->{"Reg"}})) + { + + foreach my $Elem (keys(%{$SInfo->{"Reg"}{$Reg}})) + { + if($Elem eq ".result_ptr") + { + $Hidden = 1; + $Regs{$Reg}=1; + } + elsif(index($Elem, ".result")==0) { + $Regs{$Reg}=1; + } + } + } + if(my @R = sort keys(%Regs)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", @R); + if($Hidden) { + $Conv{"Hidden"} = 1; + } + } + else + { + $Conv{"Method"} = "stack"; + $Conv{"Hidden"} = 1; + } + return %Conv; +} + +sub callingConvention_P_Real($$) +{ + my ($SInfo, $Pos) = @_; + my $PName = $SInfo->{"Param"}{$Pos}{"name"}; + my %Conv = (); + my %Regs = (); + my $Hidden = 0; + foreach my $Reg (keys(%{$SInfo->{"Reg"}})) + { + + foreach my $Elem (keys(%{$SInfo->{"Reg"}{$Reg}})) + { + if($Elem=~/\A$PName(\.|\Z)/) { + $Regs{$Reg}=1; + } + } + } + if(my @R = sort keys(%Regs)) + { + $Conv{"Method"} = "reg"; + $Conv{"Registers"} = join(", ", @R); + } + else { + $Conv{"Method"} = "stack"; + } + return %Conv; +} + +return 1;
\ No newline at end of file diff --git a/modules/Internals/RegTests.pm b/modules/Internals/RegTests.pm index 48ef2e0..628b4c0 100644 --- a/modules/Internals/RegTests.pm +++ b/modules/Internals/RegTests.pm @@ -1,5 +1,5 @@ ########################################################################### -# Internal Regression Tests for ABI Compliance Checker +# Module for ABI Compliance Checker with regression test suite # # Copyright (C) 2009-2010 The Linux Foundation # Copyright (C) 2009-2011 Institute for System Programming, RAS @@ -24,13 +24,15 @@ use strict; my ($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, -$DumpFormat, $LIB_EXT, $GCC_PATH, $Browse, $OpenReport, $SortDump); +$DumpFormat, $LIB_EXT, $GCC_PATH, $Browse, $OpenReport, $SortDump, +$CheckHeadersOnly, $CheckObjectsOnly); my $OSgroup = get_OSgroup(); sub testTool($$$$$$$$$$$) { ($TestDump, $Debug, $Quiet, $ExtendedCheck, $LogMode, $ReportFormat, - $DumpFormat, $LIB_EXT, $GCC_PATH, $Browse, $OpenReport, $SortDump) = @_; + $DumpFormat, $LIB_EXT, $GCC_PATH, $Browse, $OpenReport, $SortDump, + $CheckHeadersOnly, $CheckObjectsOnly) = @_; testC(); testCpp(); @@ -41,7 +43,7 @@ sub testCpp() printMsg("INFO", "verifying detectable C++ library changes"); my ($HEADER1, $SOURCE1, $HEADER2, $SOURCE2) = (); my $DECL_SPEC = ($OSgroup eq "windows")?"__declspec( dllexport )":""; - my $EXTERN = ($OSgroup eq "windows")?"extern ":"";# add "extern" for CL compiler + my $EXTERN = ($OSgroup eq "windows")?"extern ":""; # add "extern" for CL compiler # Begin namespace $HEADER1 .= "namespace TestNS {\n"; @@ -247,7 +249,7 @@ sub testCpp() int globalDataBecameConst = 10;"; $HEADER2 .= " - $DECL_SPEC const int globalDataBecameConst = 15;"; + $EXTERN $DECL_SPEC const int globalDataBecameConst = 15;"; # Global_Data_Became_Const # Class Member @@ -283,18 +285,18 @@ sub testCpp() # Global_Data_Value_Changed # Integer $HEADER1 .= " - $DECL_SPEC const int globalDataValue_Integer = 10;"; + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 10;"; $HEADER2 .= " - $DECL_SPEC const int globalDataValue_Integer = 15;"; + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 15;"; # Global_Data_Value_Changed # Character $HEADER1 .= " - $DECL_SPEC const char globalDataValue_Char = \'o\';"; + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'o\';"; $HEADER2 .= " - $DECL_SPEC const char globalDataValue_Char = \'N\';"; + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'N\';"; # Parameter_Became_Restrict $HEADER1 .= " @@ -2659,7 +2661,108 @@ sub testC() printMsg("INFO", "\nverifying detectable C library changes"); my ($HEADER1, $SOURCE1, $HEADER2, $SOURCE2) = (); my $DECL_SPEC = ($OSgroup eq "windows")?"__declspec( dllexport )":""; - my $EXTERN = ($OSgroup eq "windows")?"extern ":""; + my $EXTERN = ($OSgroup eq "windows")?"extern ":""; # add "extern" for CL compiler + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + int a[4]; + } ARRAY; + $DECL_SPEC void callConv5 (ARRAY i, int j);"; + $SOURCE1 .= " + void callConv5 (ARRAY i, int j) { }"; + + $HEADER2 .= " + typedef struct { + int a[4]; + } ARRAY; + $DECL_SPEC void callConv5 (ARRAY i, double j);"; + $SOURCE2 .= " + void callConv5 (ARRAY i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef union { + int a; + double b; + } UNION; + $DECL_SPEC void callConv4 (UNION i, int j);"; + $SOURCE1 .= " + void callConv4 (UNION i, int j) { }"; + + $HEADER2 .= " + typedef union { + int a; + double b; + } UNION; + $DECL_SPEC void callConv4 (UNION i, double j);"; + $SOURCE2 .= " + void callConv4 (UNION i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + long a:4; + long b:16; + } POD2; + $DECL_SPEC void callConv3 (POD2 i, int j);"; + $SOURCE1 .= " + void callConv3 (POD2 i, int j) { }"; + + $HEADER2 .= " + typedef struct { + long a:4; + long b:16; + } POD2; + $DECL_SPEC void callConv3 (POD2 i, double j);"; + $SOURCE2 .= " + void callConv3 (POD2 i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + short s:9; + int j:9; + char c; + short t:9; + short u:9; + char d; + } POD; + $DECL_SPEC void callConv2 (POD i, int j);"; + $SOURCE1 .= " + void callConv2 (POD i, int j) { }"; + + $HEADER2 .= " + typedef struct { + short s:9; + int j:9; + char c; + short t:9; + short u:9; + char d; + } POD; + $DECL_SPEC void callConv2 (POD i, double j);"; + $SOURCE2 .= " + void callConv2 (POD i, double j) { }"; + + # Parameter_Type_And_Register + $HEADER1 .= " + typedef struct { + int a, b; + double d; + } POD1; + $DECL_SPEC void callConv (int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, int k);"; + $SOURCE1 .= " + void callConv(int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, int k) { }"; + + $HEADER2 .= " + typedef struct { + int a, b; + double d; + } POD1; + $DECL_SPEC void callConv (int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, double k);"; + $SOURCE2 .= " + void callConv(int e, int f, POD1 s, int g, int h, long double ld, double m, double n, int i, int j, double k) { }"; # Parameter_Type (int to "int const") $HEADER1 .= " @@ -2914,22 +3017,22 @@ sub testC() # Global_Data_Value_Changed # Integer $HEADER1 .= " - $DECL_SPEC const int globalDataValue_Integer = 10;"; + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 10;"; $HEADER2 .= " - $DECL_SPEC const int globalDataValue_Integer = 15;"; + $EXTERN $DECL_SPEC const int globalDataValue_Integer = 15;"; # Global_Data_Value_Changed # Character $HEADER1 .= " - $DECL_SPEC const char globalDataValue_Char = \'o\';"; + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'o\';"; $HEADER2 .= " - $DECL_SPEC const char globalDataValue_Char = \'N\';"; + $EXTERN $DECL_SPEC const char globalDataValue_Char = \'N\';"; # Global_Data_Became_Non_Const $HEADER1 .= " - $DECL_SPEC const int globalDataBecameNonConst = 10;"; + $EXTERN $DECL_SPEC const int globalDataBecameNonConst = 10;"; $HEADER2 .= " extern $DECL_SPEC int globalDataBecameNonConst;"; @@ -2940,7 +3043,7 @@ sub testC() # Typedef $HEADER1 .= " typedef const int CONST_INT; - $DECL_SPEC CONST_INT globalDataBecameNonConst_Typedef = 10;"; + $EXTERN $DECL_SPEC CONST_INT globalDataBecameNonConst_Typedef = 10;"; $HEADER2 .= " extern $DECL_SPEC int globalDataBecameNonConst_Typedef;"; @@ -2954,7 +3057,7 @@ sub testC() int globalDataBecameConst = 10;"; $HEADER2 .= " - $DECL_SPEC const int globalDataBecameConst = 15;"; + $EXTERN $DECL_SPEC const int globalDataBecameConst = 15;"; # Global_Data_Became_Non_Const $HEADER1 .= " @@ -3438,12 +3541,12 @@ sub testC() int parameterTypeAndSize(long long param, int other_param) { return other_param; }"; # Parameter_Type_And_Size (test calling conventions) - $HEADER1 .= "\n + $HEADER1 .= " $DECL_SPEC int parameterCallingConvention(int p1, int p2, int p3);"; $SOURCE1 .= " int parameterCallingConvention(int p1, int p2, int p3) { return 0; }"; - $HEADER2 .= "\n + $HEADER2 .= " $DECL_SPEC float parameterCallingConvention(char p1, int p2, int p3);"; $SOURCE2 .= " float parameterCallingConvention(char p1, int p2, int p3) { return 7.0f; }"; @@ -3554,13 +3657,19 @@ sub testC() # Return_Type ("struct" to "void*") $HEADER1 .= " - struct SomeStruct {int A;long B;}; + struct SomeStruct { + int a; + double b, c, d; + }; $DECL_SPEC struct SomeStruct* returnTypeChangeToVoidPtr(int param);"; $SOURCE1 .= " struct SomeStruct* returnTypeChangeToVoidPtr(int param) { return (struct SomeStruct*)0; }"; $HEADER2 .= " - struct SomeStruct {int A;int B;}; + struct SomeStruct { + int a; + double b, c, d; + }; $DECL_SPEC void* returnTypeChangeToVoidPtr(int param);"; $SOURCE2 .= " void* returnTypeChangeToVoidPtr(int param) { return (void*)0; }"; @@ -3603,6 +3712,17 @@ sub testC() $DECL_SPEC long returnTypeChangeFromVoidToLong(int param);"; $SOURCE2 .= " long returnTypeChangeFromVoidToLong(int param) { return 0; }"; + + # Return_Type_From_Void_And_Stack_Layout (safe, "void" to "void*") + $HEADER1 .= " + $DECL_SPEC void returnTypeChangeFromVoidToVoidPtr(int param);"; + $SOURCE1 .= " + void returnTypeChangeFromVoidToVoidPtr(int param) { return; }"; + + $HEADER2 .= " + $DECL_SPEC void* returnTypeChangeFromVoidToVoidPtr(int param);"; + $SOURCE2 .= " + void* returnTypeChangeFromVoidToVoidPtr(int param) { return 0; }"; # Return_Type_From_Register_To_Stack ("int" to "struct") $HEADER1 .= " @@ -4269,6 +4389,12 @@ sub runTests($$$$$$$$) { # HTML is default format @Cmd = (@Cmd, "-report-format", $ReportFormat); } + if($CheckHeadersOnly) { + @Cmd = (@Cmd, "-headers-only"); + } + if($CheckObjectsOnly) { + @Cmd = (@Cmd, "-objects-only"); + } if($Browse) { @Cmd = (@Cmd, "-browse", $Browse); } diff --git a/modules/Internals/XmlDump.pm b/modules/Internals/XmlDump.pm index 4fa84af..76b8247 100644 --- a/modules/Internals/XmlDump.pm +++ b/modules/Internals/XmlDump.pm @@ -79,7 +79,7 @@ sub createXmlDump($) $ABI_DUMP .= openTag("data_type"); $ABI_DUMP .= addTag("id", $ID); foreach my $Attr ("Name", "Type", "Size", - "Header", "Line", "NameSpace", "Class", "Return") + "Header", "Line", "NameSpace", "Class", "Return", "Algn") { if(defined $TInfo{$Attr}) { $ABI_DUMP .= addTag(lc($Attr), $TInfo{$Attr}); @@ -546,8 +546,8 @@ sub readXmlDump($) $TInfo{"Spec"} = 1; } } - foreach my $Attr ("Name", "Type", "Class", - "Header", "Line", "NameSpace", "Return", "Size") + foreach my $Attr ("Name", "Type", "Size", + "Header", "Line", "NameSpace", "Class", "Return", "Algn") { my $Val = parseTag(\$DataType, lc($Attr)); if(defined $Val) { |