Codegen to compute hash for a particular byte size. Loops are unrolled in this process. For the case where num_bytes == 11, we'd do this by calling 1. crc64 (for first 8 bytes) 2. crc16 (for bytes 9, 10) 3. crc8 (for byte 11) The resulting IR looks like: define i32 @CrcHash11(i8* %data, i32 %len, i32 %seed) { entry: %0 = zext i32 %seed to i64 %1 = bitcast i8* %data to i64 %2 = getelementptr i64*
| 1848 | // ret i32 %12 |
| 1849 | // } |
| 1850 | llvm::Function* LlvmCodeGen::GetHashFunction(int num_bytes) { |
| 1851 | if (IS_AARCH64 || IsCPUFeatureEnabled(CpuInfo::SSE4_2)) { |
| 1852 | if (num_bytes == -1) { |
| 1853 | // -1 indicates variable length, just return the generic loop based |
| 1854 | // hash fn. |
| 1855 | return GetFunction(IRFunction::HASH_CRC, false); |
| 1856 | } |
| 1857 | |
| 1858 | map<int, llvm::Function*>::iterator cached_fn = hash_fns_.find(num_bytes); |
| 1859 | if (cached_fn != hash_fns_.end()) { |
| 1860 | return cached_fn->second; |
| 1861 | } |
| 1862 | |
| 1863 | // Generate a function to hash these bytes |
| 1864 | stringstream ss; |
| 1865 | ss << "CrcHash" << num_bytes; |
| 1866 | FnPrototype prototype(this, ss.str(), i32_type()); |
| 1867 | prototype.AddArgument(LlvmCodeGen::NamedVariable("data", ptr_type())); |
| 1868 | prototype.AddArgument(LlvmCodeGen::NamedVariable("len", i32_type())); |
| 1869 | prototype.AddArgument(LlvmCodeGen::NamedVariable("seed", i32_type())); |
| 1870 | |
| 1871 | llvm::Value* args[3]; |
| 1872 | LlvmBuilder builder(context()); |
| 1873 | llvm::Function* fn = prototype.GeneratePrototype(&builder, &args[0]); |
| 1874 | llvm::Value* data = args[0]; |
| 1875 | llvm::Value* result = args[2]; |
| 1876 | #ifdef __aarch64__ |
| 1877 | llvm::Function* crc8_fn = llvm_intrinsics_[llvm::Intrinsic::aarch64_crc32cb]; |
| 1878 | llvm::Function* crc16_fn = llvm_intrinsics_[llvm::Intrinsic::aarch64_crc32ch]; |
| 1879 | llvm::Function* crc32_fn = llvm_intrinsics_[llvm::Intrinsic::aarch64_crc32cw]; |
| 1880 | llvm::Function* crc64_fn = llvm_intrinsics_[llvm::Intrinsic::aarch64_crc32cx]; |
| 1881 | #else |
| 1882 | llvm::Function* crc8_fn = llvm_intrinsics_[llvm::Intrinsic::x86_sse42_crc32_32_8]; |
| 1883 | llvm::Function* crc16_fn = llvm_intrinsics_[llvm::Intrinsic::x86_sse42_crc32_32_16]; |
| 1884 | llvm::Function* crc32_fn = llvm_intrinsics_[llvm::Intrinsic::x86_sse42_crc32_32_32]; |
| 1885 | llvm::Function* crc64_fn = llvm_intrinsics_[llvm::Intrinsic::x86_sse42_crc32_64_64]; |
| 1886 | #endif |
| 1887 | |
| 1888 | // Generate the crc instructions starting with the highest number of bytes |
| 1889 | if (num_bytes >= 8) { |
| 1890 | #ifndef __aarch64__ |
| 1891 | llvm::Value* result_64 = builder.CreateZExt(result, i64_type()); |
| 1892 | #endif |
| 1893 | llvm::Value* ptr = builder.CreateBitCast(data, i64_ptr_type()); |
| 1894 | int i = 0; |
| 1895 | while (num_bytes >= 8) { |
| 1896 | llvm::Value* index[] = {GetI32Constant(i++)}; |
| 1897 | llvm::Value* d = builder.CreateLoad(builder.CreateInBoundsGEP(ptr, index)); |
| 1898 | #ifdef __aarch64__ |
| 1899 | result = builder.CreateCall(crc64_fn, llvm::ArrayRef<llvm::Value*>({result, d})); |
| 1900 | #else |
| 1901 | result_64 = |
| 1902 | builder.CreateCall(crc64_fn, llvm::ArrayRef<llvm::Value*>({result_64, d})); |
| 1903 | #endif |
| 1904 | num_bytes -= 8; |
| 1905 | } |
| 1906 | #ifndef __aarch64__ |
| 1907 | result = builder.CreateTrunc(result_64, i32_type()); |