Skip to content

Commit

Permalink
support for C-level blocks (note: this requires a not-yet-released Br…
Browse files Browse the repository at this point in the history
…idgeSupport with special annotations for blocks)

git-svn-id: http://svn.macosforge.org/repository/ruby/MacRuby/trunk@4495 23306eb0-4c56-4727-a40e-e92c0eb68959
  • Loading branch information
lrz committed Sep 8, 2010
1 parent c02ab1a commit 112362a
Show file tree
Hide file tree
Showing 7 changed files with 202 additions and 118 deletions.
37 changes: 14 additions & 23 deletions bs.c
Expand Up @@ -319,10 +319,10 @@ undecorate_struct_type(const char *src, char *dest, size_t dest_len,
bool ok;
int nested;

opposite =
*p_src == '{' ? '}' :
*p_src == '(' ? ')' :
*p_src == '[' ? ']' : 0;
opposite =
*p_src == '{' ? '}' :
*p_src == '(' ? ')' :
*p_src == '[' ? ']' : 0;

for (i = 0, ok = false, nested = 0;
i < src_len - (p_src - src) && !ok;
Expand Down Expand Up @@ -1100,23 +1100,18 @@ bs_parser_parse(bs_parser_t *parser, const char *path,
retval = func != NULL ? func->retval : method->retval;
}
else {
args_count = func != NULL ? func->args_count : method->args_count;
args_count = func != NULL ? func->args_count
: method->args_count;
arg = &args[args_count - 1];
}

// Let's get the old type to keep the number of pointers
char *old_type = (retval ? retval->type : arg->type);
int nb_ptr = 0;
// Determine if we deal with a block or a function pointer.
const char *old_type = (retval ? retval->type : arg->type);
const char lambda_type = *old_type == '@'
? _MR_C_LAMBDA_BLOCK
: _MR_C_LAMBDA_FUNCPTR;

while (old_type && *old_type == '^') {
nb_ptr++;
old_type++;
}
// Decrementing because if we have ^^?, the last ^ (along with ?)
// is going to be replaced by <...>, so we don't want to keep it
nb_ptr--;

char tmp_type[1026]; // 2 less to fit <, and >
char tmp_type[1025]; // 3 less to fit <, type and >
char new_type[1028];

// Function ptr return type
Expand All @@ -1127,13 +1122,9 @@ bs_parser_parse(bs_parser_t *parser, const char *path,
}
// Clear the final type string
memset(new_type, 0, sizeof(new_type));
// Copy the number of pointers
for (int i = 0; i < nb_ptr; i++) {
strlcat(new_type, "^", sizeof(new_type));
}
// Append the function pointer type
snprintf(new_type + nb_ptr, sizeof(new_type) - nb_ptr,
"%c%s%c", _MR_C_FPTR_B, tmp_type, _MR_C_FPTR_E);
snprintf(new_type, sizeof(new_type), "%c%c%s%c",
_MR_C_LAMBDA_B, lambda_type, tmp_type, _MR_C_LAMBDA_E);

// Free the old values
if (retval) {
Expand Down
12 changes: 9 additions & 3 deletions bs.h
Expand Up @@ -36,9 +36,15 @@ extern "C" {
#include <CoreFoundation/CoreFoundation.h>
#include <objc/runtime.h>

/* Extend objc/runtime.h and add function pointers tokens */
#define _MR_C_FPTR_B '<'
#define _MR_C_FPTR_E '>'
/* Extend objc/runtime.h and add function pointers and blocks tokens.
* A pointer to `void foo (int, char)' will be encoded as <^vic>.
* A `void (^)(int, char)' block will be encoded as <@vic>.
*/

#define _MR_C_LAMBDA_B '<'
#define _MR_C_LAMBDA_FUNCPTR '^'
#define _MR_C_LAMBDA_BLOCK '@'
#define _MR_C_LAMBDA_E '>'

/* Attribute and element representations.
* See BridgeSupport(5) for more information.
Expand Down
216 changes: 129 additions & 87 deletions compiler.cpp
Expand Up @@ -267,6 +267,7 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode)
rvalToDoubleFunc = get_function("vm_rval_to_double");
rvalToSelFunc = get_function("vm_rval_to_sel");
rvalToCharPtrFunc = get_function("vm_rval_to_charptr");
initBlockFunc = get_function("vm_init_c_block");

VoidTy = Type::getVoidTy(context);
Int1Ty = Type::getInt1Ty(context);
Expand Down Expand Up @@ -303,6 +304,9 @@ RoxorCompiler::RoxorCompiler(bool _debug_mode)
Int32PtrTy = PointerType::getUnqual(Int32Ty);
BitTy = Type::getInt1Ty(context);

BlockLiteralTy = module->getTypeByName("struct.ruby_block_literal");
assert(BlockLiteralTy != NULL);

#if ROXOR_COMPILER_DEBUG
level = 0;
#endif
Expand Down Expand Up @@ -5191,9 +5195,115 @@ RoxorCompiler::compile_get_cptr(Value *val, const char *type, Value *slot)
return compile_protected_call(getPointerPtrFunc, args, args + 3);
}

Value *
RoxorCompiler::compile_lambda_to_funcptr(const char *type,
Value *val, Value *slot, bool is_block)
{
GlobalVariable *proc_gvar =
new GlobalVariable(*RoxorCompiler::module,
RubyObjTy, false, GlobalValue::InternalLinkage,
nilVal, "");
new StoreInst(val, proc_gvar, bb);

const size_t buf_len = strlen(type + 1) + 1;
assert(buf_len > 1);
char *buf = (char *)malloc(buf_len);

const char *p = GetFirstType(type + 1, buf, buf_len);
const Type *ret_type = convert_type(buf);
int argc = 0;

std::vector<std::string> arg_ctypes;
std::vector<const Type *> arg_types;

if (is_block) {
// The Block ABI specifies that the first argument is a pointer
// to the block literal, which we don't really care about.
arg_types.push_back(PtrTy);
}

while (*p != _MR_C_LAMBDA_E) {
p = GetFirstType(p, buf, buf_len);
arg_ctypes.push_back(std::string(buf));
arg_types.push_back(convert_type(buf));
argc++;
}
FunctionType *ft = FunctionType::get(ret_type, arg_types,
false);

// ret_type stub(arg1, arg2, ...)
// {
// VALUE *argv = alloc(argc);
// argv[0] = arg1;
// argv[1] = arg2;
// return rb_proc_check_and_call(procval, argc, argv);
// }
Function *f = cast<Function>(module->getOrInsertFunction("",
ft));

BasicBlock *oldbb = bb;
bb = BasicBlock::Create(context, "EntryBlock", f);

Function::arg_iterator arg = f->arg_begin();

Value *argv;
if (argc == 0) {
argv = new BitCastInst(compile_const_pointer(NULL),
RubyObjPtrTy, "", bb);
}
else {
argv = new AllocaInst(RubyObjTy, ConstantInt::get(Int32Ty, argc),
"", bb);
int off = 0;
if (is_block) {
// Skip block literal argument.
off++;
arg++;
}
for (int i = 0; i < argc; i++) {
Value *index = ConstantInt::get(Int32Ty, i);
Value *aslot = GetElementPtrInst::Create(argv, index, "", bb);
Value *rval = compile_conversion_to_ruby(arg_ctypes[i].c_str(),
arg_types[i + off], arg++);
new StoreInst(rval, aslot, bb);
}
}

// VALUE rb_proc_check_and_call(
// VALUE self, int argc, VALUE *argv
// )
Function *proc_call_f =
cast<Function>(module->getOrInsertFunction(
"rb_proc_check_and_call",
RubyObjTy,
RubyObjTy, Int32Ty, RubyObjPtrTy, NULL));

Value *args[] = {
new LoadInst(proc_gvar, "", bb),
ConstantInt::get(Int32Ty, argc),
argv
};
Value *ret_val = compile_protected_call(proc_call_f, args,
args + 3);

if (ret_type != VoidTy) {
GetFirstType(type + 1, buf, buf_len);
ret_val = compile_conversion_to_c(buf, ret_val,
new AllocaInst(ret_type, "", bb));
ReturnInst::Create(context, ret_val, bb);
}
else {
ReturnInst::Create(context, bb);
}

bb = oldbb;
free(buf);
return new BitCastInst(f, PtrTy, "", bb);
}

Value *
RoxorCompiler::compile_conversion_to_c(const char *type, Value *val,
Value *slot)
Value *slot)
{
type = SkipTypeModifiers(type);

Expand Down Expand Up @@ -5327,96 +5437,28 @@ RoxorCompiler::compile_conversion_to_c(const char *type, Value *val,
}
break;

case _MR_C_FPTR_B:
case _MR_C_LAMBDA_B:
{
GlobalVariable *proc_gvar =
new GlobalVariable(*RoxorCompiler::module,
RubyObjTy, false, GlobalValue::InternalLinkage,
nilVal, "");
new StoreInst(val, proc_gvar, bb);

const size_t buf_len = strlen(type + 1) + 1;
assert(buf_len > 1);
char *buf = (char *)malloc(buf_len);

const char *p = GetFirstType(type + 1, buf, buf_len);
const Type *ret_type = convert_type(buf);
int argc = 0;

std::vector<std::string> arg_ctypes;
std::vector<const Type *> arg_types;
while (*p != _MR_C_FPTR_E) {
p = GetFirstType(p, buf, buf_len);
arg_ctypes.push_back(std::string(buf));
arg_types.push_back(convert_type(buf));
argc++;
}
FunctionType *ft = FunctionType::get(ret_type, arg_types,
false);

// ret_type stub(arg1, arg2, ...)
// {
// VALUE *argv = alloc(argc);
// argv[0] = arg1;
// argv[1] = arg2;
// return rb_proc_check_and_call(procval, argc, argv);
// }
Function *f = cast<Function>(module->getOrInsertFunction("",
ft));

BasicBlock *oldbb = bb;
bb = BasicBlock::Create(context, "EntryBlock", f);

Function::arg_iterator arg = f->arg_begin();

Value *argv;
if (argc == 0) {
argv = new BitCastInst(compile_const_pointer(NULL),
RubyObjPtrTy, "", bb);
}
else {
argv = new AllocaInst(RubyObjTy,
ConstantInt::get(Int32Ty, argc), "", bb);
for (int i = 0; i < argc; i++) {
Value *index = ConstantInt::get(Int32Ty, i);
Value *aslot = GetElementPtrInst::Create(argv, index,
"", bb);
Value *rval = compile_conversion_to_ruby(
arg_ctypes[i].c_str(), arg_types[i], arg++);
new StoreInst(rval, aslot, bb);
}
type++;
const bool is_block = *type == _MR_C_LAMBDA_BLOCK;

Value *funcptr = compile_lambda_to_funcptr(type, val, slot,
is_block);
if (!is_block) {
// A pure function pointer, let's pass it.
return funcptr;
}

// VALUE rb_proc_check_and_call(
// VALUE self, int argc, VALUE *argv
// )
Function *proc_call_f =
cast<Function>(module->getOrInsertFunction(
"rb_proc_check_and_call",
RubyObjTy,
RubyObjTy, Int32Ty, RubyObjPtrTy, NULL));

// A C-level block. We allocate on the stack the literal
// structure following the ABI, initialize it then pass
// a pointer to it.
Value *block_lit = new AllocaInst(BlockLiteralTy, "", bb);
Value *args[] = {
new LoadInst(proc_gvar, "", bb),
ConstantInt::get(Int32Ty, argc),
argv
block_lit,
funcptr
};
Value *ret_val = compile_protected_call(proc_call_f, args,
args + 3);

if (ret_type != VoidTy) {
GetFirstType(type + 1, buf, buf_len);
ret_val = compile_conversion_to_c(buf, ret_val,
new AllocaInst(ret_type, "", bb));
ReturnInst::Create(context, ret_val, bb);
}
else {
ReturnInst::Create(context, bb);
}

bb = oldbb;
free(buf);
return new BitCastInst(f, PtrTy, "", bb);
CallInst::Create(initBlockFunc, args, args + 2, "", bb);
return block_lit;
}
break;

Expand Down Expand Up @@ -5769,7 +5811,7 @@ RoxorCompiler::convert_type(const char *type)
}
break;

case _MR_C_FPTR_B:
case _MR_C_LAMBDA_B:
return PtrTy;

case _C_STRUCT_B:
Expand Down
4 changes: 4 additions & 0 deletions compiler.h
Expand Up @@ -254,6 +254,7 @@ class RoxorCompiler {
Function *rvalToDoubleFunc;
Function *rvalToSelFunc;
Function *rvalToCharPtrFunc;
Function *initBlockFunc;

Constant *zeroVal;
Constant *oneVal;
Expand Down Expand Up @@ -283,6 +284,7 @@ class RoxorCompiler {
const Type *IntTy;
const PointerType *Int32PtrTy;
const Type *BitTy;
const Type *BlockLiteralTy;

unsigned dbg_mdkind;

Expand Down Expand Up @@ -422,6 +424,8 @@ class RoxorCompiler {
void compile_set_struct(Value *rcv, int field, Value *val);
Value *compile_xmalloc(size_t len);

Value *compile_lambda_to_funcptr(const char *type, Value *val,
Value *slot, bool is_block);
Value *compile_conversion_to_c(const char *type, Value *val,
Value *slot);
Value *compile_conversion_to_ruby(const char *type,
Expand Down
31 changes: 31 additions & 0 deletions kernel.c
Expand Up @@ -1012,3 +1012,34 @@ vm_get_special(char code)
}
return val;
}

// Support for C-level blocks.
// Following the ABI specifications as documented in the
// BlockImplementation.txt file of LLVM.

struct ruby_block_descriptor {
unsigned long int reserved;
unsigned long int block_size;
};

struct ruby_block_literal {
void *isa;
int flags;
int reserved;
void *imp;
struct ruby_block_descriptor *descriptor;
};

static struct ruby_block_descriptor ruby_block_descriptor_value = {
0, sizeof(struct ruby_block_literal)
};

PRIMITIVE void
vm_init_c_block(struct ruby_block_literal *b, void *imp)
{
b->isa = &_NSConcreteStackBlock;
b->flags = (1 << 29);
b->reserved = 0;
b->imp = imp;
b->descriptor = &ruby_block_descriptor_value;
}

0 comments on commit 112362a

Please sign in to comment.