The GNU Objective-C Compiler and Runtime

We need an RS/6000 guru to help fix problems with the RS/6000 and GNU Objective-C. The following two mail messages describe the problem and a possible work-around. Please do not bother Kresten Thorup or Richard Kenner about RS/6000 problems, as both persons have a heavy workload and other priorities that eat up their time. Thanks. Note in the 2nd message that Rob Mayoff <mayoff@tkg.com> seems to have hacked the compiler to work for him.

Paul Kunz has made available via ftp more technical information about using GNU Objective-C under AIX.

Date: Mon, 13 Dec 93 09:31:09 -0800
From: Kresten_Thorup@NeXT.COM (Kresten Krab Thorup)
Cc: Richard Kenner 
Subject: Re: Objective C support for RS/6000

Hi.

According to Richard Kenner, the problem is that the machinery behind __builtin_apply, which drives forwarding/delegation in objc, is insufficient.

I don't know anything about the calling conventions on rs/600, but if I tell you about the machiney, perhaps you can figure out what is wrong. Richard Kenner asked me to explain how it works, so here it goes.

If this isn't enough, you should consult the __expand_builtin_xxx functions in expr.c of the gcc distribution. Once you have the explanations below, it should be fairly easy to unsderstand the code in expr.c. You're welcome to ask me.

Kresten

void *__builtin_apply_args(void)

Returns a pointer to a block of memory which is supposed to "capture" the state of the argument passing in the beginning of the function in which it appears. This block contains: the argument pointer register, the struct-value register if applicable, and any other registers to which FUNCTION_ARG_REGNO_P answer yes (i.e. the register is used for argument passing). If this is not enough state to later restore the call frame in order to invoke it another function, then we have a problem.

void* __builtin_apply(void(*faddr)(void), void *arg_block, int asize)

Takes the address of a function FADDR, a block returned by __builtin_apply_args ARG_BLOCK, and the size of the arguments ASIZE on the stack. Now, it copies ASIZE bytes from the argpointer in ARG_BLOCK into a newly allocated stack block, and makes the arg pointer point to that. Next, all the registers are restored from ARG_BLOCK, and finally FADDR is jumped to.

After the call returns, a new block is created carrying the "return state". This is all the registers to which FUNCTION_VALUE_REGNO_P returns yes. This is used in __builtin_return.

volatile void __builtin_return(void *return_block)

Performs a generic return, by restoring the registers saved in RETURN_BLOCK. The current generic implementation is limited to just one register, but you can overwrite the untyped_call and untyped_return (see expr.c) to add extra state to the return block.

Kresten

Date: 	Thu, 23 Feb 1995 13:31:12 -0600
From: Rob Mayoff 
Subject: gnu objective-c rs/6000 runtime

I have done some work towards making the Objective-C runtime work on the RS/6000 but now I need some help (or else I will have to spend a long long time figuring out the compiler).

Here are the problems with the runtime on the RS/6000:

1. The AIX linker does not pull in anything from Object.o in libobjc.a, even when other .o files use class Object. The symptom is an error message from the runtime about not finding class Object, and a call to abort(). I have a workaround for this on AIX 4.1 but I'm going to figure out a solution that works on 3.2 also.

2. If you manage to force the linker to include Object.o (which you can do without too much difficult on AIX 4.1 using a new linker flag), and you try to force it to include Protocol.o also, then the linker will fail because Protocol.o contains a text segment relocation item that can't be resolved at link time. Run-time text segment relocations are not allowed. I haven't gotten anywhere on this one yet so I'm just not using Protocol.o in my little sample program.

3. The compiler generates incorrect code for __builtin_apply. The code isn't too far off, though. Here is an overview of the code generated.

Suppose that SP == 1096, and *SP == 1096+256 == 1352. a. Decrement SP by 96. (now SP == 1000) b. Copy 96 bytes from the current function's incoming parameter area to the area starting at SP+24. (dest = 1024..1119). c. Reload the parameter-passing registers to the values they had when the current function was called. d. Call the untyped function. e. (untyped function returns) f. Save all the return-value registers. g. r0 = *SP (i.e., load the stack word at 1000). h. Restore SP to its value from before the __builtin_apply. ( == 1096) i. *SP = r0 (i.e. save the value from step g in the stack at SP).

There are two problems here. One is minor and one is major (causing crashes). The minor problem is that, according to the Subroutine Linkage Convention described in the AIX Assembler Reference Manual, the value at SP should *always* be the previous SP (the backchain) - that is, when SP is decremented, the previous value must be stored at the new location in the same atomic instruction. This instruction is "stu" (store with update), which gcc correctly generates in function prologues but not in __builtin_apply.

The major problem is that, as you may have noticed, steps g and i attempt to restore the backchain pointer from location 1000 to location 1096. But the backchain pointer was never copied to location 1000. So some bogus value gets restored as the backchain pointer. As soon as the function returns, the bogus backchain gets loaded and the program dies.

Here is (one variation on) the code that gcc should produce. a'. Decrement SP by 96 using "stu" (now SP == 1000 and *SP == 1096). Thus the subroutine linkage convention is followed, since 1096 is a valid backchain. b'. *SP = *(SP+96). Now the "real" backchain, which is about to get clobbered, is preserved. c'. Copy 96 bytes from the current function's incoming parameter area to the area starting at SP+24. d'. Reload the registers. e'. Call the untyped function. f'. (untyped function returns) g'. Save all the return-value registers. h'. r0 = *SP (i.e., load the saved backchain). i'. *(SP+96) = r0 (i.e., restore the saved backchain). j'. Restore SP to its value from before __builtin_apply.

Notice that we have to restore the backchain at 1096 in step i' before we adjust SP in step j', in order to follow the subroutine linkage convention.

__builtin_apply is used in sendmsg.c. I generated assembly code (with the -S flag) and hacked it by hand, and my tiny test program started working.

So then I looked at the compiler. __builtin_apply uses save_stack_block and restore_stack_block. But in rs6000.md, only restore_stack_block is defined (which is no doubt responsible for steps g-i above). But save_stack_block is not defined, so the backchain pointer is never getting copied down from 1096 to 1000.

I tried to define save_stack_block by looking at save_stack_nonlocal but it didn't work. So then I simply modified __builtin_apply to use save_stack_nonlocal and restore_stack_nonlocal (which save and restore both the backchain and SP). With this change sendmsg.c compiles without the major bug (although it still violates the subroutine linkage convention). I was able to run my test program successfully without hand-hacking the assembly.

So, my main question is: what is a good definition of save_stack_block? I'm also interested in general comments about how to solve all three of the problems with the runtime.

P.S. Here is my test program. This now runs correctly for me, with my hacked compiler, if I link it by hand with some special linker flags. I call it "Splat.m". Actually, it was written for me by a friend who knows Objective-C (I don't actually know the language).

#include 

@interface Splat : Object
{
	int value;
}
- setValue:(int)newValue;
- printValue;
@end

@implementation Splat

- setValue:(int)newValue
{
	value = newValue;
	return self;
}

- printValue
{
	printf("Object 0x%p: My value is %d\n", self, value);
	return self;
}

@end


@interface Zooby : Object
{
	id buddy;
}
- setBuddy:buddy;
- mungeSplat:(Splat *)splat value:(int)val;
@end

@implementation Zooby

- (retval_t)forward:(SEL)aSel :(arglist_t)argFrame
{
	printf("Forwarding the %s message to my buddy\n", sel_get_name(aSel));
	[buddy performv:aSel :argFrame];
	return self;
}

- setBuddy:newBuddy
{
	buddy = newBuddy;
	return self;
}

- mungeSplat:(Splat *)splat value:(int)val
{
	[[splat setValue:val] printValue];
	return self;
}

@end

int main(int argc, char **argv)
{
	Splat *x = [[Splat alloc] init];
	Zooby *z = [[Zooby alloc] init];

	[x setValue:15];
	[x printValue];

	[z setBuddy:x];
	[z setValue:9];
	[z printValue];

	return 0;
}