// test_ane_model.m — Experiments E-H: _ANEModel loading, ANECompiler, chaining, shared events // Build: make test_ane_model && ./test_ane_model #import #import #import #import #import #import #include static mach_timebase_info_data_t g_tb; static double tb_ms(uint64_t t) { return (double)t * g_tb.numer / g_tb.denom / 1e6; } __attribute__((unused)) static int g_fp16_io = 1; #pragma mark - Helpers static void dump_class(const char *name) { Class cls = NSClassFromString([NSString stringWithUTF8String:name]); if (!cls) { printf(" %s: NOT FOUND\n", name); return; } printf("\n=== %s ===\n", name); unsigned int count; Method *methods = class_copyMethodList(object_getClass(cls), &count); if (count) printf(" Class methods (%u):\n", count); for (unsigned int i = 0; i < count; i++) { SEL s = method_getName(methods[i]); const char *enc = method_getTypeEncoding(methods[i]); printf(" + %s [%s]\n", sel_getName(s), enc ? enc : "?"); } free(methods); methods = class_copyMethodList(cls, &count); if (count) printf(" Instance methods (%u):\n", count); for (unsigned int i = 0; i < count; i++) { SEL s = method_getName(methods[i]); const char *enc = method_getTypeEncoding(methods[i]); printf(" - %s [%s]\n", sel_getName(s), enc ? enc : "?"); } free(methods); unsigned int pcount; objc_property_t *props = class_copyPropertyList(cls, &pcount); if (pcount) printf(" Properties (%u):\n", pcount); for (unsigned int i = 0; i < pcount; i++) { const char *pname = property_getName(props[i]); const char *pattr = property_getAttributes(props[i]); printf(" @property %s [%s]\n", pname, pattr ? pattr : "?"); } free(props); } static void dump_all_properties(id obj, Class cls) { if (!obj) return; unsigned int pcount; objc_property_t *props = class_copyPropertyList(cls, &pcount); for (unsigned int i = 0; i < pcount; i++) { const char *pname = property_getName(props[i]); @try { id val = [obj valueForKey:[NSString stringWithUTF8String:pname]]; printf(" %s = %s\n", pname, val ? [[val description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" %s = \n", pname, [[ex reason] UTF8String]); } } free(props); } static void list_dir_recursive(NSString *path, int depth) { NSFileManager *fm = [NSFileManager defaultManager]; NSArray *items = [fm contentsOfDirectoryAtPath:path error:nil]; for (NSString *item in items) { NSString *full = [path stringByAppendingPathComponent:item]; BOOL isDir = NO; [fm fileExistsAtPath:full isDirectory:&isDir]; NSDictionary *attrs = [fm attributesOfItemAtPath:full error:nil]; unsigned long long sz = [attrs fileSize]; for (int i = 0; i < depth; i++) printf(" "); if (isDir) { printf(" [DIR] %s/\n", [item UTF8String]); list_dir_recursive(full, depth + 1); } else { printf(" %s (%llu bytes)\n", [item UTF8String], sz); } } } static IOSurfaceRef make_surface(size_t bytes) { return IOSurfaceCreate((__bridge CFDictionaryRef)@{ (id)kIOSurfaceWidth:@(bytes), (id)kIOSurfaceHeight:@1, (id)kIOSurfaceBytesPerElement:@1, (id)kIOSurfaceBytesPerRow:@(bytes), (id)kIOSurfaceAllocSize:@(bytes), (id)kIOSurfacePixelFormat:@0}); } #pragma mark - MIL Generation (FP16 conv) static NSString *gen_conv_mil(int ch, int sp) { return [NSString stringWithFormat: @"program(1.0)\n[buildInfo = dict, tensor>" "({{\"coremlc-version\", \"3505.4.1\"}})]\n{\n" " func main(tensor x) {\n" " tensor pt = const()[name=tensor(\"pt\")," " val=tensor(\"valid\")];\n" " tensor st = const()[name=tensor(\"st\")," " val=tensor([1,1])];\n" " tensor pd = const()[name=tensor(\"pd\")," " val=tensor([0,0,0,0])];\n" " tensor dl = const()[name=tensor(\"dl\")," " val=tensor([1,1])];\n" " tensor gr = const()[name=tensor(\"gr\")," " val=tensor(1)];\n" " tensor W = const()[name=tensor(\"W\"), " "val=tensor(BLOBFILE(path=tensor" "(\"@model_path/weights/weight.bin\"), offset=tensor(64)))];\n" " tensor y = conv(dilations=dl,groups=gr," "pad=pd,pad_type=pt,strides=st,weight=W,x=x)" "[name=tensor(\"conv\")];\n" " } -> (y);\n}\n", ch, sp, ch, ch, ch, ch, ch, sp]; } #pragma mark - Kernel Compilation typedef struct { id model; IOSurfaceRef ioIn, ioOut; NSString *tmpDir; NSString *hexId; int ch, sp; } CompiledKernel; static CompiledKernel compile_kernel(int ch, int sp) { CompiledKernel k = {0}; k.ch = ch; k.sp = sp; Class gD = NSClassFromString(@"_ANEInMemoryModelDescriptor"); Class gI = NSClassFromString(@"_ANEInMemoryModel"); if (!gD || !gI) { printf(" ERROR: ANE classes not found\n"); return k; } int ws = ch * ch * 2; int tot = 128 + ws; uint8_t *blob = (uint8_t *)calloc((size_t)tot, 1); blob[0] = 1; blob[4] = 2; blob[64] = 0xEF; blob[65] = 0xBE; blob[66] = 0xAD; blob[67] = 0xDE; blob[68] = 1; *(uint32_t *)(blob + 72) = (uint32_t)ws; *(uint32_t *)(blob + 80) = 128; _Float16 *wp = (_Float16 *)(blob + 128); for (int i = 0; i < ch; i++) wp[i * ch + i] = (_Float16)1.0f; NSData *wdata = [NSData dataWithBytesNoCopy:blob length:(NSUInteger)tot freeWhenDone:YES]; NSString *mil = gen_conv_mil(ch, sp); NSData *md = [mil dataUsingEncoding:NSUTF8StringEncoding]; id desc = ((id(*)(Class,SEL,id,id,id))objc_msgSend)(gD, @selector(modelWithMILText:weights:optionsPlist:), md, @{@"@model_path/weights/weight.bin": @{@"offset":@0, @"data":wdata}}, nil); id mdl = ((id(*)(Class,SEL,id))objc_msgSend)(gI, @selector(inMemoryModelWithDescriptor:), desc); if (!mdl) { printf(" ERROR: inMemoryModel creation failed\n"); return k; } id hx = ((id(*)(id,SEL))objc_msgSend)(mdl, @selector(hexStringIdentifier)); k.hexId = hx; NSString *td = [NSTemporaryDirectory() stringByAppendingPathComponent:hx]; NSFileManager *fm = [NSFileManager defaultManager]; [fm createDirectoryAtPath:[td stringByAppendingPathComponent:@"weights"] withIntermediateDirectories:YES attributes:nil error:nil]; [md writeToFile:[td stringByAppendingPathComponent:@"model.mil"] atomically:YES]; [wdata writeToFile:[td stringByAppendingPathComponent:@"weights/weight.bin"] atomically:YES]; NSError *e = nil; BOOL ok = ((BOOL(*)(id,SEL,unsigned int,id,NSError**))objc_msgSend)( mdl, @selector(compileWithQoS:options:error:), 21, @{}, &e); if (!ok) { printf(" Compile failed: %s\n", e ? [[e description] UTF8String] : "unknown"); return k; } ok = ((BOOL(*)(id,SEL,unsigned int,id,NSError**))objc_msgSend)( mdl, @selector(loadWithQoS:options:error:), 21, @{}, &e); if (!ok) { printf(" Load failed: %s\n", e ? [[e description] UTF8String] : "unknown"); return k; } k.model = mdl; k.ioIn = make_surface((size_t)ch * sp * 2); k.ioOut = make_surface((size_t)ch * sp * 2); k.tmpDir = td; return k; } static void free_kernel(CompiledKernel *k) { if (!k->model) return; NSError *e = nil; ((BOOL(*)(id,SEL,unsigned int,NSError**))objc_msgSend)( k->model, @selector(unloadWithQoS:error:), 21, &e); if (k->ioIn) CFRelease(k->ioIn); if (k->ioOut) CFRelease(k->ioOut); } #pragma mark - Main int main(int argc, const char *argv[]) { (void)argc; (void)argv; @autoreleasepool { mach_timebase_info(&g_tb); printf("==============================================================\n"); printf(" ANE Experiments E-H: _ANEModel, Compiler, Chaining\n"); printf("==============================================================\n\n"); void *handle = dlopen( "/System/Library/PrivateFrameworks/AppleNeuralEngine.framework/" "AppleNeuralEngine", RTLD_NOW); if (!handle) { printf("FATAL: dlopen ANE framework failed\n"); return 1; } Class gAIO = NSClassFromString(@"_ANEIOSurfaceObject"); Class gAR = NSClassFromString(@"_ANERequest"); Class gBuf = NSClassFromString(@"_ANEBuffer"); Class gOutSets = NSClassFromString(@"_ANEIOSurfaceOutputSets"); Class gChain = NSClassFromString(@"_ANEChainingRequest"); id client = [NSClassFromString(@"_ANEClient") performSelector:@selector(sharedConnection)]; printf("=== Compiling test kernels (64x32 FP16 conv) ===\n"); CompiledKernel k1 = compile_kernel(64, 32); CompiledKernel k2 = compile_kernel(64, 32); if (!k1.model || !k2.model) { printf("FATAL: Kernel compilation failed\n"); return 1; } printf(" k1: hexId=%s\n tmpDir=%s\n", [k1.hexId UTF8String], [k1.tmpDir UTF8String]); printf(" k2: hexId=%s\n tmpDir=%s\n", [k2.hexId UTF8String], [k2.tmpDir UTF8String]); // ================================================================= // EXPERIMENT E: Load _ANEModel from compiled temp directory // ================================================================= printf("\n------------------------------------------------------------\n"); printf(" EXPERIMENT E: Load _ANEModel from compiled temp dir\n"); printf("------------------------------------------------------------\n"); id diskModel1 = nil; id diskModel2 = nil; Class gANEModel = NSClassFromString(@"_ANEModel"); if (!gANEModel) { printf(" FATAL: _ANEModel class not found\n"); } else { printf("\n --- E.1: Full _ANEModel API surface ---\n"); dump_class("_ANEModel"); printf("\n --- E.2: Temp dir contents (k1) ---\n"); printf(" Path: %s\n", [k1.tmpDir UTF8String]); list_dir_recursive(k1.tmpDir, 0); printf("\n --- E.3: Factory method probing ---\n"); unsigned int mcount; Method *cmethods = class_copyMethodList( object_getClass(gANEModel), &mcount); printf(" All _ANEModel class methods (%u):\n", mcount); for (unsigned int i = 0; i < mcount; i++) { printf(" + %s\n", sel_getName(method_getName(cmethods[i]))); } free(cmethods); NSURL *dirURL1 = [NSURL fileURLWithPath:k1.tmpDir]; NSString *hexKey1 = k1.hexId; printf("\n URL: %s\n", [[dirURL1 absoluteString] UTF8String]); printf(" key (hexId): %s\n", [hexKey1 UTF8String]); // E.3a: modelAtURL:key: printf("\n --- E.3a: modelAtURL:key: ---\n"); @try { SEL sel = NSSelectorFromString(@"modelAtURL:key:"); if ([gANEModel respondsToSelector:sel]) { diskModel1 = ((id(*)(Class,SEL,id,id))objc_msgSend)( gANEModel, sel, dirURL1, hexKey1); printf(" Result: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); } else { printf(" NOT available\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } // E.3b: modelAtURL:key:modelAttributes: if (!diskModel1) { printf("\n --- E.3b: modelAtURL:key:modelAttributes: ---\n"); @try { SEL sel = NSSelectorFromString( @"modelAtURL:key:modelAttributes:"); if ([gANEModel respondsToSelector:sel]) { diskModel1 = ((id(*)(Class,SEL,id,id,id))objc_msgSend)( gANEModel, sel, dirURL1, hexKey1, @{}); printf(" empty attrs: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); if (!diskModel1) { diskModel1 = ((id(*)(Class,SEL,id,id,id))objc_msgSend)( gANEModel, sel, dirURL1, hexKey1, nil); printf(" nil attrs: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); } } else { printf(" NOT available\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } // E.3c: modelWithCacheURLIdentifier: if (!diskModel1) { printf("\n --- E.3c: modelWithCacheURLIdentifier: ---\n"); @try { SEL sel = NSSelectorFromString(@"modelWithCacheURLIdentifier:"); if ([gANEModel respondsToSelector:sel]) { diskModel1 = ((id(*)(Class,SEL,id))objc_msgSend)( gANEModel, sel, hexKey1); printf(" hexId: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); if (!diskModel1) { diskModel1 = ((id(*)(Class,SEL,id))objc_msgSend)( gANEModel, sel, k1.tmpDir); printf(" tmpDir: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); } } else { printf(" NOT available\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } // E.3d: modelAtURLWithSourceURL:sourceURL:key:cacheURLIdentifier: if (!diskModel1) { printf("\n --- E.3d: modelAtURLWithSourceURL:... ---\n"); @try { SEL sel = NSSelectorFromString( @"modelAtURLWithSourceURL:sourceURL:key:cacheURLIdentifier:"); if ([gANEModel respondsToSelector:sel]) { diskModel1 = ((id(*)(Class,SEL,id,id,id,id))objc_msgSend)( gANEModel, sel, dirURL1, dirURL1, hexKey1, hexKey1); printf(" Result: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); } else { printf(" NOT available\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } // E.3e: alloc/init variants if (!diskModel1) { printf("\n --- E.3e: alloc/init variants ---\n"); unsigned int imcount; Method *imethods = class_copyMethodList(gANEModel, &imcount); printf(" Init-like instance methods:\n"); for (unsigned int i = 0; i < imcount; i++) { const char *mname = sel_getName(method_getName(imethods[i])); if (strstr(mname, "init") || strstr(mname, "Init")) { printf(" - %s [%s]\n", mname, method_getTypeEncoding(imethods[i])); } } free(imethods); @try { SEL initSel = NSSelectorFromString(@"initWithURL:key:"); if ([gANEModel instancesRespondToSelector:initSel]) { id obj = [gANEModel alloc]; diskModel1 = ((id(*)(id,SEL,id,id))objc_msgSend)( obj, initSel, dirURL1, hexKey1); printf(" initWithURL:key: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); } } @catch (NSException *ex) { printf(" initWithURL:key: EXCEPTION: %s\n", [[ex reason] UTF8String]); } } // E.3f: Search for .hwx files if (!diskModel1) { printf("\n --- E.3f: Search for .hwx / .plist files ---\n"); NSFileManager *fm = [NSFileManager defaultManager]; NSDirectoryEnumerator *dirEnum = [fm enumeratorAtPath:k1.tmpDir]; NSString *file; while ((file = [dirEnum nextObject])) { NSString *ext = [file pathExtension]; if ([ext isEqualToString:@"hwx"] || [ext isEqualToString:@"plist"] || [ext isEqualToString:@"espresso"]) { NSString *fp = [k1.tmpDir stringByAppendingPathComponent:file]; NSDictionary *attrs = [fm attributesOfItemAtPath:fp error:nil]; printf(" Found: %s (%llu bytes)\n", [file UTF8String], [attrs fileSize]); } } NSString *netPlist = [k1.tmpDir stringByAppendingPathComponent:@"net.plist"]; if ([fm fileExistsAtPath:netPlist]) { printf(" net.plist found! Reading...\n"); @try { NSDictionary *plist = [NSDictionary dictionaryWithContentsOfFile:netPlist]; if (plist) { printf(" net.plist keys: %s\n", [[[plist allKeys] description] UTF8String]); } else { NSData *raw = [NSData dataWithContentsOfFile:netPlist]; printf(" net.plist: binary (%lu bytes)\n", (unsigned long)raw.length); } } @catch (NSException *ex) { printf(" net.plist EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } // E.3g: Try constructing from programHandle if (!diskModel1) { printf("\n --- E.3g: programHandle-based construction ---\n"); @try { id progHandle = [k1.model valueForKey:@"programHandle"]; printf(" k1 programHandle = %s\n", progHandle ? [[progHandle description] UTF8String] : "nil"); unsigned int mct; Method *cls_m = class_copyMethodList( object_getClass(gANEModel), &mct); for (unsigned int i = 0; i < mct; i++) { const char *mn = sel_getName(method_getName(cls_m[i])); if (strstr(mn, "Handle") || strstr(mn, "handle") || strstr(mn, "program") || strstr(mn, "Program")) { printf(" Relevant factory: +%s\n", mn); } } free(cls_m); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } // E.4: If loaded, query critical methods if (diskModel1) { printf("\n ======================================================\n"); printf(" _ANEModel LOADED SUCCESSFULLY!\n"); printf(" ======================================================\n"); printf("\n --- E.4a: All properties ---\n"); dump_all_properties(diskModel1, gANEModel); printf("\n --- E.4b: getUUID ---\n"); @try { SEL uuidSel = NSSelectorFromString(@"getUUID"); if ([diskModel1 respondsToSelector:uuidSel]) { id uuid = ((id(*)(id,SEL))objc_msgSend)( diskModel1, uuidSel); printf(" getUUID: %s\n", uuid ? [[uuid description] UTF8String] : "nil"); } else { printf(" getUUID: NOT available\n"); } } @catch (NSException *ex) { printf(" getUUID EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- E.4c: Symbol indices ---\n"); @try { SEL inSel = NSSelectorFromString( @"inputSymbolIndicesForProcedureIndex:"); if ([diskModel1 respondsToSelector:inSel]) { id idx = ((id(*)(id,SEL,NSUInteger))objc_msgSend)( diskModel1, inSel, (NSUInteger)0); printf(" inputSymbolIndices(0): %s\n", idx ? [[idx description] UTF8String] : "nil"); } SEL outSel = NSSelectorFromString( @"outputSymbolIndicesForProcedureIndex:"); if ([diskModel1 respondsToSelector:outSel]) { id idx = ((id(*)(id,SEL,NSUInteger))objc_msgSend)( diskModel1, outSel, (NSUInteger)0); printf(" outputSymbolIndices(0): %s\n", idx ? [[idx description] UTF8String] : "nil"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- E.4d: mapper ---\n"); @try { id mapper = [diskModel1 valueForKey:@"mapper"]; printf(" mapper: %s\n", mapper ? [[mapper description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- E.4e: program ---\n"); @try { id prog = [diskModel1 valueForKey:@"program"]; printf(" program: %s\n", prog ? [[prog description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } // E.4f: Copy programHandle + program from InMemoryModel printf("\n --- E.4f: Populate _ANEModel from InMemoryModel ---\n"); @try { id imProgHandle = [k1.model valueForKey:@"programHandle"]; id imProgram = [k1.model valueForKey:@"program"]; id imMapper = nil; @try { imMapper = [k1.model valueForKey:@"mapper"]; } @catch (NSException *ex) { (void)ex; } printf(" InMemoryModel programHandle: %s\n", imProgHandle ? [[imProgHandle description] UTF8String] : "nil"); printf(" InMemoryModel program: %s\n", imProgram ? [[imProgram description] UTF8String] : "nil"); if (imProgHandle) { uint64_t ph = [imProgHandle unsignedLongLongValue]; ((void(*)(id,SEL,uint64_t))objc_msgSend)( diskModel1, @selector(setProgramHandle:), ph); printf(" Set programHandle on _ANEModel: %llu\n", ph); } if (imProgram) { ((void(*)(id,SEL,id))objc_msgSend)( diskModel1, @selector(setProgram:), imProgram); printf(" Set program on _ANEModel\n"); } // Verify id newPH = [diskModel1 valueForKey:@"programHandle"]; id newProg = [diskModel1 valueForKey:@"program"]; printf(" _ANEModel programHandle now: %s\n", newPH ? [[newPH description] UTF8String] : "nil"); printf(" _ANEModel program now: %s\n", newProg ? [[newProg description] UTF8String] : "nil"); // Re-check symbol indices after populating printf("\n Re-checking symbol indices...\n"); SEL inSel2 = NSSelectorFromString( @"inputSymbolIndicesForProcedureIndex:"); if ([diskModel1 respondsToSelector:inSel2]) { id idx = ((id(*)(id,SEL,unsigned int))objc_msgSend)( diskModel1, inSel2, (unsigned int)0); printf(" inputSymbolIndices(0): %s\n", idx ? [[idx description] UTF8String] : "nil"); } SEL outSel2 = NSSelectorFromString( @"outputSymbolIndicesForProcedureIndex:"); if ([diskModel1 respondsToSelector:outSel2]) { id idx = ((id(*)(id,SEL,unsigned int))objc_msgSend)( diskModel1, outSel2, (unsigned int)0); printf(" outputSymbolIndices(0): %s\n", idx ? [[idx description] UTF8String] : "nil"); } // Try getUUID again id uuid2 = ((id(*)(id,SEL))objc_msgSend)( diskModel1, NSSelectorFromString(@"getUUID")); printf(" getUUID after populate: %s\n", uuid2 ? [[uuid2 description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" Populate EXCEPTION: %s\n", [[ex reason] UTF8String]); } // Also load k2 and populate it printf("\n --- E.5: Loading k2 as _ANEModel ---\n"); NSURL *dirURL2 = [NSURL fileURLWithPath:k2.tmpDir]; @try { SEL sel = NSSelectorFromString(@"modelAtURL:key:"); if ([gANEModel respondsToSelector:sel]) { diskModel2 = ((id(*)(Class,SEL,id,id))objc_msgSend)( gANEModel, sel, dirURL2, k2.hexId); printf(" k2 _ANEModel: %s\n", diskModel2 ? "LOADED" : "nil"); if (diskModel2) { id k2ph = [k2.model valueForKey:@"programHandle"]; id k2prog = [k2.model valueForKey:@"program"]; if (k2ph) { ((void(*)(id,SEL,uint64_t))objc_msgSend)( diskModel2, @selector(setProgramHandle:), [k2ph unsignedLongLongValue]); } if (k2prog) { ((void(*)(id,SEL,id))objc_msgSend)( diskModel2, @selector(setProgram:), k2prog); } printf(" k2 populated with programHandle + program\n"); } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } else { printf("\n _ANEModel could NOT be loaded via any factory.\n"); printf(" Proceeding to Experiment E2 (ANECompiler).\n"); } } // ================================================================= // EXPERIMENT E2: ANECompiler probing // ================================================================= printf("\n------------------------------------------------------------\n"); printf(" EXPERIMENT E2: ANECompiler / model.hwx generation\n"); printf("------------------------------------------------------------\n"); { printf("\n --- E2.1: Looking for ANECompiler ---\n"); const char *compiler_paths[] = { "/System/Library/PrivateFrameworks/" "ANECompiler.framework/ANECompiler", "/System/Library/PrivateFrameworks/" "ANECompiler.framework/Versions/Current/ANECompiler", "/System/Library/Frameworks/" "CoreML.framework/Versions/A/CoreML", NULL }; void *compilerHandle = NULL; for (int i = 0; compiler_paths[i]; i++) { compilerHandle = dlopen(compiler_paths[i], RTLD_NOW); if (compilerHandle) { printf(" Found: %s\n", compiler_paths[i]); break; } else { printf(" Not at: %s\n", compiler_paths[i]); } } printf("\n --- E2.2: Compiler class search ---\n"); const char *compiler_classes[] = { "ANECompiler", "_ANECompiler", "ANECompilerService", "_ANECompilerService", "ANECompileOptions", "_ANECompileOptions", "ANEModelCompiler", "_ANEModelCompiler", "ANECCompiler", NULL }; for (int i = 0; compiler_classes[i]; i++) { Class cls = NSClassFromString( [NSString stringWithUTF8String:compiler_classes[i]]); if (cls) { printf(" FOUND: %s\n", compiler_classes[i]); dump_class(compiler_classes[i]); } else { printf(" %s: not found\n", compiler_classes[i]); } } printf("\n --- E2.3: Compile with debug_mask ---\n"); @try { Class gD = NSClassFromString(@"_ANEInMemoryModelDescriptor"); Class gI = NSClassFromString(@"_ANEInMemoryModel"); int dbg_ch = 32, dbg_ws = dbg_ch * dbg_ch * 2; int dbg_tot = 128 + dbg_ws; uint8_t *dbg_blob = (uint8_t *)calloc((size_t)dbg_tot, 1); dbg_blob[0] = 1; dbg_blob[4] = 2; dbg_blob[64] = 0xEF; dbg_blob[65] = 0xBE; dbg_blob[66] = 0xAD; dbg_blob[67] = 0xDE; dbg_blob[68] = 1; *(uint32_t *)(dbg_blob + 72) = (uint32_t)dbg_ws; *(uint32_t *)(dbg_blob + 80) = 128; _Float16 *dbg_wp = (_Float16 *)(dbg_blob + 128); for (int i = 0; i < dbg_ch; i++) dbg_wp[i * dbg_ch + i] = (_Float16)1.0f; NSData *wdata = [NSData dataWithBytesNoCopy:dbg_blob length:(NSUInteger)dbg_tot freeWhenDone:YES]; NSString *mil = gen_conv_mil(32, 16); NSData *md = [mil dataUsingEncoding:NSUTF8StringEncoding]; id desc = ((id(*)(Class,SEL,id,id,id))objc_msgSend)(gD, @selector(modelWithMILText:weights:optionsPlist:), md, @{@"@model_path/weights/weight.bin": @{@"offset":@0, @"data":wdata}}, nil); id mdl = ((id(*)(Class,SEL,id))objc_msgSend)(gI, @selector(inMemoryModelWithDescriptor:), desc); id hx = ((id(*)(id,SEL))objc_msgSend)(mdl, @selector(hexStringIdentifier)); NSString *td = [NSTemporaryDirectory() stringByAppendingPathComponent: [NSString stringWithFormat:@"debug_%@", hx]]; NSFileManager *fm = [NSFileManager defaultManager]; [fm createDirectoryAtPath: [td stringByAppendingPathComponent:@"weights"] withIntermediateDirectories:YES attributes:nil error:nil]; [md writeToFile:[td stringByAppendingPathComponent:@"model.mil"] atomically:YES]; [wdata writeToFile: [td stringByAppendingPathComponent:@"weights/weight.bin"] atomically:YES]; NSDictionary *debugOpts = @{ @"debug_mask": @(INT_MAX), @"ANEDebugMask": @(INT_MAX), @"ane_debug_mask": @(INT_MAX), }; printf(" Compiling with debug_mask=%d...\n", INT_MAX); NSError *e = nil; BOOL ok = ((BOOL(*)(id,SEL,unsigned int,id,NSError**)) objc_msgSend)(mdl, @selector(compileWithQoS:options:error:), 21, debugOpts, &e); printf(" Compile: %s\n", ok ? "SUCCESS" : "FAILED"); if (!ok && e) printf(" Error: %s\n", [[e description] UTF8String]); if (ok) { printf(" Temp dir after debug compile:\n"); list_dir_recursive(td, 0); NSDirectoryEnumerator *de = [fm enumeratorAtPath:td]; NSString *f; int hwxCount = 0; while ((f = [de nextObject])) { if ([[f pathExtension] isEqualToString:@"hwx"]) hwxCount++; } if (hwxCount > 0) printf(" Found %d .hwx file(s)!\n", hwxCount); if (gANEModel && !diskModel1) { @try { SEL sel = NSSelectorFromString(@"modelAtURL:key:"); if ([gANEModel respondsToSelector:sel]) { NSURL *dURL = [NSURL fileURLWithPath:td]; diskModel1 = ((id(*)(Class,SEL,id,id)) objc_msgSend)(gANEModel, sel, dURL, hx); printf(" modelAtURL:key: on debug dir: %s\n", diskModel1 ? [[diskModel1 description] UTF8String] : "nil"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } [fm removeItemAtPath:td error:nil]; } @catch (NSException *ex) { printf(" E2.3 EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- E2.4: ane_compiler_service search ---\n"); { NSFileManager *fm = [NSFileManager defaultManager]; const char *svc_paths[] = { "/usr/libexec/ane_compiler_service", "/System/Library/CoreServices/ane_compiler_service", "/System/Library/PrivateFrameworks/" "ANECompiler.framework/ane_compiler_service", NULL }; for (int i = 0; svc_paths[i]; i++) { NSString *p = [NSString stringWithUTF8String:svc_paths[i]]; printf(" %s: %s\n", svc_paths[i], [fm fileExistsAtPath:p] ? "FOUND" : "not found"); } } printf("\n --- E2.5: _ANEInMemoryModel compilation methods ---\n"); { Class gI = NSClassFromString(@"_ANEInMemoryModel"); unsigned int mc; Method *ms = class_copyMethodList(gI, &mc); for (unsigned int i = 0; i < mc; i++) { const char *mn = sel_getName(method_getName(ms[i])); if (strstr(mn, "compile") || strstr(mn, "Compile") || strstr(mn, "hwx") || strstr(mn, "HWX") || strstr(mn, "binary") || strstr(mn, "Binary") || strstr(mn, "save") || strstr(mn, "Save") || strstr(mn, "export") || strstr(mn, "Export") || strstr(mn, "path") || strstr(mn, "Path") || strstr(mn, "url") || strstr(mn, "URL") || strstr(mn, "temp") || strstr(mn, "Temp") || strstr(mn, "cache") || strstr(mn, "Cache")) { printf(" - %s [%s]\n", mn, method_getTypeEncoding(ms[i])); } } free(ms); } } // ================================================================= // EXPERIMENT F: Full chaining pipeline with _ANEModel // ================================================================= printf("\n------------------------------------------------------------\n"); printf(" EXPERIMENT F: Full chaining pipeline\n"); printf("------------------------------------------------------------\n"); { if (!diskModel1) { printf(" SKIPPED: _ANEModel not loaded\n"); printf(" Fallback: prepareChainingWithModel on InMemoryModel\n\n"); @try { id ioObj1 = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioIn); id buf1 = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), ioObj1, @0, (long long)0); id outIO1 = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioOut); id outBuf1 = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), outIO1, @0, (long long)1); IOSurfaceRef statsSurf = make_surface(64); id outSet = ((id(*)(Class,SEL,IOSurfaceRef,id))objc_msgSend)( gOutSets, @selector(objectWithstatsSurRef:outputBuffer:), statsSurf, @[outBuf1]); id chainReq = ((id(*)(Class,SEL,id,id,id,id,id,id,id,id,id)) objc_msgSend)(gChain, @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId:procedureIndex: signalEvents:transactionHandle:fwEnqueueDelay: memoryPoolId:), @[buf1], @[outSet], @(-1), @(-1), @0, @[], @0, @0, @0); if (chainReq) { BOOL valid = ((BOOL(*)(id,SEL))objc_msgSend)( chainReq, @selector(validate)); printf(" validate: %s\n", valid ? "YES" : "NO"); NSError *prepErr = nil; BOOL prepOk = ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)(client, @selector(prepareChainingWithModel:options: chainingReq:qos:error:), k1.model, @{}, chainReq, (unsigned int)21, &prepErr); printf(" prepareChainingWithModel (InMemory): %s\n", prepOk ? "YES" : "NO"); if (!prepOk && prepErr) printf(" Error: %s\n", [[prepErr description] UTF8String]); } CFRelease(statsSurf); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); printf(" (Expected: getUUID unrecognized selector)\n"); } } else { printf(" Using _ANEModel for chaining!\n\n"); NSArray *inputSymbols = nil; NSArray *outputSymbols = nil; @try { SEL inSel = NSSelectorFromString( @"inputSymbolIndicesForProcedureIndex:"); inputSymbols = ((id(*)(id,SEL,NSUInteger))objc_msgSend)( diskModel1, inSel, (NSUInteger)0); SEL outSel = NSSelectorFromString( @"outputSymbolIndicesForProcedureIndex:"); outputSymbols = ((id(*)(id,SEL,NSUInteger))objc_msgSend)( diskModel1, outSel, (NSUInteger)0); printf(" Input symbols: %s\n", inputSymbols ? [[inputSymbols description] UTF8String] : "nil"); printf(" Output symbols: %s\n", outputSymbols ? [[outputSymbols description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" Symbol query EXCEPTION: %s\n", [[ex reason] UTF8String]); } @try { NSNumber *inSymIdx = (inputSymbols.count > 0) ? inputSymbols[0] : @0; NSNumber *outSymIdx = (outputSymbols.count > 0) ? outputSymbols[0] : @0; id ioObj1 = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioIn); id inBuf = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), ioObj1, inSymIdx, (long long)0); id outIO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioOut); id outBuf = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), outIO, outSymIdx, (long long)1); IOSurfaceRef statsSurf = make_surface(64); id outSet = ((id(*)(Class,SEL,IOSurfaceRef,id))objc_msgSend)( gOutSets, @selector(objectWithstatsSurRef:outputBuffer:), statsSurf, @[outBuf]); id chainReq = ((id(*)(Class,SEL,id,id,id,id,id,id,id,id,id)) objc_msgSend)(gChain, @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId:procedureIndex: signalEvents:transactionHandle:fwEnqueueDelay: memoryPoolId:), @[inBuf], @[outSet], @(-1), @(-1), @0, @[], @0, @0, @0); if (chainReq) { BOOL valid = ((BOOL(*)(id,SEL))objc_msgSend)( chainReq, @selector(validate)); printf(" validate: %s\n", valid ? "YES" : "NO"); NSError *prepErr = nil; BOOL prepOk = ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)(client, @selector(prepareChainingWithModel:options: chainingReq:qos:error:), diskModel1, @{}, chainReq, (unsigned int)21, &prepErr); printf(" prepareChainingWithModel: %s\n", prepOk ? "YES" : "NO"); if (!prepOk && prepErr) printf(" Error: %s\n", [[prepErr description] UTF8String]); if (prepOk) { printf(" CHAINING PREPARE SUCCEEDED!\n"); @try { NSError *enqErr = nil; BOOL enqOk = ((BOOL(*)(id,SEL,id,id,id, unsigned int,NSError**))objc_msgSend)( client, @selector(enqueueSetsWithModel:outputSet: options:qos:error:), diskModel1, outSet, @{}, (unsigned int)21, &enqErr); printf(" enqueueSets: %s\n", enqOk ? "YES" : "NO"); if (!enqOk && enqErr) printf(" Error: %s\n", [[enqErr description] UTF8String]); } @catch (NSException *ex) { printf(" enqueueSets EXCEPTION: %s\n", [[ex reason] UTF8String]); } @try { NSError *rdyErr = nil; BOOL rdyOk = ((BOOL(*)(id,SEL,id,id,id, unsigned int,NSError**))objc_msgSend)( client, @selector(buffersReadyWithModel: inputBuffers:options:qos:error:), diskModel1, @[inBuf], @{}, (unsigned int)21, &rdyErr); printf(" buffersReady: %s\n", rdyOk ? "YES" : "NO"); if (!rdyOk && rdyErr) printf(" Error: %s\n", [[rdyErr description] UTF8String]); } @catch (NSException *ex) { printf(" buffersReady EXCEPTION: %s\n", [[ex reason] UTF8String]); } // Benchmark sequential baseline printf("\n --- Benchmark ---\n"); id wI = ((id(*)(Class,SEL,IOSurfaceRef)) objc_msgSend)(gAIO, @selector(objectWithIOSurface:), k1.ioIn); id wO = ((id(*)(Class,SEL,IOSurfaceRef)) objc_msgSend)(gAIO, @selector(objectWithIOSurface:), k1.ioOut); id req = ((id(*)(Class,SEL,id,id,id,id,id,id,id)) objc_msgSend)(gAR, @selector(requestWithInputs:inputIndices: outputs:outputIndices:weightsBuffer: perfStats:procedureIndex:), @[wI], @[@0], @[wO], @[@0], nil, nil, @0); int iters = 100; NSError *seqErr = nil; uint64_t t0 = mach_absolute_time(); for (int i = 0; i < iters; i++) { ((BOOL(*)(id,SEL,unsigned int,id,id, NSError**))objc_msgSend)(k1.model, @selector(evaluateWithQoS:options: request:error:), 21, @{}, req, &seqErr); } double seqMs = tb_ms( mach_absolute_time() - t0) / iters; printf(" Sequential: %.3f ms/kernel\n", seqMs); } } CFRelease(statsSurf); } @catch (NSException *ex) { printf(" Chaining EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } // ================================================================= // EXPERIMENT G: IOSurfaceSharedEvent / hardware fences // ================================================================= printf("\n------------------------------------------------------------\n"); printf(" EXPERIMENT G: IOSurfaceSharedEvent / hardware fences\n"); printf("------------------------------------------------------------\n"); { Class gSigEvent = NSClassFromString(@"_ANESharedSignalEvent"); Class gWaitEvent = NSClassFromString(@"_ANESharedWaitEvent"); printf("\n --- G.1: Event class API ---\n"); if (gSigEvent) dump_class("_ANESharedSignalEvent"); else printf(" _ANESharedSignalEvent: NOT FOUND\n"); if (gWaitEvent) dump_class("_ANESharedWaitEvent"); else printf(" _ANESharedWaitEvent: NOT FOUND\n"); printf("\n --- G.2: MTLSharedEvent via Metal ---\n"); @try { void *metalH = dlopen( "/System/Library/Frameworks/Metal.framework/Metal", RTLD_NOW); if (metalH) { id (*createDev)(void) = dlsym(metalH, "MTLCreateSystemDefaultDevice"); if (createDev) { id dev = createDev(); printf(" MTLDevice: %s\n", dev ? [[dev description] UTF8String] : "nil"); if (dev) { SEL newEvt = NSSelectorFromString( @"newSharedEvent"); if ([dev respondsToSelector:newEvt]) { id shEvt = ((id(*)(id,SEL))objc_msgSend)( dev, newEvt); printf(" MTLSharedEvent: %s\n", shEvt ? [[shEvt description] UTF8String] : "nil"); if (shEvt && gSigEvent) { printf("\n --- G.3: _ANESharedSignalEvent " "with MTLSharedEvent ---\n"); // Factory: (Q16 I24 q28 @36) = // (uint64_t, unsigned int, long long, id) @try { SEL sigSel = NSSelectorFromString( @"signalEventWithValue:symbolIndex:" "eventType:sharedEvent:"); if ([gSigEvent respondsToSelector:sigSel]) { for (int et = 0; et <= 2; et++) { id se = ((id(*)(Class,SEL, uint64_t,unsigned int, long long,id)) objc_msgSend)(gSigEvent, sigSel, (uint64_t)1, (unsigned int)0, (long long)et, shEvt); printf(" eventType=%d: %s\n", et, se ? [[se description] UTF8String] : "nil"); if (se) { dump_all_properties( se, gSigEvent); } } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } if (shEvt && gWaitEvent) { printf("\n --- G.4: _ANESharedWaitEvent " "with MTLSharedEvent ---\n"); // Factory: waitEventWithValue:sharedEvent: // (Q16 @24) = (uint64_t, id) // Factory: waitEventWithValue:sharedEvent:eventType: // (Q16 @24 Q32) = (uint64_t, id, uint64_t) @try { SEL wSel = NSSelectorFromString( @"waitEventWithValue:sharedEvent:"); if ([gWaitEvent respondsToSelector:wSel]) { id we = ((id(*)(Class,SEL, uint64_t,id)) objc_msgSend)(gWaitEvent, wSel, (uint64_t)1, shEvt); printf(" waitEvent(2-param): %s\n", we ? [[we description] UTF8String] : "nil"); if (we) dump_all_properties( we, gWaitEvent); } } @catch (NSException *ex) { printf(" 2-param EXCEPTION: %s\n", [[ex reason] UTF8String]); } @try { SEL wSel3 = NSSelectorFromString( @"waitEventWithValue:" "sharedEvent:eventType:"); if ([gWaitEvent respondsToSelector:wSel3]) { id we = ((id(*)(Class,SEL, uint64_t,id,uint64_t)) objc_msgSend)(gWaitEvent, wSel3, (uint64_t)1, shEvt, (uint64_t)0); printf(" waitEvent(3-param): %s\n", we ? [[we description] UTF8String] : "nil"); if (we) dump_all_properties( we, gWaitEvent); } } @catch (NSException *ex) { printf(" 3-param EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } } } } } @catch (NSException *ex) { printf(" Metal EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- G.5: IOSurfaceSharedEventCreate ---\n"); @try { void *iosH = dlopen( "/System/Library/Frameworks/" "IOSurface.framework/IOSurface", RTLD_NOW); if (iosH) { typedef id (*CreateFunc)(void); CreateFunc fn = dlsym(iosH, "IOSurfaceSharedEventCreate"); if (fn) { id iosEvt = fn(); printf(" IOSurfaceSharedEventCreate: %s\n", iosEvt ? [[iosEvt description] UTF8String] : "nil"); // Try using IOSurfaceSharedEvent with signal/wait if (iosEvt && gSigEvent) { printf("\n G.5b: SignalEvent with IOSurfaceSharedEvent\n"); @try { SEL sigSel = NSSelectorFromString( @"signalEventWithValue:symbolIndex:" "eventType:sharedEvent:"); id se = ((id(*)(Class,SEL,uint64_t, unsigned int,long long,id)) objc_msgSend)(gSigEvent, sigSel, (uint64_t)1, (unsigned int)0, (long long)0, iosEvt); printf(" signalEvent: %s\n", se ? [[se description] UTF8String] : "nil"); if (se) dump_all_properties(se, gSigEvent); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } if (iosEvt && gWaitEvent) { printf("\n G.5c: WaitEvent with IOSurfaceSharedEvent\n"); @try { SEL wSel = NSSelectorFromString( @"waitEventWithValue:sharedEvent:"); id we = ((id(*)(Class,SEL,uint64_t,id)) objc_msgSend)(gWaitEvent, wSel, (uint64_t)1, iosEvt); printf(" waitEvent: %s\n", we ? [[we description] UTF8String] : "nil"); if (we) dump_all_properties(we, gWaitEvent); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } else { printf(" IOSurfaceSharedEventCreate: not found\n"); } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } // ================================================================= // EXPERIMENT H: Alternative chaining preparation // ================================================================= printf("\n------------------------------------------------------------\n"); printf(" EXPERIMENT H: Alternative chaining preparation\n"); printf("------------------------------------------------------------\n"); { printf("\n --- H.1: _ANEClient chaining methods ---\n"); Class clientCls = NSClassFromString(@"_ANEClient"); if (clientCls) { unsigned int mc; Method *ms = class_copyMethodList(clientCls, &mc); for (unsigned int i = 0; i < mc; i++) { const char *mn = sel_getName(method_getName(ms[i])); if (strstr(mn, "chain") || strstr(mn, "Chain") || strstr(mn, "prepare") || strstr(mn, "Prepare") || strstr(mn, "enqueue") || strstr(mn, "Enqueue") || strstr(mn, "buffer") || strstr(mn, "Buffer") || strstr(mn, "ready") || strstr(mn, "Ready") || strstr(mn, "pipeline") || strstr(mn, "Pipeline") || strstr(mn, "batch") || strstr(mn, "Batch") || strstr(mn, "async") || strstr(mn, "Async")) { printf(" - %s [%s]\n", mn, method_getTypeEncoding(ms[i])); } } free(ms); printf("\n --- H.2: doPrepareChainingWithModel ---\n"); SEL doPrep = NSSelectorFromString( @"doPrepareChainingWithModel:options:" "chainingReq:qos:error:"); if ([client respondsToSelector:doPrep]) { printf(" doPrepareChainingWithModel EXISTS\n"); @try { id ioObj = ((id(*)(Class,SEL,IOSurfaceRef)) objc_msgSend)(gAIO, @selector(objectWithIOSurface:), k1.ioIn); id buf = ((id(*)(Class,SEL,id,id,long long)) objc_msgSend)(gBuf, @selector(bufferWithIOSurfaceObject: symbolIndex:source:), ioObj, @0, (long long)0); id outIO = ((id(*)(Class,SEL,IOSurfaceRef)) objc_msgSend)(gAIO, @selector(objectWithIOSurface:), k1.ioOut); id outBuf = ((id(*)(Class,SEL,id,id,long long)) objc_msgSend)(gBuf, @selector(bufferWithIOSurfaceObject: symbolIndex:source:), outIO, @0, (long long)1); IOSurfaceRef ss = make_surface(64); id os = ((id(*)(Class,SEL,IOSurfaceRef,id)) objc_msgSend)(gOutSets, @selector(objectWithstatsSurRef:outputBuffer:), ss, @[outBuf]); id cr = ((id(*)(Class,SEL,id,id,id,id,id,id,id,id,id)) objc_msgSend)(gChain, @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId: procedureIndex:signalEvents: transactionHandle:fwEnqueueDelay: memoryPoolId:), @[buf], @[os], @(-1), @(-1), @0, @[], @0, @0, @0); NSError *err = nil; printf(" With _ANEInMemoryModel...\n"); BOOL ok = ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)(client, doPrep, k1.model, @{}, cr, (unsigned int)21, &err); printf(" Result: %s\n", ok ? "YES" : "NO"); if (!ok && err) printf(" Error: %s\n", [[err description] UTF8String]); CFRelease(ss); } @catch (NSException *ex) { printf(" InMemory EXCEPTION: %s\n", [[ex reason] UTF8String]); } if (diskModel1) { @try { id ioObj = ((id(*)(Class,SEL,IOSurfaceRef)) objc_msgSend)(gAIO, @selector(objectWithIOSurface:), k1.ioIn); id buf = ((id(*)(Class,SEL,id,id,long long)) objc_msgSend)(gBuf, @selector(bufferWithIOSurfaceObject: symbolIndex:source:), ioObj, @0, (long long)0); id outIO = ((id(*)(Class,SEL,IOSurfaceRef)) objc_msgSend)(gAIO, @selector(objectWithIOSurface:), k1.ioOut); id outBuf = ((id(*)(Class,SEL,id,id,long long)) objc_msgSend)(gBuf, @selector(bufferWithIOSurfaceObject: symbolIndex:source:), outIO, @0, (long long)1); IOSurfaceRef ss = make_surface(64); id os = ((id(*)(Class,SEL,IOSurfaceRef,id)) objc_msgSend)(gOutSets, @selector(objectWithstatsSurRef:outputBuffer:), ss, @[outBuf]); id cr = ((id(*)(Class,SEL,id,id,id,id,id,id,id,id,id)) objc_msgSend)(gChain, @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId: procedureIndex:signalEvents: transactionHandle:fwEnqueueDelay: memoryPoolId:), @[buf], @[os], @(-1), @(-1), @0, @[], @0, @0, @0); NSError *err = nil; printf(" With _ANEModel...\n"); BOOL ok = ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)(client, doPrep, diskModel1, @{}, cr, (unsigned int)21, &err); printf(" Result: %s\n", ok ? "YES" : "NO"); if (!ok && err) printf(" Error: %s\n", [[err description] UTF8String]); CFRelease(ss); } @catch (NSException *ex) { printf(" ANEModel EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } else { printf(" NOT available\n"); } printf("\n --- H.3: All _ANEClient methods ---\n"); unsigned int allC; Method *allM = class_copyMethodList(clientCls, &allC); printf(" Total: %u\n", allC); for (unsigned int i = 0; i < allC; i++) { printf(" - %s\n", sel_getName(method_getName(allM[i]))); } free(allM); } } // ================================================================= // Experiment K: ChainingRequest Factory Type Encoding Analysis // ================================================================= printf("\n==============================================================\n"); printf(" Experiment K: Type Encoding Analysis\n"); printf("==============================================================\n\n"); { Class clientCls = object_getClass(client); SEL chainFactorySel = @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId:procedureIndex: signalEvents:transactionHandle:fwEnqueueDelay:memoryPoolId:); Method chainFactory = class_getClassMethod(gChain, chainFactorySel); if (chainFactory) { const char *enc = method_getTypeEncoding(chainFactory); printf(" ChainingRequest factory encoding: %s\n", enc ? enc : "nil"); if (enc) { const char *paramNames[] = { "return", "self", "_cmd", "inputs", "outputSets", "lbInputSymbolId", "lbOutputSymbolId", "procedureIndex", "signalEvents", "transactionHandle", "fwEnqueueDelay", "memoryPoolId" }; unsigned int nargs = method_getNumberOfArguments(chainFactory); printf(" Number of arguments: %u\n", nargs); for (unsigned int i = 0; i < nargs && i < 12; i++) { char argType[64] = {0}; method_getArgumentType(chainFactory, i, argType, sizeof(argType)); printf(" arg[%u] %-20s = %s", i, paramNames[i], argType); if (argType[0] == '@') printf(" (id/object)"); else if (argType[0] == 'q') printf(" (int64_t)"); else if (argType[0] == 'Q') printf(" (uint64_t)"); else if (argType[0] == 'i') printf(" (int32_t)"); else if (argType[0] == 'I') printf(" (uint32_t)"); else if (argType[0] == 'B') printf(" (BOOL)"); else if (argType[0] == 'v') printf(" (void)"); else if (argType[0] == ':') printf(" (SEL)"); printf("\n"); } } } else { printf(" ChainingRequest factory: NOT FOUND\n"); } SEL prepSel = @selector(prepareChainingWithModel:options: chainingReq:qos:error:); Method prepMethod = class_getInstanceMethod(clientCls, prepSel); if (!prepMethod) prepMethod = class_getInstanceMethod( NSClassFromString(@"_ANEClient"), prepSel); if (prepMethod) { const char *enc = method_getTypeEncoding(prepMethod); printf("\n prepareChainingWithModel encoding: %s\n", enc ? enc : "nil"); if (enc) { const char *pNames[] = { "return", "self", "_cmd", "model", "options", "chainingReq", "qos", "error" }; unsigned int nargs = method_getNumberOfArguments(prepMethod); printf(" Number of arguments: %u\n", nargs); for (unsigned int i = 0; i < nargs && i < 8; i++) { char argType[64] = {0}; method_getArgumentType(prepMethod, i, argType, sizeof(argType)); printf(" arg[%u] %-15s = %s", i, pNames[i], argType); if (argType[0] == '@') printf(" (id/object)"); else if (argType[0] == 'q') printf(" (int64_t)"); else if (argType[0] == 'Q') printf(" (uint64_t)"); else if (argType[0] == 'I') printf(" (uint32_t)"); else if (argType[0] == 'B') printf(" (BOOL)"); printf("\n"); } } } else { printf("\n prepareChainingWithModel: NOT FOUND\n"); } SEL doPrepSel = NSSelectorFromString( @"doPrepareChainingWithModel:options:chainingReq:qos:error:"); Method doPrepMethod = class_getInstanceMethod( NSClassFromString(@"_ANEClient"), doPrepSel); if (doPrepMethod) { const char *enc = method_getTypeEncoding(doPrepMethod); printf("\n doPrepareChainingWithModel encoding: %s\n", enc ? enc : "nil"); unsigned int nargs = method_getNumberOfArguments(doPrepMethod); printf(" Number of arguments: %u\n", nargs); for (unsigned int i = 0; i < nargs; i++) { char argType[64] = {0}; method_getArgumentType(doPrepMethod, i, argType, sizeof(argType)); printf(" arg[%u] = %s\n", i, argType); } } printf("\n --- K.2: All _ANEChainingRequest methods type encodings ---\n"); { unsigned int mc; Method *cms = class_copyMethodList(object_getClass(gChain), &mc); printf(" Class methods (%u):\n", mc); for (unsigned int i = 0; i < mc; i++) { const char *name = sel_getName(method_getName(cms[i])); const char *enc = method_getTypeEncoding(cms[i]); printf(" + %s\n encoding: %s\n", name, enc ? enc : "?"); } free(cms); Method *ims = class_copyMethodList(gChain, &mc); printf(" Instance methods (%u):\n", mc); for (unsigned int i = 0; i < mc; i++) { const char *name = sel_getName(method_getName(ims[i])); const char *enc = method_getTypeEncoding(ims[i]); printf(" - %s\n encoding: %s\n", name, enc ? enc : "?"); } free(ims); } } // ================================================================= // Experiment L: Array-Typed ChainingRequest Parameters // ================================================================= printf("\n==============================================================\n"); printf(" Experiment L: Array-Typed ChainingRequest Parameters\n"); printf("==============================================================\n\n"); BOOL chainingPrepSuccess = NO; id bestChainReq = nil; id bestModel = diskModel1 ? diskModel1 : k1.model; { id ioObj = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioIn); id inBuf = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), ioObj, @0, (long long)0); id outIO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioOut); id outBuf = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), outIO, @0, (long long)1); IOSurfaceRef statsSurf = make_surface(64); id outSet = ((id(*)(Class,SEL,IOSurfaceRef,id))objc_msgSend)( gOutSets, @selector(objectWithstatsSurRef:outputBuffer:), statsSurf, @[outBuf]); struct { const char *label; id lbIn; id lbOut; id procIdx; } combos[] = { { "arrays @[@(-1)]", @[@(-1)], @[@(-1)], @[@0] }, { "arrays @[@0]", @[@0], @[@0], @[@0] }, { "empty arrays @[]", @[], @[], @[] }, { "nil values", nil, nil, nil }, { "original NSNumber", @(-1), @(-1), @0 }, }; int ncombos = sizeof(combos)/sizeof(combos[0]); for (int ci = 0; ci < ncombos; ci++) { printf(" --- L.%d: %s ---\n", ci+1, combos[ci].label); @try { id cr = ((id(*)(Class,SEL,id,id,id,id,id,id,id,id,id)) objc_msgSend)(gChain, @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId:procedureIndex: signalEvents:transactionHandle:fwEnqueueDelay: memoryPoolId:), @[inBuf], @[outSet], combos[ci].lbIn, combos[ci].lbOut, combos[ci].procIdx, @[], @0, @0, @0); if (!cr) { printf(" ChainingRequest: nil\n\n"); continue; } BOOL valid = ((BOOL(*)(id,SEL))objc_msgSend)( cr, @selector(validate)); printf(" validate: %s\n", valid ? "YES" : "NO"); printf(" desc: %s\n", [[cr description] UTF8String]); @try { NSError *prepErr = nil; BOOL prepOk = ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)(client, @selector(prepareChainingWithModel:options: chainingReq:qos:error:), bestModel, @{}, cr, (unsigned int)21, &prepErr); printf(" prepareChainingWithModel: %s\n", prepOk ? "YES" : "NO"); if (prepErr) printf(" Error: %s\n", [[prepErr description] UTF8String]); if (prepOk || !prepErr) { chainingPrepSuccess = prepOk; bestChainReq = cr; printf(" *** GOT PAST THE CRASH! ***\n"); } } @catch (NSException *prepEx) { printf(" prepare EXCEPTION: %s\n", [[prepEx reason] UTF8String]); } } @catch (NSException *ex) { printf(" factory EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n"); } CFRelease(statsSurf); } // ================================================================= // Experiment M: Load Model via _ANEClient // ================================================================= printf("\n==============================================================\n"); printf(" Experiment M: Load Model via _ANEClient\n"); printf("==============================================================\n\n"); id fullyLoadedModel = nil; { if (!diskModel1) { printf(" SKIPPED: no _ANEModel from Experiment E\n"); } else { @try { id st = [diskModel1 valueForKey:@"state"]; printf(" diskModel1 state before: %s\n", st ? [[st description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" state query exception: %s\n", [[ex reason] UTF8String]); } printf("\n --- M.1: compiledModelExistsFor: ---\n"); @try { SEL existsSel = NSSelectorFromString( @"compiledModelExistsFor:"); if ([client respondsToSelector:existsSel]) { BOOL exists = ((BOOL(*)(id,SEL,id))objc_msgSend)( client, existsSel, diskModel1); printf(" compiledModelExistsFor: %s\n", exists ? "YES" : "NO"); } else { printf(" compiledModelExistsFor: NOT AVAILABLE\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- M.2: loadModel:options:qos:error: ---\n"); @try { SEL loadSel = NSSelectorFromString( @"loadModel:options:qos:error:"); if ([client respondsToSelector:loadSel]) { NSError *loadErr = nil; BOOL loadOk = ((BOOL(*)(id,SEL,id,id,unsigned int, NSError**))objc_msgSend)(client, loadSel, diskModel1, @{}, (unsigned int)21, &loadErr); printf(" loadModel: %s\n", loadOk ? "YES" : "NO"); if (loadErr) printf(" Error: %s\n", [[loadErr description] UTF8String]); if (loadOk) { fullyLoadedModel = diskModel1; @try { SEL inSel = NSSelectorFromString( @"inputSymbolIndicesForProcedureIndex:"); id inSyms = ((id(*)(id,SEL,NSUInteger)) objc_msgSend)(diskModel1, inSel, 0); SEL outSel = NSSelectorFromString( @"outputSymbolIndicesForProcedureIndex:"); id outSyms = ((id(*)(id,SEL,NSUInteger)) objc_msgSend)(diskModel1, outSel, 0); printf(" After load - inputSymbols: %s\n", inSyms ? [[inSyms description] UTF8String] : "nil/empty"); printf(" After load - outputSymbols: %s\n", outSyms ? [[outSyms description] UTF8String] : "nil/empty"); } @catch (NSException *ex) { printf(" Symbol query EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } else { printf(" loadModel: NOT AVAILABLE\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- M.3: compileModel:options:qos:error: ---\n"); @try { SEL compileSel = NSSelectorFromString( @"compileModel:options:qos:error:"); if ([client respondsToSelector:compileSel]) { NSError *compErr = nil; BOOL compOk = ((BOOL(*)(id,SEL,id,id,unsigned int, NSError**))objc_msgSend)(client, compileSel, diskModel1, @{}, (unsigned int)21, &compErr); printf(" compileModel: %s\n", compOk ? "YES" : "NO"); if (compErr) printf(" Error: %s\n", [[compErr description] UTF8String]); if (compOk) { fullyLoadedModel = diskModel1; @try { id inSyms = ((id(*)(id,SEL,NSUInteger)) objc_msgSend)(diskModel1, NSSelectorFromString( @"inputSymbolIndicesForProcedureIndex:"), 0); id outSyms = ((id(*)(id,SEL,NSUInteger)) objc_msgSend)(diskModel1, NSSelectorFromString( @"outputSymbolIndicesForProcedureIndex:"), 0); printf(" After compile - inputSymbols: %s\n", inSyms ? [[inSyms description] UTF8String] : "nil/empty"); printf(" After compile - outputSymbols: %s\n", outSyms ? [[outSyms description] UTF8String] : "nil/empty"); } @catch (NSException *ex) { printf(" Symbol query EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } else { printf(" compileModel: NOT AVAILABLE\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } @try { id st = [diskModel1 valueForKey:@"state"]; printf("\n diskModel1 state after: %s\n", st ? [[st description] UTF8String] : "nil"); } @catch (NSException *ex) { printf("\n state query exception: %s\n", [[ex reason] UTF8String]); } } } // ================================================================= // Experiment N: IOSurface Mapping via _ANEProgramIOSurfacesMapper // ================================================================= printf("\n==============================================================\n"); printf(" Experiment N: IOSurface Mapping\n"); printf("==============================================================\n\n"); { Class gMapper = NSClassFromString(@"_ANEProgramIOSurfacesMapper"); if (!gMapper) { printf(" _ANEProgramIOSurfacesMapper: NOT FOUND\n"); } else { printf(" _ANEProgramIOSurfacesMapper: FOUND\n"); dump_class("_ANEProgramIOSurfacesMapper"); id progHandle = nil; @try { progHandle = [k1.model valueForKey:@"programHandle"]; } @catch (NSException *ex) { printf(" programHandle exception: %s\n", [[ex reason] UTF8String]); } printf("\n --- N.1: mapperWithProgramHandle: ---\n"); id mapper = nil; if (progHandle) { uint64_t ph = [progHandle unsignedLongLongValue]; printf(" programHandle = %llu\n", ph); @try { SEL mapperSel = NSSelectorFromString( @"mapperWithProgramHandle:"); mapper = ((id(*)(Class,SEL,uint64_t))objc_msgSend)( gMapper, mapperSel, ph); printf(" mapper created: %s\n", mapper ? [[mapper description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } id targetModel = diskModel1 ? diskModel1 : k1.model; const char *modelType = diskModel1 ? "_ANEModel" : "InMemoryModel"; if (mapper) { printf("\n --- N.2: mapIOSurfacesWithModel: (%s) ---\n", modelType); @try { id ioObj = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioIn); id outIO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioOut); id req = ((id(*)(Class,SEL,id,id,id,id,id,id,id)) objc_msgSend)(gAR, @selector(requestWithInputs:inputIndices: outputs:outputIndices:weightsBuffer: perfStats:procedureIndex:), @[ioObj], @[@0], @[outIO], @[@0], nil, nil, @0); if (req) { SEL mapSel = NSSelectorFromString( @"mapIOSurfacesWithModel:request:" "cacheInference:error:"); NSError *mapErr = nil; BOOL mapOk = ((BOOL(*)(id,SEL,id,id,BOOL, NSError**))objc_msgSend)(mapper, mapSel, targetModel, req, NO, &mapErr); printf(" mapIOSurfaces: %s\n", mapOk ? "YES" : "NO"); if (mapErr) printf(" Error: %s\n", [[mapErr description] UTF8String]); if (mapOk) { dump_all_properties(mapper, [mapper class]); } } else { printf(" Request creation failed\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- N.3: validateRequest:model: ---\n"); @try { id ioObj = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioIn); id outIO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioOut); id req = ((id(*)(Class,SEL,id,id,id,id,id,id,id)) objc_msgSend)(gAR, @selector(requestWithInputs:inputIndices: outputs:outputIndices:weightsBuffer: perfStats:procedureIndex:), @[ioObj], @[@0], @[outIO], @[@0], nil, nil, @0); SEL valSel = NSSelectorFromString( @"validateRequest:model:"); BOOL valOk = ((BOOL(*)(id,SEL,id,id))objc_msgSend)( mapper, valSel, req, targetModel); printf(" validateRequest: %s\n", valOk ? "YES" : "NO"); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } if (diskModel1) { printf("\n --- N.4: _ANEModel.mapper property ---\n"); @try { id modelMapper = [diskModel1 valueForKey:@"mapper"]; printf(" model.mapper: %s\n", modelMapper ? [[modelMapper description] UTF8String] : "nil"); if (modelMapper) { dump_all_properties(modelMapper, [modelMapper class]); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } } } } // ================================================================= // Experiment O: Procedure Info Extraction // ================================================================= printf("\n==============================================================\n"); printf(" Experiment O: Procedure Info Extraction\n"); printf("==============================================================\n\n"); { id targetModel = diskModel1 ? diskModel1 : k1.model; const char *modelType = diskModel1 ? "_ANEModel" : "InMemoryModel"; printf(" Using: %s\n", modelType); printf("\n --- O.1: procedureInfoForProcedureIndex:0 ---\n"); @try { SEL piSel = NSSelectorFromString( @"procedureInfoForProcedureIndex:"); if ([targetModel respondsToSelector:piSel]) { id pInfo = ((id(*)(id,SEL,NSUInteger))objc_msgSend)( targetModel, piSel, (NSUInteger)0); printf(" procedureInfo: %s\n", pInfo ? [[pInfo description] UTF8String] : "nil"); if (pInfo) { printf(" class: %s\n", [NSStringFromClass([pInfo class]) UTF8String]); dump_all_properties(pInfo, [pInfo class]); } } else { printf(" procedureInfoForProcedureIndex: NOT AVAILABLE\n"); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- O.2: procedureCount ---\n"); @try { SEL pcSel = NSSelectorFromString(@"procedureCount"); if ([targetModel respondsToSelector:pcSel]) { NSUInteger pc = ((NSUInteger(*)(id,SEL))objc_msgSend)( targetModel, pcSel); printf(" procedureCount: %lu\n", (unsigned long)pc); } else { printf(" procedureCount: NOT AVAILABLE\n"); id pcVal = nil; @try { pcVal = [targetModel valueForKey:@"procedureCount"]; printf(" procedureCount (KVC): %s\n", pcVal ? [[pcVal description] UTF8String] : "nil"); } @catch (NSException *ex2) { printf(" procedureCount KVC: %s\n", [[ex2 reason] UTF8String]); } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- O.3: modelAttributes ---\n"); @try { id attrs = [targetModel valueForKey:@"modelAttributes"]; printf(" modelAttributes: %s\n", attrs ? [[attrs description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- O.4: inputSymbolNames / outputSymbolNames ---\n"); @try { SEL inNamesSel = NSSelectorFromString(@"inputSymbolNames"); if ([targetModel respondsToSelector:inNamesSel]) { id names = ((id(*)(id,SEL))objc_msgSend)( targetModel, inNamesSel); printf(" inputSymbolNames: %s\n", names ? [[names description] UTF8String] : "nil"); } else { printf(" inputSymbolNames: NOT AVAILABLE as method\n"); @try { id n = [targetModel valueForKey:@"inputSymbolNames"]; printf(" inputSymbolNames (KVC): %s\n", n ? [[n description] UTF8String] : "nil"); } @catch (NSException *ex2) { printf(" inputSymbolNames KVC: not available\n"); } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } @try { SEL outNamesSel = NSSelectorFromString(@"outputSymbolNames"); if ([targetModel respondsToSelector:outNamesSel]) { id names = ((id(*)(id,SEL))objc_msgSend)( targetModel, outNamesSel); printf(" outputSymbolNames: %s\n", names ? [[names description] UTF8String] : "nil"); } else { printf(" outputSymbolNames: NOT AVAILABLE as method\n"); @try { id n = [targetModel valueForKey:@"outputSymbolNames"]; printf(" outputSymbolNames (KVC): %s\n", n ? [[n description] UTF8String] : "nil"); } @catch (NSException *ex2) { printf(" outputSymbolNames KVC: not available\n"); } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } if (diskModel1) { printf("\n --- O.5: Full _ANEModel property dump ---\n"); dump_all_properties(diskModel1, NSClassFromString(@"_ANEModel")); } } // ================================================================= // Experiment P: Full Chaining Retry with Fixes // ================================================================= printf("\n==============================================================\n"); printf(" Experiment P: Full Chaining Retry\n"); printf("==============================================================\n\n"); BOOL chainExecuted = NO; { void *metalHandle = dlopen( "/System/Library/Frameworks/Metal.framework/Metal", RTLD_NOW); (void)metalHandle; id mtlDev = nil; { id (*createDev)(void) = (id(*)(void)) dlsym(RTLD_DEFAULT, "MTLCreateSystemDefaultDevice"); if (createDev) mtlDev = createDev(); } id signalEvents = @[]; id shEvt = nil; if (mtlDev) { printf(" Metal device: %s\n", [[mtlDev description] UTF8String]); @try { shEvt = ((id(*)(id,SEL))objc_msgSend)( mtlDev, NSSelectorFromString(@"newSharedEvent")); if (shEvt) { Class gSigEvent = NSClassFromString( @"_ANESharedSignalEvent"); if (gSigEvent) { long long et = 0; @try { id etObj = [gSigEvent valueForKey: @"ANESignalEventTypeMTLSharedEvent"]; if (etObj) et = [etObj longLongValue]; } @catch (NSException *ex) { (void)ex; } SEL sigSel = NSSelectorFromString( @"signalEventWithValue:symbolIndex:" "eventType:sharedEvent:"); id se = ((id(*)(Class,SEL,uint64_t,unsigned int, long long,id))objc_msgSend)( gSigEvent, sigSel, (uint64_t)1, (unsigned int)0, et, shEvt); if (se) signalEvents = @[se]; printf(" SignalEvent: %s\n", se ? "created" : "nil"); } } } @catch (NSException *ex) { printf(" SharedEvent EXCEPTION: %s\n", [[ex reason] UTF8String]); } } struct { const char *label; id model; } modelCandidates[3]; int nCandidates = 0; CompiledKernel k3 = compile_kernel(64, 32); if (k3.model) { NSURL *url = [NSURL fileURLWithPath:k3.tmpDir isDirectory:YES]; Class gANEModel = NSClassFromString(@"_ANEModel"); id freshDisk = ((id(*)(Class,SEL,id,id))objc_msgSend)( gANEModel, @selector(modelAtURL:key:), url, k3.hexId); if (freshDisk) { id ph = [k3.model valueForKey:@"programHandle"]; id prog = [k3.model valueForKey:@"program"]; if (ph) ((void(*)(id,SEL,uint64_t))objc_msgSend)( freshDisk, @selector(setProgramHandle:), [ph unsignedLongLongValue]); if (prog) ((void(*)(id,SEL,id))objc_msgSend)( freshDisk, @selector(setProgram:), prog); modelCandidates[nCandidates++] = (typeof(modelCandidates[0])) {"fresh _ANEModel (state=1)", freshDisk}; } modelCandidates[nCandidates++] = (typeof(modelCandidates[0])) {"InMemoryModel (k3)", k3.model}; } if (diskModel1) { modelCandidates[nCandidates++] = (typeof(modelCandidates[0])) {"populated _ANEModel (from E)", diskModel1}; } for (int mi = 0; mi < nCandidates; mi++) { printf("\n --- P.%d: %s ---\n", mi+1, modelCandidates[mi].label); id chainModel = modelCandidates[mi].model; printf(" class: %s\n", [NSStringFromClass([chainModel class]) UTF8String]); @try { id st = [chainModel valueForKey:@"state"]; printf(" state: %s\n", st ? [[st description] UTF8String] : "N/A"); } @catch (NSException *ex) { (void)ex; } id ioObj1 = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioIn); id inBuf = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), ioObj1, @0, (long long)0); id outIO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)( gAIO, @selector(objectWithIOSurface:), k1.ioOut); id outBuf = ((id(*)(Class,SEL,id,id,long long))objc_msgSend)( gBuf, @selector(bufferWithIOSurfaceObject:symbolIndex:source:), outIO, @0, (long long)1); IOSurfaceRef sSurf = make_surface(64); id outSet = ((id(*)(Class,SEL,IOSurfaceRef,id))objc_msgSend)( gOutSets, @selector(objectWithstatsSurRef:outputBuffer:), sSurf, @[outBuf]); @try { id cr = ((id(*)(Class,SEL,id,id,id,id,id,id,id,id,id)) objc_msgSend)(gChain, @selector(chainingRequestWithInputs:outputSets: lbInputSymbolId:lbOutputSymbolId:procedureIndex: signalEvents:transactionHandle:fwEnqueueDelay: memoryPoolId:), @[inBuf], @[outSet], nil, nil, nil, signalEvents, @0, @0, @0); if (!cr) { printf(" ChainingRequest: nil\n"); CFRelease(sSurf); continue; } BOOL valid = ((BOOL(*)(id,SEL))objc_msgSend)( cr, @selector(validate)); printf(" validate: %s\n", valid ? "YES" : "NO"); NSError *prepErr = nil; BOOL prepOk = ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)(client, @selector(prepareChainingWithModel:options: chainingReq:qos:error:), chainModel, @{}, cr, (unsigned int)21, &prepErr); printf(" prepare: %s\n", prepOk ? "YES" : "NO"); if (prepErr) printf(" prepareError: %s\n", [[prepErr description] UTF8String]); if (prepOk) { printf(" *** PREPARE SUCCEEDED! ***\n"); chainingPrepSuccess = YES; chainExecuted = YES; printf("\n --- enqueueSetsWithModel ---\n"); @try { SEL eqSel = NSSelectorFromString( @"enqueueSetsWithModel:outputSet:" "options:qos:error:"); NSError *eqErr = nil; BOOL eqOk = ((BOOL(*)(id,SEL,id,id,id, unsigned int,NSError**))objc_msgSend)( client, eqSel, chainModel, outSet, @{}, (unsigned int)21, &eqErr); printf(" enqueueSets: %s\n", eqOk ? "YES" : "NO"); if (eqErr) printf(" Error: %s\n", [[eqErr description] UTF8String]); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- buffersReadyWithModel ---\n"); @try { SEL brSel = NSSelectorFromString( @"buffersReadyWithModel:inputBuffers:" "options:qos:error:"); NSError *brErr = nil; BOOL brOk = ((BOOL(*)(id,SEL,id,id,id, unsigned int,NSError**))objc_msgSend)( client, brSel, chainModel, @[inBuf], @{}, (unsigned int)21, &brErr); printf(" buffersReady: %s\n", brOk ? "YES" : "NO"); if (brErr) printf(" Error: %s\n", [[brErr description] UTF8String]); } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } printf("\n --- Benchmark ---\n"); uint64_t t0 = mach_absolute_time(); int niters = 50; for (int i = 0; i < niters; i++) { @try { SEL brSel = NSSelectorFromString( @"buffersReadyWithModel:inputBuffers:" "options:qos:error:"); ((BOOL(*)(id,SEL,id,id,id,unsigned int, NSError**))objc_msgSend)( client, brSel, chainModel, @[inBuf], @{}, (unsigned int)21, nil); } @catch (NSException *ex) { if (i == 0) printf(" Bench EXCEPTION: %s\n", [[ex reason] UTF8String]); break; } } double elapsed = tb_ms(mach_absolute_time() - t0); printf(" %d iters in %.3f ms (%.4f ms/iter)\n", niters, elapsed, elapsed / niters); } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } CFRelease(sSurf); } printf("\n --- P.extra: Try with _ANEInputBuffersReady ---\n"); @try { Class gIBR = NSClassFromString(@"_ANEInputBuffersReady"); if (gIBR) { dump_class("_ANEInputBuffersReady"); SEL ibrSel = NSSelectorFromString( @"inputBuffersWithProcedureIndex:inputBufferInfoIndex:" "inputFreeValue:executionDelay:"); if (class_getClassMethod(gIBR, ibrSel)) { Method m = class_getClassMethod(gIBR, ibrSel); const char *enc = method_getTypeEncoding(m); printf(" inputBuffersReady encoding: %s\n", enc ? enc : "?"); unsigned int na = method_getNumberOfArguments(m); printf(" args: %u\n", na); for (unsigned int i = 0; i < na; i++) { char at[64] = {0}; method_getArgumentType(m, i, at, sizeof(at)); printf(" [%u] = %s\n", i, at); } } } Class gOSE = NSClassFromString(@"_ANEOutputSetEnqueue"); if (gOSE) { dump_class("_ANEOutputSetEnqueue"); SEL oseSel = NSSelectorFromString( @"outputSetWithProcedureIndex:setIndex:signalValue:" "signalNotRequired:isOpenLoop:"); if (class_getClassMethod(gOSE, oseSel)) { Method m = class_getClassMethod(gOSE, oseSel); const char *enc = method_getTypeEncoding(m); printf(" outputSetEnqueue encoding: %s\n", enc ? enc : "?"); unsigned int na = method_getNumberOfArguments(m); printf(" args: %u\n", na); for (unsigned int i = 0; i < na; i++) { char at[64] = {0}; method_getArgumentType(m, i, at, sizeof(at)); printf(" [%u] = %s\n", i, at); } } } } @catch (NSException *ex) { printf(" EXCEPTION: %s\n", [[ex reason] UTF8String]); } if (k3.model) free_kernel(&k3); } // ================================================================= // Summary // ================================================================= printf("\n============================================================\n"); printf(" RESULTS SUMMARY\n"); printf("============================================================\n"); printf(" Exp E: _ANEModel loaded: %s\n", diskModel1 ? "YES" : "NO"); printf(" Exp E2: ANECompiler found: (see above)\n"); printf(" Exp F: Chaining pipeline: %s\n", diskModel1 ? "ATTEMPTED" : "SKIPPED"); printf(" Exp G: SharedEvents: (see above)\n"); printf(" Exp H: Alt preparation: (see above)\n"); printf(" Exp K: Type encodings: DONE\n"); printf(" Exp L: Array params: %s\n", chainingPrepSuccess ? "PREPARE SUCCEEDED" : "see above"); printf(" Exp M: Client load model: %s\n", fullyLoadedModel ? "LOADED" : "see above"); printf(" Exp N: IOSurface mapping: DONE\n"); printf(" Exp O: Procedure info: DONE\n"); printf(" Exp P: Full chaining retry: %s\n", chainExecuted ? "EXECUTED" : "see above"); printf("============================================================\n"); free_kernel(&k1); free_kernel(&k2); printf("\nDone.\n"); } return 0; }