Contents


Using inline assembly with IBM XL C/C++ compiler for Linux on z Systems, Part 1

Primer

Assembler instructions for embedding applications on mainframe computers

Comments

Content series:

This content is part # of # in the series: Using inline assembly with IBM XL C/C++ compiler for Linux on z Systems, Part 1

Stay tuned for additional content in this series.

This content is part of the series:Using inline assembly with IBM XL C/C++ compiler for Linux on z Systems, Part 1

Stay tuned for additional content in this series.

IBM XL C/C++ compiler for Linux on z Systems Version 1, released in 2015, enables support for incorporating user's assembler codes directly into C/C++ programs (inline assembly). Besides the ability to generate highly optimized codes, maximizing hardware utilization, the new compiler provides advanced users with greater flexibility to access instructions at chip level. With inline assembly, software engineers are able to handcraft assembler codes for the most performance-sensitive parts of C/C++ programs. This will further accelerate the execution of the applications to the full extent of the programmers' ingenuity.

In this series

Articles included in this series:

See descriptions of all four parts of this series.

Registers

There are 16 general registers, identified by numbers 0 to 15, each of which contains 64 bit positions. With the exception of general register 0, which cannot be designated as containing base address or index, general registers 1 to 15 can be used as base address registers and index registers in address arithmetic operations. In general arithmetic and logical operations, general registers can be used as accumulators. Designated usage of some general registers of z Systems running Linux are listed in Table 1.

Table 1: Designated usage of general registers on Linux on z Systems
Register nameSpecial usage
r2, r3 Parameters, return values
r4, r5, r6 Parameters
r13 Base register for literal pool
r14 Return address
r15 Stack pointers

General registers 0 to 15 will be regarded to as r0 to r15. There are instructions, such as multiply (MR) and divide (DR), requiring two adjacent general registers to be coupled into an operand. In these operations, the program must designate an even-odd pair of general registers. The operand of the register pair is specified with the even-numbered register. Figure 1 shows how the general registers are represented individually and in pairs.

Figure 1: Designation of 16 general registers, individually and in pairs

In addition to general registers, floating-point registers and vector registers are used by inline assembly. They will be described in detail in the subsequent articles about inline assembly with floating-point registers and vector registers.

Condition code

Condition code (cc) consists of bits 18 and 19 of the program status word (PSW) in the processor. Being a 2-bit value, cc can be set to 0, 1, 2, or 3, depending on the results obtained from executing certain instructions. Once set, cc remains unchanged until it is modified by another instruction. Most arithmetic and logical operations, as well as some other operations, set the cc. Execution-branching instructions can specify a selection of the cc values as a criterion for branching.

The snippet in Listing 01 demonstrates a usage of the cc at high level. Detail about inline assembly formats will be described in the next article about inline assembly for general instructions.

Listing 1: BRC and condition code
1  #include <stdio.h>
2 
3  void myAdd(int a, int b) {
4    int noOverflow = 1;
5    asm("AR  %0, %2\n"                 // If an overflow occurs, AR will set cc to 3
6        "BRC 0xE, OK\n"                // BRC inspects cc: if it is not 3 (no overflow), branch to line 8, that is, skip line 7
7        "XR  %1, %1\n"                 // XR unsets noOverflow variable
8        "OK:\n"
9        : "+r"(a), "+r"(noOverflow)
10       : "r"(b)
11       : "cc"                         // cc in the clobber list to inform the compiler that condition
12      );                              // code might be changed by the assembler instructions
13   if (noOverflow){
14      printf ("Sum is %d\n", a);
15   } else {
16      printf ("Overflow\n");
17   }
18 }
19 int main() {
20   int a, b;
21   a = 11, b = -2;                    // With a=11, b= -2, no overflow will occur,
22   myAdd(a, b);                       // "Sum is 9" will be printed out
23   a = 0x7fffffff, b = 0x7fffffff;    // a and b are set with large values to cause overflow
24   myAdd(a, b);                       // Overflow will occur. "Overflow" will be printed out
25   return 0;
26 }

%0, %1 and %2 refer to the first (variable a), second (variable noOverflow) and third operand (variable b) in lines 9 and 10.

The ADD (AR) instruction on line 5 sets the condition code as follows:

0Result of the addition is zero. No overflow
1Result is less than zero. No overflow
2Result is greater than zero. No overflow
3Overflow occurs

The value of the cc will be inspected by the branch on condition (BRC) instruction on line 6 to determine the subsequent path of execution. If the cc is 0, 1 or 2 (that is, no overflow), execution will branch to label "OK" on line 8, effectively skipping the exclusive OR (XR) instruction on line 7. If cc is 3 (that is, overflow did occur), the execution will continue with the next instruction on line 7; the XR instruction unsets the value of the noOverflow variable.

The code in Listing 1 prints out the sum when there was no overflow and displays "Overflow" when overflow did occur.

Listing 2: Compiling and running the program in Listing 1
$ xlc -o ex1 ./example01.c
$ ./ex1
Sum is 9                            // Result of 11 + (– 2)
Overflow                            // Overflow caused by adding a = 0x7fffffff to b = 0x7fffffff

For information about the condition-code values that may be set for all instructions, refer to the documents included with the system. In particular, a link to Appendix C. Condition-Code Settings of the book z/Architecture Principles of Operation, IBM Publication No. SA22-7832-10.

Assembler instructions

The general instructions operate on data stored in general registers and in storage. Other general instructions work with data residing in the PSW, time-of-day clock or coming from the instructions stream. Some instructions include character(s) with their names to indicate the length of the operands such as H (halfword) for 16-bit, F (word) 32-bit and G (doubleword) 64-bit. When the operands of an instruction are 32-bit long, the instruction name might or might not contain the letter F. If there is an instruction with the same name but with 64-bit operands, its name will include the letter G. An instruction with the same name but with a 64-bit first operand and a 32-bit second operand will have the letters GF in its name. For complete description of each instruction, refer the documents accompanying the system.

For the ease of discussion, assembler instructions in this article are categorized into loosely-defined groups. The article only focuses on some general instructions that are most likely used by software engineers. You can find an exhaustive list of assembler instructions in the References section.

Group of algebraic arithmetic instructions

These instructions perform algebraic operations on the operands. Operands of arithmetic operations can be a single register, a memory address, or an immediate value. In cases when there are two operands, the operation is applied on both operands, but the results are saved back in the first one. Accordingly, the instruction performs both READ and WRITE operations on the first operand.

For many algebraic arithmetic instructions, the condition code will be set as follows:

0Result of the operation is zero. No overflow
1Result is less than zero. No overflow
2Result is greater than zero. No overflow
3Overflow occurs

The following section will discuss some formats, which might be used more frequently.

OP R1, R2: Register-and-register format
The value saved in the register specified by the second operand, R2 is used to perform operations, such as add, subtract, multiply, or divide with the value currently stored in the register identified by the first operand, R1. The result of the operation is then saved in the first operand, R1.
R1 ← R1 OP R2

OP R1, I2: Register-and-immediate format
The immediate value specified by the second operand I2 is used to perform the required operation to the value currently stored in the register identified by the first operand, R1. The result of the operation is then saved in the first operand, R1.
R1 ← R1 OP I2

OP R1, D2 (X2,B2): Register-and-storage format
The value saved in the effective address specified by the second operand is used to perform the required operation with the value currently stored in the register identified by the first operand, R1. The result of the operation is then saved in the first operand, R1.
R1 ← R1 OP [value-stored-in-effective-address D2 (X2,B2)]

The effective address in operand two is computed as follows:

   D2: the displacement from the base address
+  B2: the base address
+  X2: the index to the base address

Example of algebraic arithmetic instructions
Assembler instructions can be embedded into a program by either (1) adding them directly to assembler codes or (2) using inline assembly with the C/C++ codes. This section will demonstrate how to insert an algebraic arithmetic instruction named Add halfword (AH) into a C program. AH instruction algebraically adds the contents of a two-byte field (halfword), which is in the storage specified by the second operand, to the contents of the first-operand register. The result is then stored in the first-operand register. The snippet in Listing 3 is a C program before asm instructions are inserted.

Listing 3: C program example02.c before inserting asm instructions
1   #include <stdio.h>
2   int main() {
3       int a = 0;
4       int ar[] = {0x00112233, 0x44556677};
5       printf ("a = 0x%08x, ar[0] = 0x%08x, ar[1] = 0x%08x\n", a, ar[0], ar[1]);
6       return 0;
7   }

In the runtime, example02.c prints out the values of a, ar[0], and ar[1], as exhibited in Listing 4.

Listing 4: Compiling and running example02.c
$ xlc -o ex2 ./example02.c
$ ./ex2
a = 0x00000000, ar[0] = 0x00112233, ar[1] = 0x44556677

The aim in this example is to insert AH instruction into the code so that it will add the two-byte value stored at address of ar to the value of a. Since the two-byte value stored at ar is 0x0011 and the current value of a is 0x0, the expected value of a after the operation is 0x11.

(1) Injecting AH instruction manually to the Assembler code
The assembler file named example02.s is produced by compiling example02.c with –S.
$ xlc example02.c –c –S

The most important contents of the assembler code example02.s are listed in Listing 5.

Listing 5: Contents of assembler file example02.s before inserting assembler instructions
  .L_main:
1         STMG    %r13,%r15,104(%r15)
2         LARL    %r13,$CONSTANT_AREA
3         LAY     %r15,-184(,%r15)     # Reserve 184 bytes on the stack; r15 to hold the top of the stack
4         MVHI    176(%r15),0          # Move 0 (value of a) to bytes 176 → 179 from r15
5         MVHHI   168(%r15),17         # Bytes 168 →169 from r15 hold 0x00 and 0x11 (which is 1710)
6         MVHHI   170(%r15),8755       # Bytes 170 →171 from r15 hold 0x22 and 0x33 (which is 875510)
7         MVHHI   172(%r15),17493      # Bytes 172 →173 from r15 hold 0x44 and 0x55 (which is 1749310)
8         MVHHI   174(%r15),26231      # Bytes 174 →175 from r15 hold 0x66 and 0x77 (which is 2623110)
          LA      %r2,16(,%r13)        # Load parameters for printf
          LGF     %r3,176(,%r15)
          LGF     %r4,168(,%r15)
          LGF     %r5,172(,%r15)
          BRASL   %r14,printf          # Call printf

On lines 4 to 8, the stack is populated with values of a (which is 0), ar[0] (which is 0x00112233), and ar[1] (which is 0x44556677).

Figure 2 is a visual representation of the stack after the execution of line 8. From line 9, the program prepares the parameters to be passed to printf. Function printf is then called with BRASL instruction.

Figure 2: Values on the stack after the execution of line 8

To achieve the expected results, AH instructions (Register-and-storage format) must be added to example02.s in the location just before the program starts preparing parameters for printf. In particular, it will be inserted as in Listing 6.

Listing 6: Inserting assembler instructions to assembler file example02.s
   .L_main:
1          STMG    %r13,%r15,104(%r15)
2          LARL    %r13,$CONSTANT_AREA
3          LAY     %r15,-184(,%r15)      # Reserve 184 bytes on the stack, r15 to hold the top of the stack
4          MVHI    176(%r15),0           # Move 0 (value of a) to bytes 176 → 179 from r15
5          MVHHI   168(%r15),17          # Bytes 168 →169 from r15 hold 0x00 and 0x11 (which is 1710)
6          MVHHI   170(%r15),8755        # Bytes 170 →171 from r15 hold 0x22 and 0x33 (which is 875510)
7          MVHHI   172(%r15),17493       # Bytes 172 →173 from r15 hold 0x44 and 0x55 (which is 1749310)
8          MVHHI   174(%r15),26231       # Bytes 174 →175 from r15 hold 0x66 and 0x77 (which is 2623110)

9          LA      %r1,168(,%r15)        # Load address of array ar on the stack to r1
10         L       %r0,176(,%r15)        # Load value of a (bytes 176 → 179 from r15) to r0
11         AH      %r0, 0(%r1)           # Add 2-byte stored in displacement 0 from address of ar (r1) to r0 
12         ST      %r0,176(,%r15)        # Results of AH in r0 is stored back to a (bytes 176 → 179 from r15)

           LA      %r2,16(,%r13)         # Load parameters for printf
           LGF     %r3,176(,%r15)
           LGF     %r4,168(,%r15)
           LGF     %r5,172(,%r15)
           BRASL   %r14,printf           # Call printf

AH needs to know the address of ar so that it can read two bytes in the location. It also requires the current value in a to perform the addition. Lines 9 to 10 are added to help prepare the information for AH.

  • Line 9: LA loads address of ar to r1.
  • Line 10: L loads current value of a to r0.
  • r0 and r1 are then used by AH on line 11.

On line 11, AH performs the following operations:

  1. Retrieving the two-byte value stored in displacement 0 from address of ar
    Since ar ={0x00112233, 0x44556677}, the two bytes at its displacement 0 form 0x0011.
  2. Adding the two-byte value to the content of r0
    Since r0 at this moment holds value of a, which is 0, the sum will be 0x11.
  3. Putting the results back to r0: Thus r0 will hold value 0x11.

After the execution of AH on line 11, r0 will hold the new value. The result will be stored back to a by ST instruction on line 12.

The result of inserting assembler instructions AH and others on lines 9 to 12 can be verified by compiling the edited assembler code and executing the new executable.

Listing 7: Compiling and executing the edited assembler codes
$ xlc -o ex2_new ./example02.s
$ ./ex2_new
a = 0x00000011, ar[0] = 0x00112233, ar[1] = 0x44556677           → a is 0x11, as expected

(2) Using inline assembly to embed AH instruction to the C code
Inline assembly provides a vehicle to achieve equivalent result to the above with much less effort.

Listing 8: Inserting inline asm statement to C file example02.c
1 #include <stdio.h>
2 int main() {
3     int a = 0;
4     int ar[] = {0x00112233, 0x44556677};
5     asm( "AH %0, %1" : "+r"(a) : "m"(ar[0]) );                                 // AH is inserted here  
6     printf ("a = 0x%08x, ar[0] = 0x%08x, ar[1] = 0x%08x\n", a, ar[0], ar[1]);
7     return 0;
8 }

Inline assembly only requires the user to add the main instruction (AH, in this case). It leaves the burden of adding supporting operations such LOAD ADDRESS, LOAD, and STORE on lines 9, 10 and 12 with the compiler. Using inline assembly within the C code in this manner should produce the same output at execution.

Listing 9: Compiling and executing the new C file with inline assembly
$ xlc -o ex2_asm ./example02_asm.c
$ ./ex2_asm
a = 0x00000011, ar[0] = 0x00112233, ar[1] = 0x44556677          → a is 0x11, as expected

The inline assembly instruction in this example makes use of the memory constraint, which will be discussed in subsequent article about Linux on z Systems inline assembly.

Group of Compare instructions

When there are two operands, instructions in this group compare the first operand with the second operand. The result of the comparison is recorded in the condition code as follows:

0Two operands are equal
1The first operand is less than the second one
2The first operand is more than the second one
3Not applicable

COMP R1, R2: Register-and-register format
The value saved in the register specified by the second operand, R2 is compared with the value currently stored in the register identified by the first operand, R1.

COMP R1, I2: Register-and-immediate format
The immediate value specified by the second operand I2 is compared with the value currently stored in the register identified by the first operand, R1.

COMP R1, D2 (X2,B2): Register-and-storage format
The value saved in the effective address specified by the second operand is compared with the value currently stored in the register identified by the first operand, R1.

COMP D1(B1), I2: Storage-and-immediate format
The immediate value specified by the second operand I2 is compared to the value saved in the effective address specified by the first operand.

Example of Compare instructions
CH 5, 0x30 (0, 9)
The Compare halfword (CH) instruction compares a 16-bit signed integer (halfword) in storage with the contents of a register. CH treats the first operand as a 32-bit signed integer; the displacement is treated as a 12-bit unsigned integer.

In this example, the state before the operation is assumed as follows:

Table 2: Values in registers and memory locations before CH is called

Symbols in Op code
OP R1, D2(X2,B2)
Assembler format
CH 5, 0x30 (0, 9)
Meaning Contents
OP
R1
D2
X2
B2
CH
5
0x30
0
9
Compare Halfword
Register 5
Displacement
Index 0
Register 9

0xFFFF 9000 = – 2867210
0x30
0x0
0x00012050
Storage location 12080 – 12081 0x9000 = – 2867210

The effective address is: B2 + X2 + D2 = 0x00012050 + 0x0 + 0x30 = 0x00012080.
The value of 2 bytes stored in address location 12080 is 0x9000 or – 2867210.
The content in register 5 is 0xFFFF 9000 or – 2867210.

Since the two numbers are equal, condition code 0 is set.

Again, instead of computing the effective address directly as above, inline assembly provides memory constraints, which can significantly simplify the process.

Group of branch on condition instructions

Branch on condition instruction inspects the condition code in the current PSW. If the condition code has one of the values specified by the mask, the "next instructions address" in the PSW will be replaced by the branch address stipulated in the branch instruction. Otherwise, normal instruction sequencing proceeds.

The branch on condition instructions do not change the condition code.

BC M1, R2: Register-and-register format
If the condition code has one of the values specified by the mask M1, the execution is branched to the address stored in the general register stipulated by the second operand, R2.

BC M1, D2 (X2,B2): Register-and-storage format
If the condition code has one of the values specified by the mask M1, the execution is branched to the address computed based on D2, X2, and B2.

The mask
The M operand is a four-bit mask. The four condition codes (0, 1, 2, and 3) correspond with the four bits of the mask as follows:

Table 3: Relationship between condition code and mask
2-bit condition code 4-bit mask Mask value
002 or 010 1 0 0 0 0x8
012 or 110 0 1 0 0 0x4
102 or 210 0 0 1 0 0x2
112 or 310 0 0 0 1 0x1

The expected condition code is the criterion to select the corresponding mask bit. If the mask bit selected by the condition code is one, the branch will be successful. Otherwise, normal instruction sequencing proceeds. As described in Listing 1, in order to branch only when the condition code is not equal to 3, or equivalently when the condition code is any of {0, 1, 2}, the mask must be 11102 or 0xE in hexadecimal representation.

When all four mask bits are zeros, or when R2 operand holds zero, the branch instruction is equivalent to a no-operation. Similarly, if the mask is 11112, the branch is unconditional unless R2 is zero. Execution of BCR 15, 0 may results in significant performance degradation.

Example of BRC instructions

The following function absoluteValue takes an integer and returns the absolute value of an integer. Obviously, it can be as trivial as

int absoluteValue(int a) { return a < 0 ? –a : a; }

In this example, however, it is crafted with Compare and BRC instructions to demonstrate how the instructions are used together with the condition code and the mask.

Listing 10: Inline assembly statements using Compare and Branch instructions
1   int absoluteValue(int a) {
2        asm (" CFI %0, 0\n"            // Compare value of a with 0
3             " BRC 0xA, DONE\n"        // If a >= 0, go to DONE i.e. skip line 4
4             " LCR %0, %0\n"           // Coming here means a <0, load-complement a to negate it
5             " DONE:\n"
6             :"+r"(a)
7             );
8       return a;
9   }

%0 refers to the first operand of the inline assembly statement: variable a.

On line 2, a is compared against zero. As described in the Group of Compare instructions section CFI instruction sets condition code in PSW as follows:

Table 4: Relationship between cc and mask
Compare a against 0 Condition code Mask bits
a = 0 0 = 002 1000
a < 0 1 = 012 0100
a > 0 2 = 102 0010

The logic in function absoluteValue(int a) dictates that it return a if a is equal to or greater than 0. This is equivalent to the condition code being set to either 0 or 2. The corresponding mask pattern for these cases is 1010 (that is, 0xA in hexadecimal).

BRC on line 3 checks if the current condition code in PSW matches the mask 0xA. If there is a match, it will branch to label DONE on line 5, effectively making it ready for the function to return a without modification. Following this path, absoluteValue(int a) returns a if a >= 0.

If BRC finds no match on line 3, load complement (LCR) instruction on line 4 is executed. LCR loads the two's complement of the second operand (variable a) in the first-operand location (variable a, itself), effectively negating the value of a. Following this path, the function will return –a when a is negative.

To maximize the performance of their programs, software engineers can select the optimal instructions from a great wealth of assembler instructions on z Systems. For example, the above function can be written differently to achieve higher performance such as in Listing 12 below.

Group of Load instructions

When there are two operands, Load instructions place the second operand, unchanged or sign-extended, in the first-operand location.
The direction of the operation is Operand1 Operand2.

L R1, R2: Register-and-register format
The value saved in the register specified by the second operand, R2 is placed, unchanged or sign-extended, in the register identified by the first operand, R1.

L R1, I2: Register-and-immediate format
The immediate value specified by the second operand I2 is placed, unchanged or sign-extended, in the register identified by the first operand, R1.

L R1, D2 (X2,B2): Register-and-storage format
The value saved in the effective address specified by the second operand is placed, unchanged or sign-extended, in the register identified by the first operand, R1.

Example of Load instructions
The function absoluteValue mentioned in Example of BRC instructions can be modified slightly to demonstrate the usage of load instructions.

Listing 11: Example of load instruction
1    int absoluteValue(int a) {
2        asm (" LTR %0, %0\n"           // Load and test value of a 
3             " BRC 0xA, DONE\n"        // If a >= 0, go to DONE i.e. skip line 4
4             " LCR %0, %0\n"           // Coming here means a < 0, load-complement a to negate it
5             " DONE:\n"
6             :"+r"(a)
7            );
8       return a;
9   }

On line 2, load and test (LTR) instruction performs two operations:

  1. Placing the second operand unchanged in the first operand location.
  2. Updating the condition code based on the loaded value.
0The loaded value is zero
1The loaded value is less than zero
2The loaded value is greater than zero
3Not applicable

Since both operands are the same register holding variable a, LTR on line 2 is equivalent to a test whether a is less than, equal to or greater than 0 without moving data.

On line 3, BRC inspects if the current condition code matches the mask 0xA. If there is a match (that is, a >= 0), it will branch to label DONE on line 5, and thus the function to return a without modification. Otherwise (that is, a < 0), LCR on line 4 will load the two's complement of a to negate its value. The function thus returns –a.

This very same function can be written with different assembler instructions to achieve higher performance. In the next example, a single LOAD instruction will replace those two LOAD instructions, one BRANCH instruction and a LABEL.

Listing 12: Using a different asm instruction
1   int absoluteValue(int a) {
2       asm (" LPGFR %0, %0\n" :"+r"(a) );          // Load-Positive a
3       return a;
    }

LPGFR instruction loads the absolute value of the second operand in the first operand. Since both the operands are variable a, the instruction effectively performs the assignment a = | a |. The functions can return the expected value after LPGFR.

Group of Store instructions

When there are two operands, store (ST) instructions place the first operand in the second-operand location.

The direction of the operation is Operand1 Operand2.

ST R1, D2 (X2,B2): Register-and-storage format
The value saved in the first operand, R1 is placed unchanged to the effective address specified by the second operand.

Note: There are many other instructions on z Systems. This article only discusses some representative instructions.

Example of store instructions
In the following example, ST instruction will store the value of variable a to the address which currently holds variable b. Since a = 1 and b = 0 before the operation, b will become 1 afterward. With b = 1, the function will return 0.

The example makes use of memory constraint "m", which can significantly simplify the address computation.

Listing 13: Example of ST instruction
1   int main() {
2       int a = 1, b = 0;               // b = 0
3       asm("ST %1,%0\n"                // Store value of a to the address which currently holds b 
4          :"=m"(b)
5          :"r"(a)
6          );
7       return a==b ? 0 : 1;           // Based on line 2, b is expected to become 1. Accordingly, function must return 0
8   }

Conclusion

In general, if an instruction operates on two operands, the direction of the operation depends on the type of the instruction. COMPARE and TEST instructions will record the condition code with their results. BRANCH instructions inspect the condition code to determine the next path of execution. LOAD instructions, when there are two operands, will load the second operand to the first one, that is, Operand1Operand2. On the other hand, STORE instructions store the first operand to the second one, that is, Operand1Operand2. Arithmetic instructions will apply the operation on both operands and then store the results to the first one, that is, Operand1Operand1 OP Operand2.

Although Linux on z Systems operates on the same architecture as IBM z/OS®, z/TPF, z/VSE®, and z/VM®, inline assembly for Linux on z Systems only accepts assembler instructions. High Level Assembler (HLASM) instructions and macros such as DS, DD, GETMAIN will not be accepted.

Advanced users can improve the runtime performance by fine-tuning the assembler code generated by the compiler. The effort, however, should be dedicated to the most performance-sensitive parts of the program. While inserting inline asm statements and/or assembler instructions to the program may improve the execution, it may also impede the advanced optimizations carried out by the compiler and thus result in significant performance degradation. For example, if a label is used in the inline assembly, certain optimization such as inlining will be impacted. Careful planning and thorough testing are essential when working with the assembler codes.

Acknowledgements

I would like to thank Ms. Visda Vokhshoori, the z/OS Lead for Compiler Optimizations at Toronto Software Lab, IBM Canada. Her technical advice played an important role in the composition of this article.

Resources

References


Downloadable resources


Comments

Sign in or register to add and subscribe to comments.

static.content.url=http://www.ibm.com/developerworks/js/artrating/
SITE_ID=1
Zone=Linux
ArticleID=1008213
ArticleTitle=Using inline assembly with IBM XL C/C++ compiler for Linux on z Systems, Part 1: Primer
publish-date=06172015