// test_ane_advanced.m — Probe advanced ANE interfaces // SharedEvents, weightsBuffer, procedureIndex, VirtualClient, ChainingRequest #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; } 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:\n"); 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:\n"); 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:\n"); 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 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}); } static int g_fp16_io = 0; // M1/M2: cast op unsupported, use fp16 I/O directly int main() { @autoreleasepool { setbuf(stdout, NULL); mach_timebase_info(&g_tb); dlopen("/System/Library/PrivateFrameworks/AppleNeuralEngine.framework/AppleNeuralEngine", RTLD_NOW); printf("=== ANE Advanced Interface Probe ===\n"); // === Part 1: Event/Sync classes === printf("\n--- Part 1: Event/Sync Classes ---\n"); dump_class("_ANESharedEvents"); dump_class("_ANESharedSignalEvent"); dump_class("_ANESharedWaitEvent"); dump_class("_ANEEvent"); dump_class("_ANEFenceEvent"); const char *event_classes[] = { "_ANESharedEvents", "_ANESharedSignalEvent", "_ANESharedWaitEvent", "_ANEEvent", "_ANEFenceEvent", NULL }; for (int i = 0; event_classes[i]; i++) { Class cls = NSClassFromString([NSString stringWithUTF8String:event_classes[i]]); if (!cls) continue; @try { id obj = [[cls alloc] init]; printf(" %s alloc/init: %s\n", event_classes[i], obj ? [[obj description] UTF8String] : "nil"); } @catch (NSException *ex) { printf(" %s alloc/init: EXCEPTION: %s\n", event_classes[i], [[ex reason] UTF8String]); } } // === Part 2: VirtualClient and ChainingRequest === printf("\n--- Part 2: VirtualClient / ChainingRequest ---\n"); dump_class("_ANEVirtualClient"); dump_class("_ANEChainingRequest"); dump_class("_ANEMultiRequest"); dump_class("_ANEBatchRequest"); // === Part 3: Compile working kernel for weightsBuffer + procedureIndex tests === printf("\n--- Part 3: weightsBuffer IOSurface test ---\n"); Class g_D = NSClassFromString(@"_ANEInMemoryModelDescriptor"); Class g_I = NSClassFromString(@"_ANEInMemoryModel"); Class g_AR = NSClassFromString(@"_ANERequest"); Class g_AIO= NSClassFromString(@"_ANEIOSurfaceObject"); int CH = 64, SP = 32; _Float16 *w = (_Float16*)calloc(CH*CH, sizeof(_Float16)); for (int i = 0; i < CH; i++) w[i*CH+i] = (_Float16)1.0f; int ws = CH*CH*2, tot = 128+ws; uint8_t *blob = (uint8_t*)calloc(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)=ws; *(uint32_t*)(blob+80)=128; memcpy(blob+128, w, ws); NSData *wdata = [NSData dataWithBytesNoCopy:blob length:tot freeWhenDone:YES]; NSFileManager *fm = [NSFileManager defaultManager]; retry_compile:; NSString *mil; if (g_fp16_io) { mil = [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]; } else { mil = [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 to16 = const()[name=tensor(\"to16\"), val=tensor(\"fp16\")];\n" " tensor x16 = cast(dtype=to16,x=x)[name=tensor(\"cin\")];\n" " tensor W = const()[name=tensor(\"W\"), " "val=tensor(BLOBFILE(path=tensor(\"@model_path/weights/weight.bin\"), offset=tensor(64)))];\n" " tensor y16 = conv(dilations=dl,groups=gr,pad=pd,pad_type=pt,strides=st,weight=W,x=x16)" "[name=tensor(\"conv\")];\n" " tensor to32 = const()[name=tensor(\"to32\"), val=tensor(\"fp32\")];\n" " tensor y = cast(dtype=to32,x=y16)[name=tensor(\"cout\")];\n" " } -> (y);\n}\n", CH, SP, CH, SP, CH, CH, CH, CH, CH, SP, CH, SP]; } NSData *md = [mil dataUsingEncoding:NSUTF8StringEncoding]; id desc = ((id(*)(Class,SEL,id,id,id))objc_msgSend)(g_D, @selector(modelWithMILText:weights:optionsPlist:), md, @{@"@model_path/weights/weight.bin": @{@"offset":@0, @"data":wdata}}, nil); id mdl = ((id(*)(Class,SEL,id))objc_msgSend)(g_I, @selector(inMemoryModelWithDescriptor:), desc); id hx = ((id(*)(id,SEL))objc_msgSend)(mdl, @selector(hexStringIdentifier)); NSString *td = [NSTemporaryDirectory() stringByAppendingPathComponent:hx]; [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 compiled = ((BOOL(*)(id,SEL,unsigned int,id,NSError**))objc_msgSend)(mdl, @selector(compileWithQoS:options:error:), 21, @{}, &e); if (!compiled && !g_fp16_io) { printf("[ANE] fp32 compile failed, retrying with fp16 I/O (M1/M2 fallback)\n"); g_fp16_io = 1; [fm removeItemAtPath:td error:nil]; goto retry_compile; } ((BOOL(*)(id,SEL,unsigned int,id,NSError**))objc_msgSend)(mdl, @selector(loadWithQoS:options:error:), 21, @{}, &e); int ioBytes = CH * SP * (g_fp16_io ? 2 : 4); IOSurfaceRef ioIn = make_surface(ioBytes); IOSurfaceRef ioOut = make_surface(ioBytes); IOSurfaceLock(ioIn, 0, NULL); if (g_fp16_io) { _Float16 *inp = (_Float16*)IOSurfaceGetBaseAddress(ioIn); for (int c = 0; c < CH; c++) for (int s = 0; s < SP; s++) inp[c*SP+s] = (_Float16)((float)(s+1) * 0.1f); } else { float *inp = (float*)IOSurfaceGetBaseAddress(ioIn); for (int c = 0; c < CH; c++) for (int s = 0; s < SP; s++) inp[c*SP+s] = (float)(s+1) * 0.1f; } IOSurfaceUnlock(ioIn, 0, NULL); // Baseline eval id wI = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioIn); id wO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioOut); id req0 = ((id(*)(Class,SEL,id,id,id,id,id,id,id))objc_msgSend)(g_AR, @selector(requestWithInputs:inputIndices:outputs:outputIndices:weightsBuffer:perfStats:procedureIndex:), @[wI], @[@0], @[wO], @[@0], nil, nil, @0); BOOL ok = ((BOOL(*)(id,SEL,unsigned int,id,id,NSError**))objc_msgSend)( mdl, @selector(evaluateWithQoS:options:request:error:), 21, @{}, req0, &e); printf(" Baseline eval (weightsBuffer=nil, procIdx=0): %s\n", ok ? "OK" : "FAIL"); IOSurfaceLock(ioOut, kIOSurfaceLockReadOnly, NULL); float baseline_0, baseline_1; if (g_fp16_io) { _Float16 *out0 = (_Float16*)IOSurfaceGetBaseAddress(ioOut); baseline_0 = (float)out0[0]; baseline_1 = (float)out0[1]; printf(" Output[0..3]: [%.4f, %.4f, %.4f, %.4f]\n", (float)out0[0], (float)out0[1], (float)out0[2], (float)out0[3]); } else { float *out0 = (float*)IOSurfaceGetBaseAddress(ioOut); baseline_0 = out0[0]; baseline_1 = out0[1]; printf(" Output[0..3]: [%.4f, %.4f, %.4f, %.4f]\n", out0[0], out0[1], out0[2], out0[3]); } IOSurfaceUnlock(ioOut, kIOSurfaceLockReadOnly, NULL); // Test weightsBuffer: IOSurface with 3x identity weights printf("\n Testing weightsBuffer IOSurface...\n"); _Float16 *w3 = (_Float16*)calloc(CH*CH, sizeof(_Float16)); for (int i = 0; i < CH; i++) w3[i*CH+i] = (_Float16)3.0f; IOSurfaceRef ioW = make_surface(ws); IOSurfaceLock(ioW, 0, NULL); memcpy(IOSurfaceGetBaseAddress(ioW), w3, ws); IOSurfaceUnlock(ioW, 0, NULL); free(w3); id wW = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioW); wI = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioIn); wO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioOut); id req_wb = ((id(*)(Class,SEL,id,id,id,id,id,id,id))objc_msgSend)(g_AR, @selector(requestWithInputs:inputIndices:outputs:outputIndices:weightsBuffer:perfStats:procedureIndex:), @[wI], @[@0], @[wO], @[@0], wW, nil, @0); printf(" Request with weightsBuffer: %s\n", req_wb ? "created" : "nil"); if (req_wb) { ok = ((BOOL(*)(id,SEL,unsigned int,id,id,NSError**))objc_msgSend)( mdl, @selector(evaluateWithQoS:options:request:error:), 21, @{}, req_wb, &e); printf(" Eval with weightsBuffer: %s\n", ok ? "OK" : e ? [[e description] UTF8String] : "FAIL"); if (ok) { IOSurfaceLock(ioOut, kIOSurfaceLockReadOnly, NULL); float outW_0; if (g_fp16_io) { _Float16 *outW = (_Float16*)IOSurfaceGetBaseAddress(ioOut); outW_0 = (float)outW[0]; printf(" Output[0..3]: [%.4f, %.4f, %.4f, %.4f]\n", (float)outW[0], (float)outW[1], (float)outW[2], (float)outW[3]); } else { float *outW = (float*)IOSurfaceGetBaseAddress(ioOut); outW_0 = outW[0]; printf(" Output[0..3]: [%.4f, %.4f, %.4f, %.4f]\n", outW[0], outW[1], outW[2], outW[3]); } bool changed = fabsf(outW_0 - baseline_0) > 0.001f; bool is_3x = fabsf(outW_0 - baseline_0 * 3.0f) < 0.1f; printf(" weightsBuffer: output %s", changed ? "CHANGED" : "unchanged"); if (changed) printf(" (%s)", is_3x ? "matches 3x — WORKS!" : "but not 3x as expected"); printf("\n"); IOSurfaceUnlock(ioOut, kIOSurfaceLockReadOnly, NULL); } } CFRelease(ioW); // === Part 4: procedureIndex sweep === printf("\n--- Part 4: procedureIndex sweep (0-15) ---\n"); for (int pi = 0; pi < 16; pi++) { wI = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioIn); wO = ((id(*)(Class,SEL,IOSurfaceRef))objc_msgSend)(g_AIO, @selector(objectWithIOSurface:), ioOut); id req_p = ((id(*)(Class,SEL,id,id,id,id,id,id,id))objc_msgSend)(g_AR, @selector(requestWithInputs:inputIndices:outputs:outputIndices:weightsBuffer:perfStats:procedureIndex:), @[wI], @[@0], @[wO], @[@0], nil, nil, @(pi)); if (!req_p) { printf(" procIdx %2d: request=nil\n", pi); continue; } ok = ((BOOL(*)(id,SEL,unsigned int,id,id,NSError**))objc_msgSend)( mdl, @selector(evaluateWithQoS:options:request:error:), 21, @{}, req_p, &e); printf(" procIdx %2d: %s%s\n", pi, ok ? "OK" : "FAIL", !ok && e ? [NSString stringWithFormat:@" (%@)", [e localizedDescription]].UTF8String : ""); } // === Part 5: Scan all ANE classes === printf("\n--- Part 5: All ANE-prefixed classes ---\n"); unsigned int classCount; Class *allClasses = objc_copyClassList(&classCount); for (unsigned int i = 0; i < classCount; i++) { const char *name = class_getName(allClasses[i]); if (strstr(name, "ANE") || strstr(name, "ane")) { printf(" %s\n", name); } } free(allClasses); free(w); // Cleanup ((BOOL(*)(id,SEL,unsigned int,NSError**))objc_msgSend)(mdl, @selector(unloadWithQoS:error:), 21, &e); [fm removeItemAtPath:td error:nil]; CFRelease(ioIn); CFRelease(ioOut); printf("\nDone.\n"); } return 0; }