------------------------------------------------------------------------------ -- -- -- GNAT COMPILER COMPONENTS -- -- -- -- G N A T . A L T I V E C -- -- -- -- S p e c -- -- -- -- Copyright (C) 2004-2024, Free Software Foundation, Inc. -- -- -- -- GNAT is free software; you can redistribute it and/or modify it under -- -- terms of the GNU General Public License as published by the Free Soft- -- -- ware Foundation; either version 3, or (at your option) any later ver- -- -- sion. GNAT is distributed in the hope that it will be useful, but WITH- -- -- OUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY -- -- or FITNESS FOR A PARTICULAR PURPOSE. -- -- -- -- As a special exception under Section 7 of GPL version 3, you are granted -- -- additional permissions described in the GCC Runtime Library Exception, -- -- version 3.1, as published by the Free Software Foundation. -- -- -- -- You should have received a copy of the GNU General Public License and -- -- a copy of the GCC Runtime Library Exception along with this program; -- -- see the files COPYING3 and COPYING.RUNTIME respectively. If not, see -- -- . -- -- -- -- GNAT was originally developed by the GNAT team at New York University. -- -- Extensive contributions were provided by Ada Core Technologies Inc. -- -- -- ------------------------------------------------------------------------------ ------------------------- -- General description -- ------------------------- -- This is the root of a package hierarchy offering an Ada binding to the -- PowerPC AltiVec extensions, a set of 128bit vector types together with a -- set of subprograms operating on them. Relevant documents are: -- o AltiVec Technology, Programming Interface Manual (1999-06) -- to which we will refer as [PIM], describes the data types, the -- functional interface and the ABI conventions. -- o AltiVec Technology, Programming Environments Manual (2002-02) -- to which we will refer as [PEM], describes the hardware architecture -- and instruction set. -- These documents, as well as a number of others of general interest on the -- AltiVec technology, are available from the Motorola/AltiVec Web site at: -- http://www.freescale.com/altivec -- The binding interface is structured to allow alternate implementations: -- for real AltiVec capable targets, and for other targets. In the latter -- case, everything is emulated in software. The two versions are referred -- to as: -- o The Hard binding for AltiVec capable targets (with the appropriate -- hardware support and corresponding instruction set) -- o The Soft binding for other targets (with the low level primitives -- emulated in software). -- In addition, interfaces that are not strictly part of the base AltiVec API -- are provided, such as vector conversions to and from array representations, -- which are of interest for client applications (e.g. for vector -- initialization purposes). -- Only the soft binding is available today ----------------------------------------- -- General package architecture survey -- ----------------------------------------- -- The various vector representations are all "containers" of elementary -- values, the possible types of which are declared in this root package to -- be generally accessible. -- From the user standpoint, the binding materializes as a consistent -- hierarchy of units: -- GNAT.Altivec -- (component types) -- | -- o----------------o----------------o-------------o -- | | | | -- Vector_Types Vector_Operations Vector_Views Conversions -- Users can manipulate vectors through two families of types: Vector -- types and View types. -- Vector types are available through the Vector_Types and Vector_Operations -- packages, which implement the core binding to the AltiVec API, as -- described in [PIM-2.1 data types] and [PIM-4 AltiVec operations and -- predicates]. -- The layout of Vector objects is dependant on the target machine -- endianness, and View types were devised to offer a higher level user -- interface. With Views, a vector of 4 uints (1, 2, 3, 4) is always declared -- with a VUI_View := (Values => (1, 2, 3, 4)), element 1 first, natural -- notation to denote the element values, and indexed notation is available -- to access individual elements. -- View types do not represent Altivec vectors per se, in the sense that the -- Altivec_Operations are not available for them. They are intended to allow -- Vector initializations as well as access to the Vector component values. -- The GNAT.Altivec.Conversions package is provided to convert a View to the -- corresponding Vector and vice-versa. --------------------------- -- Underlying principles -- --------------------------- -- Internally, the binding relies on an abstraction of the Altivec API, a -- rich set of functions around a core of low level primitives mapping to -- AltiVec instructions. See for instance "vec_add" in [PIM-4.4 Generic and -- Specific AltiVec operations], with no less than six result/arguments -- combinations of byte vector types that map to "vaddubm". -- The "soft" version is a software emulation of the low level primitives. -- The "hard" version would map to real AltiVec instructions via GCC builtins -- and inlining. -- See the "Design Notes" section below for additional details on the -- internals. ------------------- -- Example usage -- ------------------- -- Here is a sample program declaring and initializing two vectors, 'add'ing -- them and displaying the result components: -- with GNAT.Altivec.Vector_Types; use GNAT.Altivec.Vector_Types; -- with GNAT.Altivec.Vector_Operations; use GNAT.Altivec.Vector_Operations; -- with GNAT.Altivec.Vector_Views; use GNAT.Altivec.Vector_Views; -- with GNAT.Altivec.Conversions; use GNAT.Altivec.Conversions; -- use GNAT.Altivec; -- with Ada.Text_IO; use Ada.Text_IO; -- procedure Sample is -- Va : Vector_Unsigned_Int := To_Vector ((Values => (1, 2, 3, 4))); -- Vb : Vector_Unsigned_Int := To_Vector ((Values => (1, 2, 3, 4))); -- Vs : Vector_Unsigned_Int; -- Vs_View : VUI_View; -- begin -- Vs := Vec_Add (Va, Vb); -- Vs_View := To_View (Vs); -- for I in Vs_View.Values'Range loop -- Put_Line (Unsigned_Int'Image (Vs_View.Values (I))); -- end loop; -- end; -- $ gnatmake sample.adb -- [...] -- $ ./sample -- 2 -- 4 -- 6 -- 8 ------------------------------------------------------------------------------ with System; package GNAT.Altivec is -- Definitions of constants and vector/array component types common to all -- the versions of the binding. -- All the vector types are 128bits VECTOR_BIT : constant := 128; ------------------------------------------- -- [PIM-2.3.1 Alignment of vector types] -- ------------------------------------------- -- "A defined data item of any vector data type in memory is always -- aligned on a 16-byte boundary. A pointer to any vector data type always -- points to a 16-byte boundary. The compiler is responsible for aligning -- vector data types on 16-byte boundaries." VECTOR_ALIGNMENT : constant := Natural'Min (16, Standard'Maximum_Alignment); -- This value is used to set the alignment of vector datatypes in both the -- hard and the soft binding implementations. -- -- We want this value to never be greater than 16, because none of the -- binding implementations requires larger alignments and such a value -- would cause useless space to be allocated/wasted for vector objects. -- Furthermore, the alignment of 16 matches the hard binding leading to -- a more faithful emulation. -- -- It needs to be exactly 16 for the hard binding, and the initializing -- expression is just right for this purpose since Maximum_Alignment is -- expected to be 16 for the real Altivec ABI. -- -- The soft binding doesn't rely on strict 16byte alignment, and we want -- the value to be no greater than Standard'Maximum_Alignment in this case -- to ensure it is supported on every possible target. ------------------------------------------------------- -- [PIM-2.1] Data Types - Interpretation of contents -- ------------------------------------------------------- --------------------- -- char components -- --------------------- CHAR_BIT : constant := 8; SCHAR_MIN : constant := -2 ** (CHAR_BIT - 1); SCHAR_MAX : constant := 2 ** (CHAR_BIT - 1) - 1; UCHAR_MAX : constant := 2 ** CHAR_BIT - 1; type unsigned_char is mod UCHAR_MAX + 1; for unsigned_char'Size use CHAR_BIT; type signed_char is range SCHAR_MIN .. SCHAR_MAX; for signed_char'Size use CHAR_BIT; subtype bool_char is unsigned_char; -- ??? There is a difference here between what the Altivec Technology -- Programming Interface Manual says and what GCC says. In the manual, -- vector_bool_char is a vector_unsigned_char, while in altivec.h it -- is a vector_signed_char. bool_char_True : constant bool_char := bool_char'Last; bool_char_False : constant bool_char := 0; ---------------------- -- short components -- ---------------------- SHORT_BIT : constant := 16; SSHORT_MIN : constant := -2 ** (SHORT_BIT - 1); SSHORT_MAX : constant := 2 ** (SHORT_BIT - 1) - 1; USHORT_MAX : constant := 2 ** SHORT_BIT - 1; type unsigned_short is mod USHORT_MAX + 1; for unsigned_short'Size use SHORT_BIT; subtype unsigned_short_int is unsigned_short; type signed_short is range SSHORT_MIN .. SSHORT_MAX; for signed_short'Size use SHORT_BIT; subtype signed_short_int is signed_short; subtype bool_short is unsigned_short; -- ??? See bool_char bool_short_True : constant bool_short := bool_short'Last; bool_short_False : constant bool_short := 0; subtype bool_short_int is bool_short; -------------------- -- int components -- -------------------- INT_BIT : constant := 32; SINT_MIN : constant := -2 ** (INT_BIT - 1); SINT_MAX : constant := 2 ** (INT_BIT - 1) - 1; UINT_MAX : constant := 2 ** INT_BIT - 1; type unsigned_int is mod UINT_MAX + 1; for unsigned_int'Size use INT_BIT; type signed_int is range SINT_MIN .. SINT_MAX; for signed_int'Size use INT_BIT; subtype bool_int is unsigned_int; -- ??? See bool_char bool_int_True : constant bool_int := bool_int'Last; bool_int_False : constant bool_int := 0; ---------------------- -- float components -- ---------------------- FLOAT_BIT : constant := 32; FLOAT_DIGIT : constant := 6; FLOAT_MIN : constant := -16#0.FFFF_FF#E+32; FLOAT_MAX : constant := 16#0.FFFF_FF#E+32; type C_float is digits FLOAT_DIGIT range FLOAT_MIN .. FLOAT_MAX; for C_float'Size use FLOAT_BIT; -- Altivec operations always use the standard native floating-point -- support of the target. Note that this means that there may be -- minor differences in results between targets when the floating- -- point implementations are slightly different, as would happen -- with normal non-Altivec floating-point operations. In particular -- the Altivec simulations may yield slightly different results -- from those obtained on a true hardware Altivec target if the -- floating-point implementation is not 100% compatible. ---------------------- -- pixel components -- ---------------------- subtype pixel is unsigned_short; ----------------------------------------------------------- -- Subtypes for variants found in the GCC implementation -- ----------------------------------------------------------- subtype c_int is signed_int; subtype c_short is c_int; LONG_BIT : constant := 32; -- Some of the GCC builtins are built with "long" arguments and -- expect SImode to come in. SLONG_MIN : constant := -2 ** (LONG_BIT - 1); SLONG_MAX : constant := 2 ** (LONG_BIT - 1) - 1; ULONG_MAX : constant := 2 ** LONG_BIT - 1; type signed_long is range SLONG_MIN .. SLONG_MAX; type unsigned_long is mod ULONG_MAX + 1; subtype c_long is signed_long; subtype c_ptr is System.Address; --------------------------------------------------------- -- Access types, for the sake of some argument passing -- --------------------------------------------------------- type signed_char_ptr is access all signed_char; type unsigned_char_ptr is access all unsigned_char; type short_ptr is access all c_short; type signed_short_ptr is access all signed_short; type unsigned_short_ptr is access all unsigned_short; type int_ptr is access all c_int; type signed_int_ptr is access all signed_int; type unsigned_int_ptr is access all unsigned_int; type long_ptr is access all c_long; type signed_long_ptr is access all signed_long; type unsigned_long_ptr is access all unsigned_long; type float_ptr is access all Float; -- type const_signed_char_ptr is access constant signed_char; type const_unsigned_char_ptr is access constant unsigned_char; type const_short_ptr is access constant c_short; type const_signed_short_ptr is access constant signed_short; type const_unsigned_short_ptr is access constant unsigned_short; type const_int_ptr is access constant c_int; type const_signed_int_ptr is access constant signed_int; type const_unsigned_int_ptr is access constant unsigned_int; type const_long_ptr is access constant c_long; type const_signed_long_ptr is access constant signed_long; type const_unsigned_long_ptr is access constant unsigned_long; type const_float_ptr is access constant Float; -- Access to const volatile arguments need specialized types type volatile_float is new Float; pragma Volatile (volatile_float); type volatile_signed_char is new signed_char; pragma Volatile (volatile_signed_char); type volatile_unsigned_char is new unsigned_char; pragma Volatile (volatile_unsigned_char); type volatile_signed_short is new signed_short; pragma Volatile (volatile_signed_short); type volatile_unsigned_short is new unsigned_short; pragma Volatile (volatile_unsigned_short); type volatile_signed_int is new signed_int; pragma Volatile (volatile_signed_int); type volatile_unsigned_int is new unsigned_int; pragma Volatile (volatile_unsigned_int); type volatile_signed_long is new signed_long; pragma Volatile (volatile_signed_long); type volatile_unsigned_long is new unsigned_long; pragma Volatile (volatile_unsigned_long); type constv_char_ptr is access constant volatile_signed_char; type constv_signed_char_ptr is access constant volatile_signed_char; type constv_unsigned_char_ptr is access constant volatile_unsigned_char; type constv_short_ptr is access constant volatile_signed_short; type constv_signed_short_ptr is access constant volatile_signed_short; type constv_unsigned_short_ptr is access constant volatile_unsigned_short; type constv_int_ptr is access constant volatile_signed_int; type constv_signed_int_ptr is access constant volatile_signed_int; type constv_unsigned_int_ptr is access constant volatile_unsigned_int; type constv_long_ptr is access constant volatile_signed_long; type constv_signed_long_ptr is access constant volatile_signed_long; type constv_unsigned_long_ptr is access constant volatile_unsigned_long; type constv_float_ptr is access constant volatile_float; private ----------------------- -- Various constants -- ----------------------- CR6_EQ : constant := 0; CR6_EQ_REV : constant := 1; CR6_LT : constant := 2; CR6_LT_REV : constant := 3; end GNAT.Altivec; -------------------- -- Design Notes -- -------------------- ------------------------ -- General principles -- ------------------------ -- The internal organization has been devised from a number of driving ideas: -- o From the clients standpoint, the two versions of the binding should be -- as easily exchangable as possible, -- o From the maintenance standpoint, we want to avoid as much code -- duplication as possible. -- o From both standpoints above, we want to maintain a clear interface -- separation between the base bindings to the Motorola API and the -- additional facilities. -- The identification of the low level interface is directly inspired by the -- the base API organization, basically consisting of a rich set of functions -- around a core of low level primitives mapping to AltiVec instructions. -- See for instance "vec_add" in [PIM-4.4 Generic and Specific AltiVec -- operations]: no less than six result/arguments combinations of byte vector -- types map to "vaddubm". -- The "hard" version of the low level primitives map to real AltiVec -- instructions via the corresponding GCC builtins. The "soft" version is -- a software emulation of those. --------------------------------------- -- The Low_Level_Vectors abstraction -- --------------------------------------- -- The AltiVec C interface spirit is to map a large set of C functions down -- to a much smaller set of AltiVec instructions, most of them operating on a -- set of vector data types in a transparent manner. See for instance the -- case of vec_add, which maps six combinations of result/argument types to -- vaddubm for signed/unsigned/bool variants of 'char' components. -- The GCC implementation of this idiom for C/C++ is to setup builtins -- corresponding to the instructions and to expose the C user function as -- wrappers around those builtins with no-op type conversions as required. -- Typically, for the vec_add case mentioned above, we have (altivec.h): -- -- inline __vector signed char -- vec_add (__vector signed char a1, __vector signed char a2) -- { -- return (__vector signed char) -- __builtin_altivec_vaddubm ((__vector signed char) a1, -- (__vector signed char) a2); -- } -- inline __vector unsigned char -- vec_add (__vector __bool char a1, __vector unsigned char a2) -- { -- return (__vector unsigned char) -- __builtin_altivec_vaddubm ((__vector signed char) a1, -- (__vector signed char) a2); -- } -- The central idea for the Ada bindings is to leverage on the existing GCC -- architecture, with the introduction of a Low_Level_Vectors abstraction. -- This abstraction acts as a representative of the vector-types and builtins -- compiler interface for either the Hard or the Soft case. -- For the Hard binding, Low_Level_Vectors exposes data types with a GCC -- internal translation identical to the "vector ..." C types, and a set of -- subprograms mapping straight to the internal GCC builtins. -- For the Soft binding, Low_Level_Vectors exposes the same set of types -- and subprograms, with bodies simulating the instructions behavior. -- Vector_Types/Operations "simply" bind the user types and operations to -- some Low_Level_Vectors implementation, selected in accordance with the -- target -- To achieve a complete Hard/Soft independence in the Vector_Types and -- Vector_Operations implementations, both versions of the low level support -- are expected to expose a number of facilities: -- o Private data type declarations for base vector representations embedded -- in the user visible vector types, that is: -- LL_VBC, LL_VUC and LL_VSC -- for vector_bool_char, vector_unsigned_char and vector_signed_char -- LL_VBS, LL_VUS and LL_VSS -- for vector_bool_short, vector_unsigned_short and vector_signed_short -- LL_VBI, LL_VUI and LL_VSI -- for vector_bool_int, vector_unsigned_int and vector_signed_int -- as well as: -- LL_VP for vector_pixel and LL_VF for vector_float -- o Primitive operations corresponding to the AltiVec hardware instruction -- names, like "vaddubm". The whole set is not described here. The actual -- sets are inspired from the GCC builtins which are invoked from GCC's -- "altivec.h". -- o An LL_Altivec convention identifier, specifying the calling convention -- to be used to access the aforementioned primitive operations. -- Besides: -- o Unchecked_Conversion are expected to be allowed between any pair of -- exposed data types, and are expected to have no effect on the value -- bit patterns. ------------------------- -- Vector views layout -- ------------------------- -- Vector Views combine intuitive user level ordering for both elements -- within a vector and bytes within each element. They basically map to an -- array representation where array(i) always represents element (i), in the -- natural target representation. This way, a user vector (1, 2, 3, 4) is -- represented as: -- Increasing Addresses -- -------------------------------------------------------------------------> -- | 0x0 0x0 0x0 0x1 | 0x0 0x0 0x0 0x2 | 0x0 0x0 0x0 0x3 | 0x0 0x0 0x0 0x4 | -- | V (0), BE | V (1), BE | V (2), BE | V (3), BE | -- on a big endian target, and as: -- | 0x1 0x0 0x0 0x0 | 0x2 0x0 0x0 0x0 | 0x3 0x0 0x0 0x0 | 0x4 0x0 0x0 0x0 | -- | V (0), LE | V (1), LE | V (2), LE | V (3), LE | -- on a little-endian target ------------------------- -- Vector types layout -- ------------------------- -- In the case of the hard binding, the layout of the vector type in -- memory is documented by the Altivec documentation. In the case of the -- soft binding, the simplest solution is to represent a vector as an -- array of components. This representation can depend on the endianness. -- We can consider three possibilities: -- * First component at the lowest address, components in big endian format. -- It is the natural way to represent an array in big endian, and it would -- also be the natural way to represent a quad-word integer in big endian. -- Example: -- Let V be a vector of unsigned int which value is (1, 2, 3, 4). It is -- represented as: -- Addresses growing -- -------------------------------------------------------------------------> -- | 0x0 0x0 0x0 0x1 | 0x0 0x0 0x0 0x2 | 0x0 0x0 0x0 0x3 | 0x0 0x0 0x0 0x4 | -- | V (0), BE | V (1), BE | V (2), BE | V (3), BE | -- * First component at the lowest address, components in little endian -- format. It is the natural way to represent an array in little endian. -- Example: -- Let V be a vector of unsigned int which value is (1, 2, 3, 4). It is -- represented as: -- Addresses growing -- -------------------------------------------------------------------------> -- | 0x1 0x0 0x0 0x0 | 0x2 0x0 0x0 0x0 | 0x3 0x0 0x0 0x0 | 0x4 0x0 0x0 0x0 | -- | V (0), LE | V (1), LE | V (2), LE | V (3), LE | -- * Last component at the lowest address, components in little endian format. -- It is the natural way to represent a quad-word integer in little endian. -- Example: -- Let V be a vector of unsigned int which value is (1, 2, 3, 4). It is -- represented as: -- Addresses growing -- -------------------------------------------------------------------------> -- | 0x4 0x0 0x0 0x0 | 0x3 0x0 0x0 0x0 | 0x2 0x0 0x0 0x0 | 0x1 0x0 0x0 0x0 | -- | V (3), LE | V (2), LE | V (1), LE | V (0), LE | -- There is actually a fourth case (components in big endian, first -- component at the lowest address), but it does not have any interesting -- properties: it is neither the natural way to represent a quad-word on any -- machine, nor the natural way to represent an array on any machine. -- Example: -- Let V be a vector of unsigned int which value is (1, 2, 3, 4). It is -- represented as: -- Addresses growing -- -------------------------------------------------------------------------> -- | 0x0 0x0 0x0 0x4 | 0x0 0x0 0x0 0x3 | 0x0 0x0 0x0 0x2 | 0x0 0x0 0x0 0x1 | -- | V (3), BE | V (2), BE | V (1), BE | V (0), BE | -- Most of the Altivec operations are specific to a component size, and -- can be implemented with any of these three formats. But some operations -- are defined by the same Altivec primitive operation for different type -- sizes: -- * operations doing arithmetics on a complete vector, seen as a quad-word; -- * operations dealing with memory. -- Operations on a complete vector: -- -------------------------------- -- Examples: -- vec_sll/vsl : shift left on the entire vector. -- vec_slo/vslo: shift left on the entire vector, by octet. -- Those operations works on vectors seens as a quad-word. -- Let us suppose that we have a conversion operation named To_Quad_Word -- for converting vector types to a quad-word. -- Let A be a Altivec vector of 16 components: -- A = (A(0), A(1), A(2), A(3), ... , A(14), A(15)) -- Let B be a Altivec vector of 8 components verifying: -- B = (A(0) |8| A(1), A(2) |8| A(3), ... , A(14) |8| A(15)) -- Let C be a Altivec vector of 4 components verifying: -- C = (A(0) |8| A(1) |8| A(2) |8| A(3), ... , -- A(12) |8| A(13) |8| A(14) |8| A(15)) -- (definition: |8| is the concatenation operation between two bytes; -- i.e. 0x1 |8| 0x2 = 0x0102) -- According to [PIM - 4.2 byte ordering], we have the following property: -- To_Quad_Word (A) = To_Quad_Word (B) = To_Quad_Word (C) -- Let To_Type_Of_A be a conversion operation from the type of B to the -- type of A. The quad-word operations are only implemented by one -- Altivec primitive operation. That means that, if QW_Operation is a -- quad-word operation, we should have: -- QW_Operation (To_Type_Of_A (B)) = QW_Operation (A) -- That is true iff: -- To_Quad_Word (To_Type_Of_A (B)) = To_Quad_Word (A) -- As To_Quad_Word is a bijection. we have: -- To_Type_Of_A (B) = A -- resp. any combination of A, B, C: -- To_Type_Of_A (C) = A -- To_Type_Of_B (A) = B -- To_Type_Of_C (B) = C -- ... -- Making sure that the properties described above are verified by the -- conversion operations between vector types has different implications -- depending on the layout of the vector types: -- * with format 1 and 3: only a unchecked conversion is needed; -- * with format 2 and 4: some reorganisation is needed for conversions -- between vector types with different component sizes; that has a cost on the -- efficiency, plus the complexity of having different memory pattern for -- the same quad-word value, depending on the type. -- Operation dealing with memory: -- ------------------------------ -- These operations are either load operation (vec_ld and the -- corresponding primitive operation: vlx) or store operation (vec_st -- and the corresponding primitive operation: vstx). -- According to [PIM 4.4 - vec_ld], those operations take in input -- either an access to a vector (e.g. a const_vector_unsigned_int_ptr) -- or an access to a flow of components (e.g. a const_unsigned_int_ptr), -- relying on the same Altivec primitive operations. That means that both -- should have the same representation in memory. -- For the stream, it is easier to adopt the format of the target. That -- means that, in memory, the components of the vector should also have the -- format of the target. meaning that we will prefer: -- * On a big endian target: format 1 or 4 -- * On a little endian target: format 2 or 3 -- Conclusion: -- ----------- -- To take into consideration the constraint brought about by the routines -- operating on quad-words and the routines operating on memory, the best -- choice seems to be: -- * On a big endian target: format 1; -- * On a little endian target: format 3. -- Those layout choices are enforced by GNAT.Altivec.Low_Level_Conversions, -- which is the endianness-dependant unit providing conversions between -- vector views and vector types. ---------------------- -- Layouts summary -- ---------------------- -- For a user abstract vector of 4 uints (1, 2, 3, 4), increasing -- addresses from left to right: -- ========================================================================= -- BIG ENDIAN TARGET MEMORY LAYOUT for (1, 2, 3, 4) -- ========================================================================= -- View -- ------------------------------------------------------------------------- -- | 0x0 0x0 0x0 0x1 | 0x0 0x0 0x0 0x2 | 0x0 0x0 0x0 0x3 | 0x0 0x0 0x0 0x4 | -- | V (0), BE | V (1), BE | V (2), BE | V (3), BE | -- ------------------------------------------------------------------------- -- Vector -- ------------------------------------------------------------------------- -- | 0x0 0x0 0x0 0x1 | 0x0 0x0 0x0 0x2 | 0x0 0x0 0x0 0x3 | 0x0 0x0 0x0 0x4 | -- | V (0), BE | V (1), BE | V (2), BE | V (3), BE | -- ------------------------------------------------------------------------- -- ========================================================================= -- LITTLE ENDIAN TARGET MEMORY LAYOUT for (1, 2, 3, 4) -- ========================================================================= -- View -- ------------------------------------------------------------------------- -- | 0x1 0x0 0x0 0x0 | 0x2 0x0 0x0 0x0 | 0x3 0x0 0x0 0x0 | 0x4 0x0 0x0 0x0 | -- | V (0), LE | V (1), LE | V (2), LE | V (3), LE | -- Vector -- ------------------------------------------------------------------------- -- | 0x4 0x0 0x0 0x0 | 0x3 0x0 0x0 0x0 | 0x2 0x0 0x0 0x0 | 0x1 0x0 0x0 0x0 | -- | V (3), LE | V (2), LE | V (1), LE | V (0), LE | -- ------------------------------------------------------------------------- -- These layouts are common to both the soft and hard implementations on -- Altivec capable targets.