TweetFollow Us on Twitter

Function Calls
Volume Number:10
Issue Number:6
Column Tag:Powering UP

PowerPC Function Calls

It helps to know all that stuff that compilers take for granted.

By Richard Clark, General Magic

Note: Source code files accompanying article are located on MacTech CD-ROM or source code disks.

Most RISC processors, including the PowerPC, use a different method for calling procedures than CISC processors do. Most CISC processors make use of the stack for passing parameters, holding temporary variables, and storing return addresses. The PowerPC run-time architecture performs these functions differently. Unlike the 68K Macintosh run-time model, where multiple calling conventions are used, the PowerPC runtime model specifies one calling convention for all code, regardless of the programming language used. This impacts assembly-language programming and debugging, and a very small number of high-level language programs which use assembly language to construct a stack frame before calling a function.

This month, we’ll describe how a Power Macintosh program calls PowerPC functions, and give some hints for locating procedure calls in compiled code. We’ll also go into the mail bag and follow up on calling between 68K and PowerPC code.

How PowerPC calls procedures

Most RISC processors are designed to make extensive use of their registers, since it takes less time for a processor to access an internal register than it does to access external memory. One example of this is the PowerPC’s load/store architecture (see Powering Up, February, 1994) which requires that all of the operands of an instruction be loaded into registers before the operation is performed.

The Power Macintosh calling conventions are biased heavily towards the use of registers. (See Figure 1) Both the Fixed-point and floating-point registers are divided into “reserved” registers, parameter passing registers, and “local variable” registers. (The General Purpose Registers are shown as 32 bits wide here, but will be 64 bits wide on 64-bit implementations of the PowerPC.)

Figure 1 - The PowerPC registers

In the simplest sort of procedure, which accepts only a small number of parameters and which doesn’t call any other procedures (known as a “leaf procedure”), the procedure call doesn’t ever touch memory:


/* 1 */
/* A trivial “leaf routine” */
int average (int a, int b)
{
 return (a + b) / 2;
}

; The compiled version of the above routine
; Register usage:
;  Register 3 = a
;  Register 4 = b
;  Register 5 = unused parameter register, used as scratch
;
;  Register 3 is used for the function result
;  The return address is in a special “link register”, not on the stack

 csect     .average{PR}   ; Start of the function
 addc      r5,r3,r4; R5 = a + b (and check for carry)
 srawi     r5,r5,1 ; Shift R5 right 1 place
 addze     r3,r5 ; Copy R5 to R3 and add the carry
 ; (addze =  “add to zero extended)
 blr    ; return

In this example, both of the parameters were passed in parameter registers, the intermediate value was placed in an unused parameter register, and the return result went back in the first parameter register. Since the PowerPC uses an internal register for return addresses, nothing had to be placed on the stack.

However, most procedures aren’t nearly this simple: they use local variables, or call other procedures, have long parameter lists, or need to support variable parameter lists. If a procedure has any of these attributes, it needs to create and use a stack frame.

PowerPC stack frames

The PowerPC stack format is shown in Figure 2 . The PowerPC uses a “grow down” stack, just as the 68K does. However, that’s where the similarity ends. Each procedure is responsible for creating its own stack frame (unlike the 68K where, one could argue that the caller creates the “parameter” part of a stack frame and the callee creates the rest.) Once created, each PowerPC stack frame remains the same size - a procedure cannot “push” a few bytes onto the stack for temporary storage Finally, each stack frame must end with a pointer back to the previous stack frame.

[The diagrams here are “upside-down” from what you normally might see in Macintosh documentation, but are in line with much of the documentation you’re likely to come across. It’s a long and sordid story, and has something to do with Apple making a deal with a certain three-letter company, but we’ll save that for another time. When you see these diagrams, it never hurts to double-check where the top of memory is. In this case, up is towards zero, and that’s the direction the stack grows. - Ed stb]

Figure 2 - A single stack frame

Each stack frame may begin with a register save area. If a procedure intends to use one of the “non-volatile” registers shown in Figure 1, it must save the old value of the register and restore it later. These registers are saved at the “bottom” of the stack frame. (Only the non-volatile registers should be saved at the bottom of the stack - see “borrowing the stack” below for an explanation.)

Next comes an unstructured local variable area. The current procedure is free to put anything it wants here, including any local variables that won’t be placed in the non-volatile registers.

The parameter area comes next. This area is used to pass parameters to any procedures the current procedure calls, not to hold its own parameters. (A procedure looks into the parameter registers and its caller’s stack frame for parameters - the reason why is given in “borrowing the stack.”) As a result, the compiler looks at all of this procedure’s callees, and uses the size of the largest parameter list it finds. This area is formatted according to the rules given in “passing parameters” below.

Figure 3 - The Linkage Area

The linkage area forms the end of every stack frame, and is a special data structure used to hold a link to the previous address, the return address, and the two non-volatile registers within the branch unit. (See Powering Up, February 1994 for a description of the branch unit.) The current procedure always fills in the “previous stack frame” field, and will fill in the “old return address” field if it calls another procedure. The other fields are either filled in by the callee, or are reserved for system use. This unusual “I’ll fill in some fields and you fill in the rest” behavior is explained in “borrowing the stack.”

Passing Parameters

When one procedure calls another, the caller is responsible for providing the parameter area on the stack, and places some parameters into registers and others onto the stack. The parameters are laid out in memory (and the registers) according to a simple set of rules:

1. Each parameter begins on a 4-byte boundary. So, a C “short” or “char” is padded out to a 32-bit word, while pointers, integers, and long integers remain unchanged. The compiler only does this to individual parameters - it doesn’t go into data structures and add padding when passing them from procedure to procedure.

2. The first eight words of parameters (reading the parameter list from left to right) are assigned to Registers 3 through 10 (in the Fixed Point unit). If any of these parameters are floating-point numbers, space is set aside in one or more fixed-point registers, though the value might not actually be filled in. (See the next rule for an explanation.)

For example, try the function:


/* 2 */
void Sample (short aShort, 
 long aLong, 
 int anInt, 
 float lifesaver, 
 double seeing, 
 short changed, 
 long shot, 
 long overflow);

Showing these parameters as a structure, with each of the parameters padded out to a word (that’s a full machine word, 32 bits on the 601), the comments show which register gets which parameter:


/* 3 */
struct SampleParams {
 int  aShort;    // R3
 long aLong;// R4
 int  anInt;// R5
 float  lifesaver; // FPR1, and possibly R6 (see “3” below)
 double seeing;  // FPR2, and possibly R7/R8
 int  changed;   // R9
 long shot; // R10
 long overflow;  // Out of registers - this goes on the stack!
}

3. The first 13 Floating-Point parameters go into floating-point registers. Floating-point parameters always go into floating-point registers when the caller passes a floating-point value, as in:


/* 4 */
 void test (short numfloats, float a, float b);

 test(2, 3.1, 3.2);

The two floating-point values wind up in Floating-Point Registers 1 and 2. However, if the compiler doesn’t see a function prototype which gives the parameters for test(), it has to assume that the caller might not look in the floating-point registers, and so places a copy of each value in the corresponding Fixed-Point registers. (This explains why the example above showed FPR1 as the register used, but also mentioned R6 - the register usage has to be consistent, even if the fixed-point registers won’t be filled in by the caller.)


/* 5 */
 void test (); // prototype doesn’t show parameters

 test(3.0, 3.1, 3.2)
 void test (short numValues, long a, long b)
   // this code expects numValues in R3, a in R4, and b in R5 ...

A similar situation occurs when using variable argument lists. In that case, two things happen differently from a “normal” procedure call:

1. Any floating-point values in the first 8 words are passed in general purpose registers, just as in the “no prototype” case above.

2. Registers 3 through 10 are written out to the parameter area at the start of the call, and the parameters are then read from memory. (Since procedures with variable parameter lists often loop over all of the parameters in the list, this makes the code much simpler.) This requires that the caller set aside a minimum of 8 words in the parameters area, since the procedure will blindly “dump” all 8 parameter passing registers.

There’s one more element of a stack frame that isn’t shown here, and that’s how the end of the frame is aligned. By convention, the end of a stack frame should be on a 64-byte boundary, with extra space included in the “local variables” area to force this outcome.

A Stack Frame Example

If our “average” procedure calls another procedure (say, for integer division), a stack frame is used to hold the previous return address :


/* 6 */
extern int div (int value, int divisor);

int average (int a, int b)
{
 return div(a + b, 2);
}

; ------- resulting assembly ------
 csect     .average{PR}
 ; - - - The prolog
 mflr      r0  ; Move the return address from its “link register” into 
register 0
 stw       r0,0x0008(SP)  ; Save the link register into caller’s linkage 
area.
 stwu      SP,-0x0038(SP) ; Create a stack frame
 ; - - - The body
 addc      r3,r3,r4; R3 = a + b
 li        r4,2  ; Move 2 into R4 (load immediate)
 bl        .div  ; Call “div”, R3 = a+b, R4 = 2
 nop    ; (explained later)
 ; - - - The epilog
 lwz       r0,0x0040(SP); copy the saved link register to R0
 addic     SP,SP,56; Remove the stack frame
 mtlr      r0    ; Move R0 to the link register
 blr    ; return (branch to link register)

If you examine many PowerPC functions, you’ll notice some similarities with this one. Most functions begin with a standard “prolog” which saves link register and creates a stack frame (the first three instructions above) and (optionally) saves any non-volatile registers used. Most functions end with an “epilog” which restores the saved registers, restores the link register, removes the stack frame, and returns. If you learn to recognize these sections, reading disassembled code becomes much easier.

“Borrowing” the stack

There’s one interesting exception to the calling convention above which occurs in a “leaf procedure.” Leaf procedure don’t call any other procedures, so they are at the ends of the “calling tree.” If a leaf procedure doesn’t place any local variables on the stack, it can operate without setting up a stack frame: it gets its parameters from registers and its caller’s stack frame, and can save the condition register inside its caller’s linkage area. (This explains why the caller sets up the parameter area and why part of the linkage area is used by the callee.)

You might think that this rule is unduly restrictive, since a procedure can’t do much without local variables. Leaf procedures are permitted to use the non-volatile registers for local variables, just as any other procedure can, as long as they save the registers to the stack at the beginning of the procedure and restore them at the end.

“But I thought you said the procedure doesn’t have a stack frame!” That’s true. In this one case, the leaf procedure is allowed to write information past the end of the stack frame. Normally, the next procedure called would overwrite such information, but this is a leaf procedure, and won’t call anything else.

Now, some of you might sense a trap - what happens if an interrupt occurs during a procedure call? Won’t the interrupt service routine set up a stack frame and overwrite the saved registers? The runtime model requires that interrupts decrement the stack pointer by 224 bytes (which is the total size of the non-volatile registers rounded up to a 64-byte boundary.)

Since leaf routines are called frequently, it makes sense to speed them up in this way, even if it makes the calling conventions a little harder to understand at first.

Recognizing calls in PowerPC code

Most of this information won’t affect a C or Pascal programmer, until it comes time to debug. Then, if you can’t use the source level debugging, you might want to find function calls at the assembly level.

We’ll begin with our simplest example, the “average” leaf procedure shown at the beginning of the article. When we call this function from main:


/* 7 */
main()
{
 int a;
 a = average(3, 4);
}
the resulting code looks like this:
 
 ; - - - prolog
 mflr      r0    ; Move the return address from the
 ; “link register” into register 0
 stw       r0,0x0008(SP)  ; Save the link register
 stwu      SP,-0x0040(SP) ; Create the stack frame   
 ; - - - body
 li        r3,3  ; R3 = 3 (first parameter)     
 li        r4,4  ; R4 = 4 (second parameter)   
 bl        $-0x0024; call “average”
 stw       r3,0x0038(SP)  ; Copy the result to “a” 
 ; - - - epilog
 lwz       r0,0x0048(SP)  ; Get the saved link register
 addi      SP,SP,64  ; Remove the stack frame
 mtlr      r0    ; Restore the link register
 blr    ; Return (branch to link register)

A more common variation occurs when “main” and “average” are in different 
files. In that case, you’ll see either:
 bl        .average{PR}   ; call “average”
 nop

or

 bl    .average{GL}; call “average” via glue code
 lwz       20(SP),R2

The first case indicates that “main” and “average” were linked together into the same code fragment; the second case indicates a call between code fragments (usually into a shared library.) The second case is also known as a “Cross-TOC” call.

As we explained in the April Powering Up, each code fragment consists of both a code section and a data section. The TOC (Table of Contents) is a block of pointers in the data section which points to individual global variables and “Transition Vectors” used to call between code fragments. Register 2 should always point to the beginning of the current function’s TOC, so if one function calls another in a different module, the caller must set up register 2 before transferring control.

This setup code is inserted automatically by the linker. When the linker detects a call between code fragments, it appends some “glue” code to the fragment under construction, changes the “branch and link” instruction to point to this glue (hence the change from “.average{PR}” to “.average{GL}”), and replaces the no-op (nop) instruction with a “restore register 2” instruction.

Typical glue code looks something like this:


/* 8 */
 csect     .average{GL}
 .average
 lwz    r12,average(RTOC) ; Get average’s Transition Vector address 
 stw    RTOC,0x0014(SP)   ; Write the old value of R220 bytes below 
 ;the top of the current function’s stack frame 
 ; (this is in the linkage area)    
 lwz    r0,0x0000(r12)    ; Get the final code address
 ; from the Transition Vector lwz    RTOC,0x0004(r12)    ; Get new value 
of R2 from Transition Vector
 ; (this is the proper value for the callee)
 mtctr  r0; Stick the code address into the “count register”
 bctr   ; Jump through the count register

Since the glue code uses a “jump to” instruction to get to the destination (a “goto” for you old BASIC fans), the callee will return back into the original caller’s code, and not into the glue code. Therefore, the caller must contain the “reload R2” instruction which puts the TOC pointer back where it belongs before continuing.

Now we come to the real question: “how do I recognize a function call in PowerPC code?” The answer is deceptively simple: look for the “branch and link” (bl) instruction, and work your way backwards, looking for loads into Registers 3 through 10 (and Floating-Point registers 1 through 13.) Most developers find that this trick is much easier than reading forward through a long listing trying to track each value in and out of the registers.

Just when you thought you understood

There’s one more thing that you must know about when reading decompiled code - when you compile a file for symbolic debugging, the compiler inserts some extra code which copies all of the parameters into the caller’s parameter area on the stack, and only moves them into registers when needed. (If a parameter is changed, the changed version is written back to the stack.) This trick simplifies debugging, as the debugger only has to look on the stack for local variables and parameters, but it complicates the code. So, if you see “stw R3, 56(SP)” in a function’s prolog, you’re probably looking at code that was compiled with symbols on.

From the Mail Bag

After looking at the April issue (with the articles on the Code Fragment Manager and calling 68K code from PowerPC code), some developers have asked “how do I call PowerPC code from 68K code?”

If you want to leave the 68K code alone, you need to construct one or more Routine Descriptors in the PowerPC code and pass the addresses of these (Universal Procedure Pointers, in other words) to the 68K code. Then, the 68K code can simply jump through the universal pointer.

The PowerPC code can provide a function whose job it is to construct these pointers (as shown in the April article), or if the code is in a resource, can use the definitions in “Mixed Mode.r” to place a Routine Descriptor in front of the code.

If you want to leave the PowerPC code alone, the 68K code can use a Mixed Mode A-Trap to create the routine descriptor. The bad news is this: blindly calling “NewRoutineDescriptor” won’t do what you want. (When compiling for the 68K, NewRoutineDescriptor is an “null” macro that just returns the supplied procedure pointer.) The solution is to copy the definition of NewRoutineDescriptor, and convert the contents of the “THREEWORDINLINE” macro into an actual inline.

The result of all this work looks like this:


/* 9 */
#if USES68KINLINES
 pascal UniversalProcPtr InlineNewRoutineDescriptor(
 ProcPtr theProc,
 ProcInfoType theProcInfo,
 ISAType theISA)
 = {0x303C, 0x0000, 0xAA59};
#endif

typedef void (*OurProcPtr)(short);

void CallPowerProc (ProcPtr powerCodeAddress, 
 short onlyParameter)
{
 OurProcPtr ourUPP;
 short  procInfo = kCStackBased |
 STACK_ROUTINE_PARAMETER(1, kTwoByteCode);

#if USES68KINLINES
 ourUPP=(OurProcPtr) InlineNewRoutineDescriptor( 
 powerCodeAddress,
 procInfo,
 kPowerPCISA);
#else
 // We’re compiling for PowerPC -- see the comments below
 ourUPP= (OurProcPtr) NewRoutineDescriptor(
 powerCodeAddress,
 procInfo,
 kPowerPCISA);
#endif
 CallUniversalProc((UniversalProcPtr)ourUPP, procInfo,         
 onlyParameter); 
 // If you *know* this is 68K code, you could just use:  ourUPP(onlyParameter)
 // but using CallUniversalProc covers the possibility that this could 
be compiled 
 // for PowerPC some day without changing the sources
}

That’s all for now. Look in next month’s Powering Up column for more of the latest tips for writing, debugging, and tuning PowerPC code.

 
AAPL
$119.00
Apple Inc.
+1.40
MSFT
$47.75
Microsoft Corpora
+0.28
GOOG
$540.37
Google Inc.
-0.71

MacTech Search:
Community Search:

Software Updates via MacUpdate

Skype 7.2.0.412 - Voice-over-internet ph...
Skype allows you to talk to friends, family and co-workers across the Internet without the inconvenience of long distance telephone charges. Using peer-to-peer data transmission technology, Skype... Read more
HoudahSpot 3.9.6 - Advanced file search...
HoudahSpot is a powerful file search tool built upon MacOS X Spotlight. Spotlight unleashed Create detailed queries to locate the exact file you need Narrow down searches. Zero in on files Save... Read more
RapidWeaver 6.0.3 - Create template-base...
RapidWeaver is a next-generation Web design application to help you easily create professional-looking Web sites in minutes. No knowledge of complex code is required, RapidWeaver will take care of... Read more
iPhoto Library Manager 4.1.10 - Manage m...
iPhoto Library Manager lets you organize your photos into multiple iPhoto libraries. Separate your high school and college photos from your latest summer vacation pictures. Or keep some photo... Read more
iExplorer 3.5.1.9 - View and transfer al...
iExplorer is an iPhone browser for Mac lets you view the files on your iOS device. By using a drag and drop interface, you can quickly copy files and folders between your Mac and your iPhone or... Read more
MacUpdate Desktop 6.0.3 - Discover and i...
MacUpdate Desktop 6 brings seamless 1-click installs and version updates to your Mac. With a free MacUpdate account and MacUpdate Desktop 6, Mac users can now install almost any Mac app on macupdate.... Read more
SteerMouse 4.2.2 - Powerful third-party...
SteerMouse is an advanced driver for USB and Bluetooth mice. It also supports Apple Mighty Mouse very well. SteerMouse can assign various functions to buttons that Apple's software does not allow,... Read more
iMazing 1.1 - Complete iOS device manage...
iMazing (was DiskAid) is the ultimate iOS device manager with capabilities far beyond what iTunes offers. With iMazing and your iOS device (iPhone, iPad, or iPod), you can: Copy music to and from... Read more
PopChar X 7.0 - Floating window shows av...
PopChar X helps you get the most out of your font collection. With its crystal-clear interface, PopChar X provides a frustration-free way to access any font's special characters. Expanded... Read more
OneNote 15.4 - Free digital notebook fro...
OneNote is your very own digital notebook. With OneNote, you can capture that flash of genius, that moment of inspiration, or that list of errands that's too important to forget. Whether you're at... Read more

Latest Forum Discussions

See All

Raby (Games)
Raby 1.0.3 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0.3 (iTunes) Description: ***WARNING - Raby runs on: iPhone 5, iPhone 5C, iPhone 5S, iPhone 6, iPhone 6 Plus, iPad Mini Retina, iPad Mini 3, iPad 4, iPad Air,... | Read more »
Oddworld: Stranger's Wrath (Games)
Oddworld: Stranger's Wrath 1.0 Device: iOS Universal Category: Games Price: $5.99, Version: 1.0 (iTunes) Description: ** PLEASE NOTE: Oddworld Stranger's Wrath requires at least an iPhone 4S, iPad 2, iPad Mini or iPod Touch 5th gen... | Read more »
Bounce On Back (Games)
Bounce On Back 1.0 Device: iOS Universal Category: Games Price: $2.99, Version: 1.0 (iTunes) Description: | Read more »
Dwelp (Games)
Dwelp 1.0 Device: iOS Universal Category: Games Price: $.99, Version: 1.0 (iTunes) Description: === 50% off for a limited time, to celebrate release === Dwelp is an elegant little puzzler with a brand new game mechanic. To complete a... | Read more »
Make Way for Fat Chicken, from the Maker...
Make Way for Fat Chicken, from the Makers of Scrap Squad Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Relevant Games has announced they will be releasing their reverse tower defense game, | Read more »
Tripnary Review
Tripnary Review By Jennifer Allen on November 26th, 2014 Our Rating: :: TRAVEL BUCKET LISTiPhone App - Designed for the iPhone, compatible with the iPad Want to create a travel bucket list? Tripnary is a fun way to do exactly that... | Read more »
Ossian Studios’ RPG, The Shadow Sun, is...
Ossian Studios’ RPG, The Shadow Sun, is Now Available for $4.99 Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »
Mmmm, Tasty – Having the Angry Birds for...
The very first Angry Birds debuted on iOS back in 2009. When you sit back and tally up the number of Angry Birds games out there and the impact they’ve had on pop culture as a whole, you just need to ask yourself: “How would the birds taste... | Read more »
Rescue Quest Review
Rescue Quest Review By Jennifer Allen on November 26th, 2014 Our Rating: :: PATH BASED MATCH-3Universal App - Designed for iPhone and iPad Guide a wizard to safety by matching gems. Rescue Quest might not be an entirely original... | Read more »
You Can Play the Final Chapter of Lone W...
You Can Play the Final Chapter of Lone Wolf: Dawn Over V’taag Right Now Posted by Jessica Fisher on November 26th, 2014 [ permalink ] Universal App - Designed for iPhone and iPad | Read more »

Price Scanner via MacPrices.net

BEVL Releases Dock Tailored for iPhone 6 and...
Seattle based BEVL has released their first product: an iPhone dock that is divergent in build quality, rock-solid function and visual simplicity to complement the iPhone. BEVL is now accepting... Read more
Black Friday: $150 off 13-inch Retina MacBook...
 Best Buy has 13-inch 2.6GHz Retina MacBook Pros on sale for $150 off MSRP on their online store as part of their Black Friday sale. Choose free shipping or free local store pickup (if available).... Read more
Black Friday: $300 off 15-inch Retina MacBook...
 B&H Photo has the new 2014 15″ Retina MacBook Pros on sale for $300 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 15″ 2.2GHz Retina... Read more
Black Friday: Up to $140 off MacBook Airs, fr...
 B&H Photo has 2014 MacBook Airs on sale for up to $140 off MSRP as part of their Black Friday sale. Shipping is free, and B&H charges NY sales tax only: - 11″ 128GB MacBook Air: $799 $100... Read more
Black Friday: 13-inch 2.5GHz MacBook Pro on s...
 Best Buy has the 13″ 2.5GHz MacBook Pro on sale for $899.99 on their online store as part of their Black Friday sale. Choose free shipping or free instant local store pickup (if available). Their... Read more
2014 1.4GHz Mac mini on sale for $449, save $...
 B&H Photo has the new 1.4GHz Mac mini on sale for $449.99 including free shipping plus NY tax only. Their price is $50 off MSRP, and it’s the lowest price available for this new model. Adorama... Read more
Early Black Friday pricing on 27-inch 5K iMac...
 B&H Photo continues to offer Black Friday sale prices on the 27″ 3.5GHz 5K iMac, in stock today and on sale for $2299 including free shipping plus NY sales tax only. Their price is $200 off MSRP... Read more
Early Black Friday sale prices on iPad Air 2,...
 MacMall is discounting iPad Air 2s by up to $75 off MSRP as part of their Black Friday sale. Shipping is free: - 16GB iPad Air WiFi: $459 $40 off - 64GB iPad Air WiFi: $559 $40 off - 128GB iPad Air... Read more
Early Black Friday MacBook Air sale prices, $...
 MacMall has posted early Black Friday MacBook Air sale prices. Save $101 on all models for a limited time: - 11″ 1.4GHz/128GB MacBook Air: $798 - 11″ 1.4GHz/256GB MacBook Air: $998 - 13″ 1.4GHz/... Read more
Why iPhone 6 Tablet/Laptop Cannibalization Is...
247wallst.com blogger Douglas A. McIntyre noted last week that according to research posted on the Applovin blog site the iPhone 6 is outselling the iPhone 6 Plus by a wide margin . Hardly a surprise... Read more

Jobs Board

*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
Senior Event Manager, *Apple* Retail Market...
…This senior level position is responsible for leading and imagining the Apple Retail Team's global event strategy. Delivering an overarching brand story; in-store, Read more
*Apple* Retail - Multiple Positions (US) - A...
Sales Specialist - Retail Customer Service and Sales Transform Apple Store visitors into loyal Apple customers. When customers enter the store, you're also the Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
*Apple* Solutions Consultant (ASC) - Apple (...
**Job Summary** The ASC is an Apple employee who serves as an Apple brand ambassador and influencer in a Reseller's store. The ASC's role is to grow Apple Read more
All contents are Copyright 1984-2011 by Xplain Corporation. All rights reserved. Theme designed by Icreon.