From bf90e2b7968179440b55e72e170c6d618d09ceea Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 9 Jun 2024 11:20:33 +0100 Subject: [PATCH 001/213] attempt to modify quantizePredict * dev2. --- .../newton-irPass-LLVMIR-quantization.cpp | 1283 +++++++++-------- 1 file changed, 709 insertions(+), 574 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 687ba8c1d..4ef3b79b5 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -33,407 +33,529 @@ using namespace llvm; -#define FRAC_Q 16 -#define FRAC_BASE (1<getType(); - unsigned pointerAddr; - bool isPointer = false; - if (valueType != nullptr) - { - if (valueType->isPointerTy()) { - isPointer = true; - pointerAddr = valueType->getPointerAddressSpace(); - valueType = valueType->getPointerElementType(); - } - if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) - { - if (isPointer) { - inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - } else { - inValue->mutateType(quantizedType); - } - } - } + auto valueType = inValue->getType(); + unsigned pointerAddr; + bool isPointer = false; + if (valueType != nullptr) + { + if (valueType->isPointerTy()) + { + isPointer = true; + // If the value is a pointer, get the address space and element type + pointerAddr = valueType->getPointerAddressSpace(); + valueType = valueType->getPointerElementType(); + } + if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) + { + // If the value is a pointer, get the address space and element type + if (isPointer) + { + inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + } + else + { + // Otherwise, directly set the type to the quantized type + inValue->mutateType(quantizedType); + } + } + } } -void quantizeConstant(Instruction * inInstruction, Type * quantizedType) { - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { - Value * inValue = inInstruction->getOperand(idx); - - if (!isa(inValue)) { - continue; - } - - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); - } - else - { - assert(false && "unknown floating type"); - } - - inInstruction->replaceUsesOfWith(inValue, newValue); - } -} +// Quantize constants within an instruction +void +quantizeConstant(Instruction * inInstruction, Type * quantizedType) +{ + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { + Value * inValue = inInstruction->getOperand(idx); -void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { - auto checkDecimal = [](float decimalNum) { - int digits = 0; - /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { - decimalNum *= 10; - digits++; - } - return decimalNum; - }; - - auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { - /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ - float compensateNum = quantizedNum / decimalNum; - - Value *constOperand, *nonConstOperand; - unsigned constIdx, nonConstIdx; - if (isa(inInstruction->getOperand(0))) { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); - nonConstOperand = inInstruction->getOperand(1); - } else { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); - nonConstOperand = inInstruction->getOperand(0); - } - - auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - - if (compensateNum == 1) { - inInstruction->setOperand(constIdx, quantizeNumValue); - } else { - auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; - Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) { - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } - - inInstruction->replaceAllUsesWith(newSecondInst); - inInstruction->removeFromParent(); - } - }; - - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { - Value * inValue = inInstruction->getOperand(idx); - - if (!isa(inValue)) { - continue; - } - - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - compensateFP(checkDecimal(constValue), constValue); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - compensateFP(checkDecimal(constValue), constValue); - } - else - { - assert(false && "unknown floating type"); - } - } + // Skip if the operand is not a floating-point constant + if (!isa(inValue)) + { + continue; + } + + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; + if (inValue->getType()->isFloatTy()) + { + // Convert float constant to fixed-point + float constValue = constFp->getValueAPF().convertToFloat(); + constValue *= FRAC_BASE; + newValue = ConstantInt::get(quantizedType, round(constValue), true); + } + else if (inValue->getType()->isDoubleTy()) + { + // Convert double constant to fixed-point + double constValue = constFp->getValueAPF().convertToDouble(); + constValue *= FRAC_BASE; + newValue = ConstantInt::get(quantizedType, round(constValue), true); + } + else + { + // assert(false && "unknown floating type"); + llvm::errs() << "Unknown floating type\n"; + return; + } + + inInstruction->replaceUsesOfWith(inValue, newValue); + } } -llvm::Function * createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector& functionsToInsert) { - /* - * check if this function is exist - * */ - std::string fixmulFuncName = "fixmul"; - auto irModule = inFunction->getParent(); - for (auto & function : *irModule) { - if (function.getName() == fixmulFuncName) { - return &function; - } - } - - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - - llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(inFunction->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); - - /* - * ((int64_t)x*y)>>FRAC_Q - * - * ===========> - * - * define private i32 @mulfix(i32 %0, i32 %1) { - * %3 = sext i32 %0 to i64 - * %4 = sext i32 %1 to i64 - * %5 = mul nsw i64 %3, %4 - * %6 = ashr i64 %5, 8 - * %7 = trunc i64 %6 to i32 - * ret i32 %7 - * } - * */ - Type* higherQuantizedType; - switch (BIT_WIDTH) { - case 8: - higherQuantizedType = Type::getInt16Ty(inFunction->getContext()); - break; - case 16: - higherQuantizedType = Type::getInt32Ty(inFunction->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(inFunction->getContext()); - break; - } - - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value* sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value* sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value* mulInst = builder.CreateMul(sext1, sext2); - llvm::Value* ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value* truncInst = builder.CreateTrunc(ashrInst, quantizedType); - builder.CreateRet(truncInst); - - functionsToInsert.emplace_back(func); - - return func; +// Simplify constants in the instruction +void +simplifyConstant(Instruction * inInstruction, Type * quantizedType) +{ + auto checkDecimal = [](float decimalNum) { + int digits = 0; + /* + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + + // Ensure the decimal number can be represented within the fixed-point range + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) + { + decimalNum *= 10; + digits++; + } + return decimalNum; + }; + + auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { + /* + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ + float compensateNum = quantizedNum / decimalNum; + + Value * constOperand, *nonConstOperand; + unsigned constIdx, nonConstIdx; + if (isa(inInstruction->getOperand(0))) + { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); + nonConstOperand = inInstruction->getOperand(1); + } + else + { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); + nonConstOperand = inInstruction->getOperand(0); + } + + auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); + + if (compensateNum == 1) + { + // If the compensation factor is 1, directly set the quantized value + inInstruction->setOperand(constIdx, quantizeNumValue); + } + else + { + // Otherwise, apply compensation to the fixed-point arithmetic + auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newFisrtInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) + { + // Replace floating-point multiplication with fixed-point equivalent + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) + { + // Replace floating-point division with fixed-point equivalent (divisor is constant) + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) + { + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + } + + inInstruction->replaceAllUsesWith(newSecondInst); + inInstruction->removeFromParent(); + } + }; + + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { + Value * inValue = inInstruction->getOperand(idx); + + if (!isa(inValue)) + { + continue; + } + + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; + if (inValue->getType()->isFloatTy()) + { + float constValue = constFp->getValueAPF().convertToFloat(); + compensateFP(checkDecimal(constValue), constValue); + } + else if (inValue->getType()->isDoubleTy()) + { + double constValue = constFp->getValueAPF().convertToDouble(); + compensateFP(checkDecimal(constValue), constValue); + } + else + { + assert(false && "unknown floating type"); + } + } } -void substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) { - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); -// Value * newInst = nullptr; +// Create a fixed-point multiplication function +llvm::Function * +createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector & functionsToInsert) +{ + /* + * check if this function is exist + * */ + std::string fixmulFuncName = "fixmul"; + auto irModule = inFunction->getParent(); + for (auto & function : *irModule) + { + if (function.getName() == fixmulFuncName) + { + return &function; + } + } + + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(inFunction->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + builder.SetInsertPoint(entryBB); + + /* + * ((int64_t)x*y)>>FRAC_Q + * + * ===========> + * + * define private i32 @mulfix(i32 %0, i32 %1) { + * %3 = sext i32 %0 to i64 + * %4 = sext i32 %1 to i64 + * %5 = mul nsw i64 %3, %4 + * %6 = ashr i64 %5, 8 + * %7 = trunc i64 %6 to i32 + * ret i32 %7 + * } + * */ + Type * higherQuantizedType; + switch (BIT_WIDTH) + { + case 8: + higherQuantizedType = Type::getInt16Ty(inFunction->getContext()); + break; + case 16: + higherQuantizedType = Type::getInt32Ty(inFunction->getContext()); + break; + default: + higherQuantizedType = Type::getInt64Ty(inFunction->getContext()); + break; + } - llvm::CallInst* callInst = Builder.CreateCall(func, {inInstruction->getOperand(0), inInstruction->getOperand(1)}); -// InlineFunctionInfo inlineFuncInfo; -// llvm::InlineFunction(*callInst, inlineFuncInfo); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); + builder.CreateRet(truncInst); - inInstruction->replaceAllUsesWith(callInst); - inInstruction->removeFromParent(); + functionsToInsert.emplace_back(func); + + return func; } -CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) { - switch (predict) { - case FCmpInst::FCMP_OEQ: - case FCmpInst::FCMP_UEQ: - return ICmpInst::ICMP_EQ; - case FCmpInst::FCMP_OGT: - case FCmpInst::FCMP_UGT: - return ICmpInst::ICMP_SGT; - case FCmpInst::FCMP_OGE: - case FCmpInst::FCMP_UGE: - return ICmpInst::ICMP_SGE; - case FCmpInst::FCMP_OLT: - case FCmpInst::FCMP_ULT: - return ICmpInst::ICMP_SLT; - case FCmpInst::FCMP_OLE: - case FCmpInst::FCMP_ULE: - return ICmpInst::ICMP_SLE; - case FCmpInst::FCMP_ONE: - case FCmpInst::FCMP_UNE: - return ICmpInst::ICMP_NE; - } +// Substitute hardcoded functions like multiplication with fixed-point versions +void +substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) +{ + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + // Value * newInst = nullptr; + + llvm::CallInst * callInst = Builder.CreateCall(func, {inInstruction->getOperand(0), inInstruction->getOperand(1)}); + // InlineFunctionInfo inlineFuncInfo; + // llvm::InlineFunction(*callInst, inlineFuncInfo); + + inInstruction->replaceAllUsesWith(callInst); + inInstruction->removeFromParent(); } -void quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) { - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - switch (inInstruction->getOpcode()) - { - case Instruction::FAdd: - { - newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FSub: - { - newInst = Builder.CreateSub(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FRem: - { - newInst = Builder.CreateSRem(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FCmp: - { - FCmpInst *fcmp_inst = dyn_cast(inInstruction); - newInst = Builder.CreateICmp(quantizePredict(fcmp_inst->getPredicate()), - fcmp_inst->getOperand(0), fcmp_inst->getOperand(1)); - break; - } - /* - * Change fneg(a) to `0-a`. - * */ - case Instruction::FNeg: - { - auto constZero = ConstantInt::get(quantizedType, 0, true); - newInst = Builder.CreateSub(constZero, inInstruction->getOperand(0)); - break; - } - default: - break; - } - inInstruction->replaceAllUsesWith(newInst); - inInstruction->removeFromParent(); +// Quantize simple floating-point instructions +CmpInst::Predicate +quantizePredict(CmpInst::Predicate predict) +{ + switch (predict) + { + case FCmpInst::FCMP_OEQ: // equal + case FCmpInst::FCMP_UEQ: + return ICmpInst::ICMP_EQ; // greater than + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_UGT: + return ICmpInst::ICMP_SGT; + case FCmpInst::FCMP_OGE: // greater than or equal + case FCmpInst::FCMP_UGE: + return ICmpInst::ICMP_SGE; + case FCmpInst::FCMP_OLT: // less than + case FCmpInst::FCMP_ULT: + return ICmpInst::ICMP_SLT; + case FCmpInst::FCMP_OLE: // less than or equal + case FCmpInst::FCMP_ULE: + return ICmpInst::ICMP_SLE; + case FCmpInst::FCMP_ONE: // not equal + case FCmpInst::FCMP_UNE: + return ICmpInst::ICMP_NE; + + // TODO + case FCmpInst::FCMP_ORD: // ordered (no NaNs) + // For ordered, we map it to ICMP_NE, assuming integers cannot be NaN + return ICmpInst::ICMP_NE; + case FCmpInst::FCMP_UNO: // unordered (at least one NaN) + // For unordered, there is no direct integer equivalent, map to ICMP_EQ for safety + return ICmpInst::ICMP_EQ; + case FCmpInst::FCMP_TRUE: // always true + // There is no direct integer equivalent, but we can use a workaround + return ICmpInst::ICMP_TRUE; // custom handling needed + case FCmpInst::FCMP_FALSE: // always false + return ICmpInst::ICMP_FALSE; // custom handling needed + + default: + llvm::errs() << "Unhandled floating point predicate\n"; + return ICmpInst::ICMP_EQ; // Default to equal for safety + } +} } -void adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - Instruction *llvmIrInstruction = &*itBB++; - switch (llvmIrInstruction->getOpcode()) { - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::SIToFP: - case Instruction::UIToFP: - { - auto sourceOp = llvmIrInstruction->getOperand(0); - if (sourceOp->getType() == llvmIrInstruction->getType()) - { - llvmIrInstruction->replaceAllUsesWith(sourceOp); - llvmIrInstruction->removeFromParent(); - } - } - break; -// case Instruction::ZExt: -// case Instruction::SExt: -// case Instruction::Trunc: - /* - * since the src type changed, adapt the new instruction - * */ - case Instruction::FPExt: - case Instruction::FPTrunc: - { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) { - newInst = Builder.CreateSIToFP( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } else { - newInst = Builder.CreateFPCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); - break; - } - case Instruction::BitCast: - { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = Builder.CreateBitCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); - break; - } - } - } - } +void +quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) +{ + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + switch (inInstruction->getOpcode()) + { + case Instruction::FAdd: + { + // Replace floating-point addition with integer addition + newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); + break; + } + case Instruction::FSub: + { + // Replace floating-point subtraction with integer subtraction + newInst = Builder.CreateSub(inInstruction->getOperand(0), inInstruction->getOperand(1)); + break; + } + + case Instruction::FMul: + { + // Replace floating-point multiplication with integer multiplication + newInst = Builder.CreateMul(inInstruction->getOperand(0), inInstruction->getOperand(1)); + break; + } + case Instruction::FDiv: + { + // Replace floating-point division with integer division + newInst = Builder.CreateSDiv(inInstruction->getOperand(0), inInstruction->getOperand(1)); + break; + } + + case Instruction::FRem: + { + // Replace floating-point remainder with integer remainder + newInst = Builder.CreateSRem(inInstruction->getOperand(0), inInstruction->getOperand(1)); + break; + } + case Instruction::FCmp: + // Replace floating-point comparison with integer comparison + if (auto fcmp_inst = dyn_cast(inInstruction)) + { + CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); + if (pred == ICmpInst::ICMP_TRUE) + { + newInst = ConstantInt::getTrue(quantizedType); + } + else if (pred == ICmpInst::ICMP_FALSE) + { + newInst = ConstantInt::getFalse(quantizedType); + } + else + { + newInst = Builder.CreateICmp(pred, fcmp_inst->getOperand(0), fcmp_inst->getOperand(1)); + } + } + break; + /* + * Change fneg(a) to `0-a`. + * */ + case Instruction::FNeg: + { + // Replace floating-point negation with integer negation + auto constZero = ConstantInt::get(quantizedType, 0, true); + newInst = Builder.CreateSub(constZero, inInstruction->getOperand(0)); + break; + } + default: + llvm::errs() << "Unhandled floating point instruction\n"; + break; + } + + if (newInst) + { + inInstruction->replaceAllUsesWith(newInst); + inInstruction->removeFromParent(); + } +} +// Adapt type casts to the quantized type +void +adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) +{ + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + switch (llvmIrInstruction->getOpcode()) + { + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::SIToFP: + case Instruction::UIToFP: + { + auto sourceOp = llvmIrInstruction->getOperand(0); + if (sourceOp->getType() == llvmIrInstruction->getType()) + { + llvmIrInstruction->replaceAllUsesWith(sourceOp); + llvmIrInstruction->removeFromParent(); + } + } + break; + // case Instruction::ZExt: + // case Instruction::SExt: + // case Instruction::Trunc: + /* + * since the src type changed, adapt the new instruction + * */ + case Instruction::FPExt: + case Instruction::FPTrunc: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + { + newInst = Builder.CreateSIToFP( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + else + { + newInst = Builder.CreateFPCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); + break; + } + case Instruction::BitCast: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = Builder.CreateBitCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); + break; + } + } + } + } } +// Main function to perform LLVM IR auto quantization void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert) +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) { - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); - - Type* quantizedType; - switch (BIT_WIDTH) { - case 8: - quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); - break; - case 16: - quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); - break; - case 32: - quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); - break; - case 64: - quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); - break; - default: - flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); - return; - } - - /* - * change the type of this function if it's fp - * */ - if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) { - llvmIrFunction.mutateType(quantizedType); - } - - /* - * generate hardcode function - fixmul and fixdiv - * */ - llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); - - /* - * quantize the arguments type - * */ - for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) - { - auto paramOp = llvmIrFunction.getArg(idx); - setQuantizedType(paramOp, quantizedType); - } + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); + + Type * quantizedType; + switch (BIT_WIDTH) + { + case 8: + quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); + break; + case 16: + quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); + break; + case 32: + quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); + break; + case 64: + quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); + break; + default: + flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); + return; + } + + /* + * change the type of this function if it's fp + * */ + if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) + { + llvmIrFunction.mutateType(quantizedType); + } + + /* + * generate hardcode function - fixmul and fixdiv + * */ + llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); + + /* + * quantize the arguments type + * */ + for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) + { + auto paramOp = llvmIrFunction.getArg(idx); + setQuantizedType(paramOp, quantizedType); + } for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { @@ -442,198 +564,211 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve Instruction * llvmIrInstruction = &*itBB++; switch (llvmIrInstruction->getOpcode()) { - case Instruction::Alloca: - if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) - { - auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - auto newType = quantizedType; - if (allocaType->getTypeID() == Type::ArrayTyID) { - newType = ArrayType::get(quantizedType, - allocaType->getArrayNumElements()); - allocaType = allocaType->getArrayElementType(); - } - if (allocaType->isDoubleTy() || allocaType->isFloatTy()) { - llvmIrAllocaInstruction->setAllocatedType(newType); - } - setQuantizedType(llvmIrAllocaInstruction, newType); - } - break; + case Instruction::Alloca: + if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) + { + auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); + auto newType = quantizedType; + if (allocaType->getTypeID() == Type::ArrayTyID) + { + newType = ArrayType::get(quantizedType, + allocaType->getArrayNumElements()); + allocaType = allocaType->getArrayElementType(); + } + if (allocaType->isDoubleTy() || allocaType->isFloatTy()) + { + llvmIrAllocaInstruction->setAllocatedType(newType); + } + setQuantizedType(llvmIrAllocaInstruction, newType); + } + break; case Instruction::Call: - if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) - { - Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) - break; - if (!calledFunction->getName().startswith("llvm.dbg.value") && - !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) - { - if (calledFunction->isDeclaration()) - { - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (calledFunction->getName().str() == "sqrt") { - /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sqrt(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ - auto operand = llvmIrCallInstruction->getOperand(0); - if (operand->getType()->isIntegerTy()) { - Value * newOperand = Builder.CreateSIToFP( - operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); - } - auto cloneInst = llvmIrCallInstruction->clone(); - Value * fptosiInst = Builder.CreateFPToSI( - cloneInst, quantizedType); - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q/2); - Value * resInst = nullptr; - /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ - if (FRAC_Q%2) { - Value * lhsCompensateInst = Builder.CreateSIToFP( - shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), - 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); - } else { - resInst = shlInst; - } - llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); - } - else { - /* - * for other lib functions, de-quantize the arguments and quantize the return value - * */ - } - } else { - /* - * for user-defined function, quantize the arguments - * */ - for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); - /* - * then quantize the return type - * */ - setQuantizedType(llvmIrCallInstruction, quantizedType); - } - } - } - break; - case Instruction::GetElementPtr: - if (auto gepInst = dyn_cast(llvmIrInstruction)) - { - auto gepType = gepInst->getType(); - auto sourceType = quantizedType; -// bool isPointer = false; -// unsigned pointerAddr = 0; -// if (gepType->isPointerTy()) { -// isPointer = true; -// pointerAddr = gepType->getPointerAddressSpace(); -// valueType = gepType->getPointerElementType(); -// } - if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) { - sourceType = ArrayType::get(quantizedType, - gepInst->getSourceElementType()->getArrayNumElements()); - } -// if (isPointer) { -// inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); -// } -// if (gepType->isDoubleTy() || gepType->isFloatTy()) { - gepInst->setSourceElementType(sourceType); - gepInst->setResultElementType(quantizedType); -// } - } - case Instruction::Load: - case Instruction::PHI: - { - setQuantizedType(llvmIrInstruction, quantizedType); - } - break; - - case Instruction::Store: - { - /* - * If either of the operands is constant, change it to a int value - * */ - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); - } - break; - - /* - * For fmul/fdiv, - * - * if either one of the operands is a constant value, simplify it by multiplying with 10^n, - * then replace the instruction to mul/div; - * - * else substitute this instruction to a pre-implemented function: mulfix/divfix. - * */ - case Instruction::FMul: - case Instruction::FDiv: - { - if (isa(llvmIrInstruction->getOperand(0)) || - isa(llvmIrInstruction->getOperand(1))) { - simplifyConstant(llvmIrInstruction, quantizedType); - } - else { - substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); - } - break; - } - - /* - * If either one of the operands is a constant value, quantize it, - * then replace the instruction to the int version. - * */ - case Instruction::FCmp: - case Instruction::FAdd: - case Instruction::FSub: - case Instruction::FRem: - { - quantizeConstant(llvmIrInstruction, quantizedType); - } - case Instruction::FNeg: - { - quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); - break; - } - -// case Instruction::Add: -// case Instruction::Sub: -// case Instruction::Mul: -// case Instruction::UDiv: -// case Instruction::SDiv: -// case Instruction::URem: -// case Instruction::SRem: -// -// case Instruction::Shl: -// case Instruction::LShr: -// case Instruction::AShr: -// case Instruction::And: -// case Instruction::Or: -// case Instruction::Xor: -// -// case Instruction::ICmp: + if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) + { + Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); + if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + break; + if (!calledFunction->getName().startswith("llvm.dbg.value") && + !calledFunction->getName().startswith("llvm.dbg.declare") && + !calledFunction->getName().startswith("llvm.dbg.label")) + { + if (calledFunction->isDeclaration()) + { + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (calledFunction->getName().str() == "sqrt") + { + /* + * if the arg's type is int, convert to fp, + * after the call node, convert to int and shl FRAC_Q/2 + * + * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); + * if (FRAC_Q%2) + * return res*1.414213562; + * else + * return res; + * + * %25 = sitofp i32 %0 to double + * %26 = call double @sqrt(double %25) #3 + * %27 = fptosi double %26 to i32 + * %28 = shl i32 %27, 4 + * */ + auto operand = llvmIrCallInstruction->getOperand(0); + if (operand->getType()->isIntegerTy()) + { + Value * newOperand = Builder.CreateSIToFP( + operand, llvmIrCallInstruction->getType()); + llvmIrCallInstruction->setOperand(0, newOperand); + } + auto cloneInst = llvmIrCallInstruction->clone(); + Value * fptosiInst = Builder.CreateFPToSI( + cloneInst, quantizedType); + Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value * resInst = nullptr; + /* + * if (FRAC_Q%2) then multiply with 1.414213562; + * */ + if (FRAC_Q % 2) + { + Value * lhsCompensateInst = Builder.CreateSIToFP( + shlInst, llvmIrCallInstruction->getType()); + auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), + 1.414213562); + Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); + resInst = Builder.CreateFPToSI(mulInst, quantizedType); + } + else + { + resInst = shlInst; + } + llvmIrCallInstruction->replaceAllUsesWith(resInst); + ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); + } + else + { + /* + * for other lib functions, de-quantize the arguments and quantize the return value + * */ + } + } + else + { + /* + * for user-defined function, quantize the arguments + * */ + for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) + { + setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); + } + quantizeConstant(llvmIrCallInstruction, quantizedType); + /* + * then quantize the return type + * */ + setQuantizedType(llvmIrCallInstruction, quantizedType); + } + } + } + break; + case Instruction::GetElementPtr: + if (auto gepInst = dyn_cast(llvmIrInstruction)) + { + auto gepType = gepInst->getType(); + auto sourceType = quantizedType; + // bool isPointer = false; + // unsigned pointerAddr = 0; + // if (gepType->isPointerTy()) { + // isPointer = true; + // pointerAddr = gepType->getPointerAddressSpace(); + // valueType = gepType->getPointerElementType(); + // } + if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) + { + sourceType = ArrayType::get(quantizedType, + gepInst->getSourceElementType()->getArrayNumElements()); + } + // if (isPointer) { + // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + // } + // if (gepType->isDoubleTy() || gepType->isFloatTy()) { + gepInst->setSourceElementType(sourceType); + gepInst->setResultElementType(quantizedType); + // } + } + case Instruction::Load: + case Instruction::PHI: + { + setQuantizedType(llvmIrInstruction, quantizedType); + } + break; + + case Instruction::Store: + { + /* + * If either of the operands is constant, change it to a int value + * */ + setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + quantizeConstant(llvmIrInstruction, quantizedType); + } + break; + + /* + * For fmul/fdiv, + * + * if either one of the operands is a constant value, simplify it by multiplying with 10^n, + * then replace the instruction to mul/div; + * + * else substitute this instruction to a pre-implemented function: mulfix/divfix. + * */ + case Instruction::FMul: + case Instruction::FDiv: + { + if (isa(llvmIrInstruction->getOperand(0)) || + isa(llvmIrInstruction->getOperand(1))) + { + simplifyConstant(llvmIrInstruction, quantizedType); + } + else + { + substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); + } + break; + } + + /* + * If either one of the operands is a constant value, quantize it, + * then replace the instruction to the int version. + * */ + case Instruction::FCmp: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FRem: + { + quantizeConstant(llvmIrInstruction, quantizedType); + } + case Instruction::FNeg: + { + quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); + break; + } + + // case Instruction::Add: + // case Instruction::Sub: + // case Instruction::Mul: + // case Instruction::UDiv: + // case Instruction::SDiv: + // case Instruction::URem: + // case Instruction::SRem: + // + // case Instruction::Shl: + // case Instruction::LShr: + // case Instruction::AShr: + // case Instruction::And: + // case Instruction::Or: + // case Instruction::Xor: + // + // case Instruction::ICmp: case Instruction::FPToUI: case Instruction::FPToSI: @@ -642,10 +777,10 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::ZExt: case Instruction::SExt: case Instruction::Trunc: - case Instruction::FPExt: + case Instruction::FPExt: case Instruction::FPTrunc: case Instruction::BitCast: - break; + break; case Instruction::Ret: case Instruction::Switch: @@ -684,7 +819,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } } - adaptTypeCast(llvmIrFunction, quantizedType); + adaptTypeCast(llvmIrFunction, quantizedType); return; } From d61802d1174f865bc470418ea84dae9c3d2df382 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 9 Jun 2024 11:27:51 +0100 Subject: [PATCH 002/213] attempt to modify quantizePredict * dev2. --- .../2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt diff --git a/analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt b/analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt new file mode 100644 index 000000000..27336608b --- /dev/null +++ b/analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt @@ -0,0 +1,7 @@ + +changeset: 1595:2a56f8cead1efca3bcef193fe76a2d9a3dac4463 +char kNewtonVersion[] = "0.3-alpha-1595 (2a56f8cead1efca3bcef193fe76a2d9a3dac4463) (build 06-09-2024-11:20-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 853a92ff7b0711fd22760220b158845ba2e57720 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 9 Jun 2024 11:30:08 +0100 Subject: [PATCH 003/213] attempt to modify quantizePredict * dev2. --- .../bf90e2b7968179440b55e72e170c6d618d09ceea.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt diff --git a/analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt b/analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt new file mode 100644 index 000000000..78e87ec88 --- /dev/null +++ b/analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt @@ -0,0 +1,7 @@ + +changeset: 1596:bf90e2b7968179440b55e72e170c6d618d09ceea +char kNewtonVersion[] = "0.3-alpha-1596 (bf90e2b7968179440b55e72e170c6d618d09ceea) (build 06-09-2024-11:27-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 8b4742f73e761c5f138aad89d27caed039565d1a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 9 Jun 2024 11:32:43 +0100 Subject: [PATCH 004/213] attempt to modify quantizePredict * dev2. --- .../d61802d1174f865bc470418ea84dae9c3d2df382.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt diff --git a/analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt b/analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt new file mode 100644 index 000000000..2960e9f5c --- /dev/null +++ b/analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt @@ -0,0 +1,7 @@ + +changeset: 1597:d61802d1174f865bc470418ea84dae9c3d2df382 +char kNewtonVersion[] = "0.3-alpha-1597 (d61802d1174f865bc470418ea84dae9c3d2df382) (build 06-09-2024-11:30-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 0e74e165e743d19e41942ad7778f7a9f6d76ceec Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 9 Jun 2024 11:37:37 +0100 Subject: [PATCH 005/213] attempt to modify quantizePredict * dev2. --- .../853a92ff7b0711fd22760220b158845ba2e57720.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt diff --git a/analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt b/analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt new file mode 100644 index 000000000..e6fcb5da6 --- /dev/null +++ b/analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt @@ -0,0 +1,7 @@ + +changeset: 1598:853a92ff7b0711fd22760220b158845ba2e57720 +char kNewtonVersion[] = "0.3-alpha-1598 (853a92ff7b0711fd22760220b158845ba2e57720) (build 06-09-2024-11:32-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 88379969b25b3b3eb107838ab34ad222c3d40663 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 9 Jun 2024 11:40:48 +0100 Subject: [PATCH 006/213] attempt to modify quantizePredict * dev2. --- .../8b4742f73e761c5f138aad89d27caed039565d1a.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt diff --git a/analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt b/analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt new file mode 100644 index 000000000..f59a13774 --- /dev/null +++ b/analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt @@ -0,0 +1,7 @@ + +changeset: 1599:8b4742f73e761c5f138aad89d27caed039565d1a +char kNewtonVersion[] = "0.3-alpha-1599 (8b4742f73e761c5f138aad89d27caed039565d1a) (build 06-09-2024-11:37-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 9ebc2e5cb76f71b0fc93523430b9867c72237e64 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 16 Jun 2024 13:14:20 +0100 Subject: [PATCH 007/213] update quantization.cpp * dev2. --- ...74e165e743d19e41942ad7778f7a9f6d76ceec.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 30 +++++++----- 2 files changed, 67 insertions(+), 11 deletions(-) create mode 100644 analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt diff --git a/analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt b/analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt new file mode 100644 index 000000000..b1763d988 --- /dev/null +++ b/analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt @@ -0,0 +1,48 @@ + +changeset: 1600:0e74e165e743d19e41942ad7778f7a9f6d76ceec +char kNewtonVersion[] = "0.3-alpha-1600 (0e74e165e743d19e41942ad7778f7a9f6d76ceec) (build 06-09-2024-11:40-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 4ef3b79b5..8f06dc939 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -68,6 +68,14 @@ setQuantizedType(Value * inValue, Type * quantizedType) inValue->mutateType(quantizedType); } } + else + { + llvm::errs() << "Unsupported type for quantization\n"; + } + } + else + { + llvm::errs() << "Null type encountered in setQuantizedType\n"; } } @@ -85,8 +93,10 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) continue; } - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; + ConstantFP * constFp = llvm::dyn_cast(inValue); + + // TODO Cache the quantized value to avoid multiple calculations + Value * newValue = nullptr; if (inValue->getType()->isFloatTy()) { // Convert float constant to fixed-point @@ -105,9 +115,10 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) { // assert(false && "unknown floating type"); llvm::errs() << "Unknown floating type\n"; - return; + continue; } + // Replace all uses of the original value with the new quantized value inInstruction->replaceUsesOfWith(inValue, newValue); } } @@ -348,11 +359,6 @@ quantizePredict(CmpInst::Predicate predict) case FCmpInst::FCMP_UNO: // unordered (at least one NaN) // For unordered, there is no direct integer equivalent, map to ICMP_EQ for safety return ICmpInst::ICMP_EQ; - case FCmpInst::FCMP_TRUE: // always true - // There is no direct integer equivalent, but we can use a workaround - return ICmpInst::ICMP_TRUE; // custom handling needed - case FCmpInst::FCMP_FALSE: // always false - return ICmpInst::ICMP_FALSE; // custom handling needed default: llvm::errs() << "Unhandled floating point predicate\n"; @@ -402,16 +408,17 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) newInst = Builder.CreateSRem(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; } + case Instruction::FCmp: // Replace floating-point comparison with integer comparison if (auto fcmp_inst = dyn_cast(inInstruction)) { CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); - if (pred == ICmpInst::ICMP_TRUE) + if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) { newInst = ConstantInt::getTrue(quantizedType); } - else if (pred == ICmpInst::ICMP_FALSE) + else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) { newInst = ConstantInt::getFalse(quantizedType); } @@ -421,6 +428,7 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) } } break; + /* * Change fneg(a) to `0-a`. * */ @@ -696,6 +704,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve gepInst->setResultElementType(quantizedType); // } } + break; case Instruction::Load: case Instruction::PHI: { @@ -823,4 +832,3 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } -} From ec9b06c559c03438402efadb55b5d5d1162fdcfc Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 13:09:07 +0100 Subject: [PATCH 008/213] update optimizeByRange.cpp * dev2. --- ...379969b25b3b3eb107838ab34ad222c3d40663.txt | 48 +++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 326 ++++++++++-------- 2 files changed, 233 insertions(+), 141 deletions(-) create mode 100644 analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt diff --git a/analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt b/analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt new file mode 100644 index 000000000..d241511e7 --- /dev/null +++ b/analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt @@ -0,0 +1,48 @@ + +changeset: 1601:88379969b25b3b3eb107838ab34ad222c3d40663 +char kNewtonVersion[] = "0.3-alpha-1601 (88379969b25b3b3eb107838ab34ad222c3d40663) (build 06-16-2024-13:14-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 81c4f9f3a..e0e7fba18 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -70,6 +70,39 @@ using namespace llvm; extern "C" { +// TODO add errorLog +void +handleError(State * N, const std::string & errorMessage) +{ + flexprint(N->Fe, N->Fm, N->Fperr, "%s\n", errorMessage.c_str()); + // Log the error to a file + std::ofstream errorLog("error.log", std::ios_base::app); + errorLog << errorMessage << std::endl; + errorLog.close(); + // Throw an exception to handle the error + throw std::runtime_error(errorMessage); +} + +std::unique_ptr +loadModule(State * N) +{ + try + { + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); + if (!Mod) + { + handleError(N, "Error: Couldn't parse IR file."); + } + return Mod; + } catch (const std::exception & e) + { + flexprint(N->Fe, N->Fm, N->Fperr, "Exception: %s\n", e.what()); + throw; // Re-throw the exception for further handling + } +} + void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { @@ -218,8 +251,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); if (!Mod) { - flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); - fatal(N, Esanity); + handleError(N, "Error: Couldn't parse IR file."); } auto globalBoundInfo = new BoundInfo(); @@ -326,147 +358,159 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl Mod->getFunctionList().push_front(mi); } } + std::map callerMap; + bool useOverLoad = false; -// /* -// * analyze the range of all local variables in each function -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// std::map callerMap; -// callerMap.clear(); -// funcBoundInfo.clear(); -// bool useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// shrinkType(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// memoryAlignment(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = true; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// /* -// * simplify the condition of each branch -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// constantSubstitution(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); + processModule(N, Mod.get(), globalBoundInfo, funcBoundInfo, typeRange, virtualRegisterVectorRange, callerMap, useOverLoad); - /* - * Dump BC file to a file. - * */ + // Dump BC file to a file dumpIR(N, "output", Mod); } + +void +processModule(State * N, Module * Mod, BoundInfo * globalBoundInfo, std::map & funcBoundInfo, + std::map> & typeRange, + std::map>> & virtualRegisterVectorRange, + std::map & callerMap, bool & useOverLoad) +{ + analyzeRange(N, Mod, globalBoundInfo, funcBoundInfo, typeRange, virtualRegisterVectorRange, callerMap, useOverLoad); + + /* + * analyze the range of all local variables in each function + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + std::map callerMap; + callerMap.clear(); + funcBoundInfo.clear(); + bool useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } + + flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + shrinkType(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + // Memory Alignment + flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + memoryAlignment(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + /* + * remove the functions that are optimized by passes. + * */ + if (useOverLoad) + cleanFunctionMap(Mod, callerMap); + + if (useOverLoad) + overloadFunc(Mod, callerMap); + + callerMap.clear(); + funcBoundInfo.clear(); + useOverLoad = true; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } + + /* + * simplify the condition of each branch + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + simplifyControlFlow(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + legacy::PassManager passManager; + passManager.add(createCFGSimplificationPass()); + passManager.add(createInstSimplifyLegacyPass()); + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); + + /* + * remove the functions that are optimized by passes. + * */ + if (useOverLoad) + cleanFunctionMap(Mod, callerMap); + + if (useOverLoad) + overloadFunc(Mod, callerMap); + + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + callerMap.clear(); + funcBoundInfo.clear(); + useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } + + // constant substritution + flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + constantSubstitution(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + /* + * remove the functions that are optimized by passes. + * */ + if (useOverLoad) + cleanFunctionMap(Mod, callerMap); + + if (useOverLoad) + overloadFunc(Mod, callerMap); } From 2124d4e633cfb39b0cd4cb845a10b6113ae2a536 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:03:43 +0100 Subject: [PATCH 009/213] update optimizeByRange.cpp * dev2. --- .../9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt diff --git a/analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt b/analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt new file mode 100644 index 000000000..1e9b1ed9a --- /dev/null +++ b/analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt @@ -0,0 +1,7 @@ + +changeset: 1602:9ebc2e5cb76f71b0fc93523430b9867c72237e64 +char kNewtonVersion[] = "0.3-alpha-1602 (9ebc2e5cb76f71b0fc93523430b9867c72237e64) (build 06-17-2024-13:09-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From abcb7dec677f4a49de71e626ed0cdc36cf466d75 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:18:42 +0100 Subject: [PATCH 010/213] update optimizeByRange.cpp * dev2. --- .../ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt diff --git a/analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt b/analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt new file mode 100644 index 000000000..87acf67b0 --- /dev/null +++ b/analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt @@ -0,0 +1,7 @@ + +changeset: 1603:ec9b06c559c03438402efadb55b5d5d1162fdcfc +char kNewtonVersion[] = "0.3-alpha-1603 (ec9b06c559c03438402efadb55b5d5d1162fdcfc) (build 06-17-2024-14:03-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 0f8c66f5f08f625687e7b85387c410cc14fc2e8a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:22:46 +0100 Subject: [PATCH 011/213] update optimizeByRange.cpp * dev2. --- .../2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt diff --git a/analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt b/analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt new file mode 100644 index 000000000..7224b8f67 --- /dev/null +++ b/analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt @@ -0,0 +1,7 @@ + +changeset: 1604:2124d4e633cfb39b0cd4cb845a10b6113ae2a536 +char kNewtonVersion[] = "0.3-alpha-1604 (2124d4e633cfb39b0cd4cb845a10b6113ae2a536) (build 06-17-2024-14:18-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 19e714f0f985ac7d8b0ef5e53b76767c9cd3854e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:32:21 +0100 Subject: [PATCH 012/213] update optimizeByRange.cpp * dev2. --- .../abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt diff --git a/analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt b/analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt new file mode 100644 index 000000000..8f2da32dd --- /dev/null +++ b/analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt @@ -0,0 +1,7 @@ + +changeset: 1605:abcb7dec677f4a49de71e626ed0cdc36cf466d75 +char kNewtonVersion[] = "0.3-alpha-1605 (abcb7dec677f4a49de71e626ed0cdc36cf466d75) (build 06-17-2024-14:22-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 1d8eb5741532f733eafc4bfcb93801bfdc260f25 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:41:24 +0100 Subject: [PATCH 013/213] update optimizeByRange.cpp * dev2. --- .../0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt diff --git a/analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt b/analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt new file mode 100644 index 000000000..03410cc22 --- /dev/null +++ b/analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt @@ -0,0 +1,7 @@ + +changeset: 1606:0f8c66f5f08f625687e7b85387c410cc14fc2e8a +char kNewtonVersion[] = "0.3-alpha-1606 (0f8c66f5f08f625687e7b85387c410cc14fc2e8a) (build 06-17-2024-14:32-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:42:39 +0100 Subject: [PATCH 014/213] update optimizeByRange.cpp * dev2. --- .../19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt diff --git a/analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt b/analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt new file mode 100644 index 000000000..06e0e47d0 --- /dev/null +++ b/analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt @@ -0,0 +1,7 @@ + +changeset: 1607:19e714f0f985ac7d8b0ef5e53b76767c9cd3854e +char kNewtonVersion[] = "0.3-alpha-1607 (19e714f0f985ac7d8b0ef5e53b76767c9cd3854e) (build 06-17-2024-14:41-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From b65f1b260332cbb3a377993270710c055cab07a5 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:48:08 +0100 Subject: [PATCH 015/213] update optimizeByRange.cpp * dev2. --- .../1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt diff --git a/analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt b/analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt new file mode 100644 index 000000000..605be0497 --- /dev/null +++ b/analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt @@ -0,0 +1,7 @@ + +changeset: 1608:1d8eb5741532f733eafc4bfcb93801bfdc260f25 +char kNewtonVersion[] = "0.3-alpha-1608 (1d8eb5741532f733eafc4bfcb93801bfdc260f25) (build 06-17-2024-14:42-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From e26f66a92942bd703e2354b002aea625268138ab Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:50:41 +0100 Subject: [PATCH 016/213] update optimizeByRange.cpp * dev2. --- .../734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt diff --git a/analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt b/analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt new file mode 100644 index 000000000..af93ba3eb --- /dev/null +++ b/analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt @@ -0,0 +1,7 @@ + +changeset: 1609:734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5 +char kNewtonVersion[] = "0.3-alpha-1609 (734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5) (build 06-17-2024-14:48-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From fc87f8d40d678f18669d957fcb15f65fdc51c1bb Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 14:59:34 +0100 Subject: [PATCH 017/213] update optimizeByRange.cpp * dev2. --- .../b65f1b260332cbb3a377993270710c055cab07a5.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt diff --git a/analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt b/analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt new file mode 100644 index 000000000..70fb1c7b3 --- /dev/null +++ b/analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt @@ -0,0 +1,7 @@ + +changeset: 1610:b65f1b260332cbb3a377993270710c055cab07a5 +char kNewtonVersion[] = "0.3-alpha-1610 (b65f1b260332cbb3a377993270710c055cab07a5) (build 06-17-2024-14:50-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 27e6a6612f474a6aa090e43f29b9313f0ef7e8e2 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 15:00:47 +0100 Subject: [PATCH 018/213] update optimizeByRange.cpp * dev2. --- .../e26f66a92942bd703e2354b002aea625268138ab.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt diff --git a/analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt b/analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt new file mode 100644 index 000000000..6035f920c --- /dev/null +++ b/analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt @@ -0,0 +1,7 @@ + +changeset: 1611:e26f66a92942bd703e2354b002aea625268138ab +char kNewtonVersion[] = "0.3-alpha-1611 (e26f66a92942bd703e2354b002aea625268138ab) (build 06-17-2024-14:59-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From caa6c556fd19e0898078bf99d105835d6b817c32 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 15:04:58 +0100 Subject: [PATCH 019/213] update optimizeByRange.cpp * dev2. --- .../fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt diff --git a/analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt b/analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt new file mode 100644 index 000000000..a34af663a --- /dev/null +++ b/analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt @@ -0,0 +1,7 @@ + +changeset: 1612:fc87f8d40d678f18669d957fcb15f65fdc51c1bb +char kNewtonVersion[] = "0.3-alpha-1612 (fc87f8d40d678f18669d957fcb15f65fdc51c1bb) (build 06-17-2024-15:00-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From d3306a61229c3d7d83d74c7d1a2face3c51fcc70 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 17 Jun 2024 15:06:50 +0100 Subject: [PATCH 020/213] update optimizeByRange.cpp * dev2. --- .../27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt diff --git a/analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt b/analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt new file mode 100644 index 000000000..520011c41 --- /dev/null +++ b/analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt @@ -0,0 +1,7 @@ + +changeset: 1613:27e6a6612f474a6aa090e43f29b9313f0ef7e8e2 +char kNewtonVersion[] = "0.3-alpha-1613 (27e6a6612f474a6aa090e43f29b9313f0ef7e8e2) (build 06-17-2024-15:04-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From 6edee7a34daf3be6125212d01c917f40dc474170 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 24 Jun 2024 20:13:23 +0100 Subject: [PATCH 021/213] update Makefile * dev2. --- ...a6c556fd19e0898078bf99d105835d6b817c32.txt | 48 +++++++++++++++++++ .../newton/llvm-ir/performance_test/Makefile | 28 +++++++---- 2 files changed, 67 insertions(+), 9 deletions(-) create mode 100644 analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt diff --git a/analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt b/analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt new file mode 100644 index 000000000..e7229105f --- /dev/null +++ b/analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt @@ -0,0 +1,48 @@ + +changeset: 1614:caa6c556fd19e0898078bf99d105835d6b817c32 +char kNewtonVersion[] = "0.3-alpha-1614 (caa6c556fd19e0898078bf99d105835d6b817c32) (build 06-17-2024-15:06-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 25c8a62e5..820f1afb6 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -11,7 +11,7 @@ CHStone_DIR = ../CHStone_test/ NEWTON_BIN_DIR = ../../../../src/newton CC = clang -OUT_FILE = out.ll +OUT_FILE = out.llperformace OUT_BC = out.bc OUT_S = out.s OUT_OBJ = out.o @@ -19,9 +19,11 @@ OUT_LIB = libout.a # DEBUG_MODE=true make ... ifdef DEBUG_MODE -CC_OPT_LEVEL = -O0 -g +#CC_OPT_LEVEL = -O0 -g +CC_OPT_LEVEL = -O0 -g -fno-builtin else -CC_OPT_LEVEL = -O3 -Os +#CC_OPT_LEVEL = -O3 -Os +CC_OPT_LEVEL = -O3 -Os -g -fno-builtin endif # CROSS_COMPILE=true make ... @@ -262,7 +264,7 @@ compile_arm_sqrt_q15: $(call compile_main_fn,ARM_SQRT_Q15) madgwick_non_opt: -ifdef AUTO_QUANT +ifdef AUTO_FIX_QUANT $(call max_opt_fn,MadgwickAHRSfix) else ifdef SOFT_FLOAT_LIB $(call max_opt_fn,MadgwickAHRS_softfloat) @@ -272,9 +274,9 @@ else endif madgwick_opt: - cd $(NEWTON_BIN_DIR) && $(call newton_opt_fn,MadgwickAHRSfix) - llvm-dis ../MadgwickAHRSfix_output.bc - $(call max_opt_fn,MadgwickAHRSfix_output) + cd $(NEWTON_BIN_DIR) && $(call newton_opt_fn,MadgwickAHRS) + llvm-dis ../MadgwickAHRS_output.bc + $(call max_opt_fn,MadgwickAHRS_output) inferBoundControlFlow_non_opt: $(call max_opt_fn,inferBoundControlFlow) @@ -343,7 +345,7 @@ perf_benchmark_suite_double_opt: clean make_ll benchmark_suite_opt compile_lib c perf_benchmark_suite_float: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_float -perf_benchmark_suite_float_opt: clean make_ll benchmark_suite_opt compile_lib compile_benchmark_suite_float +perf_benchmark_suite_float_opt: clean make_ll benchmark_suite_opt compile_lib compile_benchmark_suite_floa et perf_benchmark_suite_asuint: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_asuint @@ -351,7 +353,7 @@ perf_benchmark_suite_asuint_opt: clean make_ll benchmark_suite_opt compile_lib c perf_benchmark_suite_quant: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_quant -perf_benchmark_suite_fixedpoint: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_fixed_point +perf_benchmark_suite_fixedpoint: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_fixe ed_point perf_madgwick: clean make_ll madgwick_non_opt compile_lib compile_test_madgwick @@ -374,9 +376,17 @@ auto_test_compile: all: default + + + default: perf_exp perf_exp_opt perf_log perf_log_opt perf_acosh perf_acosh_opt perf_j0 perf_j0_opt perf_y0 perf_y0_opt perf_rem_pio2 perf_rem_pio2_opt perf_sincosf perf_sincosf_opt perf_float64_add perf_float64_add_opt perf_float64_div perf_float64_div_opt perf_float64_mul perf_float64_mul_opt perf_float64_sin perf_float64_sin_opt perf_arm_sqrt_q15 perf_arm_sqrt_q15_opt auto_test_compile clean: $(QUIET)rm -f *.ll *.o *.s *.txt out.* libout.a main_out auto_test cd $(CHStone_DIR) && $(MAKE_CLEAN) cd $(SUBDIR) && $(MAKE_CLEAN) + + + + + From 549d564728d0ccca2e7a79b3ab4387cd5c68e43a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 26 Jun 2024 17:45:53 +0100 Subject: [PATCH 022/213] update more test log and save IR function * dev2. --- ...306a61229c3d7d83d74c7d1a2face3c51fcc70.txt | 48 + .../llvm-ir/CHStone_test/float64_div.ll | 2148 +++++++++++++++++ .../llvm-ir/CHStone_test/float64_mul.ll | 1597 ++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 350 ++- 4 files changed, 3962 insertions(+), 181 deletions(-) create mode 100644 analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt create mode 100644 applications/newton/llvm-ir/CHStone_test/float64_div.ll create mode 100644 applications/newton/llvm-ir/CHStone_test/float64_mul.ll diff --git a/analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt b/analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt new file mode 100644 index 000000000..1042212f9 --- /dev/null +++ b/analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt @@ -0,0 +1,48 @@ + +changeset: 1615:d3306a61229c3d7d83d74c7d1a2face3c51fcc70 +char kNewtonVersion[] = "0.3-alpha-1615 (d3306a61229c3d7d83d74c7d1a2face3c51fcc70) (build 06-24-2024-20:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/CHStone_test/float64_div.ll b/applications/newton/llvm-ir/CHStone_test/float64_div.ll new file mode 100644 index 000000000..c66752f3c --- /dev/null +++ b/applications/newton/llvm-ir/CHStone_test/float64_div.ll @@ -0,0 +1,2148 @@ +; ModuleID = 'float64_div.ll' +source_filename = "dfdiv/float64_div.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@float_rounding_mode = dso_local global i32 0, align 4, !dbg !0 +@float_exception_flags = dso_local global i32 0, align 4, !dbg !14 +@_ZZL19countLeadingZeros32jE21countLeadingZerosHigh = internal constant <{ [128 x i32], [128 x i32] }> <{ [128 x i32] [i32 8, i32 7, i32 6, i32 6, i32 5, i32 5, i32 5, i32 5, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1], [128 x i32] zeroinitializer }>, align 16, !dbg !18 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z19shift64RightJammingyiPy(i64 %0, i32 %1, i64* %2) #0 !dbg !278 { + call void @llvm.dbg.value(metadata i64 %0, metadata !283, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i32 %1, metadata !285, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i64* %2, metadata !286, metadata !DIExpression()), !dbg !284 + %4 = icmp eq i32 %1, 0, !dbg !287 + br i1 %4, label %5, label %6, !dbg !289 + +5: ; preds = %3 + call void @llvm.dbg.value(metadata i64 %0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22, !dbg !291 + +6: ; preds = %3 + %7 = icmp slt i32 %1, 64, !dbg !293 + br i1 %7, label %8, label %18, !dbg !295 + +8: ; preds = %6 + %9 = zext i32 %1 to i64, !dbg !296 + %10 = lshr i64 %0, %9, !dbg !296 + %11 = sub nsw i32 0, %1, !dbg !298 + %12 = and i32 %11, 63, !dbg !299 + %13 = zext i32 %12 to i64, !dbg !300 + %14 = shl i64 %0, %13, !dbg !300 + %15 = icmp ne i64 %14, 0, !dbg !301 + %16 = zext i1 %15 to i64, !dbg !302 + %17 = or i64 %10, %16, !dbg !303 + call void @llvm.dbg.value(metadata i64 %17, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21, !dbg !304 + +18: ; preds = %6 + %19 = icmp ne i64 %0, 0, !dbg !305 + %20 = zext i1 %19 to i64, !dbg !307 + call void @llvm.dbg.value(metadata i64 %20, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21 + +21: ; preds = %18, %8 + %.0 = phi i64 [ %17, %8 ], [ %20, %18 ], !dbg !308 + call void @llvm.dbg.value(metadata i64 %.0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22 + +22: ; preds = %21, %5 + %.1 = phi i64 [ %0, %5 ], [ %.0, %21 ], !dbg !309 + call void @llvm.dbg.value(metadata i64 %.1, metadata !290, metadata !DIExpression()), !dbg !284 + store i64 %.1, i64* %2, align 8, !dbg !310 + ret void, !dbg !311 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z6add128yyyyPyS_(i64 %0, i64 %1, i64 %2, i64 %3, i64* %4, i64* %5) #0 !dbg !312 { + call void @llvm.dbg.value(metadata i64 %0, metadata !315, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %1, metadata !317, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %2, metadata !318, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %3, metadata !319, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %4, metadata !320, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %5, metadata !321, metadata !DIExpression()), !dbg !316 + %7 = add i64 %1, %3, !dbg !322 + call void @llvm.dbg.value(metadata i64 %7, metadata !323, metadata !DIExpression()), !dbg !316 + store i64 %7, i64* %5, align 8, !dbg !324 + %8 = add i64 %0, %2, !dbg !325 + %9 = icmp ult i64 %7, %1, !dbg !326 + %10 = zext i1 %9 to i64, !dbg !327 + %11 = add i64 %8, %10, !dbg !328 + store i64 %11, i64* %4, align 8, !dbg !329 + ret void, !dbg !330 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z6sub128yyyyPyS_(i64 %0, i64 %1, i64 %2, i64 %3, i64* %4, i64* %5) #0 !dbg !331 { + call void @llvm.dbg.value(metadata i64 %0, metadata !332, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64 %1, metadata !334, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64 %2, metadata !335, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64 %3, metadata !336, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64* %4, metadata !337, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64* %5, metadata !338, metadata !DIExpression()), !dbg !333 + %7 = sub i64 %1, %3, !dbg !339 + store i64 %7, i64* %5, align 8, !dbg !340 + %8 = sub i64 %0, %2, !dbg !341 + %9 = icmp ult i64 %1, %3, !dbg !342 + %10 = zext i1 %9 to i64, !dbg !343 + %11 = sub i64 %8, %10, !dbg !344 + store i64 %11, i64* %4, align 8, !dbg !345 + ret void, !dbg !346 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z10mul64To128yyPyS_(i64 %0, i64 %1, i64* %2, i64* %3) #0 !dbg !347 { + call void @llvm.dbg.value(metadata i64 %0, metadata !350, metadata !DIExpression()), !dbg !351 + call void @llvm.dbg.value(metadata i64 %1, metadata !352, metadata !DIExpression()), !dbg !351 + call void @llvm.dbg.value(metadata i64* %2, metadata !353, metadata !DIExpression()), !dbg !351 + call void @llvm.dbg.value(metadata i64* %3, metadata !354, metadata !DIExpression()), !dbg !351 + %5 = trunc i64 %0 to i32, !dbg !355 + call void @llvm.dbg.value(metadata i32 %5, metadata !356, metadata !DIExpression()), !dbg !351 + %6 = lshr i64 %0, 32, !dbg !357 + %7 = trunc i64 %6 to i32, !dbg !358 + call void @llvm.dbg.value(metadata i32 %7, metadata !359, metadata !DIExpression()), !dbg !351 + %8 = trunc i64 %1 to i32, !dbg !360 + call void @llvm.dbg.value(metadata i32 %8, metadata !361, metadata !DIExpression()), !dbg !351 + %9 = lshr i64 %1, 32, !dbg !362 + %10 = trunc i64 %9 to i32, !dbg !363 + call void @llvm.dbg.value(metadata i32 %10, metadata !364, metadata !DIExpression()), !dbg !351 + %11 = zext i32 %5 to i64, !dbg !365 + %12 = zext i32 %8 to i64, !dbg !366 + %13 = mul i64 %11, %12, !dbg !367 + call void @llvm.dbg.value(metadata i64 %13, metadata !368, metadata !DIExpression()), !dbg !351 + %14 = zext i32 %5 to i64, !dbg !369 + %15 = zext i32 %10 to i64, !dbg !370 + %16 = mul i64 %14, %15, !dbg !371 + call void @llvm.dbg.value(metadata i64 %16, metadata !372, metadata !DIExpression()), !dbg !351 + %17 = zext i32 %7 to i64, !dbg !373 + %18 = zext i32 %8 to i64, !dbg !374 + %19 = mul i64 %17, %18, !dbg !375 + call void @llvm.dbg.value(metadata i64 %19, metadata !376, metadata !DIExpression()), !dbg !351 + %20 = zext i32 %7 to i64, !dbg !377 + %21 = zext i32 %10 to i64, !dbg !378 + %22 = mul i64 %20, %21, !dbg !379 + call void @llvm.dbg.value(metadata i64 %22, metadata !380, metadata !DIExpression()), !dbg !351 + %23 = add i64 %16, %19, !dbg !381 + call void @llvm.dbg.value(metadata i64 %23, metadata !372, metadata !DIExpression()), !dbg !351 + %24 = icmp ult i64 %23, %19, !dbg !382 + %25 = zext i1 %24 to i64, !dbg !383 + %26 = shl i64 %25, 32, !dbg !384 + %27 = lshr i64 %23, 32, !dbg !385 + %28 = add i64 %26, %27, !dbg !386 + %29 = add i64 %22, %28, !dbg !387 + call void @llvm.dbg.value(metadata i64 %29, metadata !380, metadata !DIExpression()), !dbg !351 + %30 = shl i64 %23, 32, !dbg !388 + call void @llvm.dbg.value(metadata i64 %30, metadata !372, metadata !DIExpression()), !dbg !351 + %31 = add i64 %13, %30, !dbg !389 + call void @llvm.dbg.value(metadata i64 %31, metadata !368, metadata !DIExpression()), !dbg !351 + %32 = icmp ult i64 %31, %30, !dbg !390 + %33 = zext i1 %32 to i64, !dbg !391 + %34 = add i64 %29, %33, !dbg !392 + call void @llvm.dbg.value(metadata i64 %34, metadata !380, metadata !DIExpression()), !dbg !351 + store i64 %31, i64* %3, align 8, !dbg !393 + store i64 %34, i64* %2, align 8, !dbg !394 + ret void, !dbg !395 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z11float_raisei(i32 %0) #0 !dbg !396 { + call void @llvm.dbg.value(metadata i32 %0, metadata !400, metadata !DIExpression()), !dbg !401 + %2 = load i32, i32* @float_exception_flags, align 4, !dbg !402 + %3 = or i32 %2, %0, !dbg !402 + store i32 %3, i32* @float_exception_flags, align 4, !dbg !402 + ret void, !dbg !403 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z14float64_is_nany(i64 %0) #0 !dbg !404 { + call void @llvm.dbg.value(metadata i64 %0, metadata !410, metadata !DIExpression()), !dbg !411 + %2 = shl i64 %0, 1, !dbg !412 + %3 = icmp ult i64 -9007199254740992, %2, !dbg !413 + %4 = zext i1 %3 to i32, !dbg !414 + ret i32 %4, !dbg !415 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z24float64_is_signaling_nany(i64 %0) #0 !dbg !416 { + call void @llvm.dbg.value(metadata i64 %0, metadata !417, metadata !DIExpression()), !dbg !418 + %2 = lshr i64 %0, 51, !dbg !419 + %3 = and i64 %2, 4095, !dbg !420 + %4 = icmp eq i64 %3, 4094, !dbg !421 + br i1 %4, label %5, label %8, !dbg !422 + +5: ; preds = %1 + %6 = and i64 %0, 2251799813685247, !dbg !423 + %7 = icmp ne i64 %6, 0, !dbg !424 + br label %8 + +8: ; preds = %5, %1 + %9 = phi i1 [ false, %1 ], [ %7, %5 ], !dbg !418 + %10 = zext i1 %9 to i32, !dbg !425 + ret i32 %10, !dbg !426 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @extractFloat64Frac(i64 %0) #2 !dbg !427 { + call void @llvm.dbg.value(metadata i64 %0, metadata !430, metadata !DIExpression()), !dbg !431 + %2 = and i64 %0, 4503599627370495, !dbg !432 + ret i64 %2, !dbg !433 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Exp(i64 %0) #2 !dbg !434 { + call void @llvm.dbg.value(metadata i64 %0, metadata !437, metadata !DIExpression()), !dbg !438 + %2 = lshr i64 %0, 52, !dbg !439 + %3 = and i64 %2, 2047, !dbg !440 + %4 = trunc i64 %3 to i32, !dbg !441 + ret i32 %4, !dbg !442 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Sign(i64 %0) #2 !dbg !443 { + call void @llvm.dbg.value(metadata i64 %0, metadata !444, metadata !DIExpression()), !dbg !445 + %2 = lshr i64 %0, 63, !dbg !446 + %3 = trunc i64 %2 to i32, !dbg !447 + ret i32 %3, !dbg !448 +} + +; Function Attrs: alwaysinline mustprogress uwtable +define dso_local void @_Z25normalizeFloat64SubnormalyPiPy(i64 %0, i32* %1, i64* %2) #3 !dbg !449 { + call void @llvm.dbg.value(metadata i64 %0, metadata !453, metadata !DIExpression()), !dbg !454 + call void @llvm.dbg.value(metadata i32* %1, metadata !455, metadata !DIExpression()), !dbg !454 + call void @llvm.dbg.value(metadata i64* %2, metadata !456, metadata !DIExpression()), !dbg !454 + %4 = call i32 @_ZL19countLeadingZeros64y(i64 %0), !dbg !457 + %5 = sub nsw i32 %4, 11, !dbg !458 + call void @llvm.dbg.value(metadata i32 %5, metadata !459, metadata !DIExpression()), !dbg !454 + %6 = zext i32 %5 to i64, !dbg !460 + %7 = shl i64 %0, %6, !dbg !460 + store i64 %7, i64* %2, align 8, !dbg !461 + %8 = sub nsw i32 1, %5, !dbg !462 + store i32 %8, i32* %1, align 4, !dbg !463 + ret void, !dbg !464 +} + +; Function Attrs: mustprogress noinline uwtable +define internal i32 @_ZL19countLeadingZeros64y(i64 %0) #4 !dbg !465 { + call void @llvm.dbg.value(metadata i64 %0, metadata !468, metadata !DIExpression()), !dbg !469 + call void @llvm.dbg.value(metadata i32 0, metadata !470, metadata !DIExpression()), !dbg !469 + %2 = icmp ult i64 %0, 4294967296, !dbg !471 + br i1 %2, label %3, label %5, !dbg !473 + +3: ; preds = %1 + %4 = add nsw i32 0, 32, !dbg !474 + call void @llvm.dbg.value(metadata i32 %4, metadata !470, metadata !DIExpression()), !dbg !469 + br label %7, !dbg !476 + +5: ; preds = %1 + %6 = lshr i64 %0, 32, !dbg !477 + call void @llvm.dbg.value(metadata i64 %6, metadata !468, metadata !DIExpression()), !dbg !469 + br label %7 + +7: ; preds = %5, %3 + %.01 = phi i32 [ %4, %3 ], [ 0, %5 ], !dbg !469 + %.0 = phi i64 [ %0, %3 ], [ %6, %5 ] + call void @llvm.dbg.value(metadata i64 %.0, metadata !468, metadata !DIExpression()), !dbg !469 + call void @llvm.dbg.value(metadata i32 %.01, metadata !470, metadata !DIExpression()), !dbg !469 + %8 = trunc i64 %.0 to i32, !dbg !479 + %9 = call i32 @_ZL19countLeadingZeros32j(i32 %8), !dbg !480 + %10 = add nsw i32 %.01, %9, !dbg !481 + call void @llvm.dbg.value(metadata i32 %10, metadata !470, metadata !DIExpression()), !dbg !469 + ret i32 %10, !dbg !482 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @packFloat64(i32 %0, i32 %1, i64 %2) #2 !dbg !483 { + call void @llvm.dbg.value(metadata i32 %0, metadata !486, metadata !DIExpression()), !dbg !487 + call void @llvm.dbg.value(metadata i32 %1, metadata !488, metadata !DIExpression()), !dbg !487 + call void @llvm.dbg.value(metadata i64 %2, metadata !489, metadata !DIExpression()), !dbg !487 + %4 = sext i32 %0 to i64, !dbg !490 + %5 = shl i64 %4, 63, !dbg !491 + %6 = sext i32 %1 to i64, !dbg !492 + %7 = shl i64 %6, 52, !dbg !493 + %8 = add i64 %5, %7, !dbg !494 + %9 = add i64 %8, %2, !dbg !495 + ret i64 %9, !dbg !496 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @roundAndPackFloat64(i32 %0, i32 %1, i64 %2) #2 !dbg !497 { + %4 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i32 %0, metadata !498, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 %1, metadata !500, metadata !DIExpression()), !dbg !499 + store i64 %2, i64* %4, align 8 + call void @llvm.dbg.declare(metadata i64* %4, metadata !501, metadata !DIExpression()), !dbg !502 + %5 = load i32, i32* @float_rounding_mode, align 4, !dbg !503 + call void @llvm.dbg.value(metadata i32 %5, metadata !504, metadata !DIExpression()), !dbg !499 + %6 = icmp eq i32 %5, 0, !dbg !505 + %7 = zext i1 %6 to i32, !dbg !506 + call void @llvm.dbg.value(metadata i32 %7, metadata !507, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 512, metadata !508, metadata !DIExpression()), !dbg !499 + %8 = icmp ne i32 %7, 0, !dbg !509 + br i1 %8, label %24, label %9, !dbg !511 + +9: ; preds = %3 + %10 = icmp eq i32 %5, 1, !dbg !512 + br i1 %10, label %11, label %12, !dbg !515 + +11: ; preds = %9 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !499 + br label %23, !dbg !516 + +12: ; preds = %9 + call void @llvm.dbg.value(metadata i32 1023, metadata !508, metadata !DIExpression()), !dbg !499 + %13 = icmp ne i32 %0, 0, !dbg !518 + br i1 %13, label %14, label %18, !dbg !521 + +14: ; preds = %12 + %15 = icmp eq i32 %5, 2, !dbg !522 + br i1 %15, label %16, label %17, !dbg !525 + +16: ; preds = %14 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !499 + br label %17, !dbg !526 + +17: ; preds = %16, %14 + %.01 = phi i32 [ 0, %16 ], [ 1023, %14 ], !dbg !527 + call void @llvm.dbg.value(metadata i32 %.01, metadata !508, metadata !DIExpression()), !dbg !499 + br label %22, !dbg !528 + +18: ; preds = %12 + %19 = icmp eq i32 %5, 3, !dbg !529 + br i1 %19, label %20, label %21, !dbg !532 + +20: ; preds = %18 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !499 + br label %21, !dbg !533 + +21: ; preds = %20, %18 + %.12 = phi i32 [ 0, %20 ], [ 1023, %18 ], !dbg !527 + call void @llvm.dbg.value(metadata i32 %.12, metadata !508, metadata !DIExpression()), !dbg !499 + br label %22 + +22: ; preds = %21, %17 + %.2 = phi i32 [ %.01, %17 ], [ %.12, %21 ], !dbg !534 + call void @llvm.dbg.value(metadata i32 %.2, metadata !508, metadata !DIExpression()), !dbg !499 + br label %23 + +23: ; preds = %22, %11 + %.3 = phi i32 [ 0, %11 ], [ %.2, %22 ], !dbg !535 + call void @llvm.dbg.value(metadata i32 %.3, metadata !508, metadata !DIExpression()), !dbg !499 + br label %24, !dbg !536 + +24: ; preds = %23, %3 + %.4 = phi i32 [ 512, %3 ], [ %.3, %23 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.4, metadata !508, metadata !DIExpression()), !dbg !499 + %25 = load i64, i64* %4, align 8, !dbg !537 + %26 = and i64 %25, 1023, !dbg !538 + %27 = trunc i64 %26 to i32, !dbg !537 + call void @llvm.dbg.value(metadata i32 %27, metadata !539, metadata !DIExpression()), !dbg !499 + %28 = trunc i32 %1 to i16, !dbg !540 + %29 = zext i16 %28 to i32, !dbg !542 + %30 = icmp sle i32 2045, %29, !dbg !543 + br i1 %30, label %31, label %64, !dbg !544 + +31: ; preds = %24 + %32 = icmp slt i32 2045, %1, !dbg !545 + br i1 %32, label %40, label %33, !dbg !548 + +33: ; preds = %31 + %34 = icmp eq i32 %1, 2045, !dbg !549 + br i1 %34, label %35, label %50, !dbg !550 + +35: ; preds = %33 + %36 = load i64, i64* %4, align 8, !dbg !551 + %37 = sext i32 %.4 to i64, !dbg !552 + %38 = add i64 %36, %37, !dbg !553 + %39 = icmp slt i64 %38, 0, !dbg !554 + br i1 %39, label %40, label %50, !dbg !555 + +40: ; preds = %35, %31 + call void @_Z11float_raisei(i32 9), !dbg !556 + call void @llvm.dbg.value(metadata i32 %0, metadata !486, metadata !DIExpression()), !dbg !558 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !558 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !558 + %41 = sext i32 %0 to i64, !dbg !560 + %42 = shl i64 %41, 63, !dbg !561 + %43 = sext i32 2047 to i64, !dbg !562 + %44 = shl i64 %43, 52, !dbg !563 + %45 = add i64 %42, %44, !dbg !564 + %46 = add i64 %45, 0, !dbg !565 + %47 = icmp eq i32 %.4, 0, !dbg !566 + %48 = zext i1 %47 to i64, !dbg !567 + %49 = sub i64 %46, %48, !dbg !568 + br label %93, !dbg !569 + +50: ; preds = %35, %33 + %51 = icmp slt i32 %1, 0, !dbg !570 + br i1 %51, label %52, label %63, !dbg !572 + +52: ; preds = %50 + call void @llvm.dbg.value(metadata i32 1, metadata !573, metadata !DIExpression()), !dbg !499 + %53 = load i64, i64* %4, align 8, !dbg !574 + %54 = sub nsw i32 0, %1, !dbg !576 + call void @_Z19shift64RightJammingyiPy(i64 %53, i32 %54, i64* %4), !dbg !577 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !499 + %55 = load i64, i64* %4, align 8, !dbg !578 + %56 = and i64 %55, 1023, !dbg !579 + %57 = trunc i64 %56 to i32, !dbg !578 + call void @llvm.dbg.value(metadata i32 %57, metadata !539, metadata !DIExpression()), !dbg !499 + %58 = icmp ne i32 1, 0, !dbg !580 + br i1 %58, label %59, label %62, !dbg !582 + +59: ; preds = %52 + %60 = icmp ne i32 %57, 0, !dbg !583 + br i1 %60, label %61, label %62, !dbg !584 + +61: ; preds = %59 + call void @_Z11float_raisei(i32 4), !dbg !585 + br label %62, !dbg !585 + +62: ; preds = %61, %59, %52 + br label %63, !dbg !586 + +63: ; preds = %62, %50 + %.03 = phi i32 [ 0, %62 ], [ %1, %50 ] + %.0 = phi i32 [ %57, %62 ], [ %27, %50 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.0, metadata !539, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 %.03, metadata !500, metadata !DIExpression()), !dbg !499 + br label %64, !dbg !587 + +64: ; preds = %63, %24 + %.14 = phi i32 [ %.03, %63 ], [ %1, %24 ] + %.1 = phi i32 [ %.0, %63 ], [ %27, %24 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.1, metadata !539, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 %.14, metadata !500, metadata !DIExpression()), !dbg !499 + %65 = icmp ne i32 %.1, 0, !dbg !588 + br i1 %65, label %66, label %69, !dbg !590 + +66: ; preds = %64 + %67 = load i32, i32* @float_exception_flags, align 4, !dbg !591 + %68 = or i32 %67, 1, !dbg !591 + store i32 %68, i32* @float_exception_flags, align 4, !dbg !591 + br label %69, !dbg !592 + +69: ; preds = %66, %64 + %70 = load i64, i64* %4, align 8, !dbg !593 + %71 = sext i32 %.4 to i64, !dbg !594 + %72 = add i64 %70, %71, !dbg !595 + %73 = lshr i64 %72, 10, !dbg !596 + store i64 %73, i64* %4, align 8, !dbg !597 + %74 = xor i32 %.1, 512, !dbg !598 + %75 = icmp eq i32 %74, 0, !dbg !599 + %76 = zext i1 %75 to i32, !dbg !600 + %77 = and i32 %76, %7, !dbg !601 + %78 = xor i32 %77, -1, !dbg !602 + %79 = sext i32 %78 to i64, !dbg !602 + %80 = load i64, i64* %4, align 8, !dbg !603 + %81 = and i64 %80, %79, !dbg !603 + store i64 %81, i64* %4, align 8, !dbg !603 + %82 = load i64, i64* %4, align 8, !dbg !604 + %83 = icmp eq i64 %82, 0, !dbg !606 + br i1 %83, label %84, label %85, !dbg !607 + +84: ; preds = %69 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !499 + br label %85, !dbg !608 + +85: ; preds = %84, %69 + %.25 = phi i32 [ 0, %84 ], [ %.14, %69 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.25, metadata !500, metadata !DIExpression()), !dbg !499 + %86 = load i64, i64* %4, align 8, !dbg !609 + call void @llvm.dbg.value(metadata i32 %0, metadata !486, metadata !DIExpression()), !dbg !610 + call void @llvm.dbg.value(metadata i32 %.25, metadata !488, metadata !DIExpression()), !dbg !610 + call void @llvm.dbg.value(metadata i64 %86, metadata !489, metadata !DIExpression()), !dbg !610 + %87 = sext i32 %0 to i64, !dbg !612 + %88 = shl i64 %87, 63, !dbg !613 + %89 = sext i32 %.25 to i64, !dbg !614 + %90 = shl i64 %89, 52, !dbg !615 + %91 = add i64 %88, %90, !dbg !616 + %92 = add i64 %91, %86, !dbg !617 + br label %93, !dbg !618 + +93: ; preds = %85, %40 + %.06 = phi i64 [ %49, %40 ], [ %92, %85 ], !dbg !499 + ret i64 %.06, !dbg !619 +} + +; Function Attrs: mustprogress noinline uwtable +define dso_local i64 @float64_div(i64 %0, i64 %1) #4 !dbg !620 { + %3 = alloca i64, align 8 + %4 = alloca i64, align 8 + %5 = alloca i64, align 8 + %6 = alloca i64, align 8 + %7 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i64 %0, metadata !625, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !627, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.declare(metadata i64* %4, metadata !628, metadata !DIExpression()), !dbg !629 + call void @llvm.dbg.declare(metadata i64* %5, metadata !630, metadata !DIExpression()), !dbg !631 + call void @llvm.dbg.declare(metadata i64* %6, metadata !632, metadata !DIExpression()), !dbg !633 + call void @llvm.dbg.declare(metadata i64* %7, metadata !634, metadata !DIExpression()), !dbg !635 + call void @llvm.dbg.value(metadata i64 %0, metadata !430, metadata !DIExpression()), !dbg !636 + %8 = and i64 %0, 4503599627370495, !dbg !638 + call void @llvm.dbg.value(metadata i64 %8, metadata !639, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %0, metadata !437, metadata !DIExpression()), !dbg !640 + %9 = lshr i64 %0, 52, !dbg !642 + %10 = and i64 %9, 2047, !dbg !643 + %11 = trunc i64 %10 to i32, !dbg !644 + call void @llvm.dbg.value(metadata i32 %11, metadata !645, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %0, metadata !444, metadata !DIExpression()), !dbg !646 + %12 = lshr i64 %0, 63, !dbg !648 + %13 = trunc i64 %12 to i32, !dbg !649 + call void @llvm.dbg.value(metadata i32 %13, metadata !650, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !430, metadata !DIExpression()), !dbg !651 + %14 = and i64 %1, 4503599627370495, !dbg !653 + call void @llvm.dbg.value(metadata i64 %14, metadata !654, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !437, metadata !DIExpression()), !dbg !655 + %15 = lshr i64 %1, 52, !dbg !657 + %16 = and i64 %15, 2047, !dbg !658 + %17 = trunc i64 %16 to i32, !dbg !659 + call void @llvm.dbg.value(metadata i32 %17, metadata !660, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !444, metadata !DIExpression()), !dbg !661 + %18 = lshr i64 %1, 63, !dbg !663 + %19 = trunc i64 %18 to i32, !dbg !664 + call void @llvm.dbg.value(metadata i32 %19, metadata !665, metadata !DIExpression()), !dbg !626 + %20 = xor i32 %13, %19, !dbg !666 + call void @llvm.dbg.value(metadata i32 %20, metadata !667, metadata !DIExpression()), !dbg !626 + %21 = icmp eq i32 %11, 2047, !dbg !668 + br i1 %21, label %22, label %40, !dbg !670 + +22: ; preds = %2 + %23 = icmp ne i64 %8, 0, !dbg !671 + br i1 %23, label %24, label %26, !dbg !674 + +24: ; preds = %22 + %25 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !675 + br label %213, !dbg !676 + +26: ; preds = %22 + %27 = icmp eq i32 %17, 2047, !dbg !677 + br i1 %27, label %28, label %33, !dbg !679 + +28: ; preds = %26 + %29 = icmp ne i64 %14, 0, !dbg !680 + br i1 %29, label %30, label %32, !dbg !683 + +30: ; preds = %28 + %31 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !684 + br label %213, !dbg !685 + +32: ; preds = %28 + call void @_Z11float_raisei(i32 16), !dbg !686 + br label %213, !dbg !687 + +33: ; preds = %26 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !688 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !688 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !688 + %34 = sext i32 %20 to i64, !dbg !690 + %35 = shl i64 %34, 63, !dbg !691 + %36 = sext i32 2047 to i64, !dbg !692 + %37 = shl i64 %36, 52, !dbg !693 + %38 = add i64 %35, %37, !dbg !694 + %39 = add i64 %38, 0, !dbg !695 + br label %213, !dbg !696 + +40: ; preds = %2 + %41 = icmp eq i32 %17, 2047, !dbg !697 + br i1 %41, label %42, label %53, !dbg !699 + +42: ; preds = %40 + %43 = icmp ne i64 %14, 0, !dbg !700 + br i1 %43, label %44, label %46, !dbg !703 + +44: ; preds = %42 + %45 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !704 + br label %213, !dbg !705 + +46: ; preds = %42 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !706 + call void @llvm.dbg.value(metadata i32 0, metadata !488, metadata !DIExpression()), !dbg !706 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !706 + %47 = sext i32 %20 to i64, !dbg !708 + %48 = shl i64 %47, 63, !dbg !709 + %49 = sext i32 0 to i64, !dbg !710 + %50 = shl i64 %49, 52, !dbg !711 + %51 = add i64 %48, %50, !dbg !712 + %52 = add i64 %51, 0, !dbg !713 + br label %213, !dbg !714 + +53: ; preds = %40 + %54 = icmp eq i32 %17, 0, !dbg !715 + br i1 %54, label %55, label %75, !dbg !717 + +55: ; preds = %53 + %56 = icmp eq i64 %14, 0, !dbg !718 + br i1 %56, label %57, label %69, !dbg !721 + +57: ; preds = %55 + %58 = sext i32 %11 to i64, !dbg !722 + %59 = or i64 %58, %8, !dbg !725 + %60 = icmp eq i64 %59, 0, !dbg !726 + br i1 %60, label %61, label %62, !dbg !727 + +61: ; preds = %57 + call void @_Z11float_raisei(i32 16), !dbg !728 + br label %213, !dbg !730 + +62: ; preds = %57 + call void @_Z11float_raisei(i32 2), !dbg !731 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !732 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !732 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !732 + %63 = sext i32 %20 to i64, !dbg !734 + %64 = shl i64 %63, 63, !dbg !735 + %65 = sext i32 2047 to i64, !dbg !736 + %66 = shl i64 %65, 52, !dbg !737 + %67 = add i64 %64, %66, !dbg !738 + %68 = add i64 %67, 0, !dbg !739 + br label %213, !dbg !740 + +69: ; preds = %55 + call void @llvm.dbg.value(metadata i64 %14, metadata !453, metadata !DIExpression()), !dbg !741 + call void @llvm.dbg.value(metadata i32* undef, metadata !455, metadata !DIExpression()), !dbg !741 + call void @llvm.dbg.value(metadata i64* undef, metadata !456, metadata !DIExpression()), !dbg !741 + %70 = call i32 @_ZL19countLeadingZeros64y(i64 %14), !dbg !743 + %71 = sub nsw i32 %70, 11, !dbg !744 + call void @llvm.dbg.value(metadata i32 %71, metadata !459, metadata !DIExpression()), !dbg !741 + %72 = zext i32 %71 to i64, !dbg !745 + %73 = shl i64 %14, %72, !dbg !745 + call void @llvm.dbg.value(metadata i64 %73, metadata !654, metadata !DIExpression()), !dbg !626 + %74 = sub nsw i32 1, %71, !dbg !746 + call void @llvm.dbg.value(metadata i32 %74, metadata !660, metadata !DIExpression()), !dbg !626 + br label %75, !dbg !747 + +75: ; preds = %69, %53 + %.015 = phi i64 [ %73, %69 ], [ %14, %53 ], !dbg !626 + %.012 = phi i32 [ %74, %69 ], [ %17, %53 ], !dbg !626 + call void @llvm.dbg.value(metadata i32 %.012, metadata !660, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %.015, metadata !654, metadata !DIExpression()), !dbg !626 + %76 = icmp eq i32 %11, 0, !dbg !748 + br i1 %76, label %77, label %92, !dbg !750 + +77: ; preds = %75 + %78 = icmp eq i64 %8, 0, !dbg !751 + br i1 %78, label %79, label %86, !dbg !754 + +79: ; preds = %77 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !755 + call void @llvm.dbg.value(metadata i32 0, metadata !488, metadata !DIExpression()), !dbg !755 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !755 + %80 = sext i32 %20 to i64, !dbg !757 + %81 = shl i64 %80, 63, !dbg !758 + %82 = sext i32 0 to i64, !dbg !759 + %83 = shl i64 %82, 52, !dbg !760 + %84 = add i64 %81, %83, !dbg !761 + %85 = add i64 %84, 0, !dbg !762 + br label %213, !dbg !763 + +86: ; preds = %77 + call void @llvm.dbg.value(metadata i64 %8, metadata !453, metadata !DIExpression()), !dbg !764 + call void @llvm.dbg.value(metadata i32* undef, metadata !455, metadata !DIExpression()), !dbg !764 + call void @llvm.dbg.value(metadata i64* undef, metadata !456, metadata !DIExpression()), !dbg !764 + %87 = call i32 @_ZL19countLeadingZeros64y(i64 %8), !dbg !766 + %88 = sub nsw i32 %87, 11, !dbg !767 + call void @llvm.dbg.value(metadata i32 %88, metadata !459, metadata !DIExpression()), !dbg !764 + %89 = zext i32 %88 to i64, !dbg !768 + %90 = shl i64 %8, %89, !dbg !768 + call void @llvm.dbg.value(metadata i64 %90, metadata !639, metadata !DIExpression()), !dbg !626 + %91 = sub nsw i32 1, %88, !dbg !769 + call void @llvm.dbg.value(metadata i32 %91, metadata !645, metadata !DIExpression()), !dbg !626 + br label %92, !dbg !770 + +92: ; preds = %86, %75 + %.013 = phi i64 [ %90, %86 ], [ %8, %75 ], !dbg !626 + %.011 = phi i32 [ %91, %86 ], [ %11, %75 ], !dbg !626 + call void @llvm.dbg.value(metadata i32 %.011, metadata !645, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %.013, metadata !639, metadata !DIExpression()), !dbg !626 + %93 = sub nsw i32 %.011, %.012, !dbg !771 + %94 = add nsw i32 %93, 1021, !dbg !772 + call void @llvm.dbg.value(metadata i32 %94, metadata !773, metadata !DIExpression()), !dbg !626 + %95 = or i64 %.013, 4503599627370496, !dbg !774 + %96 = shl i64 %95, 10, !dbg !775 + call void @llvm.dbg.value(metadata i64 %96, metadata !639, metadata !DIExpression()), !dbg !626 + %97 = or i64 %.015, 4503599627370496, !dbg !776 + %98 = shl i64 %97, 11, !dbg !777 + call void @llvm.dbg.value(metadata i64 %98, metadata !654, metadata !DIExpression()), !dbg !626 + %99 = add i64 %96, %96, !dbg !778 + %100 = icmp ule i64 %98, %99, !dbg !780 + br i1 %100, label %101, label %104, !dbg !781 + +101: ; preds = %92 + %102 = lshr i64 %96, 1, !dbg !782 + call void @llvm.dbg.value(metadata i64 %102, metadata !639, metadata !DIExpression()), !dbg !626 + %103 = add nsw i32 %94, 1, !dbg !784 + call void @llvm.dbg.value(metadata i32 %103, metadata !773, metadata !DIExpression()), !dbg !626 + br label %104, !dbg !785 + +104: ; preds = %101, %92 + %.114 = phi i64 [ %102, %101 ], [ %96, %92 ], !dbg !626 + %.01 = phi i32 [ %103, %101 ], [ %94, %92 ], !dbg !626 + call void @llvm.dbg.value(metadata i64 %.114, metadata !639, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i32 %.01, metadata !773, metadata !DIExpression()), !dbg !626 + %105 = call i64 @_ZL18estimateDiv128To64yyy(i64 %.114, i64 0, i64 %98), !dbg !786 + call void @llvm.dbg.value(metadata i64 %105, metadata !787, metadata !DIExpression()), !dbg !626 + %106 = and i64 %105, 511, !dbg !788 + %107 = icmp ule i64 %106, 2, !dbg !790 + br i1 %107, label %108, label %123, !dbg !791 + +108: ; preds = %104 + call void @_Z10mul64To128yyPyS_(i64 %98, i64 %105, i64* %6, i64* %7), !dbg !792 + %109 = load i64, i64* %6, align 8, !dbg !794 + %110 = load i64, i64* %7, align 8, !dbg !795 + call void @_Z6sub128yyyyPyS_(i64 %.114, i64 0, i64 %109, i64 %110, i64* %4, i64* %5), !dbg !796 + br label %111, !dbg !797 + +111: ; preds = %114, %108 + %.0 = phi i64 [ %105, %108 ], [ %115, %114 ], !dbg !626 + call void @llvm.dbg.value(metadata i64 %.0, metadata !787, metadata !DIExpression()), !dbg !626 + %112 = load i64, i64* %4, align 8, !dbg !798 + %113 = icmp slt i64 %112, 0, !dbg !799 + br i1 %113, label %114, label %118, !dbg !797 + +114: ; preds = %111 + %115 = add i64 %.0, -1, !dbg !800 + call void @llvm.dbg.value(metadata i64 %115, metadata !787, metadata !DIExpression()), !dbg !626 + %116 = load i64, i64* %4, align 8, !dbg !802 + %117 = load i64, i64* %5, align 8, !dbg !803 + call void @_Z6add128yyyyPyS_(i64 %116, i64 %117, i64 0, i64 %98, i64* %4, i64* %5), !dbg !804 + br label %111, !dbg !797, !llvm.loop !805 + +118: ; preds = %111 + %119 = load i64, i64* %5, align 8, !dbg !808 + %120 = icmp ne i64 %119, 0, !dbg !809 + %121 = zext i1 %120 to i64, !dbg !810 + %122 = or i64 %.0, %121, !dbg !811 + call void @llvm.dbg.value(metadata i64 %122, metadata !787, metadata !DIExpression()), !dbg !626 + br label %123, !dbg !812 + +123: ; preds = %118, %104 + %.1 = phi i64 [ %122, %118 ], [ %105, %104 ], !dbg !626 + call void @llvm.dbg.value(metadata i64 %.1, metadata !787, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i32 %20, metadata !498, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 %.01, metadata !500, metadata !DIExpression()), !dbg !813 + store i64 %.1, i64* %3, align 8 + call void @llvm.dbg.declare(metadata i64* %3, metadata !501, metadata !DIExpression()) #5, !dbg !815 + %124 = load i32, i32* @float_rounding_mode, align 4, !dbg !816 + call void @llvm.dbg.value(metadata i32 %124, metadata !504, metadata !DIExpression()), !dbg !813 + %125 = icmp eq i32 %124, 0, !dbg !817 + %126 = zext i1 %125 to i32, !dbg !818 + call void @llvm.dbg.value(metadata i32 %126, metadata !507, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 512, metadata !508, metadata !DIExpression()), !dbg !813 + %127 = icmp ne i32 %126, 0, !dbg !819 + br i1 %127, label %143, label %128, !dbg !820 + +128: ; preds = %123 + %129 = icmp eq i32 %124, 1, !dbg !821 + br i1 %129, label %130, label %131, !dbg !822 + +130: ; preds = %128 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !813 + br label %142, !dbg !823 + +131: ; preds = %128 + call void @llvm.dbg.value(metadata i32 1023, metadata !508, metadata !DIExpression()), !dbg !813 + %132 = icmp ne i32 %20, 0, !dbg !824 + br i1 %132, label %133, label %137, !dbg !825 + +133: ; preds = %131 + %134 = icmp eq i32 %124, 2, !dbg !826 + br i1 %134, label %135, label %136, !dbg !827 + +135: ; preds = %133 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !813 + br label %136, !dbg !828 + +136: ; preds = %135, %133 + %.05 = phi i32 [ 0, %135 ], [ 1023, %133 ], !dbg !829 + call void @llvm.dbg.value(metadata i32 %.05, metadata !508, metadata !DIExpression()), !dbg !813 + br label %141, !dbg !830 + +137: ; preds = %131 + %138 = icmp eq i32 %124, 3, !dbg !831 + br i1 %138, label %139, label %140, !dbg !832 + +139: ; preds = %137 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !813 + br label %140, !dbg !833 + +140: ; preds = %139, %137 + %.16 = phi i32 [ 0, %139 ], [ 1023, %137 ], !dbg !829 + call void @llvm.dbg.value(metadata i32 %.16, metadata !508, metadata !DIExpression()), !dbg !813 + br label %141 + +141: ; preds = %140, %136 + %.2 = phi i32 [ %.05, %136 ], [ %.16, %140 ], !dbg !834 + call void @llvm.dbg.value(metadata i32 %.2, metadata !508, metadata !DIExpression()), !dbg !813 + br label %142 + +142: ; preds = %141, %130 + %.3 = phi i32 [ 0, %130 ], [ %.2, %141 ], !dbg !835 + call void @llvm.dbg.value(metadata i32 %.3, metadata !508, metadata !DIExpression()), !dbg !813 + br label %143, !dbg !836 + +143: ; preds = %142, %123 + %.4 = phi i32 [ 512, %123 ], [ %.3, %142 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.4, metadata !508, metadata !DIExpression()), !dbg !813 + %144 = load i64, i64* %3, align 8, !dbg !837 + %145 = and i64 %144, 1023, !dbg !838 + %146 = trunc i64 %145 to i32, !dbg !837 + call void @llvm.dbg.value(metadata i32 %146, metadata !539, metadata !DIExpression()), !dbg !813 + %147 = trunc i32 %.01 to i16, !dbg !839 + %148 = zext i16 %147 to i32, !dbg !840 + %149 = icmp sle i32 2045, %148, !dbg !841 + br i1 %149, label %150, label %183, !dbg !842 + +150: ; preds = %143 + %151 = icmp slt i32 2045, %.01, !dbg !843 + br i1 %151, label %159, label %152, !dbg !844 + +152: ; preds = %150 + %153 = icmp eq i32 %.01, 2045, !dbg !845 + br i1 %153, label %154, label %169, !dbg !846 + +154: ; preds = %152 + %155 = load i64, i64* %3, align 8, !dbg !847 + %156 = sext i32 %.4 to i64, !dbg !848 + %157 = add i64 %155, %156, !dbg !849 + %158 = icmp slt i64 %157, 0, !dbg !850 + br i1 %158, label %159, label %169, !dbg !851 + +159: ; preds = %154, %150 + call void @_Z11float_raisei(i32 9) #5, !dbg !852 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !853 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !853 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !853 + %160 = sext i32 %20 to i64, !dbg !855 + %161 = shl i64 %160, 63, !dbg !856 + %162 = sext i32 2047 to i64, !dbg !857 + %163 = shl i64 %162, 52, !dbg !858 + %164 = add i64 %161, %163, !dbg !859 + %165 = add i64 %164, 0, !dbg !860 + %166 = icmp eq i32 %.4, 0, !dbg !861 + %167 = zext i1 %166 to i64, !dbg !862 + %168 = sub i64 %165, %167, !dbg !863 + br label %212, !dbg !864 + +169: ; preds = %154, %152 + %170 = icmp slt i32 %.01, 0, !dbg !865 + br i1 %170, label %171, label %182, !dbg !866 + +171: ; preds = %169 + call void @llvm.dbg.value(metadata i32 1, metadata !573, metadata !DIExpression()), !dbg !813 + %172 = load i64, i64* %3, align 8, !dbg !867 + %173 = sub nsw i32 0, %.01, !dbg !868 + call void @_Z19shift64RightJammingyiPy(i64 %172, i32 %173, i64* %3) #5, !dbg !869 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !813 + %174 = load i64, i64* %3, align 8, !dbg !870 + %175 = and i64 %174, 1023, !dbg !871 + %176 = trunc i64 %175 to i32, !dbg !870 + call void @llvm.dbg.value(metadata i32 %176, metadata !539, metadata !DIExpression()), !dbg !813 + %177 = icmp ne i32 1, 0, !dbg !872 + br i1 %177, label %178, label %181, !dbg !873 + +178: ; preds = %171 + %179 = icmp ne i32 %176, 0, !dbg !874 + br i1 %179, label %180, label %181, !dbg !875 + +180: ; preds = %178 + call void @_Z11float_raisei(i32 4) #5, !dbg !876 + br label %181, !dbg !876 + +181: ; preds = %180, %178, %171 + br label %182, !dbg !877 + +182: ; preds = %181, %169 + %.07 = phi i32 [ 0, %181 ], [ %.01, %169 ] + %.03 = phi i32 [ %176, %181 ], [ %146, %169 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.03, metadata !539, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 %.07, metadata !500, metadata !DIExpression()), !dbg !813 + br label %183, !dbg !878 + +183: ; preds = %182, %143 + %.18 = phi i32 [ %.07, %182 ], [ %.01, %143 ] + %.14 = phi i32 [ %.03, %182 ], [ %146, %143 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.14, metadata !539, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 %.18, metadata !500, metadata !DIExpression()), !dbg !813 + %184 = icmp ne i32 %.14, 0, !dbg !879 + br i1 %184, label %185, label %188, !dbg !880 + +185: ; preds = %183 + %186 = load i32, i32* @float_exception_flags, align 4, !dbg !881 + %187 = or i32 %186, 1, !dbg !881 + store i32 %187, i32* @float_exception_flags, align 4, !dbg !881 + br label %188, !dbg !882 + +188: ; preds = %185, %183 + %189 = load i64, i64* %3, align 8, !dbg !883 + %190 = sext i32 %.4 to i64, !dbg !884 + %191 = add i64 %189, %190, !dbg !885 + %192 = lshr i64 %191, 10, !dbg !886 + store i64 %192, i64* %3, align 8, !dbg !887 + %193 = xor i32 %.14, 512, !dbg !888 + %194 = icmp eq i32 %193, 0, !dbg !889 + %195 = zext i1 %194 to i32, !dbg !890 + %196 = and i32 %195, %126, !dbg !891 + %197 = xor i32 %196, -1, !dbg !892 + %198 = sext i32 %197 to i64, !dbg !892 + %199 = load i64, i64* %3, align 8, !dbg !893 + %200 = and i64 %199, %198, !dbg !893 + store i64 %200, i64* %3, align 8, !dbg !893 + %201 = load i64, i64* %3, align 8, !dbg !894 + %202 = icmp eq i64 %201, 0, !dbg !895 + br i1 %202, label %203, label %204, !dbg !896 + +203: ; preds = %188 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !813 + br label %204, !dbg !897 + +204: ; preds = %203, %188 + %.29 = phi i32 [ 0, %203 ], [ %.18, %188 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.29, metadata !500, metadata !DIExpression()), !dbg !813 + %205 = load i64, i64* %3, align 8, !dbg !898 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !899 + call void @llvm.dbg.value(metadata i32 %.29, metadata !488, metadata !DIExpression()), !dbg !899 + call void @llvm.dbg.value(metadata i64 %205, metadata !489, metadata !DIExpression()), !dbg !899 + %206 = sext i32 %20 to i64, !dbg !901 + %207 = shl i64 %206, 63, !dbg !902 + %208 = sext i32 %.29 to i64, !dbg !903 + %209 = shl i64 %208, 52, !dbg !904 + %210 = add i64 %207, %209, !dbg !905 + %211 = add i64 %210, %205, !dbg !906 + br label %212, !dbg !907 + +212: ; preds = %204, %159 + %.010 = phi i64 [ %168, %159 ], [ %211, %204 ], !dbg !813 + br label %213, !dbg !908 + +213: ; preds = %212, %79, %62, %61, %46, %44, %33, %32, %30, %24 + %.02 = phi i64 [ %25, %24 ], [ %31, %30 ], [ 9223372036854775807, %32 ], [ %39, %33 ], [ %45, %44 ], [ %52, %46 ], [ 9223372036854775807, %61 ], [ %68, %62 ], [ %85, %79 ], [ %.010, %212 ], !dbg !626 + ret i64 %.02, !dbg !909 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1) #0 !dbg !910 { + call void @llvm.dbg.value(metadata i64 %0, metadata !913, metadata !DIExpression()), !dbg !914 + call void @llvm.dbg.value(metadata i64 %1, metadata !915, metadata !DIExpression()), !dbg !914 + %3 = call i32 @_Z14float64_is_nany(i64 %0), !dbg !916 + call void @llvm.dbg.value(metadata i32 %3, metadata !917, metadata !DIExpression()), !dbg !914 + %4 = call i32 @_Z24float64_is_signaling_nany(i64 %0), !dbg !918 + call void @llvm.dbg.value(metadata i32 %4, metadata !919, metadata !DIExpression()), !dbg !914 + %5 = call i32 @_Z14float64_is_nany(i64 %1), !dbg !920 + call void @llvm.dbg.value(metadata i32 %5, metadata !921, metadata !DIExpression()), !dbg !914 + %6 = call i32 @_Z24float64_is_signaling_nany(i64 %1), !dbg !922 + call void @llvm.dbg.value(metadata i32 %6, metadata !923, metadata !DIExpression()), !dbg !914 + %7 = or i64 %0, 2251799813685248, !dbg !924 + call void @llvm.dbg.value(metadata i64 %7, metadata !913, metadata !DIExpression()), !dbg !914 + %8 = or i64 %1, 2251799813685248, !dbg !925 + call void @llvm.dbg.value(metadata i64 %8, metadata !915, metadata !DIExpression()), !dbg !914 + %9 = or i32 %4, %6, !dbg !926 + %10 = icmp ne i32 %9, 0, !dbg !928 + br i1 %10, label %11, label %12, !dbg !929 + +11: ; preds = %2 + call void @_Z11float_raisei(i32 16), !dbg !930 + br label %12, !dbg !930 + +12: ; preds = %11, %2 + %13 = icmp ne i32 %6, 0, !dbg !931 + br i1 %13, label %14, label %15, !dbg !931 + +14: ; preds = %12 + br label %26, !dbg !931 + +15: ; preds = %12 + %16 = icmp ne i32 %4, 0, !dbg !932 + br i1 %16, label %17, label %18, !dbg !932 + +17: ; preds = %15 + br label %24, !dbg !932 + +18: ; preds = %15 + %19 = icmp ne i32 %5, 0, !dbg !933 + br i1 %19, label %20, label %21, !dbg !933 + +20: ; preds = %18 + br label %22, !dbg !933 + +21: ; preds = %18 + br label %22, !dbg !933 + +22: ; preds = %21, %20 + %23 = phi i64 [ %8, %20 ], [ %7, %21 ], !dbg !933 + br label %24, !dbg !932 + +24: ; preds = %22, %17 + %25 = phi i64 [ %7, %17 ], [ %23, %22 ], !dbg !932 + br label %26, !dbg !931 + +26: ; preds = %24, %14 + %27 = phi i64 [ %8, %14 ], [ %25, %24 ], !dbg !931 + ret i64 %27, !dbg !934 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i64 @_ZL18estimateDiv128To64yyy(i64 %0, i64 %1, i64 %2) #0 !dbg !935 { + %4 = alloca i64, align 8 + %5 = alloca i64, align 8 + %6 = alloca i64, align 8 + %7 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i64 %0, metadata !938, metadata !DIExpression()), !dbg !939 + call void @llvm.dbg.value(metadata i64 %1, metadata !940, metadata !DIExpression()), !dbg !939 + call void @llvm.dbg.value(metadata i64 %2, metadata !941, metadata !DIExpression()), !dbg !939 + call void @llvm.dbg.declare(metadata i64* %4, metadata !942, metadata !DIExpression()), !dbg !943 + call void @llvm.dbg.declare(metadata i64* %5, metadata !944, metadata !DIExpression()), !dbg !945 + call void @llvm.dbg.declare(metadata i64* %6, metadata !946, metadata !DIExpression()), !dbg !947 + call void @llvm.dbg.declare(metadata i64* %7, metadata !948, metadata !DIExpression()), !dbg !949 + %8 = icmp ule i64 %2, %0, !dbg !950 + br i1 %8, label %9, label %10, !dbg !952 + +9: ; preds = %3 + br label %46, !dbg !953 + +10: ; preds = %3 + %11 = lshr i64 %2, 32, !dbg !954 + call void @llvm.dbg.value(metadata i64 %11, metadata !955, metadata !DIExpression()), !dbg !939 + %12 = shl i64 %11, 32, !dbg !956 + %13 = icmp ule i64 %12, %0, !dbg !957 + br i1 %13, label %14, label %15, !dbg !958 + +14: ; preds = %10 + br label %18, !dbg !958 + +15: ; preds = %10 + %16 = udiv i64 %0, %11, !dbg !959 + %17 = shl i64 %16, 32, !dbg !960 + br label %18, !dbg !958 + +18: ; preds = %15, %14 + %19 = phi i64 [ -4294967296, %14 ], [ %17, %15 ], !dbg !958 + call void @llvm.dbg.value(metadata i64 %19, metadata !961, metadata !DIExpression()), !dbg !939 + call void @_Z10mul64To128yyPyS_(i64 %2, i64 %19, i64* %6, i64* %7), !dbg !962 + %20 = load i64, i64* %6, align 8, !dbg !963 + %21 = load i64, i64* %7, align 8, !dbg !964 + call void @_Z6sub128yyyyPyS_(i64 %0, i64 %1, i64 %20, i64 %21, i64* %4, i64* %5), !dbg !965 + br label %22, !dbg !966 + +22: ; preds = %25, %18 + %.01 = phi i64 [ %19, %18 ], [ %26, %25 ], !dbg !939 + call void @llvm.dbg.value(metadata i64 %.01, metadata !961, metadata !DIExpression()), !dbg !939 + %23 = load i64, i64* %4, align 8, !dbg !967 + %24 = icmp slt i64 %23, 0, !dbg !968 + br i1 %24, label %25, label %30, !dbg !966 + +25: ; preds = %22 + %26 = sub i64 %.01, 4294967296, !dbg !969 + call void @llvm.dbg.value(metadata i64 %26, metadata !961, metadata !DIExpression()), !dbg !939 + %27 = shl i64 %2, 32, !dbg !971 + call void @llvm.dbg.value(metadata i64 %27, metadata !972, metadata !DIExpression()), !dbg !939 + %28 = load i64, i64* %4, align 8, !dbg !973 + %29 = load i64, i64* %5, align 8, !dbg !974 + call void @_Z6add128yyyyPyS_(i64 %28, i64 %29, i64 %11, i64 %27, i64* %4, i64* %5), !dbg !975 + br label %22, !dbg !966, !llvm.loop !976 + +30: ; preds = %22 + %31 = load i64, i64* %4, align 8, !dbg !978 + %32 = shl i64 %31, 32, !dbg !979 + %33 = load i64, i64* %5, align 8, !dbg !980 + %34 = lshr i64 %33, 32, !dbg !981 + %35 = or i64 %32, %34, !dbg !982 + store i64 %35, i64* %4, align 8, !dbg !983 + %36 = shl i64 %11, 32, !dbg !984 + %37 = load i64, i64* %4, align 8, !dbg !985 + %38 = icmp ule i64 %36, %37, !dbg !986 + br i1 %38, label %39, label %40, !dbg !987 + +39: ; preds = %30 + br label %43, !dbg !987 + +40: ; preds = %30 + %41 = load i64, i64* %4, align 8, !dbg !988 + %42 = udiv i64 %41, %11, !dbg !989 + br label %43, !dbg !987 + +43: ; preds = %40, %39 + %44 = phi i64 [ 4294967295, %39 ], [ %42, %40 ], !dbg !987 + %45 = or i64 %.01, %44, !dbg !990 + call void @llvm.dbg.value(metadata i64 %45, metadata !961, metadata !DIExpression()), !dbg !939 + br label %46, !dbg !991 + +46: ; preds = %43, %9 + %.0 = phi i64 [ -1, %9 ], [ %45, %43 ], !dbg !939 + ret i64 %.0, !dbg !992 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i32 @_ZL19countLeadingZeros32j(i32 %0) #0 !dbg !20 { + call void @llvm.dbg.value(metadata i32 %0, metadata !993, metadata !DIExpression()), !dbg !994 + call void @llvm.dbg.value(metadata i32 0, metadata !995, metadata !DIExpression()), !dbg !994 + %2 = icmp ult i32 %0, 65536, !dbg !996 + br i1 %2, label %3, label %6, !dbg !998 + +3: ; preds = %1 + %4 = add nsw i32 0, 16, !dbg !999 + call void @llvm.dbg.value(metadata i32 %4, metadata !995, metadata !DIExpression()), !dbg !994 + %5 = shl i32 %0, 16, !dbg !1001 + call void @llvm.dbg.value(metadata i32 %5, metadata !993, metadata !DIExpression()), !dbg !994 + br label %6, !dbg !1002 + +6: ; preds = %3, %1 + %.01 = phi i32 [ %4, %3 ], [ 0, %1 ], !dbg !994 + %.0 = phi i32 [ %5, %3 ], [ %0, %1 ] + call void @llvm.dbg.value(metadata i32 %.0, metadata !993, metadata !DIExpression()), !dbg !994 + call void @llvm.dbg.value(metadata i32 %.01, metadata !995, metadata !DIExpression()), !dbg !994 + %7 = icmp ult i32 %.0, 16777216, !dbg !1003 + br i1 %7, label %8, label %11, !dbg !1005 + +8: ; preds = %6 + %9 = add nsw i32 %.01, 8, !dbg !1006 + call void @llvm.dbg.value(metadata i32 %9, metadata !995, metadata !DIExpression()), !dbg !994 + %10 = shl i32 %.0, 8, !dbg !1008 + call void @llvm.dbg.value(metadata i32 %10, metadata !993, metadata !DIExpression()), !dbg !994 + br label %11, !dbg !1009 + +11: ; preds = %8, %6 + %.12 = phi i32 [ %9, %8 ], [ %.01, %6 ], !dbg !994 + %.1 = phi i32 [ %10, %8 ], [ %.0, %6 ], !dbg !994 + call void @llvm.dbg.value(metadata i32 %.1, metadata !993, metadata !DIExpression()), !dbg !994 + call void @llvm.dbg.value(metadata i32 %.12, metadata !995, metadata !DIExpression()), !dbg !994 + %12 = lshr i32 %.1, 24, !dbg !1010 + %13 = zext i32 %12 to i64, !dbg !1011 + %14 = getelementptr inbounds [256 x i32], [256 x i32]* bitcast (<{ [128 x i32], [128 x i32] }>* @_ZZL19countLeadingZeros32jE21countLeadingZerosHigh to [256 x i32]*), i64 0, i64 %13, !dbg !1011 + %15 = load i32, i32* %14, align 4, !dbg !1011 + %16 = add nsw i32 %.12, %15, !dbg !1012 + call void @llvm.dbg.value(metadata i32 %16, metadata !995, metadata !DIExpression()), !dbg !994 + ret i32 %16, !dbg !1013 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { mustprogress noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { alwaysinline mustprogress nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { alwaysinline mustprogress uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #4 = { mustprogress noinline uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #5 = { nounwind } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!272, !273, !274, !275, !276} +!llvm.ident = !{!277} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "float_rounding_mode", scope: !2, file: !3, line: 60, type: !16, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "Ubuntu clang version 13.0.1-2ubuntu2.2", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !13, imports: !30, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "dfdiv/float64_div.cpp", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!4 = !{} +!5 = !{!6, !9, !11} +!6 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits64", file: !7, line: 70, baseType: !8) +!7 = !DIFile(filename: "dfdiv/include/SPARC-GCC.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!8 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!9 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits16", file: !7, line: 68, baseType: !10) +!10 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned) +!11 = !DIDerivedType(tag: DW_TAG_typedef, name: "sbits64", file: !7, line: 71, baseType: !12) +!12 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!13 = !{!0, !14, !18} +!14 = !DIGlobalVariableExpression(var: !15, expr: !DIExpression()) +!15 = distinct !DIGlobalVariable(name: "float_exception_flags", scope: !2, file: !3, line: 61, type: !16, isLocal: false, isDefinition: true) +!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "int8", file: !7, line: 59, baseType: !17) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) +!19 = distinct !DIGlobalVariable(name: "countLeadingZerosHigh", scope: !20, file: !21, line: 189, type: !26, isLocal: true, isDefinition: true) +!20 = distinct !DISubprogram(name: "countLeadingZeros32", linkageName: "_ZL19countLeadingZeros32j", scope: !21, file: !21, line: 187, type: !22, scopeLine: 188, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!21 = !DIFile(filename: "dfdiv/include/softfloat-macros", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!22 = !DISubroutineType(types: !23) +!23 = !{!16, !24} +!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits32", file: !7, line: 69, baseType: !25) +!25 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 8192, elements: !28) +!27 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !16) +!28 = !{!29} +!29 = !DISubrange(count: 256) +!30 = !{!31, !38, !42, !49, !53, !58, !60, !68, !72, !76, !90, !94, !98, !102, !106, !111, !115, !119, !123, !127, !135, !139, !143, !145, !149, !153, !157, !163, !167, !171, !173, !181, !185, !192, !194, !198, !202, !206, !210, !214, !219, !224, !225, !226, !227, !229, !230, !231, !232, !233, !234, !235, !237, !238, !239, !240, !241, !242, !243, !248, !249, !250, !251, !252, !253, !254, !255, !256, !257, !258, !259, !260, !261, !262, !263, !264, !265, !266, !267, !268, !269, !270, !271} +!31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !33, file: !37, line: 52) +!32 = !DINamespace(name: "std", scope: null) +!33 = !DISubprogram(name: "abs", scope: !34, file: !34, line: 848, type: !35, flags: DIFlagPrototyped, spFlags: 0) +!34 = !DIFile(filename: "/usr/include/stdlib.h", directory: "") +!35 = !DISubroutineType(types: !36) +!36 = !{!17, !17} +!37 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_abs.h", directory: "") +!38 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !39, file: !41, line: 127) +!39 = !DIDerivedType(tag: DW_TAG_typedef, name: "div_t", file: !34, line: 63, baseType: !40) +!40 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 59, size: 64, flags: DIFlagFwdDecl, identifier: "_ZTS5div_t") +!41 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/cstdlib", directory: "") +!42 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !43, file: !41, line: 128) +!43 = !DIDerivedType(tag: DW_TAG_typedef, name: "ldiv_t", file: !34, line: 71, baseType: !44) +!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 67, size: 128, flags: DIFlagTypePassByValue, elements: !45, identifier: "_ZTS6ldiv_t") +!45 = !{!46, !48} +!46 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !44, file: !34, line: 69, baseType: !47, size: 64) +!47 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!48 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !44, file: !34, line: 70, baseType: !47, size: 64, offset: 64) +!49 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !50, file: !41, line: 130) +!50 = !DISubprogram(name: "abort", scope: !34, file: !34, line: 598, type: !51, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!51 = !DISubroutineType(types: !52) +!52 = !{null} +!53 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !54, file: !41, line: 134) +!54 = !DISubprogram(name: "atexit", scope: !34, file: !34, line: 602, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!55 = !DISubroutineType(types: !56) +!56 = !{!17, !57} +!57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) +!58 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !59, file: !41, line: 137) +!59 = !DISubprogram(name: "at_quick_exit", scope: !34, file: !34, line: 607, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!60 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !61, file: !41, line: 140) +!61 = !DISubprogram(name: "atof", scope: !34, file: !34, line: 102, type: !62, flags: DIFlagPrototyped, spFlags: 0) +!62 = !DISubroutineType(types: !63) +!63 = !{!64, !65} +!64 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) +!66 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !67) +!67 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!68 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !69, file: !41, line: 141) +!69 = !DISubprogram(name: "atoi", scope: !34, file: !34, line: 105, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!70 = !DISubroutineType(types: !71) +!71 = !{!17, !65} +!72 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !73, file: !41, line: 142) +!73 = !DISubprogram(name: "atol", scope: !34, file: !34, line: 108, type: !74, flags: DIFlagPrototyped, spFlags: 0) +!74 = !DISubroutineType(types: !75) +!75 = !{!47, !65} +!76 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !77, file: !41, line: 143) +!77 = !DISubprogram(name: "bsearch", scope: !34, file: !34, line: 828, type: !78, flags: DIFlagPrototyped, spFlags: 0) +!78 = !DISubroutineType(types: !79) +!79 = !{!80, !81, !81, !83, !83, !86} +!80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!81 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !82, size: 64) +!82 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) +!83 = !DIDerivedType(tag: DW_TAG_typedef, name: "size_t", file: !84, line: 46, baseType: !85) +!84 = !DIFile(filename: "/usr/lib/llvm-13/lib/clang/13.0.1/include/stddef.h", directory: "") +!85 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!86 = !DIDerivedType(tag: DW_TAG_typedef, name: "__compar_fn_t", file: !34, line: 816, baseType: !87) +!87 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !88, size: 64) +!88 = !DISubroutineType(types: !89) +!89 = !{!17, !81, !81} +!90 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !91, file: !41, line: 144) +!91 = !DISubprogram(name: "calloc", scope: !34, file: !34, line: 543, type: !92, flags: DIFlagPrototyped, spFlags: 0) +!92 = !DISubroutineType(types: !93) +!93 = !{!80, !83, !83} +!94 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !95, file: !41, line: 145) +!95 = !DISubprogram(name: "div", scope: !34, file: !34, line: 860, type: !96, flags: DIFlagPrototyped, spFlags: 0) +!96 = !DISubroutineType(types: !97) +!97 = !{!39, !17, !17} +!98 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !99, file: !41, line: 146) +!99 = !DISubprogram(name: "exit", scope: !34, file: !34, line: 624, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!100 = !DISubroutineType(types: !101) +!101 = !{null, !17} +!102 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !103, file: !41, line: 147) +!103 = !DISubprogram(name: "free", scope: !34, file: !34, line: 555, type: !104, flags: DIFlagPrototyped, spFlags: 0) +!104 = !DISubroutineType(types: !105) +!105 = !{null, !80} +!106 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !107, file: !41, line: 148) +!107 = !DISubprogram(name: "getenv", scope: !34, file: !34, line: 641, type: !108, flags: DIFlagPrototyped, spFlags: 0) +!108 = !DISubroutineType(types: !109) +!109 = !{!110, !65} +!110 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !67, size: 64) +!111 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !112, file: !41, line: 149) +!112 = !DISubprogram(name: "labs", scope: !34, file: !34, line: 849, type: !113, flags: DIFlagPrototyped, spFlags: 0) +!113 = !DISubroutineType(types: !114) +!114 = !{!47, !47} +!115 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !116, file: !41, line: 150) +!116 = !DISubprogram(name: "ldiv", scope: !34, file: !34, line: 862, type: !117, flags: DIFlagPrototyped, spFlags: 0) +!117 = !DISubroutineType(types: !118) +!118 = !{!43, !47, !47} +!119 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !120, file: !41, line: 151) +!120 = !DISubprogram(name: "malloc", scope: !34, file: !34, line: 540, type: !121, flags: DIFlagPrototyped, spFlags: 0) +!121 = !DISubroutineType(types: !122) +!122 = !{!80, !83} +!123 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !124, file: !41, line: 153) +!124 = !DISubprogram(name: "mblen", scope: !34, file: !34, line: 930, type: !125, flags: DIFlagPrototyped, spFlags: 0) +!125 = !DISubroutineType(types: !126) +!126 = !{!17, !65, !83} +!127 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !128, file: !41, line: 154) +!128 = !DISubprogram(name: "mbstowcs", scope: !34, file: !34, line: 941, type: !129, flags: DIFlagPrototyped, spFlags: 0) +!129 = !DISubroutineType(types: !130) +!130 = !{!83, !131, !134, !83} +!131 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !132) +!132 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !133, size: 64) +!133 = !DIBasicType(name: "wchar_t", size: 32, encoding: DW_ATE_signed) +!134 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !65) +!135 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !136, file: !41, line: 155) +!136 = !DISubprogram(name: "mbtowc", scope: !34, file: !34, line: 933, type: !137, flags: DIFlagPrototyped, spFlags: 0) +!137 = !DISubroutineType(types: !138) +!138 = !{!17, !131, !134, !83} +!139 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !140, file: !41, line: 157) +!140 = !DISubprogram(name: "qsort", scope: !34, file: !34, line: 838, type: !141, flags: DIFlagPrototyped, spFlags: 0) +!141 = !DISubroutineType(types: !142) +!142 = !{null, !80, !83, !83, !86} +!143 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !144, file: !41, line: 160) +!144 = !DISubprogram(name: "quick_exit", scope: !34, file: !34, line: 630, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!145 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !146, file: !41, line: 163) +!146 = !DISubprogram(name: "rand", scope: !34, file: !34, line: 454, type: !147, flags: DIFlagPrototyped, spFlags: 0) +!147 = !DISubroutineType(types: !148) +!148 = !{!17} +!149 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !150, file: !41, line: 164) +!150 = !DISubprogram(name: "realloc", scope: !34, file: !34, line: 551, type: !151, flags: DIFlagPrototyped, spFlags: 0) +!151 = !DISubroutineType(types: !152) +!152 = !{!80, !80, !83} +!153 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !154, file: !41, line: 165) +!154 = !DISubprogram(name: "srand", scope: !34, file: !34, line: 456, type: !155, flags: DIFlagPrototyped, spFlags: 0) +!155 = !DISubroutineType(types: !156) +!156 = !{null, !25} +!157 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !158, file: !41, line: 166) +!158 = !DISubprogram(name: "strtod", scope: !34, file: !34, line: 118, type: !159, flags: DIFlagPrototyped, spFlags: 0) +!159 = !DISubroutineType(types: !160) +!160 = !{!64, !134, !161} +!161 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !162) +!162 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !110, size: 64) +!163 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !164, file: !41, line: 167) +!164 = !DISubprogram(name: "strtol", scope: !34, file: !34, line: 177, type: !165, flags: DIFlagPrototyped, spFlags: 0) +!165 = !DISubroutineType(types: !166) +!166 = !{!47, !134, !161, !17} +!167 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !168, file: !41, line: 168) +!168 = !DISubprogram(name: "strtoul", scope: !34, file: !34, line: 181, type: !169, flags: DIFlagPrototyped, spFlags: 0) +!169 = !DISubroutineType(types: !170) +!170 = !{!85, !134, !161, !17} +!171 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !172, file: !41, line: 169) +!172 = !DISubprogram(name: "system", scope: !34, file: !34, line: 791, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!173 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !174, file: !41, line: 171) +!174 = !DISubprogram(name: "wcstombs", scope: !34, file: !34, line: 945, type: !175, flags: DIFlagPrototyped, spFlags: 0) +!175 = !DISubroutineType(types: !176) +!176 = !{!83, !177, !178, !83} +!177 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !110) +!178 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !179) +!179 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !180, size: 64) +!180 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !133) +!181 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !182, file: !41, line: 172) +!182 = !DISubprogram(name: "wctomb", scope: !34, file: !34, line: 937, type: !183, flags: DIFlagPrototyped, spFlags: 0) +!183 = !DISubroutineType(types: !184) +!184 = !{!17, !110, !133} +!185 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !187, file: !41, line: 200) +!186 = !DINamespace(name: "__gnu_cxx", scope: null) +!187 = !DIDerivedType(tag: DW_TAG_typedef, name: "lldiv_t", file: !34, line: 81, baseType: !188) +!188 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 77, size: 128, flags: DIFlagTypePassByValue, elements: !189, identifier: "_ZTS7lldiv_t") +!189 = !{!190, !191} +!190 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !188, file: !34, line: 79, baseType: !12, size: 64) +!191 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !188, file: !34, line: 80, baseType: !12, size: 64, offset: 64) +!192 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !193, file: !41, line: 206) +!193 = !DISubprogram(name: "_Exit", scope: !34, file: !34, line: 636, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!194 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !195, file: !41, line: 210) +!195 = !DISubprogram(name: "llabs", scope: !34, file: !34, line: 852, type: !196, flags: DIFlagPrototyped, spFlags: 0) +!196 = !DISubroutineType(types: !197) +!197 = !{!12, !12} +!198 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !199, file: !41, line: 216) +!199 = !DISubprogram(name: "lldiv", scope: !34, file: !34, line: 866, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!200 = !DISubroutineType(types: !201) +!201 = !{!187, !12, !12} +!202 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !203, file: !41, line: 227) +!203 = !DISubprogram(name: "atoll", scope: !34, file: !34, line: 113, type: !204, flags: DIFlagPrototyped, spFlags: 0) +!204 = !DISubroutineType(types: !205) +!205 = !{!12, !65} +!206 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !207, file: !41, line: 228) +!207 = !DISubprogram(name: "strtoll", scope: !34, file: !34, line: 201, type: !208, flags: DIFlagPrototyped, spFlags: 0) +!208 = !DISubroutineType(types: !209) +!209 = !{!12, !134, !161, !17} +!210 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !211, file: !41, line: 229) +!211 = !DISubprogram(name: "strtoull", scope: !34, file: !34, line: 206, type: !212, flags: DIFlagPrototyped, spFlags: 0) +!212 = !DISubroutineType(types: !213) +!213 = !{!8, !134, !161, !17} +!214 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !215, file: !41, line: 231) +!215 = !DISubprogram(name: "strtof", scope: !34, file: !34, line: 124, type: !216, flags: DIFlagPrototyped, spFlags: 0) +!216 = !DISubroutineType(types: !217) +!217 = !{!218, !134, !161} +!218 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!219 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !220, file: !41, line: 232) +!220 = !DISubprogram(name: "strtold", scope: !34, file: !34, line: 127, type: !221, flags: DIFlagPrototyped, spFlags: 0) +!221 = !DISubroutineType(types: !222) +!222 = !{!223, !134, !161} +!223 = !DIBasicType(name: "long double", size: 128, encoding: DW_ATE_float) +!224 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !187, file: !41, line: 240) +!225 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !193, file: !41, line: 242) +!226 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !195, file: !41, line: 244) +!227 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !228, file: !41, line: 245) +!228 = !DISubprogram(name: "div", linkageName: "_ZN9__gnu_cxx3divExx", scope: !186, file: !41, line: 213, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!229 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !199, file: !41, line: 246) +!230 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !203, file: !41, line: 248) +!231 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !215, file: !41, line: 249) +!232 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !207, file: !41, line: 250) +!233 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !211, file: !41, line: 251) +!234 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !220, file: !41, line: 252) +!235 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !50, file: !236, line: 38) +!236 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/stdlib.h", directory: "") +!237 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !54, file: !236, line: 39) +!238 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !99, file: !236, line: 40) +!239 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !59, file: !236, line: 43) +!240 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !144, file: !236, line: 46) +!241 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !39, file: !236, line: 51) +!242 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !43, file: !236, line: 52) +!243 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !244, file: !236, line: 54) +!244 = !DISubprogram(name: "abs", linkageName: "_ZSt3absg", scope: !32, file: !37, line: 103, type: !245, flags: DIFlagPrototyped, spFlags: 0) +!245 = !DISubroutineType(types: !246) +!246 = !{!247, !247} +!247 = !DIBasicType(name: "__float128", size: 128, encoding: DW_ATE_float) +!248 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !61, file: !236, line: 55) +!249 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !69, file: !236, line: 56) +!250 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !73, file: !236, line: 57) +!251 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !77, file: !236, line: 58) +!252 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !91, file: !236, line: 59) +!253 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !228, file: !236, line: 60) +!254 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !103, file: !236, line: 61) +!255 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !107, file: !236, line: 62) +!256 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !112, file: !236, line: 63) +!257 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !116, file: !236, line: 64) +!258 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !120, file: !236, line: 65) +!259 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !124, file: !236, line: 67) +!260 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !128, file: !236, line: 68) +!261 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !136, file: !236, line: 69) +!262 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !140, file: !236, line: 71) +!263 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !146, file: !236, line: 72) +!264 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !150, file: !236, line: 73) +!265 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !154, file: !236, line: 74) +!266 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !158, file: !236, line: 75) +!267 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !164, file: !236, line: 76) +!268 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !168, file: !236, line: 77) +!269 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !172, file: !236, line: 78) +!270 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !174, file: !236, line: 80) +!271 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !182, file: !236, line: 81) +!272 = !{i32 7, !"Dwarf Version", i32 4} +!273 = !{i32 2, !"Debug Info Version", i32 3} +!274 = !{i32 1, !"wchar_size", i32 4} +!275 = !{i32 7, !"uwtable", i32 1} +!276 = !{i32 7, !"frame-pointer", i32 2} +!277 = !{!"Ubuntu clang version 13.0.1-2ubuntu2.2"} +!278 = distinct !DISubprogram(name: "shift64RightJamming", linkageName: "_Z19shift64RightJammingyiPy", scope: !21, file: !21, line: 60, type: !279, scopeLine: 61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!279 = !DISubroutineType(types: !280) +!280 = !{null, !6, !281, !282} +!281 = !DIDerivedType(tag: DW_TAG_typedef, name: "int16", file: !7, line: 60, baseType: !17) +!282 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!283 = !DILocalVariable(name: "a", arg: 1, scope: !278, file: !21, line: 60, type: !6) +!284 = !DILocation(line: 0, scope: !278) +!285 = !DILocalVariable(name: "count", arg: 2, scope: !278, file: !21, line: 60, type: !281) +!286 = !DILocalVariable(name: "zPtr", arg: 3, scope: !278, file: !21, line: 60, type: !282) +!287 = !DILocation(line: 64, column: 13, scope: !288) +!288 = distinct !DILexicalBlock(scope: !278, file: !21, line: 64, column: 7) +!289 = !DILocation(line: 64, column: 7, scope: !278) +!290 = !DILocalVariable(name: "z", scope: !278, file: !21, line: 62, type: !6) +!291 = !DILocation(line: 67, column: 5, scope: !292) +!292 = distinct !DILexicalBlock(scope: !288, file: !21, line: 65, column: 5) +!293 = !DILocation(line: 68, column: 18, scope: !294) +!294 = distinct !DILexicalBlock(scope: !288, file: !21, line: 68, column: 12) +!295 = !DILocation(line: 68, column: 12, scope: !288) +!296 = !DILocation(line: 70, column: 14, scope: !297) +!297 = distinct !DILexicalBlock(scope: !294, file: !21, line: 69, column: 5) +!298 = !DILocation(line: 70, column: 35, scope: !297) +!299 = !DILocation(line: 70, column: 43, scope: !297) +!300 = !DILocation(line: 70, column: 30, scope: !297) +!301 = !DILocation(line: 70, column: 50, scope: !297) +!302 = !DILocation(line: 70, column: 26, scope: !297) +!303 = !DILocation(line: 70, column: 24, scope: !297) +!304 = !DILocation(line: 71, column: 5, scope: !297) +!305 = !DILocation(line: 74, column: 14, scope: !306) +!306 = distinct !DILexicalBlock(scope: !294, file: !21, line: 73, column: 5) +!307 = !DILocation(line: 74, column: 11, scope: !306) +!308 = !DILocation(line: 0, scope: !294) +!309 = !DILocation(line: 0, scope: !288) +!310 = !DILocation(line: 76, column: 9, scope: !278) +!311 = !DILocation(line: 78, column: 1, scope: !278) +!312 = distinct !DISubprogram(name: "add128", linkageName: "_Z6add128yyyyPyS_", scope: !21, file: !21, line: 88, type: !313, scopeLine: 90, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!313 = !DISubroutineType(types: !314) +!314 = !{null, !6, !6, !6, !6, !282, !282} +!315 = !DILocalVariable(name: "a0", arg: 1, scope: !312, file: !21, line: 88, type: !6) +!316 = !DILocation(line: 0, scope: !312) +!317 = !DILocalVariable(name: "a1", arg: 2, scope: !312, file: !21, line: 88, type: !6) +!318 = !DILocalVariable(name: "b0", arg: 3, scope: !312, file: !21, line: 88, type: !6) +!319 = !DILocalVariable(name: "b1", arg: 4, scope: !312, file: !21, line: 88, type: !6) +!320 = !DILocalVariable(name: "z0Ptr", arg: 5, scope: !312, file: !21, line: 88, type: !282) +!321 = !DILocalVariable(name: "z1Ptr", arg: 6, scope: !312, file: !21, line: 89, type: !282) +!322 = !DILocation(line: 93, column: 11, scope: !312) +!323 = !DILocalVariable(name: "z1", scope: !312, file: !21, line: 91, type: !6) +!324 = !DILocation(line: 94, column: 10, scope: !312) +!325 = !DILocation(line: 95, column: 15, scope: !312) +!326 = !DILocation(line: 95, column: 26, scope: !312) +!327 = !DILocation(line: 95, column: 22, scope: !312) +!328 = !DILocation(line: 95, column: 20, scope: !312) +!329 = !DILocation(line: 95, column: 10, scope: !312) +!330 = !DILocation(line: 97, column: 1, scope: !312) +!331 = distinct !DISubprogram(name: "sub128", linkageName: "_Z6sub128yyyyPyS_", scope: !21, file: !21, line: 108, type: !313, scopeLine: 110, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!332 = !DILocalVariable(name: "a0", arg: 1, scope: !331, file: !21, line: 108, type: !6) +!333 = !DILocation(line: 0, scope: !331) +!334 = !DILocalVariable(name: "a1", arg: 2, scope: !331, file: !21, line: 108, type: !6) +!335 = !DILocalVariable(name: "b0", arg: 3, scope: !331, file: !21, line: 108, type: !6) +!336 = !DILocalVariable(name: "b1", arg: 4, scope: !331, file: !21, line: 108, type: !6) +!337 = !DILocalVariable(name: "z0Ptr", arg: 5, scope: !331, file: !21, line: 108, type: !282) +!338 = !DILocalVariable(name: "z1Ptr", arg: 6, scope: !331, file: !21, line: 109, type: !282) +!339 = !DILocation(line: 112, column: 15, scope: !331) +!340 = !DILocation(line: 112, column: 10, scope: !331) +!341 = !DILocation(line: 113, column: 15, scope: !331) +!342 = !DILocation(line: 113, column: 26, scope: !331) +!343 = !DILocation(line: 113, column: 22, scope: !331) +!344 = !DILocation(line: 113, column: 20, scope: !331) +!345 = !DILocation(line: 113, column: 10, scope: !331) +!346 = !DILocation(line: 115, column: 1, scope: !331) +!347 = distinct !DISubprogram(name: "mul64To128", linkageName: "_Z10mul64To128yyPyS_", scope: !21, file: !21, line: 124, type: !348, scopeLine: 125, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!348 = !DISubroutineType(types: !349) +!349 = !{null, !6, !6, !282, !282} +!350 = !DILocalVariable(name: "a", arg: 1, scope: !347, file: !21, line: 124, type: !6) +!351 = !DILocation(line: 0, scope: !347) +!352 = !DILocalVariable(name: "b", arg: 2, scope: !347, file: !21, line: 124, type: !6) +!353 = !DILocalVariable(name: "z0Ptr", arg: 3, scope: !347, file: !21, line: 124, type: !282) +!354 = !DILocalVariable(name: "z1Ptr", arg: 4, scope: !347, file: !21, line: 124, type: !282) +!355 = !DILocation(line: 129, column: 10, scope: !347) +!356 = !DILocalVariable(name: "aLow", scope: !347, file: !21, line: 126, type: !24) +!357 = !DILocation(line: 130, column: 13, scope: !347) +!358 = !DILocation(line: 130, column: 11, scope: !347) +!359 = !DILocalVariable(name: "aHigh", scope: !347, file: !21, line: 126, type: !24) +!360 = !DILocation(line: 131, column: 10, scope: !347) +!361 = !DILocalVariable(name: "bLow", scope: !347, file: !21, line: 126, type: !24) +!362 = !DILocation(line: 132, column: 13, scope: !347) +!363 = !DILocation(line: 132, column: 11, scope: !347) +!364 = !DILocalVariable(name: "bHigh", scope: !347, file: !21, line: 126, type: !24) +!365 = !DILocation(line: 133, column: 18, scope: !347) +!366 = !DILocation(line: 133, column: 26, scope: !347) +!367 = !DILocation(line: 133, column: 24, scope: !347) +!368 = !DILocalVariable(name: "z1", scope: !347, file: !21, line: 127, type: !6) +!369 = !DILocation(line: 134, column: 24, scope: !347) +!370 = !DILocation(line: 134, column: 32, scope: !347) +!371 = !DILocation(line: 134, column: 30, scope: !347) +!372 = !DILocalVariable(name: "zMiddleA", scope: !347, file: !21, line: 127, type: !6) +!373 = !DILocation(line: 135, column: 24, scope: !347) +!374 = !DILocation(line: 135, column: 33, scope: !347) +!375 = !DILocation(line: 135, column: 31, scope: !347) +!376 = !DILocalVariable(name: "zMiddleB", scope: !347, file: !21, line: 127, type: !6) +!377 = !DILocation(line: 136, column: 18, scope: !347) +!378 = !DILocation(line: 136, column: 27, scope: !347) +!379 = !DILocation(line: 136, column: 25, scope: !347) +!380 = !DILocalVariable(name: "z0", scope: !347, file: !21, line: 127, type: !6) +!381 = !DILocation(line: 137, column: 12, scope: !347) +!382 = !DILocation(line: 138, column: 30, scope: !347) +!383 = !DILocation(line: 138, column: 20, scope: !347) +!384 = !DILocation(line: 138, column: 43, scope: !347) +!385 = !DILocation(line: 138, column: 62, scope: !347) +!386 = !DILocation(line: 138, column: 50, scope: !347) +!387 = !DILocation(line: 138, column: 6, scope: !347) +!388 = !DILocation(line: 139, column: 12, scope: !347) +!389 = !DILocation(line: 140, column: 6, scope: !347) +!390 = !DILocation(line: 141, column: 13, scope: !347) +!391 = !DILocation(line: 141, column: 9, scope: !347) +!392 = !DILocation(line: 141, column: 6, scope: !347) +!393 = !DILocation(line: 142, column: 10, scope: !347) +!394 = !DILocation(line: 143, column: 10, scope: !347) +!395 = !DILocation(line: 145, column: 1, scope: !347) +!396 = distinct !DISubprogram(name: "float_raise", linkageName: "_Z11float_raisei", scope: !397, file: !397, line: 64, type: !398, scopeLine: 65, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!397 = !DIFile(filename: "dfdiv/include/softfloat-specialize", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!398 = !DISubroutineType(types: !399) +!399 = !{null, !16} +!400 = !DILocalVariable(name: "flags", arg: 1, scope: !396, file: !397, line: 64, type: !16) +!401 = !DILocation(line: 0, scope: !396) +!402 = !DILocation(line: 66, column: 25, scope: !396) +!403 = !DILocation(line: 68, column: 1, scope: !396) +!404 = distinct !DISubprogram(name: "float64_is_nan", linkageName: "_Z14float64_is_nany", scope: !397, file: !397, line: 82, type: !405, scopeLine: 83, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!405 = !DISubroutineType(types: !406) +!406 = !{!407, !408} +!407 = !DIDerivedType(tag: DW_TAG_typedef, name: "flag", file: !7, line: 58, baseType: !17) +!408 = !DIDerivedType(tag: DW_TAG_typedef, name: "float64", file: !409, line: 54, baseType: !8) +!409 = !DIFile(filename: "dfdiv/include/softfloat.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!410 = !DILocalVariable(name: "a", arg: 1, scope: !404, file: !397, line: 82, type: !408) +!411 = !DILocation(line: 0, scope: !404) +!412 = !DILocation(line: 85, column: 52, scope: !404) +!413 = !DILocation(line: 85, column: 38, scope: !404) +!414 = !DILocation(line: 85, column: 10, scope: !404) +!415 = !DILocation(line: 85, column: 3, scope: !404) +!416 = distinct !DISubprogram(name: "float64_is_signaling_nan", linkageName: "_Z24float64_is_signaling_nany", scope: !397, file: !397, line: 95, type: !405, scopeLine: 96, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!417 = !DILocalVariable(name: "a", arg: 1, scope: !416, file: !397, line: 95, type: !408) +!418 = !DILocation(line: 0, scope: !416) +!419 = !DILocation(line: 98, column: 15, scope: !416) +!420 = !DILocation(line: 98, column: 22, scope: !416) +!421 = !DILocation(line: 98, column: 31, scope: !416) +!422 = !DILocation(line: 98, column: 41, scope: !416) +!423 = !DILocation(line: 98, column: 47, scope: !416) +!424 = !DILocation(line: 98, column: 44, scope: !416) +!425 = !DILocation(line: 98, column: 10, scope: !416) +!426 = !DILocation(line: 98, column: 3, scope: !416) +!427 = distinct !DISubprogram(name: "extractFloat64Frac", scope: !3, file: !3, line: 87, type: !428, scopeLine: 88, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!428 = !DISubroutineType(types: !429) +!429 = !{!6, !408} +!430 = !DILocalVariable(name: "a", arg: 1, scope: !427, file: !3, line: 87, type: !408) +!431 = !DILocation(line: 0, scope: !427) +!432 = !DILocation(line: 89, column: 12, scope: !427) +!433 = !DILocation(line: 89, column: 3, scope: !427) +!434 = distinct !DISubprogram(name: "extractFloat64Exp", scope: !3, file: !3, line: 102, type: !435, scopeLine: 103, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!435 = !DISubroutineType(types: !436) +!436 = !{!281, !408} +!437 = !DILocalVariable(name: "a", arg: 1, scope: !434, file: !3, line: 102, type: !408) +!438 = !DILocation(line: 0, scope: !434) +!439 = !DILocation(line: 104, column: 13, scope: !434) +!440 = !DILocation(line: 104, column: 20, scope: !434) +!441 = !DILocation(line: 104, column: 10, scope: !434) +!442 = !DILocation(line: 104, column: 3, scope: !434) +!443 = distinct !DISubprogram(name: "extractFloat64Sign", scope: !3, file: !3, line: 117, type: !405, scopeLine: 118, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!444 = !DILocalVariable(name: "a", arg: 1, scope: !443, file: !3, line: 117, type: !408) +!445 = !DILocation(line: 0, scope: !443) +!446 = !DILocation(line: 120, column: 12, scope: !443) +!447 = !DILocation(line: 120, column: 10, scope: !443) +!448 = !DILocation(line: 120, column: 3, scope: !443) +!449 = distinct !DISubprogram(name: "normalizeFloat64Subnormal", linkageName: "_Z25normalizeFloat64SubnormalyPiPy", scope: !3, file: !3, line: 134, type: !450, scopeLine: 135, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!450 = !DISubroutineType(types: !451) +!451 = !{null, !6, !452, !282} +!452 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !281, size: 64) +!453 = !DILocalVariable(name: "aSig", arg: 1, scope: !449, file: !3, line: 134, type: !6) +!454 = !DILocation(line: 0, scope: !449) +!455 = !DILocalVariable(name: "zExpPtr", arg: 2, scope: !449, file: !3, line: 134, type: !452) +!456 = !DILocalVariable(name: "zSigPtr", arg: 3, scope: !449, file: !3, line: 134, type: !282) +!457 = !DILocation(line: 138, column: 16, scope: !449) +!458 = !DILocation(line: 138, column: 43, scope: !449) +!459 = !DILocalVariable(name: "shiftCount", scope: !449, file: !3, line: 136, type: !16) +!460 = !DILocation(line: 139, column: 19, scope: !449) +!461 = !DILocation(line: 139, column: 12, scope: !449) +!462 = !DILocation(line: 140, column: 16, scope: !449) +!463 = !DILocation(line: 140, column: 12, scope: !449) +!464 = !DILocation(line: 141, column: 1, scope: !449) +!465 = distinct !DISubprogram(name: "countLeadingZeros64", linkageName: "_ZL19countLeadingZeros64y", scope: !21, file: !21, line: 231, type: !466, scopeLine: 232, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!466 = !DISubroutineType(types: !467) +!467 = !{!16, !6} +!468 = !DILocalVariable(name: "a", arg: 1, scope: !465, file: !21, line: 231, type: !6) +!469 = !DILocation(line: 0, scope: !465) +!470 = !DILocalVariable(name: "shiftCount", scope: !465, file: !21, line: 233, type: !16) +!471 = !DILocation(line: 236, column: 9, scope: !472) +!472 = distinct !DILexicalBlock(scope: !465, file: !21, line: 236, column: 7) +!473 = !DILocation(line: 236, column: 7, scope: !465) +!474 = !DILocation(line: 238, column: 18, scope: !475) +!475 = distinct !DILexicalBlock(scope: !472, file: !21, line: 237, column: 5) +!476 = !DILocation(line: 239, column: 5, scope: !475) +!477 = !DILocation(line: 242, column: 9, scope: !478) +!478 = distinct !DILexicalBlock(scope: !472, file: !21, line: 241, column: 5) +!479 = !DILocation(line: 244, column: 38, scope: !465) +!480 = !DILocation(line: 244, column: 17, scope: !465) +!481 = !DILocation(line: 244, column: 14, scope: !465) +!482 = !DILocation(line: 245, column: 3, scope: !465) +!483 = distinct !DISubprogram(name: "packFloat64", scope: !3, file: !3, line: 157, type: !484, scopeLine: 158, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!484 = !DISubroutineType(types: !485) +!485 = !{!408, !407, !281, !6} +!486 = !DILocalVariable(name: "zSign", arg: 1, scope: !483, file: !3, line: 157, type: !407) +!487 = !DILocation(line: 0, scope: !483) +!488 = !DILocalVariable(name: "zExp", arg: 2, scope: !483, file: !3, line: 157, type: !281) +!489 = !DILocalVariable(name: "zSig", arg: 3, scope: !483, file: !3, line: 157, type: !6) +!490 = !DILocation(line: 159, column: 21, scope: !483) +!491 = !DILocation(line: 159, column: 28, scope: !483) +!492 = !DILocation(line: 159, column: 48, scope: !483) +!493 = !DILocation(line: 159, column: 54, scope: !483) +!494 = !DILocation(line: 159, column: 35, scope: !483) +!495 = !DILocation(line: 159, column: 61, scope: !483) +!496 = !DILocation(line: 159, column: 3, scope: !483) +!497 = distinct !DISubprogram(name: "roundAndPackFloat64", scope: !3, file: !3, line: 190, type: !484, scopeLine: 191, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!498 = !DILocalVariable(name: "zSign", arg: 1, scope: !497, file: !3, line: 190, type: !407) +!499 = !DILocation(line: 0, scope: !497) +!500 = !DILocalVariable(name: "zExp", arg: 2, scope: !497, file: !3, line: 190, type: !281) +!501 = !DILocalVariable(name: "zSig", arg: 3, scope: !497, file: !3, line: 190, type: !6) +!502 = !DILocation(line: 190, column: 61, scope: !497) +!503 = !DILocation(line: 196, column: 18, scope: !497) +!504 = !DILocalVariable(name: "roundingMode", scope: !497, file: !3, line: 192, type: !16) +!505 = !DILocation(line: 197, column: 36, scope: !497) +!506 = !DILocation(line: 197, column: 22, scope: !497) +!507 = !DILocalVariable(name: "roundNearestEven", scope: !497, file: !3, line: 193, type: !407) +!508 = !DILocalVariable(name: "roundIncrement", scope: !497, file: !3, line: 194, type: !281) +!509 = !DILocation(line: 199, column: 8, scope: !510) +!510 = distinct !DILexicalBlock(scope: !497, file: !3, line: 199, column: 7) +!511 = !DILocation(line: 199, column: 7, scope: !497) +!512 = !DILocation(line: 201, column: 24, scope: !513) +!513 = distinct !DILexicalBlock(scope: !514, file: !3, line: 201, column: 11) +!514 = distinct !DILexicalBlock(scope: !510, file: !3, line: 200, column: 5) +!515 = !DILocation(line: 201, column: 11, scope: !514) +!516 = !DILocation(line: 204, column: 2, scope: !517) +!517 = distinct !DILexicalBlock(scope: !513, file: !3, line: 202, column: 2) +!518 = !DILocation(line: 208, column: 8, scope: !519) +!519 = distinct !DILexicalBlock(scope: !520, file: !3, line: 208, column: 8) +!520 = distinct !DILexicalBlock(scope: !513, file: !3, line: 206, column: 2) +!521 = !DILocation(line: 208, column: 8, scope: !520) +!522 = !DILocation(line: 210, column: 25, scope: !523) +!523 = distinct !DILexicalBlock(scope: !524, file: !3, line: 210, column: 12) +!524 = distinct !DILexicalBlock(scope: !519, file: !3, line: 209, column: 6) +!525 = !DILocation(line: 210, column: 12, scope: !524) +!526 = !DILocation(line: 211, column: 3, scope: !523) +!527 = !DILocation(line: 0, scope: !520) +!528 = !DILocation(line: 212, column: 6, scope: !524) +!529 = !DILocation(line: 215, column: 25, scope: !530) +!530 = distinct !DILexicalBlock(scope: !531, file: !3, line: 215, column: 12) +!531 = distinct !DILexicalBlock(scope: !519, file: !3, line: 214, column: 6) +!532 = !DILocation(line: 215, column: 12, scope: !531) +!533 = !DILocation(line: 216, column: 3, scope: !530) +!534 = !DILocation(line: 0, scope: !519) +!535 = !DILocation(line: 0, scope: !513) +!536 = !DILocation(line: 219, column: 5, scope: !514) +!537 = !DILocation(line: 220, column: 15, scope: !497) +!538 = !DILocation(line: 220, column: 20, scope: !497) +!539 = !DILocalVariable(name: "roundBits", scope: !497, file: !3, line: 194, type: !281) +!540 = !DILocation(line: 221, column: 25, scope: !541) +!541 = distinct !DILexicalBlock(scope: !497, file: !3, line: 221, column: 7) +!542 = !DILocation(line: 221, column: 16, scope: !541) +!543 = !DILocation(line: 221, column: 13, scope: !541) +!544 = !DILocation(line: 221, column: 7, scope: !497) +!545 = !DILocation(line: 223, column: 18, scope: !546) +!546 = distinct !DILexicalBlock(scope: !547, file: !3, line: 223, column: 11) +!547 = distinct !DILexicalBlock(scope: !541, file: !3, line: 222, column: 5) +!548 = !DILocation(line: 224, column: 4, scope: !546) +!549 = !DILocation(line: 224, column: 14, scope: !546) +!550 = !DILocation(line: 224, column: 24, scope: !546) +!551 = !DILocation(line: 224, column: 39, scope: !546) +!552 = !DILocation(line: 224, column: 46, scope: !546) +!553 = !DILocation(line: 224, column: 44, scope: !546) +!554 = !DILocation(line: 224, column: 62, scope: !546) +!555 = !DILocation(line: 223, column: 11, scope: !547) +!556 = !DILocation(line: 226, column: 4, scope: !557) +!557 = distinct !DILexicalBlock(scope: !546, file: !3, line: 225, column: 2) +!558 = !DILocation(line: 0, scope: !483, inlinedAt: !559) +!559 = distinct !DILocation(line: 227, column: 11, scope: !557) +!560 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !559) +!561 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !559) +!562 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !559) +!563 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !559) +!564 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !559) +!565 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !559) +!566 = !DILocation(line: 227, column: 59, scope: !557) +!567 = !DILocation(line: 227, column: 43, scope: !557) +!568 = !DILocation(line: 227, column: 41, scope: !557) +!569 = !DILocation(line: 227, column: 4, scope: !557) +!570 = !DILocation(line: 229, column: 16, scope: !571) +!571 = distinct !DILexicalBlock(scope: !547, file: !3, line: 229, column: 11) +!572 = !DILocation(line: 229, column: 11, scope: !547) +!573 = !DILocalVariable(name: "isTiny", scope: !497, file: !3, line: 193, type: !407) +!574 = !DILocation(line: 234, column: 25, scope: !575) +!575 = distinct !DILexicalBlock(scope: !571, file: !3, line: 230, column: 2) +!576 = !DILocation(line: 234, column: 31, scope: !575) +!577 = !DILocation(line: 234, column: 4, scope: !575) +!578 = !DILocation(line: 236, column: 16, scope: !575) +!579 = !DILocation(line: 236, column: 21, scope: !575) +!580 = !DILocation(line: 237, column: 8, scope: !581) +!581 = distinct !DILexicalBlock(scope: !575, file: !3, line: 237, column: 8) +!582 = !DILocation(line: 237, column: 15, scope: !581) +!583 = !DILocation(line: 237, column: 18, scope: !581) +!584 = !DILocation(line: 237, column: 8, scope: !575) +!585 = !DILocation(line: 238, column: 6, scope: !581) +!586 = !DILocation(line: 239, column: 2, scope: !575) +!587 = !DILocation(line: 240, column: 5, scope: !547) +!588 = !DILocation(line: 241, column: 7, scope: !589) +!589 = distinct !DILexicalBlock(scope: !497, file: !3, line: 241, column: 7) +!590 = !DILocation(line: 241, column: 7, scope: !497) +!591 = !DILocation(line: 242, column: 27, scope: !589) +!592 = !DILocation(line: 242, column: 5, scope: !589) +!593 = !DILocation(line: 243, column: 11, scope: !497) +!594 = !DILocation(line: 243, column: 18, scope: !497) +!595 = !DILocation(line: 243, column: 16, scope: !497) +!596 = !DILocation(line: 243, column: 34, scope: !497) +!597 = !DILocation(line: 243, column: 8, scope: !497) +!598 = !DILocation(line: 244, column: 25, scope: !497) +!599 = !DILocation(line: 244, column: 34, scope: !497) +!600 = !DILocation(line: 244, column: 13, scope: !497) +!601 = !DILocation(line: 244, column: 40, scope: !497) +!602 = !DILocation(line: 244, column: 11, scope: !497) +!603 = !DILocation(line: 244, column: 8, scope: !497) +!604 = !DILocation(line: 245, column: 7, scope: !605) +!605 = distinct !DILexicalBlock(scope: !497, file: !3, line: 245, column: 7) +!606 = !DILocation(line: 245, column: 12, scope: !605) +!607 = !DILocation(line: 245, column: 7, scope: !497) +!608 = !DILocation(line: 246, column: 5, scope: !605) +!609 = !DILocation(line: 247, column: 36, scope: !497) +!610 = !DILocation(line: 0, scope: !483, inlinedAt: !611) +!611 = distinct !DILocation(line: 247, column: 10, scope: !497) +!612 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !611) +!613 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !611) +!614 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !611) +!615 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !611) +!616 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !611) +!617 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !611) +!618 = !DILocation(line: 247, column: 3, scope: !497) +!619 = !DILocation(line: 249, column: 1, scope: !497) +!620 = distinct !DISubprogram(name: "float64_div", scope: !3, file: !3, line: 273, type: !621, scopeLine: 274, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!621 = !DISubroutineType(types: !622) +!622 = !{!408, !623, !624} +!623 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055xAcceleration", file: !3, line: 263, baseType: !408) +!624 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055yAcceleration", file: !3, line: 264, baseType: !408) +!625 = !DILocalVariable(name: "a", arg: 1, scope: !620, file: !3, line: 273, type: !623) +!626 = !DILocation(line: 0, scope: !620) +!627 = !DILocalVariable(name: "b", arg: 2, scope: !620, file: !3, line: 273, type: !624) +!628 = !DILocalVariable(name: "rem0", scope: !620, file: !3, line: 289, type: !6) +!629 = !DILocation(line: 289, column: 10, scope: !620) +!630 = !DILocalVariable(name: "rem1", scope: !620, file: !3, line: 289, type: !6) +!631 = !DILocation(line: 289, column: 16, scope: !620) +!632 = !DILocalVariable(name: "term0", scope: !620, file: !3, line: 289, type: !6) +!633 = !DILocation(line: 289, column: 22, scope: !620) +!634 = !DILocalVariable(name: "term1", scope: !620, file: !3, line: 289, type: !6) +!635 = !DILocation(line: 289, column: 29, scope: !620) +!636 = !DILocation(line: 0, scope: !427, inlinedAt: !637) +!637 = distinct !DILocation(line: 291, column: 10, scope: !620) +!638 = !DILocation(line: 89, column: 12, scope: !427, inlinedAt: !637) +!639 = !DILocalVariable(name: "aSig", scope: !620, file: !3, line: 288, type: !6) +!640 = !DILocation(line: 0, scope: !434, inlinedAt: !641) +!641 = distinct !DILocation(line: 292, column: 10, scope: !620) +!642 = !DILocation(line: 104, column: 13, scope: !434, inlinedAt: !641) +!643 = !DILocation(line: 104, column: 20, scope: !434, inlinedAt: !641) +!644 = !DILocation(line: 104, column: 10, scope: !434, inlinedAt: !641) +!645 = !DILocalVariable(name: "aExp", scope: !620, file: !3, line: 287, type: !281) +!646 = !DILocation(line: 0, scope: !443, inlinedAt: !647) +!647 = distinct !DILocation(line: 293, column: 11, scope: !620) +!648 = !DILocation(line: 120, column: 12, scope: !443, inlinedAt: !647) +!649 = !DILocation(line: 120, column: 10, scope: !443, inlinedAt: !647) +!650 = !DILocalVariable(name: "aSign", scope: !620, file: !3, line: 286, type: !407) +!651 = !DILocation(line: 0, scope: !427, inlinedAt: !652) +!652 = distinct !DILocation(line: 294, column: 10, scope: !620) +!653 = !DILocation(line: 89, column: 12, scope: !427, inlinedAt: !652) +!654 = !DILocalVariable(name: "bSig", scope: !620, file: !3, line: 288, type: !6) +!655 = !DILocation(line: 0, scope: !434, inlinedAt: !656) +!656 = distinct !DILocation(line: 295, column: 10, scope: !620) +!657 = !DILocation(line: 104, column: 13, scope: !434, inlinedAt: !656) +!658 = !DILocation(line: 104, column: 20, scope: !434, inlinedAt: !656) +!659 = !DILocation(line: 104, column: 10, scope: !434, inlinedAt: !656) +!660 = !DILocalVariable(name: "bExp", scope: !620, file: !3, line: 287, type: !281) +!661 = !DILocation(line: 0, scope: !443, inlinedAt: !662) +!662 = distinct !DILocation(line: 296, column: 11, scope: !620) +!663 = !DILocation(line: 120, column: 12, scope: !443, inlinedAt: !662) +!664 = !DILocation(line: 120, column: 10, scope: !443, inlinedAt: !662) +!665 = !DILocalVariable(name: "bSign", scope: !620, file: !3, line: 286, type: !407) +!666 = !DILocation(line: 297, column: 17, scope: !620) +!667 = !DILocalVariable(name: "zSign", scope: !620, file: !3, line: 286, type: !407) +!668 = !DILocation(line: 298, column: 12, scope: !669) +!669 = distinct !DILexicalBlock(scope: !620, file: !3, line: 298, column: 7) +!670 = !DILocation(line: 298, column: 7, scope: !620) +!671 = !DILocation(line: 300, column: 11, scope: !672) +!672 = distinct !DILexicalBlock(scope: !673, file: !3, line: 300, column: 11) +!673 = distinct !DILexicalBlock(scope: !669, file: !3, line: 299, column: 5) +!674 = !DILocation(line: 300, column: 11, scope: !673) +!675 = !DILocation(line: 301, column: 9, scope: !672) +!676 = !DILocation(line: 301, column: 2, scope: !672) +!677 = !DILocation(line: 302, column: 16, scope: !678) +!678 = distinct !DILexicalBlock(scope: !673, file: !3, line: 302, column: 11) +!679 = !DILocation(line: 302, column: 11, scope: !673) +!680 = !DILocation(line: 304, column: 8, scope: !681) +!681 = distinct !DILexicalBlock(scope: !682, file: !3, line: 304, column: 8) +!682 = distinct !DILexicalBlock(scope: !678, file: !3, line: 303, column: 2) +!683 = !DILocation(line: 304, column: 8, scope: !682) +!684 = !DILocation(line: 305, column: 13, scope: !681) +!685 = !DILocation(line: 305, column: 6, scope: !681) +!686 = !DILocation(line: 306, column: 4, scope: !682) +!687 = !DILocation(line: 307, column: 4, scope: !682) +!688 = !DILocation(line: 0, scope: !483, inlinedAt: !689) +!689 = distinct !DILocation(line: 309, column: 14, scope: !673) +!690 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !689) +!691 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !689) +!692 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !689) +!693 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !689) +!694 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !689) +!695 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !689) +!696 = !DILocation(line: 309, column: 7, scope: !673) +!697 = !DILocation(line: 311, column: 12, scope: !698) +!698 = distinct !DILexicalBlock(scope: !620, file: !3, line: 311, column: 7) +!699 = !DILocation(line: 311, column: 7, scope: !620) +!700 = !DILocation(line: 313, column: 11, scope: !701) +!701 = distinct !DILexicalBlock(scope: !702, file: !3, line: 313, column: 11) +!702 = distinct !DILexicalBlock(scope: !698, file: !3, line: 312, column: 5) +!703 = !DILocation(line: 313, column: 11, scope: !702) +!704 = !DILocation(line: 314, column: 9, scope: !701) +!705 = !DILocation(line: 314, column: 2, scope: !701) +!706 = !DILocation(line: 0, scope: !483, inlinedAt: !707) +!707 = distinct !DILocation(line: 315, column: 14, scope: !702) +!708 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !707) +!709 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !707) +!710 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !707) +!711 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !707) +!712 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !707) +!713 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !707) +!714 = !DILocation(line: 315, column: 7, scope: !702) +!715 = !DILocation(line: 317, column: 12, scope: !716) +!716 = distinct !DILexicalBlock(scope: !620, file: !3, line: 317, column: 7) +!717 = !DILocation(line: 317, column: 7, scope: !620) +!718 = !DILocation(line: 319, column: 16, scope: !719) +!719 = distinct !DILexicalBlock(scope: !720, file: !3, line: 319, column: 11) +!720 = distinct !DILexicalBlock(scope: !716, file: !3, line: 318, column: 5) +!721 = !DILocation(line: 319, column: 11, scope: !720) +!722 = !DILocation(line: 321, column: 9, scope: !723) +!723 = distinct !DILexicalBlock(scope: !724, file: !3, line: 321, column: 8) +!724 = distinct !DILexicalBlock(scope: !719, file: !3, line: 320, column: 2) +!725 = !DILocation(line: 321, column: 14, scope: !723) +!726 = !DILocation(line: 321, column: 22, scope: !723) +!727 = !DILocation(line: 321, column: 8, scope: !724) +!728 = !DILocation(line: 323, column: 8, scope: !729) +!729 = distinct !DILexicalBlock(scope: !723, file: !3, line: 322, column: 6) +!730 = !DILocation(line: 324, column: 8, scope: !729) +!731 = !DILocation(line: 326, column: 4, scope: !724) +!732 = !DILocation(line: 0, scope: !483, inlinedAt: !733) +!733 = distinct !DILocation(line: 327, column: 11, scope: !724) +!734 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !733) +!735 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !733) +!736 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !733) +!737 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !733) +!738 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !733) +!739 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !733) +!740 = !DILocation(line: 327, column: 4, scope: !724) +!741 = !DILocation(line: 0, scope: !449, inlinedAt: !742) +!742 = distinct !DILocation(line: 329, column: 7, scope: !720) +!743 = !DILocation(line: 138, column: 16, scope: !449, inlinedAt: !742) +!744 = !DILocation(line: 138, column: 43, scope: !449, inlinedAt: !742) +!745 = !DILocation(line: 139, column: 19, scope: !449, inlinedAt: !742) +!746 = !DILocation(line: 140, column: 16, scope: !449, inlinedAt: !742) +!747 = !DILocation(line: 330, column: 5, scope: !720) +!748 = !DILocation(line: 331, column: 12, scope: !749) +!749 = distinct !DILexicalBlock(scope: !620, file: !3, line: 331, column: 7) +!750 = !DILocation(line: 331, column: 7, scope: !620) +!751 = !DILocation(line: 333, column: 16, scope: !752) +!752 = distinct !DILexicalBlock(scope: !753, file: !3, line: 333, column: 11) +!753 = distinct !DILexicalBlock(scope: !749, file: !3, line: 332, column: 5) +!754 = !DILocation(line: 333, column: 11, scope: !753) +!755 = !DILocation(line: 0, scope: !483, inlinedAt: !756) +!756 = distinct !DILocation(line: 334, column: 9, scope: !752) +!757 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !756) +!758 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !756) +!759 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !756) +!760 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !756) +!761 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !756) +!762 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !756) +!763 = !DILocation(line: 334, column: 2, scope: !752) +!764 = !DILocation(line: 0, scope: !449, inlinedAt: !765) +!765 = distinct !DILocation(line: 335, column: 7, scope: !753) +!766 = !DILocation(line: 138, column: 16, scope: !449, inlinedAt: !765) +!767 = !DILocation(line: 138, column: 43, scope: !449, inlinedAt: !765) +!768 = !DILocation(line: 139, column: 19, scope: !449, inlinedAt: !765) +!769 = !DILocation(line: 140, column: 16, scope: !449, inlinedAt: !765) +!770 = !DILocation(line: 336, column: 5, scope: !753) +!771 = !DILocation(line: 337, column: 15, scope: !620) +!772 = !DILocation(line: 337, column: 22, scope: !620) +!773 = !DILocalVariable(name: "zExp", scope: !620, file: !3, line: 287, type: !281) +!774 = !DILocation(line: 338, column: 16, scope: !620) +!775 = !DILocation(line: 338, column: 46, scope: !620) +!776 = !DILocation(line: 339, column: 16, scope: !620) +!777 = !DILocation(line: 339, column: 46, scope: !620) +!778 = !DILocation(line: 340, column: 21, scope: !779) +!779 = distinct !DILexicalBlock(scope: !620, file: !3, line: 340, column: 7) +!780 = !DILocation(line: 340, column: 12, scope: !779) +!781 = !DILocation(line: 340, column: 7, scope: !620) +!782 = !DILocation(line: 342, column: 12, scope: !783) +!783 = distinct !DILexicalBlock(scope: !779, file: !3, line: 341, column: 5) +!784 = !DILocation(line: 343, column: 7, scope: !783) +!785 = !DILocation(line: 344, column: 5, scope: !783) +!786 = !DILocation(line: 345, column: 10, scope: !620) +!787 = !DILocalVariable(name: "zSig", scope: !620, file: !3, line: 288, type: !6) +!788 = !DILocation(line: 346, column: 13, scope: !789) +!789 = distinct !DILexicalBlock(scope: !620, file: !3, line: 346, column: 7) +!790 = !DILocation(line: 346, column: 22, scope: !789) +!791 = !DILocation(line: 346, column: 7, scope: !620) +!792 = !DILocation(line: 348, column: 7, scope: !793) +!793 = distinct !DILexicalBlock(scope: !789, file: !3, line: 347, column: 5) +!794 = !DILocation(line: 349, column: 24, scope: !793) +!795 = !DILocation(line: 349, column: 31, scope: !793) +!796 = !DILocation(line: 349, column: 7, scope: !793) +!797 = !DILocation(line: 350, column: 7, scope: !793) +!798 = !DILocation(line: 350, column: 24, scope: !793) +!799 = !DILocation(line: 350, column: 29, scope: !793) +!800 = !DILocation(line: 352, column: 4, scope: !801) +!801 = distinct !DILexicalBlock(scope: !793, file: !3, line: 351, column: 2) +!802 = !DILocation(line: 353, column: 12, scope: !801) +!803 = !DILocation(line: 353, column: 18, scope: !801) +!804 = !DILocation(line: 353, column: 4, scope: !801) +!805 = distinct !{!805, !797, !806, !807} +!806 = !DILocation(line: 354, column: 2, scope: !793) +!807 = !{!"llvm.loop.mustprogress"} +!808 = !DILocation(line: 355, column: 16, scope: !793) +!809 = !DILocation(line: 355, column: 21, scope: !793) +!810 = !DILocation(line: 355, column: 15, scope: !793) +!811 = !DILocation(line: 355, column: 12, scope: !793) +!812 = !DILocation(line: 356, column: 5, scope: !793) +!813 = !DILocation(line: 0, scope: !497, inlinedAt: !814) +!814 = distinct !DILocation(line: 357, column: 10, scope: !620) +!815 = !DILocation(line: 190, column: 61, scope: !497, inlinedAt: !814) +!816 = !DILocation(line: 196, column: 18, scope: !497, inlinedAt: !814) +!817 = !DILocation(line: 197, column: 36, scope: !497, inlinedAt: !814) +!818 = !DILocation(line: 197, column: 22, scope: !497, inlinedAt: !814) +!819 = !DILocation(line: 199, column: 8, scope: !510, inlinedAt: !814) +!820 = !DILocation(line: 199, column: 7, scope: !497, inlinedAt: !814) +!821 = !DILocation(line: 201, column: 24, scope: !513, inlinedAt: !814) +!822 = !DILocation(line: 201, column: 11, scope: !514, inlinedAt: !814) +!823 = !DILocation(line: 204, column: 2, scope: !517, inlinedAt: !814) +!824 = !DILocation(line: 208, column: 8, scope: !519, inlinedAt: !814) +!825 = !DILocation(line: 208, column: 8, scope: !520, inlinedAt: !814) +!826 = !DILocation(line: 210, column: 25, scope: !523, inlinedAt: !814) +!827 = !DILocation(line: 210, column: 12, scope: !524, inlinedAt: !814) +!828 = !DILocation(line: 211, column: 3, scope: !523, inlinedAt: !814) +!829 = !DILocation(line: 0, scope: !520, inlinedAt: !814) +!830 = !DILocation(line: 212, column: 6, scope: !524, inlinedAt: !814) +!831 = !DILocation(line: 215, column: 25, scope: !530, inlinedAt: !814) +!832 = !DILocation(line: 215, column: 12, scope: !531, inlinedAt: !814) +!833 = !DILocation(line: 216, column: 3, scope: !530, inlinedAt: !814) +!834 = !DILocation(line: 0, scope: !519, inlinedAt: !814) +!835 = !DILocation(line: 0, scope: !513, inlinedAt: !814) +!836 = !DILocation(line: 219, column: 5, scope: !514, inlinedAt: !814) +!837 = !DILocation(line: 220, column: 15, scope: !497, inlinedAt: !814) +!838 = !DILocation(line: 220, column: 20, scope: !497, inlinedAt: !814) +!839 = !DILocation(line: 221, column: 25, scope: !541, inlinedAt: !814) +!840 = !DILocation(line: 221, column: 16, scope: !541, inlinedAt: !814) +!841 = !DILocation(line: 221, column: 13, scope: !541, inlinedAt: !814) +!842 = !DILocation(line: 221, column: 7, scope: !497, inlinedAt: !814) +!843 = !DILocation(line: 223, column: 18, scope: !546, inlinedAt: !814) +!844 = !DILocation(line: 224, column: 4, scope: !546, inlinedAt: !814) +!845 = !DILocation(line: 224, column: 14, scope: !546, inlinedAt: !814) +!846 = !DILocation(line: 224, column: 24, scope: !546, inlinedAt: !814) +!847 = !DILocation(line: 224, column: 39, scope: !546, inlinedAt: !814) +!848 = !DILocation(line: 224, column: 46, scope: !546, inlinedAt: !814) +!849 = !DILocation(line: 224, column: 44, scope: !546, inlinedAt: !814) +!850 = !DILocation(line: 224, column: 62, scope: !546, inlinedAt: !814) +!851 = !DILocation(line: 223, column: 11, scope: !547, inlinedAt: !814) +!852 = !DILocation(line: 226, column: 4, scope: !557, inlinedAt: !814) +!853 = !DILocation(line: 0, scope: !483, inlinedAt: !854) +!854 = distinct !DILocation(line: 227, column: 11, scope: !557, inlinedAt: !814) +!855 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !854) +!856 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !854) +!857 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !854) +!858 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !854) +!859 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !854) +!860 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !854) +!861 = !DILocation(line: 227, column: 59, scope: !557, inlinedAt: !814) +!862 = !DILocation(line: 227, column: 43, scope: !557, inlinedAt: !814) +!863 = !DILocation(line: 227, column: 41, scope: !557, inlinedAt: !814) +!864 = !DILocation(line: 227, column: 4, scope: !557, inlinedAt: !814) +!865 = !DILocation(line: 229, column: 16, scope: !571, inlinedAt: !814) +!866 = !DILocation(line: 229, column: 11, scope: !547, inlinedAt: !814) +!867 = !DILocation(line: 234, column: 25, scope: !575, inlinedAt: !814) +!868 = !DILocation(line: 234, column: 31, scope: !575, inlinedAt: !814) +!869 = !DILocation(line: 234, column: 4, scope: !575, inlinedAt: !814) +!870 = !DILocation(line: 236, column: 16, scope: !575, inlinedAt: !814) +!871 = !DILocation(line: 236, column: 21, scope: !575, inlinedAt: !814) +!872 = !DILocation(line: 237, column: 8, scope: !581, inlinedAt: !814) +!873 = !DILocation(line: 237, column: 15, scope: !581, inlinedAt: !814) +!874 = !DILocation(line: 237, column: 18, scope: !581, inlinedAt: !814) +!875 = !DILocation(line: 237, column: 8, scope: !575, inlinedAt: !814) +!876 = !DILocation(line: 238, column: 6, scope: !581, inlinedAt: !814) +!877 = !DILocation(line: 239, column: 2, scope: !575, inlinedAt: !814) +!878 = !DILocation(line: 240, column: 5, scope: !547, inlinedAt: !814) +!879 = !DILocation(line: 241, column: 7, scope: !589, inlinedAt: !814) +!880 = !DILocation(line: 241, column: 7, scope: !497, inlinedAt: !814) +!881 = !DILocation(line: 242, column: 27, scope: !589, inlinedAt: !814) +!882 = !DILocation(line: 242, column: 5, scope: !589, inlinedAt: !814) +!883 = !DILocation(line: 243, column: 11, scope: !497, inlinedAt: !814) +!884 = !DILocation(line: 243, column: 18, scope: !497, inlinedAt: !814) +!885 = !DILocation(line: 243, column: 16, scope: !497, inlinedAt: !814) +!886 = !DILocation(line: 243, column: 34, scope: !497, inlinedAt: !814) +!887 = !DILocation(line: 243, column: 8, scope: !497, inlinedAt: !814) +!888 = !DILocation(line: 244, column: 25, scope: !497, inlinedAt: !814) +!889 = !DILocation(line: 244, column: 34, scope: !497, inlinedAt: !814) +!890 = !DILocation(line: 244, column: 13, scope: !497, inlinedAt: !814) +!891 = !DILocation(line: 244, column: 40, scope: !497, inlinedAt: !814) +!892 = !DILocation(line: 244, column: 11, scope: !497, inlinedAt: !814) +!893 = !DILocation(line: 244, column: 8, scope: !497, inlinedAt: !814) +!894 = !DILocation(line: 245, column: 7, scope: !605, inlinedAt: !814) +!895 = !DILocation(line: 245, column: 12, scope: !605, inlinedAt: !814) +!896 = !DILocation(line: 245, column: 7, scope: !497, inlinedAt: !814) +!897 = !DILocation(line: 246, column: 5, scope: !605, inlinedAt: !814) +!898 = !DILocation(line: 247, column: 36, scope: !497, inlinedAt: !814) +!899 = !DILocation(line: 0, scope: !483, inlinedAt: !900) +!900 = distinct !DILocation(line: 247, column: 10, scope: !497, inlinedAt: !814) +!901 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !900) +!902 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !900) +!903 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !900) +!904 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !900) +!905 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !900) +!906 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !900) +!907 = !DILocation(line: 247, column: 3, scope: !497, inlinedAt: !814) +!908 = !DILocation(line: 357, column: 3, scope: !620) +!909 = !DILocation(line: 359, column: 1, scope: !620) +!910 = distinct !DISubprogram(name: "propagateFloat64NaN", linkageName: "_ZL19propagateFloat64NaNyy", scope: !397, file: !397, line: 109, type: !911, scopeLine: 110, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!911 = !DISubroutineType(types: !912) +!912 = !{!408, !408, !408} +!913 = !DILocalVariable(name: "a", arg: 1, scope: !910, file: !397, line: 109, type: !408) +!914 = !DILocation(line: 0, scope: !910) +!915 = !DILocalVariable(name: "b", arg: 2, scope: !910, file: !397, line: 109, type: !408) +!916 = !DILocation(line: 113, column: 12, scope: !910) +!917 = !DILocalVariable(name: "aIsNaN", scope: !910, file: !397, line: 111, type: !407) +!918 = !DILocation(line: 114, column: 21, scope: !910) +!919 = !DILocalVariable(name: "aIsSignalingNaN", scope: !910, file: !397, line: 111, type: !407) +!920 = !DILocation(line: 115, column: 12, scope: !910) +!921 = !DILocalVariable(name: "bIsNaN", scope: !910, file: !397, line: 111, type: !407) +!922 = !DILocation(line: 116, column: 21, scope: !910) +!923 = !DILocalVariable(name: "bIsSignalingNaN", scope: !910, file: !397, line: 111, type: !407) +!924 = !DILocation(line: 117, column: 5, scope: !910) +!925 = !DILocation(line: 118, column: 5, scope: !910) +!926 = !DILocation(line: 119, column: 23, scope: !927) +!927 = distinct !DILexicalBlock(scope: !910, file: !397, line: 119, column: 7) +!928 = !DILocation(line: 119, column: 7, scope: !927) +!929 = !DILocation(line: 119, column: 7, scope: !910) +!930 = !DILocation(line: 120, column: 5, scope: !927) +!931 = !DILocation(line: 121, column: 10, scope: !910) +!932 = !DILocation(line: 121, column: 32, scope: !910) +!933 = !DILocation(line: 121, column: 54, scope: !910) +!934 = !DILocation(line: 121, column: 3, scope: !910) +!935 = distinct !DISubprogram(name: "estimateDiv128To64", linkageName: "_ZL18estimateDiv128To64yyy", scope: !21, file: !21, line: 157, type: !936, scopeLine: 158, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!936 = !DISubroutineType(types: !937) +!937 = !{!6, !6, !6, !6} +!938 = !DILocalVariable(name: "a0", arg: 1, scope: !935, file: !21, line: 157, type: !6) +!939 = !DILocation(line: 0, scope: !935) +!940 = !DILocalVariable(name: "a1", arg: 2, scope: !935, file: !21, line: 157, type: !6) +!941 = !DILocalVariable(name: "b", arg: 3, scope: !935, file: !21, line: 157, type: !6) +!942 = !DILocalVariable(name: "rem0", scope: !935, file: !21, line: 160, type: !6) +!943 = !DILocation(line: 160, column: 10, scope: !935) +!944 = !DILocalVariable(name: "rem1", scope: !935, file: !21, line: 160, type: !6) +!945 = !DILocation(line: 160, column: 16, scope: !935) +!946 = !DILocalVariable(name: "term0", scope: !935, file: !21, line: 160, type: !6) +!947 = !DILocation(line: 160, column: 22, scope: !935) +!948 = !DILocalVariable(name: "term1", scope: !935, file: !21, line: 160, type: !6) +!949 = !DILocation(line: 160, column: 29, scope: !935) +!950 = !DILocation(line: 163, column: 9, scope: !951) +!951 = distinct !DILexicalBlock(scope: !935, file: !21, line: 163, column: 7) +!952 = !DILocation(line: 163, column: 7, scope: !935) +!953 = !DILocation(line: 164, column: 5, scope: !951) +!954 = !DILocation(line: 165, column: 10, scope: !935) +!955 = !DILocalVariable(name: "b0", scope: !935, file: !21, line: 159, type: !6) +!956 = !DILocation(line: 166, column: 11, scope: !935) +!957 = !DILocation(line: 166, column: 17, scope: !935) +!958 = !DILocation(line: 166, column: 7, scope: !935) +!959 = !DILocation(line: 166, column: 59, scope: !935) +!960 = !DILocation(line: 166, column: 65, scope: !935) +!961 = !DILocalVariable(name: "z", scope: !935, file: !21, line: 161, type: !6) +!962 = !DILocation(line: 167, column: 3, scope: !935) +!963 = !DILocation(line: 168, column: 19, scope: !935) +!964 = !DILocation(line: 168, column: 26, scope: !935) +!965 = !DILocation(line: 168, column: 3, scope: !935) +!966 = !DILocation(line: 169, column: 3, scope: !935) +!967 = !DILocation(line: 169, column: 21, scope: !935) +!968 = !DILocation(line: 169, column: 27, scope: !935) +!969 = !DILocation(line: 171, column: 9, scope: !970) +!970 = distinct !DILexicalBlock(scope: !935, file: !21, line: 170, column: 5) +!971 = !DILocation(line: 172, column: 14, scope: !970) +!972 = !DILocalVariable(name: "b1", scope: !935, file: !21, line: 159, type: !6) +!973 = !DILocation(line: 173, column: 15, scope: !970) +!974 = !DILocation(line: 173, column: 21, scope: !970) +!975 = !DILocation(line: 173, column: 7, scope: !970) +!976 = distinct !{!976, !966, !977, !807} +!977 = !DILocation(line: 174, column: 5, scope: !935) +!978 = !DILocation(line: 175, column: 11, scope: !935) +!979 = !DILocation(line: 175, column: 16, scope: !935) +!980 = !DILocation(line: 175, column: 26, scope: !935) +!981 = !DILocation(line: 175, column: 31, scope: !935) +!982 = !DILocation(line: 175, column: 23, scope: !935) +!983 = !DILocation(line: 175, column: 8, scope: !935) +!984 = !DILocation(line: 176, column: 12, scope: !935) +!985 = !DILocation(line: 176, column: 21, scope: !935) +!986 = !DILocation(line: 176, column: 18, scope: !935) +!987 = !DILocation(line: 176, column: 8, scope: !935) +!988 = !DILocation(line: 176, column: 42, scope: !935) +!989 = !DILocation(line: 176, column: 47, scope: !935) +!990 = !DILocation(line: 176, column: 5, scope: !935) +!991 = !DILocation(line: 177, column: 3, scope: !935) +!992 = !DILocation(line: 179, column: 1, scope: !935) +!993 = !DILocalVariable(name: "a", arg: 1, scope: !20, file: !21, line: 187, type: !24) +!994 = !DILocation(line: 0, scope: !20) +!995 = !DILocalVariable(name: "shiftCount", scope: !20, file: !21, line: 207, type: !16) +!996 = !DILocation(line: 210, column: 9, scope: !997) +!997 = distinct !DILexicalBlock(scope: !20, file: !21, line: 210, column: 7) +!998 = !DILocation(line: 210, column: 7, scope: !20) +!999 = !DILocation(line: 212, column: 18, scope: !1000) +!1000 = distinct !DILexicalBlock(scope: !997, file: !21, line: 211, column: 5) +!1001 = !DILocation(line: 213, column: 9, scope: !1000) +!1002 = !DILocation(line: 214, column: 5, scope: !1000) +!1003 = !DILocation(line: 215, column: 9, scope: !1004) +!1004 = distinct !DILexicalBlock(scope: !20, file: !21, line: 215, column: 7) +!1005 = !DILocation(line: 215, column: 7, scope: !20) +!1006 = !DILocation(line: 217, column: 18, scope: !1007) +!1007 = distinct !DILexicalBlock(scope: !1004, file: !21, line: 216, column: 5) +!1008 = !DILocation(line: 218, column: 9, scope: !1007) +!1009 = !DILocation(line: 219, column: 5, scope: !1007) +!1010 = !DILocation(line: 220, column: 41, scope: !20) +!1011 = !DILocation(line: 220, column: 17, scope: !20) +!1012 = !DILocation(line: 220, column: 14, scope: !20) +!1013 = !DILocation(line: 221, column: 3, scope: !20) diff --git a/applications/newton/llvm-ir/CHStone_test/float64_mul.ll b/applications/newton/llvm-ir/CHStone_test/float64_mul.ll new file mode 100644 index 000000000..bb87be5f1 --- /dev/null +++ b/applications/newton/llvm-ir/CHStone_test/float64_mul.ll @@ -0,0 +1,1597 @@ +; ModuleID = 'float64_mul.ll' +source_filename = "dfmul/float64_mul.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@float_rounding_mode = dso_local global i32 0, align 4, !dbg !0 +@float_exception_flags = dso_local global i32 0, align 4, !dbg !14 +@_ZZL19countLeadingZeros32jE21countLeadingZerosHigh = internal constant <{ [128 x i32], [128 x i32] }> <{ [128 x i32] [i32 8, i32 7, i32 6, i32 6, i32 5, i32 5, i32 5, i32 5, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1], [128 x i32] zeroinitializer }>, align 16, !dbg !18 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z19shift64RightJammingyiPy(i64 %0, i32 %1, i64* %2) #0 !dbg !278 { + call void @llvm.dbg.value(metadata i64 %0, metadata !283, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i32 %1, metadata !285, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i64* %2, metadata !286, metadata !DIExpression()), !dbg !284 + %4 = icmp eq i32 %1, 0, !dbg !287 + br i1 %4, label %5, label %6, !dbg !289 + +5: ; preds = %3 + call void @llvm.dbg.value(metadata i64 %0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22, !dbg !291 + +6: ; preds = %3 + %7 = icmp slt i32 %1, 64, !dbg !293 + br i1 %7, label %8, label %18, !dbg !295 + +8: ; preds = %6 + %9 = zext i32 %1 to i64, !dbg !296 + %10 = lshr i64 %0, %9, !dbg !296 + %11 = sub nsw i32 0, %1, !dbg !298 + %12 = and i32 %11, 63, !dbg !299 + %13 = zext i32 %12 to i64, !dbg !300 + %14 = shl i64 %0, %13, !dbg !300 + %15 = icmp ne i64 %14, 0, !dbg !301 + %16 = zext i1 %15 to i64, !dbg !302 + %17 = or i64 %10, %16, !dbg !303 + call void @llvm.dbg.value(metadata i64 %17, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21, !dbg !304 + +18: ; preds = %6 + %19 = icmp ne i64 %0, 0, !dbg !305 + %20 = zext i1 %19 to i64, !dbg !307 + call void @llvm.dbg.value(metadata i64 %20, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21 + +21: ; preds = %18, %8 + %.0 = phi i64 [ %17, %8 ], [ %20, %18 ], !dbg !308 + call void @llvm.dbg.value(metadata i64 %.0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22 + +22: ; preds = %21, %5 + %.1 = phi i64 [ %0, %5 ], [ %.0, %21 ], !dbg !309 + call void @llvm.dbg.value(metadata i64 %.1, metadata !290, metadata !DIExpression()), !dbg !284 + store i64 %.1, i64* %2, align 8, !dbg !310 + ret void, !dbg !311 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z10mul64To128yyPyS_(i64 %0, i64 %1, i64* %2, i64* %3) #0 !dbg !312 { + call void @llvm.dbg.value(metadata i64 %0, metadata !315, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %1, metadata !317, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %2, metadata !318, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %3, metadata !319, metadata !DIExpression()), !dbg !316 + %5 = trunc i64 %0 to i32, !dbg !320 + call void @llvm.dbg.value(metadata i32 %5, metadata !321, metadata !DIExpression()), !dbg !316 + %6 = lshr i64 %0, 32, !dbg !322 + %7 = trunc i64 %6 to i32, !dbg !323 + call void @llvm.dbg.value(metadata i32 %7, metadata !324, metadata !DIExpression()), !dbg !316 + %8 = trunc i64 %1 to i32, !dbg !325 + call void @llvm.dbg.value(metadata i32 %8, metadata !326, metadata !DIExpression()), !dbg !316 + %9 = lshr i64 %1, 32, !dbg !327 + %10 = trunc i64 %9 to i32, !dbg !328 + call void @llvm.dbg.value(metadata i32 %10, metadata !329, metadata !DIExpression()), !dbg !316 + %11 = zext i32 %5 to i64, !dbg !330 + %12 = zext i32 %8 to i64, !dbg !331 + %13 = mul i64 %11, %12, !dbg !332 + call void @llvm.dbg.value(metadata i64 %13, metadata !333, metadata !DIExpression()), !dbg !316 + %14 = zext i32 %5 to i64, !dbg !334 + %15 = zext i32 %10 to i64, !dbg !335 + %16 = mul i64 %14, %15, !dbg !336 + call void @llvm.dbg.value(metadata i64 %16, metadata !337, metadata !DIExpression()), !dbg !316 + %17 = zext i32 %7 to i64, !dbg !338 + %18 = zext i32 %8 to i64, !dbg !339 + %19 = mul i64 %17, %18, !dbg !340 + call void @llvm.dbg.value(metadata i64 %19, metadata !341, metadata !DIExpression()), !dbg !316 + %20 = zext i32 %7 to i64, !dbg !342 + %21 = zext i32 %10 to i64, !dbg !343 + %22 = mul i64 %20, %21, !dbg !344 + call void @llvm.dbg.value(metadata i64 %22, metadata !345, metadata !DIExpression()), !dbg !316 + %23 = add i64 %16, %19, !dbg !346 + call void @llvm.dbg.value(metadata i64 %23, metadata !337, metadata !DIExpression()), !dbg !316 + %24 = icmp ult i64 %23, %19, !dbg !347 + %25 = zext i1 %24 to i64, !dbg !348 + %26 = shl i64 %25, 32, !dbg !349 + %27 = lshr i64 %23, 32, !dbg !350 + %28 = add i64 %26, %27, !dbg !351 + %29 = add i64 %22, %28, !dbg !352 + call void @llvm.dbg.value(metadata i64 %29, metadata !345, metadata !DIExpression()), !dbg !316 + %30 = shl i64 %23, 32, !dbg !353 + call void @llvm.dbg.value(metadata i64 %30, metadata !337, metadata !DIExpression()), !dbg !316 + %31 = add i64 %13, %30, !dbg !354 + call void @llvm.dbg.value(metadata i64 %31, metadata !333, metadata !DIExpression()), !dbg !316 + %32 = icmp ult i64 %31, %30, !dbg !355 + %33 = zext i1 %32 to i64, !dbg !356 + %34 = add i64 %29, %33, !dbg !357 + call void @llvm.dbg.value(metadata i64 %34, metadata !345, metadata !DIExpression()), !dbg !316 + store i64 %31, i64* %3, align 8, !dbg !358 + store i64 %34, i64* %2, align 8, !dbg !359 + ret void, !dbg !360 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z11float_raisei(i32 %0) #0 !dbg !361 { + call void @llvm.dbg.value(metadata i32 %0, metadata !365, metadata !DIExpression()), !dbg !366 + %2 = load i32, i32* @float_exception_flags, align 4, !dbg !367 + %3 = or i32 %2, %0, !dbg !367 + store i32 %3, i32* @float_exception_flags, align 4, !dbg !367 + ret void, !dbg !368 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z14float64_is_nany(i64 %0) #0 !dbg !369 { + call void @llvm.dbg.value(metadata i64 %0, metadata !375, metadata !DIExpression()), !dbg !376 + %2 = shl i64 %0, 1, !dbg !377 + %3 = icmp ult i64 -9007199254740992, %2, !dbg !378 + %4 = zext i1 %3 to i32, !dbg !379 + ret i32 %4, !dbg !380 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z24float64_is_signaling_nany(i64 %0) #0 !dbg !381 { + call void @llvm.dbg.value(metadata i64 %0, metadata !382, metadata !DIExpression()), !dbg !383 + %2 = lshr i64 %0, 51, !dbg !384 + %3 = and i64 %2, 4095, !dbg !385 + %4 = icmp eq i64 %3, 4094, !dbg !386 + br i1 %4, label %5, label %8, !dbg !387 + +5: ; preds = %1 + %6 = and i64 %0, 2251799813685247, !dbg !388 + %7 = icmp ne i64 %6, 0, !dbg !389 + br label %8 + +8: ; preds = %5, %1 + %9 = phi i1 [ false, %1 ], [ %7, %5 ], !dbg !383 + %10 = zext i1 %9 to i32, !dbg !390 + ret i32 %10, !dbg !391 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @extractFloat64Frac(i64 %0) #2 !dbg !392 { + call void @llvm.dbg.value(metadata i64 %0, metadata !395, metadata !DIExpression()), !dbg !396 + %2 = and i64 %0, 4503599627370495, !dbg !397 + ret i64 %2, !dbg !398 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Exp(i64 %0) #2 !dbg !399 { + call void @llvm.dbg.value(metadata i64 %0, metadata !402, metadata !DIExpression()), !dbg !403 + %2 = lshr i64 %0, 52, !dbg !404 + %3 = and i64 %2, 2047, !dbg !405 + %4 = trunc i64 %3 to i32, !dbg !406 + ret i32 %4, !dbg !407 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Sign(i64 %0) #2 !dbg !408 { + call void @llvm.dbg.value(metadata i64 %0, metadata !409, metadata !DIExpression()), !dbg !410 + %2 = lshr i64 %0, 63, !dbg !411 + %3 = trunc i64 %2 to i32, !dbg !412 + ret i32 %3, !dbg !413 +} + +; Function Attrs: mustprogress noinline uwtable +define dso_local void @normalizeFloat64Subnormal(i64 %0, i32* %1, i64* %2) #3 !dbg !414 { + call void @llvm.dbg.value(metadata i64 %0, metadata !418, metadata !DIExpression()), !dbg !419 + call void @llvm.dbg.value(metadata i32* %1, metadata !420, metadata !DIExpression()), !dbg !419 + call void @llvm.dbg.value(metadata i64* %2, metadata !421, metadata !DIExpression()), !dbg !419 + %4 = call i32 @_ZL19countLeadingZeros64y(i64 %0), !dbg !422 + %5 = sub nsw i32 %4, 11, !dbg !423 + call void @llvm.dbg.value(metadata i32 %5, metadata !424, metadata !DIExpression()), !dbg !419 + %6 = zext i32 %5 to i64, !dbg !425 + %7 = shl i64 %0, %6, !dbg !425 + store i64 %7, i64* %2, align 8, !dbg !426 + %8 = sub nsw i32 1, %5, !dbg !427 + store i32 %8, i32* %1, align 4, !dbg !428 + ret void, !dbg !429 +} + +; Function Attrs: mustprogress noinline uwtable +define internal i32 @_ZL19countLeadingZeros64y(i64 %0) #3 !dbg !430 { + call void @llvm.dbg.value(metadata i64 %0, metadata !433, metadata !DIExpression()), !dbg !434 + call void @llvm.dbg.value(metadata i32 0, metadata !435, metadata !DIExpression()), !dbg !434 + %2 = icmp ult i64 %0, 4294967296, !dbg !436 + br i1 %2, label %3, label %5, !dbg !438 + +3: ; preds = %1 + %4 = add nsw i32 0, 32, !dbg !439 + call void @llvm.dbg.value(metadata i32 %4, metadata !435, metadata !DIExpression()), !dbg !434 + br label %7, !dbg !441 + +5: ; preds = %1 + %6 = lshr i64 %0, 32, !dbg !442 + call void @llvm.dbg.value(metadata i64 %6, metadata !433, metadata !DIExpression()), !dbg !434 + br label %7 + +7: ; preds = %5, %3 + %.01 = phi i32 [ %4, %3 ], [ 0, %5 ], !dbg !434 + %.0 = phi i64 [ %0, %3 ], [ %6, %5 ] + call void @llvm.dbg.value(metadata i64 %.0, metadata !433, metadata !DIExpression()), !dbg !434 + call void @llvm.dbg.value(metadata i32 %.01, metadata !435, metadata !DIExpression()), !dbg !434 + %8 = trunc i64 %.0 to i32, !dbg !444 + %9 = call i32 @_ZL19countLeadingZeros32j(i32 %8), !dbg !445 + %10 = add nsw i32 %.01, %9, !dbg !446 + call void @llvm.dbg.value(metadata i32 %10, metadata !435, metadata !DIExpression()), !dbg !434 + ret i32 %10, !dbg !447 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @packFloat64(i32 %0, i32 %1, i64 %2) #2 !dbg !448 { + call void @llvm.dbg.value(metadata i32 %0, metadata !451, metadata !DIExpression()), !dbg !452 + call void @llvm.dbg.value(metadata i32 %1, metadata !453, metadata !DIExpression()), !dbg !452 + call void @llvm.dbg.value(metadata i64 %2, metadata !454, metadata !DIExpression()), !dbg !452 + %4 = sext i32 %0 to i64, !dbg !455 + %5 = shl i64 %4, 63, !dbg !456 + %6 = sext i32 %1 to i64, !dbg !457 + %7 = shl i64 %6, 52, !dbg !458 + %8 = add i64 %5, %7, !dbg !459 + %9 = add i64 %8, %2, !dbg !460 + ret i64 %9, !dbg !461 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i64 @roundAndPackFloat64(i32 %0, i32 %1, i64 %2) #0 !dbg !462 { + %4 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i32 %0, metadata !463, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 %1, metadata !465, metadata !DIExpression()), !dbg !464 + store i64 %2, i64* %4, align 8 + call void @llvm.dbg.declare(metadata i64* %4, metadata !466, metadata !DIExpression()), !dbg !467 + %5 = load i32, i32* @float_rounding_mode, align 4, !dbg !468 + call void @llvm.dbg.value(metadata i32 %5, metadata !469, metadata !DIExpression()), !dbg !464 + %6 = icmp eq i32 %5, 0, !dbg !470 + %7 = zext i1 %6 to i32, !dbg !471 + call void @llvm.dbg.value(metadata i32 %7, metadata !472, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 512, metadata !473, metadata !DIExpression()), !dbg !464 + %8 = icmp ne i32 %7, 0, !dbg !474 + br i1 %8, label %24, label %9, !dbg !476 + +9: ; preds = %3 + %10 = icmp eq i32 %5, 1, !dbg !477 + br i1 %10, label %11, label %12, !dbg !480 + +11: ; preds = %9 + call void @llvm.dbg.value(metadata i32 0, metadata !473, metadata !DIExpression()), !dbg !464 + br label %23, !dbg !481 + +12: ; preds = %9 + call void @llvm.dbg.value(metadata i32 1023, metadata !473, metadata !DIExpression()), !dbg !464 + %13 = icmp ne i32 %0, 0, !dbg !483 + br i1 %13, label %14, label %18, !dbg !486 + +14: ; preds = %12 + %15 = icmp eq i32 %5, 2, !dbg !487 + br i1 %15, label %16, label %17, !dbg !490 + +16: ; preds = %14 + call void @llvm.dbg.value(metadata i32 0, metadata !473, metadata !DIExpression()), !dbg !464 + br label %17, !dbg !491 + +17: ; preds = %16, %14 + %.01 = phi i32 [ 0, %16 ], [ 1023, %14 ], !dbg !492 + call void @llvm.dbg.value(metadata i32 %.01, metadata !473, metadata !DIExpression()), !dbg !464 + br label %22, !dbg !493 + +18: ; preds = %12 + %19 = icmp eq i32 %5, 3, !dbg !494 + br i1 %19, label %20, label %21, !dbg !497 + +20: ; preds = %18 + call void @llvm.dbg.value(metadata i32 0, metadata !473, metadata !DIExpression()), !dbg !464 + br label %21, !dbg !498 + +21: ; preds = %20, %18 + %.12 = phi i32 [ 0, %20 ], [ 1023, %18 ], !dbg !492 + call void @llvm.dbg.value(metadata i32 %.12, metadata !473, metadata !DIExpression()), !dbg !464 + br label %22 + +22: ; preds = %21, %17 + %.2 = phi i32 [ %.01, %17 ], [ %.12, %21 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.2, metadata !473, metadata !DIExpression()), !dbg !464 + br label %23 + +23: ; preds = %22, %11 + %.3 = phi i32 [ 0, %11 ], [ %.2, %22 ], !dbg !500 + call void @llvm.dbg.value(metadata i32 %.3, metadata !473, metadata !DIExpression()), !dbg !464 + br label %24, !dbg !501 + +24: ; preds = %23, %3 + %.4 = phi i32 [ 512, %3 ], [ %.3, %23 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.4, metadata !473, metadata !DIExpression()), !dbg !464 + %25 = load i64, i64* %4, align 8, !dbg !502 + %26 = and i64 %25, 1023, !dbg !503 + %27 = trunc i64 %26 to i32, !dbg !502 + call void @llvm.dbg.value(metadata i32 %27, metadata !504, metadata !DIExpression()), !dbg !464 + %28 = trunc i32 %1 to i16, !dbg !505 + %29 = zext i16 %28 to i32, !dbg !507 + %30 = icmp sle i32 2045, %29, !dbg !508 + br i1 %30, label %31, label %64, !dbg !509 + +31: ; preds = %24 + %32 = icmp slt i32 2045, %1, !dbg !510 + br i1 %32, label %40, label %33, !dbg !513 + +33: ; preds = %31 + %34 = icmp eq i32 %1, 2045, !dbg !514 + br i1 %34, label %35, label %50, !dbg !515 + +35: ; preds = %33 + %36 = load i64, i64* %4, align 8, !dbg !516 + %37 = sext i32 %.4 to i64, !dbg !517 + %38 = add i64 %36, %37, !dbg !518 + %39 = icmp slt i64 %38, 0, !dbg !519 + br i1 %39, label %40, label %50, !dbg !520 + +40: ; preds = %35, %31 + call void @_Z11float_raisei(i32 9), !dbg !521 + call void @llvm.dbg.value(metadata i32 %0, metadata !451, metadata !DIExpression()), !dbg !523 + call void @llvm.dbg.value(metadata i32 2047, metadata !453, metadata !DIExpression()), !dbg !523 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !523 + %41 = sext i32 %0 to i64, !dbg !525 + %42 = shl i64 %41, 63, !dbg !526 + %43 = sext i32 2047 to i64, !dbg !527 + %44 = shl i64 %43, 52, !dbg !528 + %45 = add i64 %42, %44, !dbg !529 + %46 = add i64 %45, 0, !dbg !530 + %47 = icmp eq i32 %.4, 0, !dbg !531 + %48 = zext i1 %47 to i64, !dbg !532 + %49 = sub i64 %46, %48, !dbg !533 + br label %93, !dbg !534 + +50: ; preds = %35, %33 + %51 = icmp slt i32 %1, 0, !dbg !535 + br i1 %51, label %52, label %63, !dbg !537 + +52: ; preds = %50 + call void @llvm.dbg.value(metadata i32 1, metadata !538, metadata !DIExpression()), !dbg !464 + %53 = load i64, i64* %4, align 8, !dbg !539 + %54 = sub nsw i32 0, %1, !dbg !541 + call void @_Z19shift64RightJammingyiPy(i64 %53, i32 %54, i64* %4), !dbg !542 + call void @llvm.dbg.value(metadata i32 0, metadata !465, metadata !DIExpression()), !dbg !464 + %55 = load i64, i64* %4, align 8, !dbg !543 + %56 = and i64 %55, 1023, !dbg !544 + %57 = trunc i64 %56 to i32, !dbg !543 + call void @llvm.dbg.value(metadata i32 %57, metadata !504, metadata !DIExpression()), !dbg !464 + %58 = icmp ne i32 1, 0, !dbg !545 + br i1 %58, label %59, label %62, !dbg !547 + +59: ; preds = %52 + %60 = icmp ne i32 %57, 0, !dbg !548 + br i1 %60, label %61, label %62, !dbg !549 + +61: ; preds = %59 + call void @_Z11float_raisei(i32 4), !dbg !550 + br label %62, !dbg !550 + +62: ; preds = %61, %59, %52 + br label %63, !dbg !551 + +63: ; preds = %62, %50 + %.03 = phi i32 [ 0, %62 ], [ %1, %50 ] + %.0 = phi i32 [ %57, %62 ], [ %27, %50 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.0, metadata !504, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 %.03, metadata !465, metadata !DIExpression()), !dbg !464 + br label %64, !dbg !552 + +64: ; preds = %63, %24 + %.14 = phi i32 [ %.03, %63 ], [ %1, %24 ] + %.1 = phi i32 [ %.0, %63 ], [ %27, %24 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.1, metadata !504, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 %.14, metadata !465, metadata !DIExpression()), !dbg !464 + %65 = icmp ne i32 %.1, 0, !dbg !553 + br i1 %65, label %66, label %69, !dbg !555 + +66: ; preds = %64 + %67 = load i32, i32* @float_exception_flags, align 4, !dbg !556 + %68 = or i32 %67, 1, !dbg !556 + store i32 %68, i32* @float_exception_flags, align 4, !dbg !556 + br label %69, !dbg !557 + +69: ; preds = %66, %64 + %70 = load i64, i64* %4, align 8, !dbg !558 + %71 = sext i32 %.4 to i64, !dbg !559 + %72 = add i64 %70, %71, !dbg !560 + %73 = lshr i64 %72, 10, !dbg !561 + store i64 %73, i64* %4, align 8, !dbg !562 + %74 = xor i32 %.1, 512, !dbg !563 + %75 = icmp eq i32 %74, 0, !dbg !564 + %76 = zext i1 %75 to i32, !dbg !565 + %77 = and i32 %76, %7, !dbg !566 + %78 = xor i32 %77, -1, !dbg !567 + %79 = sext i32 %78 to i64, !dbg !567 + %80 = load i64, i64* %4, align 8, !dbg !568 + %81 = and i64 %80, %79, !dbg !568 + store i64 %81, i64* %4, align 8, !dbg !568 + %82 = load i64, i64* %4, align 8, !dbg !569 + %83 = icmp eq i64 %82, 0, !dbg !571 + br i1 %83, label %84, label %85, !dbg !572 + +84: ; preds = %69 + call void @llvm.dbg.value(metadata i32 0, metadata !465, metadata !DIExpression()), !dbg !464 + br label %85, !dbg !573 + +85: ; preds = %84, %69 + %.25 = phi i32 [ 0, %84 ], [ %.14, %69 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.25, metadata !465, metadata !DIExpression()), !dbg !464 + %86 = load i64, i64* %4, align 8, !dbg !574 + call void @llvm.dbg.value(metadata i32 %0, metadata !451, metadata !DIExpression()), !dbg !575 + call void @llvm.dbg.value(metadata i32 %.25, metadata !453, metadata !DIExpression()), !dbg !575 + call void @llvm.dbg.value(metadata i64 %86, metadata !454, metadata !DIExpression()), !dbg !575 + %87 = sext i32 %0 to i64, !dbg !577 + %88 = shl i64 %87, 63, !dbg !578 + %89 = sext i32 %.25 to i64, !dbg !579 + %90 = shl i64 %89, 52, !dbg !580 + %91 = add i64 %88, %90, !dbg !581 + %92 = add i64 %91, %86, !dbg !582 + br label %93, !dbg !583 + +93: ; preds = %85, %40 + %.06 = phi i64 [ %49, %40 ], [ %92, %85 ], !dbg !464 + ret i64 %.06, !dbg !584 +} + +; Function Attrs: mustprogress noinline uwtable +define dso_local i64 @float64_mul(i64 %0, i64 %1) #3 !dbg !585 { + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + %5 = alloca i64, align 8 + %6 = alloca i64, align 8 + %7 = alloca i64, align 8 + %8 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i64 %0, metadata !590, metadata !DIExpression()), !dbg !591 + call void @llvm.dbg.value(metadata i64 %1, metadata !592, metadata !DIExpression()), !dbg !591 + call void @llvm.dbg.declare(metadata i32* %3, metadata !593, metadata !DIExpression()), !dbg !594 + call void @llvm.dbg.declare(metadata i32* %4, metadata !595, metadata !DIExpression()), !dbg !596 + call void @llvm.dbg.declare(metadata i64* %5, metadata !597, metadata !DIExpression()), !dbg !598 + call void @llvm.dbg.declare(metadata i64* %6, metadata !599, metadata !DIExpression()), !dbg !600 + call void @llvm.dbg.declare(metadata i64* %7, metadata !601, metadata !DIExpression()), !dbg !602 + call void @llvm.dbg.declare(metadata i64* %8, metadata !603, metadata !DIExpression()), !dbg !604 + call void @llvm.dbg.value(metadata i64 %0, metadata !395, metadata !DIExpression()), !dbg !605 + %9 = and i64 %0, 4503599627370495, !dbg !607 + store i64 %9, i64* %5, align 8, !dbg !608 + call void @llvm.dbg.value(metadata i64 %0, metadata !402, metadata !DIExpression()), !dbg !609 + %10 = lshr i64 %0, 52, !dbg !611 + %11 = and i64 %10, 2047, !dbg !612 + %12 = trunc i64 %11 to i32, !dbg !613 + store i32 %12, i32* %3, align 4, !dbg !614 + call void @llvm.dbg.value(metadata i64 %0, metadata !409, metadata !DIExpression()), !dbg !615 + %13 = lshr i64 %0, 63, !dbg !617 + %14 = trunc i64 %13 to i32, !dbg !618 + call void @llvm.dbg.value(metadata i32 %14, metadata !619, metadata !DIExpression()), !dbg !591 + call void @llvm.dbg.value(metadata i64 %1, metadata !395, metadata !DIExpression()), !dbg !620 + %15 = and i64 %1, 4503599627370495, !dbg !622 + store i64 %15, i64* %6, align 8, !dbg !623 + call void @llvm.dbg.value(metadata i64 %1, metadata !402, metadata !DIExpression()), !dbg !624 + %16 = lshr i64 %1, 52, !dbg !626 + %17 = and i64 %16, 2047, !dbg !627 + %18 = trunc i64 %17 to i32, !dbg !628 + store i32 %18, i32* %4, align 4, !dbg !629 + call void @llvm.dbg.value(metadata i64 %1, metadata !409, metadata !DIExpression()), !dbg !630 + %19 = lshr i64 %1, 63, !dbg !632 + %20 = trunc i64 %19 to i32, !dbg !633 + call void @llvm.dbg.value(metadata i32 %20, metadata !634, metadata !DIExpression()), !dbg !591 + %21 = xor i32 %14, %20, !dbg !635 + call void @llvm.dbg.value(metadata i32 %21, metadata !636, metadata !DIExpression()), !dbg !591 + %22 = load i32, i32* %3, align 4, !dbg !637 + %23 = icmp eq i32 %22, 2047, !dbg !639 + br i1 %23, label %24, label %49, !dbg !640 + +24: ; preds = %2 + %25 = load i64, i64* %5, align 8, !dbg !641 + %26 = icmp ne i64 %25, 0, !dbg !641 + br i1 %26, label %33, label %27, !dbg !644 + +27: ; preds = %24 + %28 = load i32, i32* %4, align 4, !dbg !645 + %29 = icmp eq i32 %28, 2047, !dbg !646 + br i1 %29, label %30, label %35, !dbg !647 + +30: ; preds = %27 + %31 = load i64, i64* %6, align 8, !dbg !648 + %32 = icmp ne i64 %31, 0, !dbg !648 + br i1 %32, label %33, label %35, !dbg !649 + +33: ; preds = %30, %24 + %34 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !650 + br label %129, !dbg !651 + +35: ; preds = %30, %27 + %36 = load i32, i32* %4, align 4, !dbg !652 + %37 = sext i32 %36 to i64, !dbg !652 + %38 = load i64, i64* %6, align 8, !dbg !654 + %39 = or i64 %37, %38, !dbg !655 + %40 = icmp eq i64 %39, 0, !dbg !656 + br i1 %40, label %41, label %42, !dbg !657 + +41: ; preds = %35 + call void @_Z11float_raisei(i32 16), !dbg !658 + br label %129, !dbg !660 + +42: ; preds = %35 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !661 + call void @llvm.dbg.value(metadata i32 2047, metadata !453, metadata !DIExpression()), !dbg !661 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !661 + %43 = sext i32 %21 to i64, !dbg !663 + %44 = shl i64 %43, 63, !dbg !664 + %45 = sext i32 2047 to i64, !dbg !665 + %46 = shl i64 %45, 52, !dbg !666 + %47 = add i64 %44, %46, !dbg !667 + %48 = add i64 %47, 0, !dbg !668 + br label %129, !dbg !669 + +49: ; preds = %2 + %50 = load i32, i32* %4, align 4, !dbg !670 + %51 = icmp eq i32 %50, 2047, !dbg !672 + br i1 %51, label %52, label %71, !dbg !673 + +52: ; preds = %49 + %53 = load i64, i64* %6, align 8, !dbg !674 + %54 = icmp ne i64 %53, 0, !dbg !674 + br i1 %54, label %55, label %57, !dbg !677 + +55: ; preds = %52 + %56 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !678 + br label %129, !dbg !679 + +57: ; preds = %52 + %58 = load i32, i32* %3, align 4, !dbg !680 + %59 = sext i32 %58 to i64, !dbg !680 + %60 = load i64, i64* %5, align 8, !dbg !682 + %61 = or i64 %59, %60, !dbg !683 + %62 = icmp eq i64 %61, 0, !dbg !684 + br i1 %62, label %63, label %64, !dbg !685 + +63: ; preds = %57 + call void @_Z11float_raisei(i32 16), !dbg !686 + br label %129, !dbg !688 + +64: ; preds = %57 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !689 + call void @llvm.dbg.value(metadata i32 2047, metadata !453, metadata !DIExpression()), !dbg !689 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !689 + %65 = sext i32 %21 to i64, !dbg !691 + %66 = shl i64 %65, 63, !dbg !692 + %67 = sext i32 2047 to i64, !dbg !693 + %68 = shl i64 %67, 52, !dbg !694 + %69 = add i64 %66, %68, !dbg !695 + %70 = add i64 %69, 0, !dbg !696 + br label %129, !dbg !697 + +71: ; preds = %49 + %72 = load i32, i32* %3, align 4, !dbg !698 + %73 = icmp eq i32 %72, 0, !dbg !700 + br i1 %73, label %74, label %86, !dbg !701 + +74: ; preds = %71 + %75 = load i64, i64* %5, align 8, !dbg !702 + %76 = icmp eq i64 %75, 0, !dbg !705 + br i1 %76, label %77, label %84, !dbg !706 + +77: ; preds = %74 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !707 + call void @llvm.dbg.value(metadata i32 0, metadata !453, metadata !DIExpression()), !dbg !707 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !707 + %78 = sext i32 %21 to i64, !dbg !709 + %79 = shl i64 %78, 63, !dbg !710 + %80 = sext i32 0 to i64, !dbg !711 + %81 = shl i64 %80, 52, !dbg !712 + %82 = add i64 %79, %81, !dbg !713 + %83 = add i64 %82, 0, !dbg !714 + br label %129, !dbg !715 + +84: ; preds = %74 + %85 = load i64, i64* %5, align 8, !dbg !716 + call void @normalizeFloat64Subnormal(i64 %85, i32* %3, i64* %5), !dbg !717 + br label %86, !dbg !718 + +86: ; preds = %84, %71 + %87 = load i32, i32* %4, align 4, !dbg !719 + %88 = icmp eq i32 %87, 0, !dbg !721 + br i1 %88, label %89, label %101, !dbg !722 + +89: ; preds = %86 + %90 = load i64, i64* %6, align 8, !dbg !723 + %91 = icmp eq i64 %90, 0, !dbg !726 + br i1 %91, label %92, label %99, !dbg !727 + +92: ; preds = %89 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !728 + call void @llvm.dbg.value(metadata i32 0, metadata !453, metadata !DIExpression()), !dbg !728 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !728 + %93 = sext i32 %21 to i64, !dbg !730 + %94 = shl i64 %93, 63, !dbg !731 + %95 = sext i32 0 to i64, !dbg !732 + %96 = shl i64 %95, 52, !dbg !733 + %97 = add i64 %94, %96, !dbg !734 + %98 = add i64 %97, 0, !dbg !735 + br label %129, !dbg !736 + +99: ; preds = %89 + %100 = load i64, i64* %6, align 8, !dbg !737 + call void @normalizeFloat64Subnormal(i64 %100, i32* %4, i64* %6), !dbg !738 + br label %101, !dbg !739 + +101: ; preds = %99, %86 + %102 = load i32, i32* %3, align 4, !dbg !740 + %103 = load i32, i32* %4, align 4, !dbg !741 + %104 = add nsw i32 %102, %103, !dbg !742 + %105 = sub nsw i32 %104, 1023, !dbg !743 + call void @llvm.dbg.value(metadata i32 %105, metadata !744, metadata !DIExpression()), !dbg !591 + %106 = load i64, i64* %5, align 8, !dbg !745 + %107 = or i64 %106, 4503599627370496, !dbg !746 + %108 = shl i64 %107, 10, !dbg !747 + store i64 %108, i64* %5, align 8, !dbg !748 + %109 = load i64, i64* %6, align 8, !dbg !749 + %110 = or i64 %109, 4503599627370496, !dbg !750 + %111 = shl i64 %110, 11, !dbg !751 + store i64 %111, i64* %6, align 8, !dbg !752 + %112 = load i64, i64* %5, align 8, !dbg !753 + %113 = load i64, i64* %6, align 8, !dbg !754 + call void @_Z10mul64To128yyPyS_(i64 %112, i64 %113, i64* %7, i64* %8), !dbg !755 + %114 = load i64, i64* %8, align 8, !dbg !756 + %115 = icmp ne i64 %114, 0, !dbg !757 + %116 = zext i1 %115 to i64, !dbg !758 + %117 = load i64, i64* %7, align 8, !dbg !759 + %118 = or i64 %117, %116, !dbg !759 + store i64 %118, i64* %7, align 8, !dbg !759 + %119 = load i64, i64* %7, align 8, !dbg !760 + %120 = shl i64 %119, 1, !dbg !762 + %121 = icmp sle i64 0, %120, !dbg !763 + br i1 %121, label %122, label %126, !dbg !764 + +122: ; preds = %101 + %123 = load i64, i64* %7, align 8, !dbg !765 + %124 = shl i64 %123, 1, !dbg !765 + store i64 %124, i64* %7, align 8, !dbg !765 + %125 = add nsw i32 %105, -1, !dbg !767 + call void @llvm.dbg.value(metadata i32 %125, metadata !744, metadata !DIExpression()), !dbg !591 + br label %126, !dbg !768 + +126: ; preds = %122, %101 + %.0 = phi i32 [ %125, %122 ], [ %105, %101 ], !dbg !591 + call void @llvm.dbg.value(metadata i32 %.0, metadata !744, metadata !DIExpression()), !dbg !591 + %127 = load i64, i64* %7, align 8, !dbg !769 + %128 = call i64 @roundAndPackFloat64(i32 %21, i32 %.0, i64 %127), !dbg !770 + br label %129, !dbg !771 + +129: ; preds = %126, %92, %77, %64, %63, %55, %42, %41, %33 + %.01 = phi i64 [ %34, %33 ], [ 9223372036854775807, %41 ], [ %48, %42 ], [ %56, %55 ], [ 9223372036854775807, %63 ], [ %70, %64 ], [ %83, %77 ], [ %98, %92 ], [ %128, %126 ], !dbg !591 + ret i64 %.01, !dbg !772 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1) #0 !dbg !773 { + call void @llvm.dbg.value(metadata i64 %0, metadata !776, metadata !DIExpression()), !dbg !777 + call void @llvm.dbg.value(metadata i64 %1, metadata !778, metadata !DIExpression()), !dbg !777 + %3 = call i32 @_Z14float64_is_nany(i64 %0), !dbg !779 + call void @llvm.dbg.value(metadata i32 %3, metadata !780, metadata !DIExpression()), !dbg !777 + %4 = call i32 @_Z24float64_is_signaling_nany(i64 %0), !dbg !781 + call void @llvm.dbg.value(metadata i32 %4, metadata !782, metadata !DIExpression()), !dbg !777 + %5 = call i32 @_Z14float64_is_nany(i64 %1), !dbg !783 + call void @llvm.dbg.value(metadata i32 %5, metadata !784, metadata !DIExpression()), !dbg !777 + %6 = call i32 @_Z24float64_is_signaling_nany(i64 %1), !dbg !785 + call void @llvm.dbg.value(metadata i32 %6, metadata !786, metadata !DIExpression()), !dbg !777 + %7 = or i64 %0, 2251799813685248, !dbg !787 + call void @llvm.dbg.value(metadata i64 %7, metadata !776, metadata !DIExpression()), !dbg !777 + %8 = or i64 %1, 2251799813685248, !dbg !788 + call void @llvm.dbg.value(metadata i64 %8, metadata !778, metadata !DIExpression()), !dbg !777 + %9 = or i32 %4, %6, !dbg !789 + %10 = icmp ne i32 %9, 0, !dbg !791 + br i1 %10, label %11, label %12, !dbg !792 + +11: ; preds = %2 + call void @_Z11float_raisei(i32 16), !dbg !793 + br label %12, !dbg !793 + +12: ; preds = %11, %2 + %13 = icmp ne i32 %6, 0, !dbg !794 + br i1 %13, label %14, label %15, !dbg !794 + +14: ; preds = %12 + br label %26, !dbg !794 + +15: ; preds = %12 + %16 = icmp ne i32 %4, 0, !dbg !795 + br i1 %16, label %17, label %18, !dbg !795 + +17: ; preds = %15 + br label %24, !dbg !795 + +18: ; preds = %15 + %19 = icmp ne i32 %5, 0, !dbg !796 + br i1 %19, label %20, label %21, !dbg !796 + +20: ; preds = %18 + br label %22, !dbg !796 + +21: ; preds = %18 + br label %22, !dbg !796 + +22: ; preds = %21, %20 + %23 = phi i64 [ %8, %20 ], [ %7, %21 ], !dbg !796 + br label %24, !dbg !795 + +24: ; preds = %22, %17 + %25 = phi i64 [ %7, %17 ], [ %23, %22 ], !dbg !795 + br label %26, !dbg !794 + +26: ; preds = %24, %14 + %27 = phi i64 [ %8, %14 ], [ %25, %24 ], !dbg !794 + ret i64 %27, !dbg !797 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i32 @_ZL19countLeadingZeros32j(i32 %0) #0 !dbg !20 { + call void @llvm.dbg.value(metadata i32 %0, metadata !798, metadata !DIExpression()), !dbg !799 + call void @llvm.dbg.value(metadata i32 0, metadata !800, metadata !DIExpression()), !dbg !799 + %2 = icmp ult i32 %0, 65536, !dbg !801 + br i1 %2, label %3, label %6, !dbg !803 + +3: ; preds = %1 + %4 = add nsw i32 0, 16, !dbg !804 + call void @llvm.dbg.value(metadata i32 %4, metadata !800, metadata !DIExpression()), !dbg !799 + %5 = shl i32 %0, 16, !dbg !806 + call void @llvm.dbg.value(metadata i32 %5, metadata !798, metadata !DIExpression()), !dbg !799 + br label %6, !dbg !807 + +6: ; preds = %3, %1 + %.01 = phi i32 [ %4, %3 ], [ 0, %1 ], !dbg !799 + %.0 = phi i32 [ %5, %3 ], [ %0, %1 ] + call void @llvm.dbg.value(metadata i32 %.0, metadata !798, metadata !DIExpression()), !dbg !799 + call void @llvm.dbg.value(metadata i32 %.01, metadata !800, metadata !DIExpression()), !dbg !799 + %7 = icmp ult i32 %.0, 16777216, !dbg !808 + br i1 %7, label %8, label %11, !dbg !810 + +8: ; preds = %6 + %9 = add nsw i32 %.01, 8, !dbg !811 + call void @llvm.dbg.value(metadata i32 %9, metadata !800, metadata !DIExpression()), !dbg !799 + %10 = shl i32 %.0, 8, !dbg !813 + call void @llvm.dbg.value(metadata i32 %10, metadata !798, metadata !DIExpression()), !dbg !799 + br label %11, !dbg !814 + +11: ; preds = %8, %6 + %.12 = phi i32 [ %9, %8 ], [ %.01, %6 ], !dbg !799 + %.1 = phi i32 [ %10, %8 ], [ %.0, %6 ], !dbg !799 + call void @llvm.dbg.value(metadata i32 %.1, metadata !798, metadata !DIExpression()), !dbg !799 + call void @llvm.dbg.value(metadata i32 %.12, metadata !800, metadata !DIExpression()), !dbg !799 + %12 = lshr i32 %.1, 24, !dbg !815 + %13 = zext i32 %12 to i64, !dbg !816 + %14 = getelementptr inbounds [256 x i32], [256 x i32]* bitcast (<{ [128 x i32], [128 x i32] }>* @_ZZL19countLeadingZeros32jE21countLeadingZerosHigh to [256 x i32]*), i64 0, i64 %13, !dbg !816 + %15 = load i32, i32* %14, align 4, !dbg !816 + %16 = add nsw i32 %.12, %15, !dbg !817 + call void @llvm.dbg.value(metadata i32 %16, metadata !800, metadata !DIExpression()), !dbg !799 + ret i32 %16, !dbg !818 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { mustprogress noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { alwaysinline mustprogress nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { mustprogress noinline uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!272, !273, !274, !275, !276} +!llvm.ident = !{!277} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "float_rounding_mode", scope: !2, file: !3, line: 61, type: !16, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "Ubuntu clang version 13.0.1-2ubuntu2.2", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !13, imports: !30, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "dfmul/float64_mul.cpp", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!4 = !{} +!5 = !{!6, !9, !11} +!6 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits64", file: !7, line: 70, baseType: !8) +!7 = !DIFile(filename: "dfmul/include/SPARC-GCC.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!8 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!9 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits16", file: !7, line: 68, baseType: !10) +!10 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned) +!11 = !DIDerivedType(tag: DW_TAG_typedef, name: "sbits64", file: !7, line: 71, baseType: !12) +!12 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!13 = !{!0, !14, !18} +!14 = !DIGlobalVariableExpression(var: !15, expr: !DIExpression()) +!15 = distinct !DIGlobalVariable(name: "float_exception_flags", scope: !2, file: !3, line: 62, type: !16, isLocal: false, isDefinition: true) +!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "int8", file: !7, line: 59, baseType: !17) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) +!19 = distinct !DIGlobalVariable(name: "countLeadingZerosHigh", scope: !20, file: !21, line: 118, type: !26, isLocal: true, isDefinition: true) +!20 = distinct !DISubprogram(name: "countLeadingZeros32", linkageName: "_ZL19countLeadingZeros32j", scope: !21, file: !21, line: 116, type: !22, scopeLine: 117, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!21 = !DIFile(filename: "dfmul/include/softfloat-macros", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!22 = !DISubroutineType(types: !23) +!23 = !{!16, !24} +!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits32", file: !7, line: 69, baseType: !25) +!25 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 8192, elements: !28) +!27 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !16) +!28 = !{!29} +!29 = !DISubrange(count: 256) +!30 = !{!31, !38, !42, !49, !53, !58, !60, !68, !72, !76, !90, !94, !98, !102, !106, !111, !115, !119, !123, !127, !135, !139, !143, !145, !149, !153, !157, !163, !167, !171, !173, !181, !185, !192, !194, !198, !202, !206, !210, !214, !219, !224, !225, !226, !227, !229, !230, !231, !232, !233, !234, !235, !237, !238, !239, !240, !241, !242, !243, !248, !249, !250, !251, !252, !253, !254, !255, !256, !257, !258, !259, !260, !261, !262, !263, !264, !265, !266, !267, !268, !269, !270, !271} +!31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !33, file: !37, line: 52) +!32 = !DINamespace(name: "std", scope: null) +!33 = !DISubprogram(name: "abs", scope: !34, file: !34, line: 848, type: !35, flags: DIFlagPrototyped, spFlags: 0) +!34 = !DIFile(filename: "/usr/include/stdlib.h", directory: "") +!35 = !DISubroutineType(types: !36) +!36 = !{!17, !17} +!37 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_abs.h", directory: "") +!38 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !39, file: !41, line: 127) +!39 = !DIDerivedType(tag: DW_TAG_typedef, name: "div_t", file: !34, line: 63, baseType: !40) +!40 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 59, size: 64, flags: DIFlagFwdDecl, identifier: "_ZTS5div_t") +!41 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/cstdlib", directory: "") +!42 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !43, file: !41, line: 128) +!43 = !DIDerivedType(tag: DW_TAG_typedef, name: "ldiv_t", file: !34, line: 71, baseType: !44) +!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 67, size: 128, flags: DIFlagTypePassByValue, elements: !45, identifier: "_ZTS6ldiv_t") +!45 = !{!46, !48} +!46 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !44, file: !34, line: 69, baseType: !47, size: 64) +!47 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!48 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !44, file: !34, line: 70, baseType: !47, size: 64, offset: 64) +!49 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !50, file: !41, line: 130) +!50 = !DISubprogram(name: "abort", scope: !34, file: !34, line: 598, type: !51, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!51 = !DISubroutineType(types: !52) +!52 = !{null} +!53 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !54, file: !41, line: 134) +!54 = !DISubprogram(name: "atexit", scope: !34, file: !34, line: 602, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!55 = !DISubroutineType(types: !56) +!56 = !{!17, !57} +!57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) +!58 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !59, file: !41, line: 137) +!59 = !DISubprogram(name: "at_quick_exit", scope: !34, file: !34, line: 607, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!60 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !61, file: !41, line: 140) +!61 = !DISubprogram(name: "atof", scope: !34, file: !34, line: 102, type: !62, flags: DIFlagPrototyped, spFlags: 0) +!62 = !DISubroutineType(types: !63) +!63 = !{!64, !65} +!64 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) +!66 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !67) +!67 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!68 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !69, file: !41, line: 141) +!69 = !DISubprogram(name: "atoi", scope: !34, file: !34, line: 105, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!70 = !DISubroutineType(types: !71) +!71 = !{!17, !65} +!72 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !73, file: !41, line: 142) +!73 = !DISubprogram(name: "atol", scope: !34, file: !34, line: 108, type: !74, flags: DIFlagPrototyped, spFlags: 0) +!74 = !DISubroutineType(types: !75) +!75 = !{!47, !65} +!76 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !77, file: !41, line: 143) +!77 = !DISubprogram(name: "bsearch", scope: !34, file: !34, line: 828, type: !78, flags: DIFlagPrototyped, spFlags: 0) +!78 = !DISubroutineType(types: !79) +!79 = !{!80, !81, !81, !83, !83, !86} +!80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!81 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !82, size: 64) +!82 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) +!83 = !DIDerivedType(tag: DW_TAG_typedef, name: "size_t", file: !84, line: 46, baseType: !85) +!84 = !DIFile(filename: "/usr/lib/llvm-13/lib/clang/13.0.1/include/stddef.h", directory: "") +!85 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!86 = !DIDerivedType(tag: DW_TAG_typedef, name: "__compar_fn_t", file: !34, line: 816, baseType: !87) +!87 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !88, size: 64) +!88 = !DISubroutineType(types: !89) +!89 = !{!17, !81, !81} +!90 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !91, file: !41, line: 144) +!91 = !DISubprogram(name: "calloc", scope: !34, file: !34, line: 543, type: !92, flags: DIFlagPrototyped, spFlags: 0) +!92 = !DISubroutineType(types: !93) +!93 = !{!80, !83, !83} +!94 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !95, file: !41, line: 145) +!95 = !DISubprogram(name: "div", scope: !34, file: !34, line: 860, type: !96, flags: DIFlagPrototyped, spFlags: 0) +!96 = !DISubroutineType(types: !97) +!97 = !{!39, !17, !17} +!98 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !99, file: !41, line: 146) +!99 = !DISubprogram(name: "exit", scope: !34, file: !34, line: 624, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!100 = !DISubroutineType(types: !101) +!101 = !{null, !17} +!102 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !103, file: !41, line: 147) +!103 = !DISubprogram(name: "free", scope: !34, file: !34, line: 555, type: !104, flags: DIFlagPrototyped, spFlags: 0) +!104 = !DISubroutineType(types: !105) +!105 = !{null, !80} +!106 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !107, file: !41, line: 148) +!107 = !DISubprogram(name: "getenv", scope: !34, file: !34, line: 641, type: !108, flags: DIFlagPrototyped, spFlags: 0) +!108 = !DISubroutineType(types: !109) +!109 = !{!110, !65} +!110 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !67, size: 64) +!111 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !112, file: !41, line: 149) +!112 = !DISubprogram(name: "labs", scope: !34, file: !34, line: 849, type: !113, flags: DIFlagPrototyped, spFlags: 0) +!113 = !DISubroutineType(types: !114) +!114 = !{!47, !47} +!115 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !116, file: !41, line: 150) +!116 = !DISubprogram(name: "ldiv", scope: !34, file: !34, line: 862, type: !117, flags: DIFlagPrototyped, spFlags: 0) +!117 = !DISubroutineType(types: !118) +!118 = !{!43, !47, !47} +!119 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !120, file: !41, line: 151) +!120 = !DISubprogram(name: "malloc", scope: !34, file: !34, line: 540, type: !121, flags: DIFlagPrototyped, spFlags: 0) +!121 = !DISubroutineType(types: !122) +!122 = !{!80, !83} +!123 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !124, file: !41, line: 153) +!124 = !DISubprogram(name: "mblen", scope: !34, file: !34, line: 930, type: !125, flags: DIFlagPrototyped, spFlags: 0) +!125 = !DISubroutineType(types: !126) +!126 = !{!17, !65, !83} +!127 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !128, file: !41, line: 154) +!128 = !DISubprogram(name: "mbstowcs", scope: !34, file: !34, line: 941, type: !129, flags: DIFlagPrototyped, spFlags: 0) +!129 = !DISubroutineType(types: !130) +!130 = !{!83, !131, !134, !83} +!131 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !132) +!132 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !133, size: 64) +!133 = !DIBasicType(name: "wchar_t", size: 32, encoding: DW_ATE_signed) +!134 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !65) +!135 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !136, file: !41, line: 155) +!136 = !DISubprogram(name: "mbtowc", scope: !34, file: !34, line: 933, type: !137, flags: DIFlagPrototyped, spFlags: 0) +!137 = !DISubroutineType(types: !138) +!138 = !{!17, !131, !134, !83} +!139 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !140, file: !41, line: 157) +!140 = !DISubprogram(name: "qsort", scope: !34, file: !34, line: 838, type: !141, flags: DIFlagPrototyped, spFlags: 0) +!141 = !DISubroutineType(types: !142) +!142 = !{null, !80, !83, !83, !86} +!143 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !144, file: !41, line: 160) +!144 = !DISubprogram(name: "quick_exit", scope: !34, file: !34, line: 630, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!145 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !146, file: !41, line: 163) +!146 = !DISubprogram(name: "rand", scope: !34, file: !34, line: 454, type: !147, flags: DIFlagPrototyped, spFlags: 0) +!147 = !DISubroutineType(types: !148) +!148 = !{!17} +!149 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !150, file: !41, line: 164) +!150 = !DISubprogram(name: "realloc", scope: !34, file: !34, line: 551, type: !151, flags: DIFlagPrototyped, spFlags: 0) +!151 = !DISubroutineType(types: !152) +!152 = !{!80, !80, !83} +!153 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !154, file: !41, line: 165) +!154 = !DISubprogram(name: "srand", scope: !34, file: !34, line: 456, type: !155, flags: DIFlagPrototyped, spFlags: 0) +!155 = !DISubroutineType(types: !156) +!156 = !{null, !25} +!157 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !158, file: !41, line: 166) +!158 = !DISubprogram(name: "strtod", scope: !34, file: !34, line: 118, type: !159, flags: DIFlagPrototyped, spFlags: 0) +!159 = !DISubroutineType(types: !160) +!160 = !{!64, !134, !161} +!161 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !162) +!162 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !110, size: 64) +!163 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !164, file: !41, line: 167) +!164 = !DISubprogram(name: "strtol", scope: !34, file: !34, line: 177, type: !165, flags: DIFlagPrototyped, spFlags: 0) +!165 = !DISubroutineType(types: !166) +!166 = !{!47, !134, !161, !17} +!167 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !168, file: !41, line: 168) +!168 = !DISubprogram(name: "strtoul", scope: !34, file: !34, line: 181, type: !169, flags: DIFlagPrototyped, spFlags: 0) +!169 = !DISubroutineType(types: !170) +!170 = !{!85, !134, !161, !17} +!171 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !172, file: !41, line: 169) +!172 = !DISubprogram(name: "system", scope: !34, file: !34, line: 791, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!173 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !174, file: !41, line: 171) +!174 = !DISubprogram(name: "wcstombs", scope: !34, file: !34, line: 945, type: !175, flags: DIFlagPrototyped, spFlags: 0) +!175 = !DISubroutineType(types: !176) +!176 = !{!83, !177, !178, !83} +!177 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !110) +!178 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !179) +!179 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !180, size: 64) +!180 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !133) +!181 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !182, file: !41, line: 172) +!182 = !DISubprogram(name: "wctomb", scope: !34, file: !34, line: 937, type: !183, flags: DIFlagPrototyped, spFlags: 0) +!183 = !DISubroutineType(types: !184) +!184 = !{!17, !110, !133} +!185 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !187, file: !41, line: 200) +!186 = !DINamespace(name: "__gnu_cxx", scope: null) +!187 = !DIDerivedType(tag: DW_TAG_typedef, name: "lldiv_t", file: !34, line: 81, baseType: !188) +!188 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 77, size: 128, flags: DIFlagTypePassByValue, elements: !189, identifier: "_ZTS7lldiv_t") +!189 = !{!190, !191} +!190 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !188, file: !34, line: 79, baseType: !12, size: 64) +!191 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !188, file: !34, line: 80, baseType: !12, size: 64, offset: 64) +!192 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !193, file: !41, line: 206) +!193 = !DISubprogram(name: "_Exit", scope: !34, file: !34, line: 636, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!194 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !195, file: !41, line: 210) +!195 = !DISubprogram(name: "llabs", scope: !34, file: !34, line: 852, type: !196, flags: DIFlagPrototyped, spFlags: 0) +!196 = !DISubroutineType(types: !197) +!197 = !{!12, !12} +!198 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !199, file: !41, line: 216) +!199 = !DISubprogram(name: "lldiv", scope: !34, file: !34, line: 866, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!200 = !DISubroutineType(types: !201) +!201 = !{!187, !12, !12} +!202 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !203, file: !41, line: 227) +!203 = !DISubprogram(name: "atoll", scope: !34, file: !34, line: 113, type: !204, flags: DIFlagPrototyped, spFlags: 0) +!204 = !DISubroutineType(types: !205) +!205 = !{!12, !65} +!206 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !207, file: !41, line: 228) +!207 = !DISubprogram(name: "strtoll", scope: !34, file: !34, line: 201, type: !208, flags: DIFlagPrototyped, spFlags: 0) +!208 = !DISubroutineType(types: !209) +!209 = !{!12, !134, !161, !17} +!210 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !211, file: !41, line: 229) +!211 = !DISubprogram(name: "strtoull", scope: !34, file: !34, line: 206, type: !212, flags: DIFlagPrototyped, spFlags: 0) +!212 = !DISubroutineType(types: !213) +!213 = !{!8, !134, !161, !17} +!214 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !215, file: !41, line: 231) +!215 = !DISubprogram(name: "strtof", scope: !34, file: !34, line: 124, type: !216, flags: DIFlagPrototyped, spFlags: 0) +!216 = !DISubroutineType(types: !217) +!217 = !{!218, !134, !161} +!218 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!219 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !220, file: !41, line: 232) +!220 = !DISubprogram(name: "strtold", scope: !34, file: !34, line: 127, type: !221, flags: DIFlagPrototyped, spFlags: 0) +!221 = !DISubroutineType(types: !222) +!222 = !{!223, !134, !161} +!223 = !DIBasicType(name: "long double", size: 128, encoding: DW_ATE_float) +!224 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !187, file: !41, line: 240) +!225 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !193, file: !41, line: 242) +!226 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !195, file: !41, line: 244) +!227 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !228, file: !41, line: 245) +!228 = !DISubprogram(name: "div", linkageName: "_ZN9__gnu_cxx3divExx", scope: !186, file: !41, line: 213, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!229 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !199, file: !41, line: 246) +!230 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !203, file: !41, line: 248) +!231 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !215, file: !41, line: 249) +!232 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !207, file: !41, line: 250) +!233 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !211, file: !41, line: 251) +!234 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !220, file: !41, line: 252) +!235 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !50, file: !236, line: 38) +!236 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/stdlib.h", directory: "") +!237 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !54, file: !236, line: 39) +!238 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !99, file: !236, line: 40) +!239 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !59, file: !236, line: 43) +!240 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !144, file: !236, line: 46) +!241 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !39, file: !236, line: 51) +!242 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !43, file: !236, line: 52) +!243 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !244, file: !236, line: 54) +!244 = !DISubprogram(name: "abs", linkageName: "_ZSt3absg", scope: !32, file: !37, line: 103, type: !245, flags: DIFlagPrototyped, spFlags: 0) +!245 = !DISubroutineType(types: !246) +!246 = !{!247, !247} +!247 = !DIBasicType(name: "__float128", size: 128, encoding: DW_ATE_float) +!248 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !61, file: !236, line: 55) +!249 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !69, file: !236, line: 56) +!250 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !73, file: !236, line: 57) +!251 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !77, file: !236, line: 58) +!252 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !91, file: !236, line: 59) +!253 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !228, file: !236, line: 60) +!254 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !103, file: !236, line: 61) +!255 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !107, file: !236, line: 62) +!256 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !112, file: !236, line: 63) +!257 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !116, file: !236, line: 64) +!258 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !120, file: !236, line: 65) +!259 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !124, file: !236, line: 67) +!260 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !128, file: !236, line: 68) +!261 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !136, file: !236, line: 69) +!262 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !140, file: !236, line: 71) +!263 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !146, file: !236, line: 72) +!264 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !150, file: !236, line: 73) +!265 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !154, file: !236, line: 74) +!266 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !158, file: !236, line: 75) +!267 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !164, file: !236, line: 76) +!268 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !168, file: !236, line: 77) +!269 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !172, file: !236, line: 78) +!270 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !174, file: !236, line: 80) +!271 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !182, file: !236, line: 81) +!272 = !{i32 7, !"Dwarf Version", i32 4} +!273 = !{i32 2, !"Debug Info Version", i32 3} +!274 = !{i32 1, !"wchar_size", i32 4} +!275 = !{i32 7, !"uwtable", i32 1} +!276 = !{i32 7, !"frame-pointer", i32 2} +!277 = !{!"Ubuntu clang version 13.0.1-2ubuntu2.2"} +!278 = distinct !DISubprogram(name: "shift64RightJamming", linkageName: "_Z19shift64RightJammingyiPy", scope: !21, file: !21, line: 60, type: !279, scopeLine: 61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!279 = !DISubroutineType(types: !280) +!280 = !{null, !6, !281, !282} +!281 = !DIDerivedType(tag: DW_TAG_typedef, name: "int16", file: !7, line: 60, baseType: !17) +!282 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!283 = !DILocalVariable(name: "a", arg: 1, scope: !278, file: !21, line: 60, type: !6) +!284 = !DILocation(line: 0, scope: !278) +!285 = !DILocalVariable(name: "count", arg: 2, scope: !278, file: !21, line: 60, type: !281) +!286 = !DILocalVariable(name: "zPtr", arg: 3, scope: !278, file: !21, line: 60, type: !282) +!287 = !DILocation(line: 64, column: 13, scope: !288) +!288 = distinct !DILexicalBlock(scope: !278, file: !21, line: 64, column: 7) +!289 = !DILocation(line: 64, column: 7, scope: !278) +!290 = !DILocalVariable(name: "z", scope: !278, file: !21, line: 62, type: !6) +!291 = !DILocation(line: 67, column: 5, scope: !292) +!292 = distinct !DILexicalBlock(scope: !288, file: !21, line: 65, column: 5) +!293 = !DILocation(line: 68, column: 18, scope: !294) +!294 = distinct !DILexicalBlock(scope: !288, file: !21, line: 68, column: 12) +!295 = !DILocation(line: 68, column: 12, scope: !288) +!296 = !DILocation(line: 70, column: 14, scope: !297) +!297 = distinct !DILexicalBlock(scope: !294, file: !21, line: 69, column: 5) +!298 = !DILocation(line: 70, column: 35, scope: !297) +!299 = !DILocation(line: 70, column: 43, scope: !297) +!300 = !DILocation(line: 70, column: 30, scope: !297) +!301 = !DILocation(line: 70, column: 50, scope: !297) +!302 = !DILocation(line: 70, column: 26, scope: !297) +!303 = !DILocation(line: 70, column: 24, scope: !297) +!304 = !DILocation(line: 71, column: 5, scope: !297) +!305 = !DILocation(line: 74, column: 14, scope: !306) +!306 = distinct !DILexicalBlock(scope: !294, file: !21, line: 73, column: 5) +!307 = !DILocation(line: 74, column: 11, scope: !306) +!308 = !DILocation(line: 0, scope: !294) +!309 = !DILocation(line: 0, scope: !288) +!310 = !DILocation(line: 76, column: 9, scope: !278) +!311 = !DILocation(line: 78, column: 1, scope: !278) +!312 = distinct !DISubprogram(name: "mul64To128", linkageName: "_Z10mul64To128yyPyS_", scope: !21, file: !21, line: 87, type: !313, scopeLine: 88, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!313 = !DISubroutineType(types: !314) +!314 = !{null, !6, !6, !282, !282} +!315 = !DILocalVariable(name: "a", arg: 1, scope: !312, file: !21, line: 87, type: !6) +!316 = !DILocation(line: 0, scope: !312) +!317 = !DILocalVariable(name: "b", arg: 2, scope: !312, file: !21, line: 87, type: !6) +!318 = !DILocalVariable(name: "z0Ptr", arg: 3, scope: !312, file: !21, line: 87, type: !282) +!319 = !DILocalVariable(name: "z1Ptr", arg: 4, scope: !312, file: !21, line: 87, type: !282) +!320 = !DILocation(line: 92, column: 10, scope: !312) +!321 = !DILocalVariable(name: "aLow", scope: !312, file: !21, line: 89, type: !24) +!322 = !DILocation(line: 93, column: 13, scope: !312) +!323 = !DILocation(line: 93, column: 11, scope: !312) +!324 = !DILocalVariable(name: "aHigh", scope: !312, file: !21, line: 89, type: !24) +!325 = !DILocation(line: 94, column: 10, scope: !312) +!326 = !DILocalVariable(name: "bLow", scope: !312, file: !21, line: 89, type: !24) +!327 = !DILocation(line: 95, column: 13, scope: !312) +!328 = !DILocation(line: 95, column: 11, scope: !312) +!329 = !DILocalVariable(name: "bHigh", scope: !312, file: !21, line: 89, type: !24) +!330 = !DILocation(line: 96, column: 18, scope: !312) +!331 = !DILocation(line: 96, column: 26, scope: !312) +!332 = !DILocation(line: 96, column: 24, scope: !312) +!333 = !DILocalVariable(name: "z1", scope: !312, file: !21, line: 90, type: !6) +!334 = !DILocation(line: 97, column: 24, scope: !312) +!335 = !DILocation(line: 97, column: 32, scope: !312) +!336 = !DILocation(line: 97, column: 30, scope: !312) +!337 = !DILocalVariable(name: "zMiddleA", scope: !312, file: !21, line: 90, type: !6) +!338 = !DILocation(line: 98, column: 24, scope: !312) +!339 = !DILocation(line: 98, column: 33, scope: !312) +!340 = !DILocation(line: 98, column: 31, scope: !312) +!341 = !DILocalVariable(name: "zMiddleB", scope: !312, file: !21, line: 90, type: !6) +!342 = !DILocation(line: 99, column: 18, scope: !312) +!343 = !DILocation(line: 99, column: 27, scope: !312) +!344 = !DILocation(line: 99, column: 25, scope: !312) +!345 = !DILocalVariable(name: "z0", scope: !312, file: !21, line: 90, type: !6) +!346 = !DILocation(line: 100, column: 12, scope: !312) +!347 = !DILocation(line: 101, column: 30, scope: !312) +!348 = !DILocation(line: 101, column: 20, scope: !312) +!349 = !DILocation(line: 101, column: 43, scope: !312) +!350 = !DILocation(line: 101, column: 62, scope: !312) +!351 = !DILocation(line: 101, column: 50, scope: !312) +!352 = !DILocation(line: 101, column: 6, scope: !312) +!353 = !DILocation(line: 102, column: 12, scope: !312) +!354 = !DILocation(line: 103, column: 6, scope: !312) +!355 = !DILocation(line: 104, column: 13, scope: !312) +!356 = !DILocation(line: 104, column: 9, scope: !312) +!357 = !DILocation(line: 104, column: 6, scope: !312) +!358 = !DILocation(line: 105, column: 10, scope: !312) +!359 = !DILocation(line: 106, column: 10, scope: !312) +!360 = !DILocation(line: 108, column: 1, scope: !312) +!361 = distinct !DISubprogram(name: "float_raise", linkageName: "_Z11float_raisei", scope: !362, file: !362, line: 64, type: !363, scopeLine: 65, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!362 = !DIFile(filename: "dfmul/include/softfloat-specialize", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!363 = !DISubroutineType(types: !364) +!364 = !{null, !16} +!365 = !DILocalVariable(name: "flags", arg: 1, scope: !361, file: !362, line: 64, type: !16) +!366 = !DILocation(line: 0, scope: !361) +!367 = !DILocation(line: 66, column: 25, scope: !361) +!368 = !DILocation(line: 68, column: 1, scope: !361) +!369 = distinct !DISubprogram(name: "float64_is_nan", linkageName: "_Z14float64_is_nany", scope: !362, file: !362, line: 81, type: !370, scopeLine: 82, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!370 = !DISubroutineType(types: !371) +!371 = !{!372, !373} +!372 = !DIDerivedType(tag: DW_TAG_typedef, name: "flag", file: !7, line: 58, baseType: !17) +!373 = !DIDerivedType(tag: DW_TAG_typedef, name: "float64", file: !374, line: 54, baseType: !8) +!374 = !DIFile(filename: "dfmul/include/softfloat.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!375 = !DILocalVariable(name: "a", arg: 1, scope: !369, file: !362, line: 81, type: !373) +!376 = !DILocation(line: 0, scope: !369) +!377 = !DILocation(line: 84, column: 52, scope: !369) +!378 = !DILocation(line: 84, column: 38, scope: !369) +!379 = !DILocation(line: 84, column: 10, scope: !369) +!380 = !DILocation(line: 84, column: 3, scope: !369) +!381 = distinct !DISubprogram(name: "float64_is_signaling_nan", linkageName: "_Z24float64_is_signaling_nany", scope: !362, file: !362, line: 94, type: !370, scopeLine: 95, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!382 = !DILocalVariable(name: "a", arg: 1, scope: !381, file: !362, line: 94, type: !373) +!383 = !DILocation(line: 0, scope: !381) +!384 = !DILocation(line: 97, column: 15, scope: !381) +!385 = !DILocation(line: 97, column: 22, scope: !381) +!386 = !DILocation(line: 97, column: 31, scope: !381) +!387 = !DILocation(line: 97, column: 41, scope: !381) +!388 = !DILocation(line: 97, column: 47, scope: !381) +!389 = !DILocation(line: 97, column: 44, scope: !381) +!390 = !DILocation(line: 97, column: 10, scope: !381) +!391 = !DILocation(line: 97, column: 3, scope: !381) +!392 = distinct !DISubprogram(name: "extractFloat64Frac", scope: !3, file: !3, line: 89, type: !393, scopeLine: 89, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!393 = !DISubroutineType(types: !394) +!394 = !{!6, !373} +!395 = !DILocalVariable(name: "a", arg: 1, scope: !392, file: !3, line: 89, type: !373) +!396 = !DILocation(line: 0, scope: !392) +!397 = !DILocation(line: 90, column: 12, scope: !392) +!398 = !DILocation(line: 90, column: 3, scope: !392) +!399 = distinct !DISubprogram(name: "extractFloat64Exp", scope: !3, file: !3, line: 103, type: !400, scopeLine: 103, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!400 = !DISubroutineType(types: !401) +!401 = !{!281, !373} +!402 = !DILocalVariable(name: "a", arg: 1, scope: !399, file: !3, line: 103, type: !373) +!403 = !DILocation(line: 0, scope: !399) +!404 = !DILocation(line: 104, column: 13, scope: !399) +!405 = !DILocation(line: 104, column: 20, scope: !399) +!406 = !DILocation(line: 104, column: 10, scope: !399) +!407 = !DILocation(line: 104, column: 3, scope: !399) +!408 = distinct !DISubprogram(name: "extractFloat64Sign", scope: !3, file: !3, line: 117, type: !370, scopeLine: 117, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!409 = !DILocalVariable(name: "a", arg: 1, scope: !408, file: !3, line: 117, type: !373) +!410 = !DILocation(line: 0, scope: !408) +!411 = !DILocation(line: 118, column: 12, scope: !408) +!412 = !DILocation(line: 118, column: 10, scope: !408) +!413 = !DILocation(line: 118, column: 3, scope: !408) +!414 = distinct !DISubprogram(name: "normalizeFloat64Subnormal", scope: !3, file: !3, line: 133, type: !415, scopeLine: 133, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!415 = !DISubroutineType(types: !416) +!416 = !{null, !6, !417, !282} +!417 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !281, size: 64) +!418 = !DILocalVariable(name: "aSig", arg: 1, scope: !414, file: !3, line: 133, type: !6) +!419 = !DILocation(line: 0, scope: !414) +!420 = !DILocalVariable(name: "zExpPtr", arg: 2, scope: !414, file: !3, line: 133, type: !417) +!421 = !DILocalVariable(name: "zSigPtr", arg: 3, scope: !414, file: !3, line: 133, type: !282) +!422 = !DILocation(line: 136, column: 16, scope: !414) +!423 = !DILocation(line: 136, column: 43, scope: !414) +!424 = !DILocalVariable(name: "shiftCount", scope: !414, file: !3, line: 134, type: !16) +!425 = !DILocation(line: 137, column: 19, scope: !414) +!426 = !DILocation(line: 137, column: 12, scope: !414) +!427 = !DILocation(line: 138, column: 16, scope: !414) +!428 = !DILocation(line: 138, column: 12, scope: !414) +!429 = !DILocation(line: 140, column: 1, scope: !414) +!430 = distinct !DISubprogram(name: "countLeadingZeros64", linkageName: "_ZL19countLeadingZeros64y", scope: !21, file: !21, line: 160, type: !431, scopeLine: 161, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!431 = !DISubroutineType(types: !432) +!432 = !{!16, !6} +!433 = !DILocalVariable(name: "a", arg: 1, scope: !430, file: !21, line: 160, type: !6) +!434 = !DILocation(line: 0, scope: !430) +!435 = !DILocalVariable(name: "shiftCount", scope: !430, file: !21, line: 162, type: !16) +!436 = !DILocation(line: 165, column: 9, scope: !437) +!437 = distinct !DILexicalBlock(scope: !430, file: !21, line: 165, column: 7) +!438 = !DILocation(line: 165, column: 7, scope: !430) +!439 = !DILocation(line: 167, column: 18, scope: !440) +!440 = distinct !DILexicalBlock(scope: !437, file: !21, line: 166, column: 5) +!441 = !DILocation(line: 168, column: 5, scope: !440) +!442 = !DILocation(line: 171, column: 9, scope: !443) +!443 = distinct !DILexicalBlock(scope: !437, file: !21, line: 170, column: 5) +!444 = !DILocation(line: 173, column: 38, scope: !430) +!445 = !DILocation(line: 173, column: 17, scope: !430) +!446 = !DILocation(line: 173, column: 14, scope: !430) +!447 = !DILocation(line: 174, column: 3, scope: !430) +!448 = distinct !DISubprogram(name: "packFloat64", scope: !3, file: !3, line: 159, type: !449, scopeLine: 159, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!449 = !DISubroutineType(types: !450) +!450 = !{!373, !372, !281, !6} +!451 = !DILocalVariable(name: "zSign", arg: 1, scope: !448, file: !3, line: 159, type: !372) +!452 = !DILocation(line: 0, scope: !448) +!453 = !DILocalVariable(name: "zExp", arg: 2, scope: !448, file: !3, line: 159, type: !281) +!454 = !DILocalVariable(name: "zSig", arg: 3, scope: !448, file: !3, line: 159, type: !6) +!455 = !DILocation(line: 160, column: 21, scope: !448) +!456 = !DILocation(line: 160, column: 28, scope: !448) +!457 = !DILocation(line: 160, column: 48, scope: !448) +!458 = !DILocation(line: 160, column: 54, scope: !448) +!459 = !DILocation(line: 160, column: 35, scope: !448) +!460 = !DILocation(line: 160, column: 61, scope: !448) +!461 = !DILocation(line: 160, column: 3, scope: !448) +!462 = distinct !DISubprogram(name: "roundAndPackFloat64", scope: !3, file: !3, line: 190, type: !449, scopeLine: 190, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!463 = !DILocalVariable(name: "zSign", arg: 1, scope: !462, file: !3, line: 190, type: !372) +!464 = !DILocation(line: 0, scope: !462) +!465 = !DILocalVariable(name: "zExp", arg: 2, scope: !462, file: !3, line: 190, type: !281) +!466 = !DILocalVariable(name: "zSig", arg: 3, scope: !462, file: !3, line: 190, type: !6) +!467 = !DILocation(line: 190, column: 61, scope: !462) +!468 = !DILocation(line: 195, column: 18, scope: !462) +!469 = !DILocalVariable(name: "roundingMode", scope: !462, file: !3, line: 191, type: !16) +!470 = !DILocation(line: 196, column: 36, scope: !462) +!471 = !DILocation(line: 196, column: 22, scope: !462) +!472 = !DILocalVariable(name: "roundNearestEven", scope: !462, file: !3, line: 192, type: !372) +!473 = !DILocalVariable(name: "roundIncrement", scope: !462, file: !3, line: 193, type: !281) +!474 = !DILocation(line: 198, column: 8, scope: !475) +!475 = distinct !DILexicalBlock(scope: !462, file: !3, line: 198, column: 7) +!476 = !DILocation(line: 198, column: 7, scope: !462) +!477 = !DILocation(line: 200, column: 24, scope: !478) +!478 = distinct !DILexicalBlock(scope: !479, file: !3, line: 200, column: 11) +!479 = distinct !DILexicalBlock(scope: !475, file: !3, line: 199, column: 5) +!480 = !DILocation(line: 200, column: 11, scope: !479) +!481 = !DILocation(line: 203, column: 2, scope: !482) +!482 = distinct !DILexicalBlock(scope: !478, file: !3, line: 201, column: 2) +!483 = !DILocation(line: 207, column: 8, scope: !484) +!484 = distinct !DILexicalBlock(scope: !485, file: !3, line: 207, column: 8) +!485 = distinct !DILexicalBlock(scope: !478, file: !3, line: 205, column: 2) +!486 = !DILocation(line: 207, column: 8, scope: !485) +!487 = !DILocation(line: 209, column: 25, scope: !488) +!488 = distinct !DILexicalBlock(scope: !489, file: !3, line: 209, column: 12) +!489 = distinct !DILexicalBlock(scope: !484, file: !3, line: 208, column: 6) +!490 = !DILocation(line: 209, column: 12, scope: !489) +!491 = !DILocation(line: 210, column: 3, scope: !488) +!492 = !DILocation(line: 0, scope: !485) +!493 = !DILocation(line: 211, column: 6, scope: !489) +!494 = !DILocation(line: 214, column: 25, scope: !495) +!495 = distinct !DILexicalBlock(scope: !496, file: !3, line: 214, column: 12) +!496 = distinct !DILexicalBlock(scope: !484, file: !3, line: 213, column: 6) +!497 = !DILocation(line: 214, column: 12, scope: !496) +!498 = !DILocation(line: 215, column: 3, scope: !495) +!499 = !DILocation(line: 0, scope: !484) +!500 = !DILocation(line: 0, scope: !478) +!501 = !DILocation(line: 218, column: 5, scope: !479) +!502 = !DILocation(line: 219, column: 15, scope: !462) +!503 = !DILocation(line: 219, column: 20, scope: !462) +!504 = !DILocalVariable(name: "roundBits", scope: !462, file: !3, line: 193, type: !281) +!505 = !DILocation(line: 220, column: 25, scope: !506) +!506 = distinct !DILexicalBlock(scope: !462, file: !3, line: 220, column: 7) +!507 = !DILocation(line: 220, column: 16, scope: !506) +!508 = !DILocation(line: 220, column: 13, scope: !506) +!509 = !DILocation(line: 220, column: 7, scope: !462) +!510 = !DILocation(line: 222, column: 18, scope: !511) +!511 = distinct !DILexicalBlock(scope: !512, file: !3, line: 222, column: 11) +!512 = distinct !DILexicalBlock(scope: !506, file: !3, line: 221, column: 5) +!513 = !DILocation(line: 223, column: 4, scope: !511) +!514 = !DILocation(line: 223, column: 14, scope: !511) +!515 = !DILocation(line: 223, column: 24, scope: !511) +!516 = !DILocation(line: 223, column: 39, scope: !511) +!517 = !DILocation(line: 223, column: 46, scope: !511) +!518 = !DILocation(line: 223, column: 44, scope: !511) +!519 = !DILocation(line: 223, column: 62, scope: !511) +!520 = !DILocation(line: 222, column: 11, scope: !512) +!521 = !DILocation(line: 225, column: 4, scope: !522) +!522 = distinct !DILexicalBlock(scope: !511, file: !3, line: 224, column: 2) +!523 = !DILocation(line: 0, scope: !448, inlinedAt: !524) +!524 = distinct !DILocation(line: 226, column: 11, scope: !522) +!525 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !524) +!526 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !524) +!527 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !524) +!528 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !524) +!529 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !524) +!530 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !524) +!531 = !DILocation(line: 226, column: 59, scope: !522) +!532 = !DILocation(line: 226, column: 43, scope: !522) +!533 = !DILocation(line: 226, column: 41, scope: !522) +!534 = !DILocation(line: 226, column: 4, scope: !522) +!535 = !DILocation(line: 228, column: 16, scope: !536) +!536 = distinct !DILexicalBlock(scope: !512, file: !3, line: 228, column: 11) +!537 = !DILocation(line: 228, column: 11, scope: !512) +!538 = !DILocalVariable(name: "isTiny", scope: !462, file: !3, line: 192, type: !372) +!539 = !DILocation(line: 233, column: 25, scope: !540) +!540 = distinct !DILexicalBlock(scope: !536, file: !3, line: 229, column: 2) +!541 = !DILocation(line: 233, column: 31, scope: !540) +!542 = !DILocation(line: 233, column: 4, scope: !540) +!543 = !DILocation(line: 235, column: 16, scope: !540) +!544 = !DILocation(line: 235, column: 21, scope: !540) +!545 = !DILocation(line: 236, column: 8, scope: !546) +!546 = distinct !DILexicalBlock(scope: !540, file: !3, line: 236, column: 8) +!547 = !DILocation(line: 236, column: 15, scope: !546) +!548 = !DILocation(line: 236, column: 18, scope: !546) +!549 = !DILocation(line: 236, column: 8, scope: !540) +!550 = !DILocation(line: 237, column: 6, scope: !546) +!551 = !DILocation(line: 238, column: 2, scope: !540) +!552 = !DILocation(line: 239, column: 5, scope: !512) +!553 = !DILocation(line: 240, column: 7, scope: !554) +!554 = distinct !DILexicalBlock(scope: !462, file: !3, line: 240, column: 7) +!555 = !DILocation(line: 240, column: 7, scope: !462) +!556 = !DILocation(line: 241, column: 27, scope: !554) +!557 = !DILocation(line: 241, column: 5, scope: !554) +!558 = !DILocation(line: 242, column: 11, scope: !462) +!559 = !DILocation(line: 242, column: 18, scope: !462) +!560 = !DILocation(line: 242, column: 16, scope: !462) +!561 = !DILocation(line: 242, column: 34, scope: !462) +!562 = !DILocation(line: 242, column: 8, scope: !462) +!563 = !DILocation(line: 243, column: 25, scope: !462) +!564 = !DILocation(line: 243, column: 34, scope: !462) +!565 = !DILocation(line: 243, column: 13, scope: !462) +!566 = !DILocation(line: 243, column: 40, scope: !462) +!567 = !DILocation(line: 243, column: 11, scope: !462) +!568 = !DILocation(line: 243, column: 8, scope: !462) +!569 = !DILocation(line: 244, column: 7, scope: !570) +!570 = distinct !DILexicalBlock(scope: !462, file: !3, line: 244, column: 7) +!571 = !DILocation(line: 244, column: 12, scope: !570) +!572 = !DILocation(line: 244, column: 7, scope: !462) +!573 = !DILocation(line: 245, column: 5, scope: !570) +!574 = !DILocation(line: 246, column: 36, scope: !462) +!575 = !DILocation(line: 0, scope: !448, inlinedAt: !576) +!576 = distinct !DILocation(line: 246, column: 10, scope: !462) +!577 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !576) +!578 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !576) +!579 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !576) +!580 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !576) +!581 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !576) +!582 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !576) +!583 = !DILocation(line: 246, column: 3, scope: !462) +!584 = !DILocation(line: 248, column: 1, scope: !462) +!585 = distinct !DISubprogram(name: "float64_mul", scope: !3, file: !3, line: 272, type: !586, scopeLine: 272, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!586 = !DISubroutineType(types: !587) +!587 = !{!373, !588, !589} +!588 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055xAcceleration", file: !3, line: 262, baseType: !373) +!589 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055yAcceleration", file: !3, line: 263, baseType: !373) +!590 = !DILocalVariable(name: "a", arg: 1, scope: !585, file: !3, line: 272, type: !588) +!591 = !DILocation(line: 0, scope: !585) +!592 = !DILocalVariable(name: "b", arg: 2, scope: !585, file: !3, line: 272, type: !589) +!593 = !DILocalVariable(name: "aExp", scope: !585, file: !3, line: 284, type: !281) +!594 = !DILocation(line: 284, column: 9, scope: !585) +!595 = !DILocalVariable(name: "bExp", scope: !585, file: !3, line: 284, type: !281) +!596 = !DILocation(line: 284, column: 15, scope: !585) +!597 = !DILocalVariable(name: "aSig", scope: !585, file: !3, line: 285, type: !6) +!598 = !DILocation(line: 285, column: 10, scope: !585) +!599 = !DILocalVariable(name: "bSig", scope: !585, file: !3, line: 285, type: !6) +!600 = !DILocation(line: 285, column: 16, scope: !585) +!601 = !DILocalVariable(name: "zSig0", scope: !585, file: !3, line: 285, type: !6) +!602 = !DILocation(line: 285, column: 22, scope: !585) +!603 = !DILocalVariable(name: "zSig1", scope: !585, file: !3, line: 285, type: !6) +!604 = !DILocation(line: 285, column: 29, scope: !585) +!605 = !DILocation(line: 0, scope: !392, inlinedAt: !606) +!606 = distinct !DILocation(line: 287, column: 10, scope: !585) +!607 = !DILocation(line: 90, column: 12, scope: !392, inlinedAt: !606) +!608 = !DILocation(line: 287, column: 8, scope: !585) +!609 = !DILocation(line: 0, scope: !399, inlinedAt: !610) +!610 = distinct !DILocation(line: 288, column: 10, scope: !585) +!611 = !DILocation(line: 104, column: 13, scope: !399, inlinedAt: !610) +!612 = !DILocation(line: 104, column: 20, scope: !399, inlinedAt: !610) +!613 = !DILocation(line: 104, column: 10, scope: !399, inlinedAt: !610) +!614 = !DILocation(line: 288, column: 8, scope: !585) +!615 = !DILocation(line: 0, scope: !408, inlinedAt: !616) +!616 = distinct !DILocation(line: 289, column: 11, scope: !585) +!617 = !DILocation(line: 118, column: 12, scope: !408, inlinedAt: !616) +!618 = !DILocation(line: 118, column: 10, scope: !408, inlinedAt: !616) +!619 = !DILocalVariable(name: "aSign", scope: !585, file: !3, line: 283, type: !372) +!620 = !DILocation(line: 0, scope: !392, inlinedAt: !621) +!621 = distinct !DILocation(line: 290, column: 10, scope: !585) +!622 = !DILocation(line: 90, column: 12, scope: !392, inlinedAt: !621) +!623 = !DILocation(line: 290, column: 8, scope: !585) +!624 = !DILocation(line: 0, scope: !399, inlinedAt: !625) +!625 = distinct !DILocation(line: 291, column: 10, scope: !585) +!626 = !DILocation(line: 104, column: 13, scope: !399, inlinedAt: !625) +!627 = !DILocation(line: 104, column: 20, scope: !399, inlinedAt: !625) +!628 = !DILocation(line: 104, column: 10, scope: !399, inlinedAt: !625) +!629 = !DILocation(line: 291, column: 8, scope: !585) +!630 = !DILocation(line: 0, scope: !408, inlinedAt: !631) +!631 = distinct !DILocation(line: 292, column: 11, scope: !585) +!632 = !DILocation(line: 118, column: 12, scope: !408, inlinedAt: !631) +!633 = !DILocation(line: 118, column: 10, scope: !408, inlinedAt: !631) +!634 = !DILocalVariable(name: "bSign", scope: !585, file: !3, line: 283, type: !372) +!635 = !DILocation(line: 293, column: 17, scope: !585) +!636 = !DILocalVariable(name: "zSign", scope: !585, file: !3, line: 283, type: !372) +!637 = !DILocation(line: 294, column: 7, scope: !638) +!638 = distinct !DILexicalBlock(scope: !585, file: !3, line: 294, column: 7) +!639 = !DILocation(line: 294, column: 12, scope: !638) +!640 = !DILocation(line: 294, column: 7, scope: !585) +!641 = !DILocation(line: 296, column: 11, scope: !642) +!642 = distinct !DILexicalBlock(scope: !643, file: !3, line: 296, column: 11) +!643 = distinct !DILexicalBlock(scope: !638, file: !3, line: 295, column: 5) +!644 = !DILocation(line: 296, column: 16, scope: !642) +!645 = !DILocation(line: 296, column: 21, scope: !642) +!646 = !DILocation(line: 296, column: 26, scope: !642) +!647 = !DILocation(line: 296, column: 36, scope: !642) +!648 = !DILocation(line: 296, column: 39, scope: !642) +!649 = !DILocation(line: 296, column: 11, scope: !643) +!650 = !DILocation(line: 297, column: 9, scope: !642) +!651 = !DILocation(line: 297, column: 2, scope: !642) +!652 = !DILocation(line: 298, column: 12, scope: !653) +!653 = distinct !DILexicalBlock(scope: !643, file: !3, line: 298, column: 11) +!654 = !DILocation(line: 298, column: 19, scope: !653) +!655 = !DILocation(line: 298, column: 17, scope: !653) +!656 = !DILocation(line: 298, column: 25, scope: !653) +!657 = !DILocation(line: 298, column: 11, scope: !643) +!658 = !DILocation(line: 300, column: 4, scope: !659) +!659 = distinct !DILexicalBlock(scope: !653, file: !3, line: 299, column: 2) +!660 = !DILocation(line: 301, column: 4, scope: !659) +!661 = !DILocation(line: 0, scope: !448, inlinedAt: !662) +!662 = distinct !DILocation(line: 303, column: 14, scope: !643) +!663 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !662) +!664 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !662) +!665 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !662) +!666 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !662) +!667 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !662) +!668 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !662) +!669 = !DILocation(line: 303, column: 7, scope: !643) +!670 = !DILocation(line: 305, column: 7, scope: !671) +!671 = distinct !DILexicalBlock(scope: !585, file: !3, line: 305, column: 7) +!672 = !DILocation(line: 305, column: 12, scope: !671) +!673 = !DILocation(line: 305, column: 7, scope: !585) +!674 = !DILocation(line: 307, column: 11, scope: !675) +!675 = distinct !DILexicalBlock(scope: !676, file: !3, line: 307, column: 11) +!676 = distinct !DILexicalBlock(scope: !671, file: !3, line: 306, column: 5) +!677 = !DILocation(line: 307, column: 11, scope: !676) +!678 = !DILocation(line: 308, column: 9, scope: !675) +!679 = !DILocation(line: 308, column: 2, scope: !675) +!680 = !DILocation(line: 309, column: 12, scope: !681) +!681 = distinct !DILexicalBlock(scope: !676, file: !3, line: 309, column: 11) +!682 = !DILocation(line: 309, column: 19, scope: !681) +!683 = !DILocation(line: 309, column: 17, scope: !681) +!684 = !DILocation(line: 309, column: 25, scope: !681) +!685 = !DILocation(line: 309, column: 11, scope: !676) +!686 = !DILocation(line: 311, column: 4, scope: !687) +!687 = distinct !DILexicalBlock(scope: !681, file: !3, line: 310, column: 2) +!688 = !DILocation(line: 312, column: 4, scope: !687) +!689 = !DILocation(line: 0, scope: !448, inlinedAt: !690) +!690 = distinct !DILocation(line: 314, column: 14, scope: !676) +!691 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !690) +!692 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !690) +!693 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !690) +!694 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !690) +!695 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !690) +!696 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !690) +!697 = !DILocation(line: 314, column: 7, scope: !676) +!698 = !DILocation(line: 316, column: 7, scope: !699) +!699 = distinct !DILexicalBlock(scope: !585, file: !3, line: 316, column: 7) +!700 = !DILocation(line: 316, column: 12, scope: !699) +!701 = !DILocation(line: 316, column: 7, scope: !585) +!702 = !DILocation(line: 318, column: 11, scope: !703) +!703 = distinct !DILexicalBlock(scope: !704, file: !3, line: 318, column: 11) +!704 = distinct !DILexicalBlock(scope: !699, file: !3, line: 317, column: 5) +!705 = !DILocation(line: 318, column: 16, scope: !703) +!706 = !DILocation(line: 318, column: 11, scope: !704) +!707 = !DILocation(line: 0, scope: !448, inlinedAt: !708) +!708 = distinct !DILocation(line: 319, column: 9, scope: !703) +!709 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !708) +!710 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !708) +!711 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !708) +!712 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !708) +!713 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !708) +!714 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !708) +!715 = !DILocation(line: 319, column: 2, scope: !703) +!716 = !DILocation(line: 320, column: 34, scope: !704) +!717 = !DILocation(line: 320, column: 7, scope: !704) +!718 = !DILocation(line: 321, column: 5, scope: !704) +!719 = !DILocation(line: 322, column: 7, scope: !720) +!720 = distinct !DILexicalBlock(scope: !585, file: !3, line: 322, column: 7) +!721 = !DILocation(line: 322, column: 12, scope: !720) +!722 = !DILocation(line: 322, column: 7, scope: !585) +!723 = !DILocation(line: 324, column: 11, scope: !724) +!724 = distinct !DILexicalBlock(scope: !725, file: !3, line: 324, column: 11) +!725 = distinct !DILexicalBlock(scope: !720, file: !3, line: 323, column: 5) +!726 = !DILocation(line: 324, column: 16, scope: !724) +!727 = !DILocation(line: 324, column: 11, scope: !725) +!728 = !DILocation(line: 0, scope: !448, inlinedAt: !729) +!729 = distinct !DILocation(line: 325, column: 9, scope: !724) +!730 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !729) +!731 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !729) +!732 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !729) +!733 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !729) +!734 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !729) +!735 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !729) +!736 = !DILocation(line: 325, column: 2, scope: !724) +!737 = !DILocation(line: 326, column: 34, scope: !725) +!738 = !DILocation(line: 326, column: 7, scope: !725) +!739 = !DILocation(line: 327, column: 5, scope: !725) +!740 = !DILocation(line: 328, column: 10, scope: !585) +!741 = !DILocation(line: 328, column: 17, scope: !585) +!742 = !DILocation(line: 328, column: 15, scope: !585) +!743 = !DILocation(line: 328, column: 22, scope: !585) +!744 = !DILocalVariable(name: "zExp", scope: !585, file: !3, line: 284, type: !281) +!745 = !DILocation(line: 329, column: 11, scope: !585) +!746 = !DILocation(line: 329, column: 16, scope: !585) +!747 = !DILocation(line: 329, column: 46, scope: !585) +!748 = !DILocation(line: 329, column: 8, scope: !585) +!749 = !DILocation(line: 330, column: 11, scope: !585) +!750 = !DILocation(line: 330, column: 16, scope: !585) +!751 = !DILocation(line: 330, column: 46, scope: !585) +!752 = !DILocation(line: 330, column: 8, scope: !585) +!753 = !DILocation(line: 331, column: 15, scope: !585) +!754 = !DILocation(line: 331, column: 21, scope: !585) +!755 = !DILocation(line: 331, column: 3, scope: !585) +!756 = !DILocation(line: 332, column: 13, scope: !585) +!757 = !DILocation(line: 332, column: 19, scope: !585) +!758 = !DILocation(line: 332, column: 12, scope: !585) +!759 = !DILocation(line: 332, column: 9, scope: !585) +!760 = !DILocation(line: 333, column: 23, scope: !761) +!761 = distinct !DILexicalBlock(scope: !585, file: !3, line: 333, column: 7) +!762 = !DILocation(line: 333, column: 29, scope: !761) +!763 = !DILocation(line: 333, column: 9, scope: !761) +!764 = !DILocation(line: 333, column: 7, scope: !585) +!765 = !DILocation(line: 335, column: 13, scope: !766) +!766 = distinct !DILexicalBlock(scope: !761, file: !3, line: 334, column: 5) +!767 = !DILocation(line: 336, column: 7, scope: !766) +!768 = !DILocation(line: 337, column: 5, scope: !766) +!769 = !DILocation(line: 338, column: 44, scope: !585) +!770 = !DILocation(line: 338, column: 10, scope: !585) +!771 = !DILocation(line: 338, column: 3, scope: !585) +!772 = !DILocation(line: 340, column: 1, scope: !585) +!773 = distinct !DISubprogram(name: "propagateFloat64NaN", linkageName: "_ZL19propagateFloat64NaNyy", scope: !362, file: !362, line: 108, type: !774, scopeLine: 109, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!774 = !DISubroutineType(types: !775) +!775 = !{!373, !373, !373} +!776 = !DILocalVariable(name: "a", arg: 1, scope: !773, file: !362, line: 108, type: !373) +!777 = !DILocation(line: 0, scope: !773) +!778 = !DILocalVariable(name: "b", arg: 2, scope: !773, file: !362, line: 108, type: !373) +!779 = !DILocation(line: 112, column: 12, scope: !773) +!780 = !DILocalVariable(name: "aIsNaN", scope: !773, file: !362, line: 110, type: !372) +!781 = !DILocation(line: 113, column: 21, scope: !773) +!782 = !DILocalVariable(name: "aIsSignalingNaN", scope: !773, file: !362, line: 110, type: !372) +!783 = !DILocation(line: 114, column: 12, scope: !773) +!784 = !DILocalVariable(name: "bIsNaN", scope: !773, file: !362, line: 110, type: !372) +!785 = !DILocation(line: 115, column: 21, scope: !773) +!786 = !DILocalVariable(name: "bIsSignalingNaN", scope: !773, file: !362, line: 110, type: !372) +!787 = !DILocation(line: 116, column: 5, scope: !773) +!788 = !DILocation(line: 117, column: 5, scope: !773) +!789 = !DILocation(line: 118, column: 23, scope: !790) +!790 = distinct !DILexicalBlock(scope: !773, file: !362, line: 118, column: 7) +!791 = !DILocation(line: 118, column: 7, scope: !790) +!792 = !DILocation(line: 118, column: 7, scope: !773) +!793 = !DILocation(line: 119, column: 5, scope: !790) +!794 = !DILocation(line: 120, column: 10, scope: !773) +!795 = !DILocation(line: 120, column: 32, scope: !773) +!796 = !DILocation(line: 120, column: 54, scope: !773) +!797 = !DILocation(line: 120, column: 3, scope: !773) +!798 = !DILocalVariable(name: "a", arg: 1, scope: !20, file: !21, line: 116, type: !24) +!799 = !DILocation(line: 0, scope: !20) +!800 = !DILocalVariable(name: "shiftCount", scope: !20, file: !21, line: 136, type: !16) +!801 = !DILocation(line: 139, column: 9, scope: !802) +!802 = distinct !DILexicalBlock(scope: !20, file: !21, line: 139, column: 7) +!803 = !DILocation(line: 139, column: 7, scope: !20) +!804 = !DILocation(line: 141, column: 18, scope: !805) +!805 = distinct !DILexicalBlock(scope: !802, file: !21, line: 140, column: 5) +!806 = !DILocation(line: 142, column: 9, scope: !805) +!807 = !DILocation(line: 143, column: 5, scope: !805) +!808 = !DILocation(line: 144, column: 9, scope: !809) +!809 = distinct !DILexicalBlock(scope: !20, file: !21, line: 144, column: 7) +!810 = !DILocation(line: 144, column: 7, scope: !20) +!811 = !DILocation(line: 146, column: 18, scope: !812) +!812 = distinct !DILexicalBlock(scope: !809, file: !21, line: 145, column: 5) +!813 = !DILocation(line: 147, column: 9, scope: !812) +!814 = !DILocation(line: 148, column: 5, scope: !812) +!815 = !DILocation(line: 149, column: 41, scope: !20) +!816 = !DILocation(line: 149, column: 17, scope: !20) +!817 = !DILocation(line: 149, column: 14, scope: !20) +!818 = !DILocation(line: 150, column: 3, scope: !20) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index e0e7fba18..8d20edb5f 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -66,43 +66,29 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FileSystem.h" using namespace llvm; extern "C" { -// TODO add errorLog -void -handleError(State * N, const std::string & errorMessage) -{ - flexprint(N->Fe, N->Fm, N->Fperr, "%s\n", errorMessage.c_str()); - // Log the error to a file - std::ofstream errorLog("error.log", std::ios_base::app); - errorLog << errorMessage << std::endl; - errorLog.close(); - // Throw an exception to handle the error - throw std::runtime_error(errorMessage); -} -std::unique_ptr -loadModule(State * N) -{ - try - { - SMDiagnostic Err; - LLVMContext Context; - std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); - if (!Mod) - { - handleError(N, "Error: Couldn't parse IR file."); - } - return Mod; - } catch (const std::exception & e) - { - flexprint(N->Fe, N->Fm, N->Fperr, "Exception: %s\n", e.what()); - throw; // Re-throw the exception for further handling +// Function to save the IR of a module to a file +void saveModuleIR(llvm::Module &M, const std::string &fileName) { + std::error_code EC; + llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); + if (EC) { + llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; + return; } + M.print(file, nullptr); + llvm::errs() << "IR saved to " << fileName << "\n"; + file.close(); } + + void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { @@ -240,9 +226,11 @@ overloadFunc(std::unique_ptr & Mod, std::map & void irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverload, bool enableBuiltinAssume) { + llvm::errs() << "Entering irPassLLVMIROptimizeByRange\n"; if (N->llvmIR == nullptr) { flexprint(N->Fe, N->Fm, N->Fperr, "Please specify the LLVM IR input file\n"); + llvm::errs() << "Error: llvmIR is nullptr\n"; fatal(N, Esanity); } @@ -251,9 +239,11 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); if (!Mod) { - handleError(N, "Error: Couldn't parse IR file."); + flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); + llvm::errs() << "Error: Couldn't parse IR file: " << N->llvmIR << "\n"; + fatal(N, Esanity); } - + llvm::errs() << "Module successfully parsed: " << N->llvmIR << "\n"; auto globalBoundInfo = new BoundInfo(); std::map funcBoundInfo; @@ -280,8 +270,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl { if (!globalVar.hasInitializer()) { + llvm::errs() << "Global variable " << globalVar.getName() << " has no initializer\n"; continue; } + llvm::errs() << "Processing global variable: " << globalVar.getName() << "\n"; auto constValue = globalVar.getInitializer(); if (ConstantFP * constFp = llvm::dyn_cast(constValue)) { @@ -347,9 +339,11 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl if (enableQuantization) { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; std::vector functionsToInsert; for (auto & mi : *Mod) { + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; irPassLLVMIRAutoQuantization(N, mi, functionsToInsert); } for (auto mi : functionsToInsert) @@ -358,159 +352,153 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl Mod->getFunctionList().push_front(mi); } } - std::map callerMap; - bool useOverLoad = false; - - processModule(N, Mod.get(), globalBoundInfo, funcBoundInfo, typeRange, virtualRegisterVectorRange, callerMap, useOverLoad); - - // Dump BC file to a file - dumpIR(N, "output", Mod); -} - -void -processModule(State * N, Module * Mod, BoundInfo * globalBoundInfo, std::map & funcBoundInfo, - std::map> & typeRange, - std::map>> & virtualRegisterVectorRange, - std::map & callerMap, bool & useOverLoad) -{ - analyzeRange(N, Mod, globalBoundInfo, funcBoundInfo, typeRange, virtualRegisterVectorRange, callerMap, useOverLoad); - - /* - * analyze the range of all local variables in each function - * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - std::map callerMap; - callerMap.clear(); - funcBoundInfo.clear(); - bool useOverLoad = false; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - } - flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - shrinkType(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } +// /* +// * analyze the range of all local variables in each function +// * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// std::map callerMap; +// callerMap.clear(); +// funcBoundInfo.clear(); +// bool useOverLoad = false; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// shrinkType(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// memoryAlignment(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); +// +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = true; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } +// +// /* +// * simplify the condition of each branch +// * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// simplifyControlFlow(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// legacy::PassManager passManager; +// passManager.add(createCFGSimplificationPass()); +// passManager.add(createInstSimplifyLegacyPass()); +// passManager.add(createGlobalDCEPass()); +// passManager.run(*Mod); +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = false; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// constantSubstitution(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); + + + // 打印处理后的模块IR并保存到文件 + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); - // Memory Alignment - flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - memoryAlignment(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } /* - * remove the functions that are optimized by passes. + * Dump BC file to a file. * */ - if (useOverLoad) - cleanFunctionMap(Mod, callerMap); - - if (useOverLoad) - overloadFunc(Mod, callerMap); - - callerMap.clear(); - funcBoundInfo.clear(); - useOverLoad = true; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - } - - /* - * simplify the condition of each branch - * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - simplifyControlFlow(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } - - legacy::PassManager passManager; - passManager.add(createCFGSimplificationPass()); - passManager.add(createInstSimplifyLegacyPass()); - passManager.add(createGlobalDCEPass()); - passManager.run(*Mod); - - /* - * remove the functions that are optimized by passes. - * */ - if (useOverLoad) - cleanFunctionMap(Mod, callerMap); - - if (useOverLoad) - overloadFunc(Mod, callerMap); - - flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - callerMap.clear(); - funcBoundInfo.clear(); - useOverLoad = false; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - } - - // constant substritution - flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - constantSubstitution(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } - - /* - * remove the functions that are optimized by passes. - * */ - if (useOverLoad) - cleanFunctionMap(Mod, callerMap); - - if (useOverLoad) - overloadFunc(Mod, callerMap); + dumpIR(N, "output", Mod); + llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; +} } From 22cb3a8af33722eaeb5c723fb55dd826c3b8d13c Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 26 Jun 2024 20:11:05 +0100 Subject: [PATCH 023/213] update more test log and save IR function * dev2. --- ...dee7a34daf3be6125212d01c917f40dc474170.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt diff --git a/analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt b/analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt new file mode 100644 index 000000000..135d808ed --- /dev/null +++ b/analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt @@ -0,0 +1,48 @@ + +changeset: 1616:6edee7a34daf3be6125212d01c917f40dc474170 +char kNewtonVersion[] = "0.3-alpha-1616 (6edee7a34daf3be6125212d01c917f40dc474170) (build 06-26-2024-17:45-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From e770cd3b992411d872bc7946f0e905acdffceee2 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 6 Jul 2024 21:36:16 +0100 Subject: [PATCH 024/213] solved store and load type issue * dev2. --- ...9d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt | 48 + .../newton-irPass-LLVMIR-quantization.cpp | 933 ++++++++++++------ 2 files changed, 658 insertions(+), 323 deletions(-) create mode 100644 analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt diff --git a/analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt b/analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt new file mode 100644 index 000000000..d06264c82 --- /dev/null +++ b/analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt @@ -0,0 +1,48 @@ + +changeset: 1617:549d564728d0ccca2e7a79b3ab4387cd5c68e43a +char kNewtonVersion[] = "0.3-alpha-1617 (549d564728d0ccca2e7a79b3ab4387cd5c68e43a) (build 06-26-2024-20:11-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 8f06dc939..bac12d2c3 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -30,7 +30,10 @@ */ #include "newton-irPass-LLVMIR-quantization.h" - +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Utils/ValueMapper.h" +#include "llvm/ADT/SmallVector.h" +#include using namespace llvm; #define FRAC_Q 16 @@ -39,50 +42,213 @@ using namespace llvm; extern "C" { +//check if the type is a floating type +bool isValidFloatingType(Type *type) { + return type->isFloatTy() || type->isDoubleTy(); +} + + + // Set the quantized type for a given value -void -setQuantizedType(Value * inValue, Type * quantizedType) -{ - auto valueType = inValue->getType(); +void setQuantizedType(Value * inValue, Type * quantizedType) { + auto valueType = inValue->getType(); unsigned pointerAddr; bool isPointer = false; if (valueType != nullptr) { - if (valueType->isPointerTy()) - { + if (valueType->isPointerTy()) { isPointer = true; - // If the value is a pointer, get the address space and element type pointerAddr = valueType->getPointerAddressSpace(); - valueType = valueType->getPointerElementType(); + valueType = valueType->getPointerElementType(); } if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) { - // If the value is a pointer, get the address space and element type - if (isPointer) - { + if (isPointer) { inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - } - else - { - // Otherwise, directly set the type to the quantized type + } else { inValue->mutateType(quantizedType); } } - else - { - llvm::errs() << "Unsupported type for quantization\n"; + } +} + +// Quantize constants within an instruction +std::unordered_map quantizedValueCache; + +// Ensure load and store instructions have matching types +void handleLoadStoreInstructions(Function &llvmIrFunction, Type *quantizedType) { + llvm::errs() << "Entering handleLoadStoreInstructions\n"; + for (BasicBlock &llvmIrBasicBlock : llvmIrFunction) { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { + Instruction *llvmIrInstruction = &*itBB++; + if (llvmIrInstruction->getOpcode() == Instruction::Load) { + if (auto loadInst = dyn_cast(llvmIrInstruction)) { + auto ptr = loadInst->getPointerOperand(); + if (ptr->getType()->getPointerElementType()->isFloatTy() || + ptr->getType()->getPointerElementType()->isDoubleTy()) { + ptr->mutateType(quantizedType->getPointerTo()); + } + } + } else if (llvmIrInstruction->getOpcode() == Instruction::Store) { + if (auto storeInst = dyn_cast(llvmIrInstruction)) { + auto ptr = storeInst->getPointerOperand(); + if (ptr->getType()->getPointerElementType()->isFloatTy() || + ptr->getType()->getPointerElementType()->isDoubleTy()) { + ptr->mutateType(quantizedType->getPointerTo()); + } + } + } } } - else - { - llvm::errs() << "Null type encountered in setQuantizedType\n"; +} + +/*Function *replaceFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { + auto &context = llvmIrFunction.getContext(); + std::vector paramTypes; + for (auto &arg : llvmIrFunction.args()) { + if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) { + paramTypes.push_back(quantizedType); + } else { + paramTypes.push_back(arg.getType()); + } } + + Type *returnType = llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy() + ? quantizedType + : llvmIrFunction.getReturnType(); + + FunctionType *newFuncType = FunctionType::get(returnType, paramTypes, llvmIrFunction.isVarArg()); + Function *newFunction = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName(), llvmIrFunction.getParent()); + + // Copy the function body to the new function + newFunction->getBasicBlockList().splice(newFunction->begin(), llvmIrFunction.getBasicBlockList()); + + // Update the arguments mapping + auto newArgIter = newFunction->arg_begin(); + for (auto &arg : llvmIrFunction.args()) { + arg.replaceAllUsesWith(&*newArgIter); + newArgIter->takeName(&arg); + ++newArgIter; + } + + return newFunction; +}*/ + + + +// Function to create a new function with quantized return type and parameters +llvm::Function* createNewFunctionWithQuantizedType(llvm::Function &oldFunction, Type *quantizedType) { + // Create a new function type with quantized arguments and return type + std::vector paramTypes; + for (auto &arg : oldFunction.args()) { + if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) { + paramTypes.push_back(quantizedType); + } else { + paramTypes.push_back(arg.getType()); + } + } + + // Determine the return type for the new function + Type *returnType = oldFunction.getReturnType(); + if (returnType->isFloatTy() || returnType->isDoubleTy()) { + returnType = quantizedType; + } + // Create a new function type with the quantized return type and parameters + llvm::FunctionType *newFuncType = llvm::FunctionType::get(returnType, paramTypes, oldFunction.isVarArg()); + // Create a new function with the new function type in the same module as the old function + llvm::Function *newFunction = llvm::Function::Create(newFuncType, oldFunction.getLinkage(), oldFunction.getName(), oldFunction.getParent()); + + + return newFunction; +} + + +// Function to rename the old function to avoid name conflict +void renameOldFunction(llvm::Function &oldFunction) { + std::string oldFunctionName = oldFunction.getName().str(); + oldFunction.setName(oldFunctionName + "_old"); } +// Function to replace all calls to the old function with calls to the new function +void replaceCallsToOldFunction(llvm::Function &oldFunction, llvm::Function &newFunction) { + std::vector callsToReplace; + + for (auto &use : oldFunction.uses()) { + if (auto *call = llvm::dyn_cast(use.getUser())) { + callsToReplace.push_back(call); + } + } + + // Replace each call to the old function with a call to the new function + for (auto *call : callsToReplace) { + llvm::SmallVector args(call->arg_begin(), call->arg_end()); + llvm::IRBuilder<> builder(call); + llvm::CallInst *newCall = builder.CreateCall(&newFunction, args); + call->replaceAllUsesWith(newCall); + call->eraseFromParent(); + } +} + + + // Quantize constants within an instruction -void + +void quantizeConstant(Instruction *inInstruction, Type *quantizedType) { + llvm::errs() << "Entering quantizeConstant\n"; + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { + Value *inValue = inInstruction->getOperand(idx); + + + // Skip if the operand is not a floating-point constant + if (!isa(inValue)) { + continue; + } + + + auto *constFp = llvm::dyn_cast(inValue); + + + // Check the cache for the quantized value + auto cachedValue = quantizedValueCache.find(constFp); + + if (cachedValue != quantizedValueCache.end()) { + inInstruction->replaceUsesOfWith(inValue, cachedValue->second); + continue; + } + + // Compute the quantized value if not found in cache + Value *newValue = nullptr; + if (inValue->getType()->isFloatTy()) { + // Convert float constant to fixed-point + float constValue = constFp->getValueAPF().convertToFloat(); + constValue *= FRAC_BASE; + int32_t fixedPointValue = round(constValue); + //newValue = ConstantInt::get(quantizedType, round(constValue), true); + newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / FRAC_BASE); + } else if (inValue->getType()->isDoubleTy()) { + // Convert double constant to fixed-point and back to double + double constValue = constFp->getValueAPF().convertToDouble(); + constValue *= FRAC_BASE; + int64_t fixedPointValue = round(constValue); + //newValue = ConstantInt::get(quantizedType, round(constValue), true); + newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / FRAC_BASE); + } else { + llvm::errs() << "Unknown floating type: " << *inValue->getType() << "\n"; + //assert(false && "unknown floating type"); + continue; + } + + // Cache the quantized value + quantizedValueCache[constFp] = newValue; + + // Replace all uses of the original value with the new quantized value + inInstruction->replaceUsesOfWith(inValue, newValue); + } +} +/*void quantizeConstant(Instruction * inInstruction, Type * quantizedType) { + llvm::errs() << "Entering quantizeConstant\n"; for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { Value * inValue = inInstruction->getOperand(idx); @@ -95,7 +261,7 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) ConstantFP * constFp = llvm::dyn_cast(inValue); - // TODO Cache the quantized value to avoid multiple calculations + Value * newValue = nullptr; if (inValue->getType()->isFloatTy()) { @@ -121,12 +287,13 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // Replace all uses of the original value with the new quantized value inInstruction->replaceUsesOfWith(inValue, newValue); } -} +}*/ // Simplify constants in the instruction void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { + llvm::errs() << "Entering simplifyConstant\n"; auto checkDecimal = [](float decimalNum) { int digits = 0; /* @@ -137,7 +304,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) // Ensure the decimal number can be represented within the fixed-point range while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { - decimalNum *= 10; + decimalNum *= 10; // Scale decimal to avoid precision issues digits++; } return decimalNum; @@ -207,6 +374,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { + // Handle fixed-point division (constant dividend) newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } @@ -222,6 +390,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) if (!isa(inValue)) { + // Skip non-floating-point constants continue; } @@ -248,6 +417,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) llvm::Function * createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector & functionsToInsert) { + llvm::errs() << "Entering createFixMul\n"; /* * check if this function is exist * */ @@ -314,6 +484,7 @@ createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); @@ -331,6 +502,7 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) { + llvm::errs() << "Entering quantizePredict with predicate: " << predict << "\n"; switch (predict) { case FCmpInst::FCMP_OEQ: // equal @@ -352,51 +524,91 @@ quantizePredict(CmpInst::Predicate predict) case FCmpInst::FCMP_UNE: return ICmpInst::ICMP_NE; - // TODO - case FCmpInst::FCMP_ORD: // ordered (no NaNs) - // For ordered, we map it to ICMP_NE, assuming integers cannot be NaN - return ICmpInst::ICMP_NE; - case FCmpInst::FCMP_UNO: // unordered (at least one NaN) - // For unordered, there is no direct integer equivalent, map to ICMP_EQ for safety - return ICmpInst::ICMP_EQ; + /* + case FCmpInst::FCMP_ORD: // ordered (no NaNs) + // For ordered, we map it to ICMP_NE, assuming integers cannot be NaN + return ICmpInst::ICMP_NE; + case FCmpInst::FCMP_UNO: // unordered (at least one NaN) + // For unordered, there is no direct integer equivalent, map to ICMP_EQ for safety + return ICmpInst::ICMP_EQ;*/ default: llvm::errs() << "Unhandled floating point predicate\n"; return ICmpInst::ICMP_EQ; // Default to equal for safety } } -} + + +std::unordered_set processedInstructions; void quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) { + llvm::errs() << "Entering quantizeSimpleFPInstruction\n"; + if (!inInstruction) + { + llvm::errs() << "inInstruction is nullptr\n"; + return; + } + + + // Check if the instruction has already been processed + if (processedInstructions.find(inInstruction) != processedInstructions.end()) { + llvm::errs() << "Instruction already processed: " << *inInstruction << "\n"; + return; + } + processedInstructions.insert(inInstruction); + + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); Value * newInst = nullptr; + + // Ensure the quantized type is not null + if (!quantizedType) + { + llvm::errs() << "Error: Quantized type is nullptr\n"; + return; + } + + llvm::errs() << "Instruction Opcode: " << inInstruction->getOpcodeName() << "\n"; switch (inInstruction->getOpcode()) { case Instruction::FAdd: { + llvm::errs() << "Handling FAdd\n"; // Replace floating-point addition with integer addition newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; } case Instruction::FSub: { - // Replace floating-point subtraction with integer subtraction - newInst = Builder.CreateSub(inInstruction->getOperand(0), inInstruction->getOperand(1)); + llvm::errs() << "Handling FSub\n"; + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + { + op0 = Builder.CreateFPToSI(op0, quantizedType); + } + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + op1 = Builder.CreateFPToSI(op1, quantizedType); + } + newInst = Builder.CreateSub(op0, op1); break; } case Instruction::FMul: { + llvm::errs() << "Handling FMul\n"; // Replace floating-point multiplication with integer multiplication newInst = Builder.CreateMul(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; } case Instruction::FDiv: { + llvm::errs() << "Handling FDiv\n"; // Replace floating-point division with integer division newInst = Builder.CreateSDiv(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; @@ -404,36 +616,61 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) case Instruction::FRem: { + llvm::errs() << "Handling FRem\n"; // Replace floating-point remainder with integer remainder newInst = Builder.CreateSRem(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; } case Instruction::FCmp: - // Replace floating-point comparison with integer comparison - if (auto fcmp_inst = dyn_cast(inInstruction)) - { - CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); - if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) - { - newInst = ConstantInt::getTrue(quantizedType); - } - else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) - { - newInst = ConstantInt::getFalse(quantizedType); - } - else + // Replace floating-point comparison with integer comparisonm + llvm::errs() << "Handling FCmp\n"; + if (auto fcmp_inst = dyn_cast(inInstruction)) { - newInst = Builder.CreateICmp(pred, fcmp_inst->getOperand(0), fcmp_inst->getOperand(1)); + CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); + if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) + { + newInst = ConstantInt::getTrue(quantizedType); + } + else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) + { + newInst = ConstantInt::getFalse(quantizedType); + } + else + { + // Convert floating-point operands to integer operands + Value * fpOp0 = fcmp_inst->getOperand(0); + Value * fpOp1 = fcmp_inst->getOperand(1); + + // Debug output to check types + llvm::errs() << "Entering FCmp case\n"; + llvm::errs() << "Original Operand 0 type: " << *fpOp0->getType() << "\n"; + llvm::errs() << "Original Operand 1 type: " << *fpOp1->getType() << "\n"; + + Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); + Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); + + // Debug output to check types after conversion + llvm::errs() << "Converted Operand 0 type: " << *intOp0->getType() << "\n"; + llvm::errs() << "Converted Operand 1 type: " << *intOp1->getType() << "\n"; + llvm::errs() << "Expected quantizedType: " << *quantizedType << "\n"; + + // Assert to ensure types are correct + assert(intOp0->getType() == intOp1->getType() && "Both operands to ICmp instruction are not of the same type!"); + + newInst = Builder.CreateICmp(pred, intOp0, intOp1); + } } - } - break; + break; + + /* * Change fneg(a) to `0-a`. * */ case Instruction::FNeg: { + llvm::errs() << "Handling FNeg\n"; // Replace floating-point negation with integer negation auto constZero = ConstantInt::get(quantizedType, 0, true); newInst = Builder.CreateSub(constZero, inInstruction->getOperand(0)); @@ -446,14 +683,21 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) if (newInst) { + llvm::errs() << "Replacing instruction with newInst\n"; inInstruction->replaceAllUsesWith(newInst); inInstruction->removeFromParent(); } + else + { + llvm::errs() << "No new instruction created\n"; + } } + // Adapt type casts to the quantized type void adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { + llvm::errs() << "Entering adaptTypeCast\n"; for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) @@ -469,6 +713,7 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) auto sourceOp = llvmIrInstruction->getOperand(0); if (sourceOp->getType() == llvmIrInstruction->getType()) { + // Replace with source if types match llvmIrInstruction->replaceAllUsesWith(sourceOp); llvmIrInstruction->removeFromParent(); } @@ -489,11 +734,13 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) Value * newInst = nullptr; if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) { + // Handle integer to float cast newInst = Builder.CreateSIToFP( llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); } else { + // Handle float cast newInst = Builder.CreateFPCast( llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); } @@ -515,320 +762,360 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } } } + llvm::errs() << "Exiting adaptTypeCast\n"; +} } +std::unordered_set processedFunctions; // Main function to perform LLVM IR auto quantization -void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) -{ - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); +//void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) { +void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector& functionsToInsert) { - Type * quantizedType; - switch (BIT_WIDTH) - { - case 8: - quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); - break; - case 16: - quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); - break; - case 32: - quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); - break; - case 64: - quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); - break; - default: - flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); + + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); + llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; + + Type * quantizedType; + switch (BIT_WIDTH) + { + case 8: + quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); + break; + case 16: + quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); + break; + case 32: + quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); + break; + case 64: + quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); + break; + default: + flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); + llvm_unreachable("Unknown bit width for quantization"); + return; + } + + if (processedFunctions.find(llvmIrFunction.getName().str()) != processedFunctions.end()) { + llvm::errs() << "Function already processed: " << llvmIrFunction.getName() << "\n"; return; - } + } + processedFunctions.insert(llvmIrFunction.getName().str()); - /* - * change the type of this function if it's fp - * */ - if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) - { - llvmIrFunction.mutateType(quantizedType); - } + /* // Create a new function with the desired function type + llvm::Function * newFunction = createNewFunctionWithQuantizedType(llvmIrFunction, quantizedType); - /* - * generate hardcode function - fixmul and fixdiv - * */ - llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); + // Handle Load and Store instructions in the old function + handleLoadStoreInstructions(llvmIrFunction, quantizedType); - /* - * quantize the arguments type - * */ - for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) - { - auto paramOp = llvmIrFunction.getArg(idx); - setQuantizedType(paramOp, quantizedType); - } + // Replace all calls to the old function with calls to the new function + replaceCallsToOldFunction(llvmIrFunction, *newFunction); - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) - { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) - { - Instruction * llvmIrInstruction = &*itBB++; - switch (llvmIrInstruction->getOpcode()) - { - case Instruction::Alloca: - if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) - { - auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - auto newType = quantizedType; - if (allocaType->getTypeID() == Type::ArrayTyID) - { - newType = ArrayType::get(quantizedType, - allocaType->getArrayNumElements()); - allocaType = allocaType->getArrayElementType(); - } - if (allocaType->isDoubleTy() || allocaType->isFloatTy()) + // Rename the old function to avoid name conflict + renameOldFunction(llvmIrFunction); + + // Adapt type casts in the old function + adaptTypeCast(llvmIrFunction, quantizedType); + + // Replace all uses of the old function with the new function + llvmIrFunction.replaceAllUsesWith(newFunction);*/ + // TODO + + /* + * generate hardcode function - fixmul and fixdiv + * */ + + //llvm::Function *fixmul = createFixMul(newFunction, quantizedType, functionsToInsert); + llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); + + /* +* quantize the arguments type +* */ + /* for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) +{ +auto paramOp = llvmIrFunction.getArg(idx); +setQuantizedType(paramOp, quantizedType); +}*/ + + +/* // Continue with the rest of your quantization logic... + for (llvm::BasicBlock &llvmIrBasicBlock : *newFunction) { + for (llvm::BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { + llvm::Instruction *llvmIrInstruction = &*itBB++; + static std::unordered_set processedInstructions; + if (processedInstructions.find(llvmIrInstruction) != processedInstructions.end()) { + continue; + } + processedInstructions.insert(llvmIrInstruction);*/ + + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) +{ +for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) +{ +Instruction * llvmIrInstruction = &*itBB++; +llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; + switch (llvmIrInstruction->getOpcode()) + { + case Instruction::Alloca: + if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) { - llvmIrAllocaInstruction->setAllocatedType(newType); + auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); + auto newType = quantizedType; + if (allocaType->getTypeID() == Type::ArrayTyID) + { + newType = ArrayType::get(quantizedType, + allocaType->getArrayNumElements()); + allocaType = allocaType->getArrayElementType(); + } + if (allocaType->isDoubleTy() || allocaType->isFloatTy()) + { + llvmIrAllocaInstruction->setAllocatedType(newType); + } + setQuantizedType(llvmIrAllocaInstruction, newType); } - setQuantizedType(llvmIrAllocaInstruction, newType); - } - break; - case Instruction::Call: - if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) - { - Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) - break; - if (!calledFunction->getName().startswith("llvm.dbg.value") && - !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) + break; + case Instruction::Call: + if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) { - if (calledFunction->isDeclaration()) + Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); + if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + break; + if (!calledFunction->getName().startswith("llvm.dbg.value") && + !calledFunction->getName().startswith("llvm.dbg.declare") && + !calledFunction->getName().startswith("llvm.dbg.label")) { - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (calledFunction->getName().str() == "sqrt") + if (calledFunction->isDeclaration()) { - /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sqrt(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ - auto operand = llvmIrCallInstruction->getOperand(0); - if (operand->getType()->isIntegerTy()) - { - Value * newOperand = Builder.CreateSIToFP( - operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); - } - auto cloneInst = llvmIrCallInstruction->clone(); - Value * fptosiInst = Builder.CreateFPToSI( - cloneInst, quantizedType); - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value * resInst = nullptr; - /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ - if (FRAC_Q % 2) + // For library functions + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (calledFunction->getName().str() == "sqrt") { - Value * lhsCompensateInst = Builder.CreateSIToFP( - shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), - 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); + /* +* if the arg's type is int, convert to fp, +* after the call node, convert to int and shl FRAC_Q/2 +* +* int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); +* if (FRAC_Q%2) +* return res*1.414213562; +* else +* return res; +* +* %25 = sitofp i32 %0 to double +* %26 = call double @sqrt(double %25) #3 +* %27 = fptosi double %26 to i32 +* %28 = shl i32 %27, 4 +* */ + auto operand = llvmIrCallInstruction->getOperand(0); + if (operand->getType()->isIntegerTy()) + { + Value * newOperand = Builder.CreateSIToFP( + operand, llvmIrCallInstruction->getType()); + llvmIrCallInstruction->setOperand(0, newOperand); + } + auto cloneInst = llvmIrCallInstruction->clone(); + Value * fptosiInst = Builder.CreateFPToSI( + cloneInst, quantizedType); + Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value * resInst = nullptr; + /* +* if (FRAC_Q%2) then multiply with 1.414213562; +* */ + if (FRAC_Q % 2) + { + Value * lhsCompensateInst = Builder.CreateSIToFP( + shlInst, llvmIrCallInstruction->getType()); + auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), + 1.414213562); + Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); + resInst = Builder.CreateFPToSI(mulInst, quantizedType); + } + else + { + resInst = shlInst; + } + llvmIrCallInstruction->replaceAllUsesWith(resInst); + ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); } else { - resInst = shlInst; + /* +* for other lib functions, de-quantize the arguments and quantize the return value +* */ } - llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); } else { /* - * for other lib functions, de-quantize the arguments and quantize the return value - * */ +* for user-defined function, quantize the arguments +* */ + for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) + { + setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); + } + quantizeConstant(llvmIrCallInstruction, quantizedType); + /* +* then quantize the return type +* */ + setQuantizedType(llvmIrCallInstruction, quantizedType); } } - else + } + break; + case Instruction::GetElementPtr: + if (auto gepInst = dyn_cast(llvmIrInstruction)) + { + auto gepType = gepInst->getType(); + auto sourceType = quantizedType; + // bool isPointer = false; + // unsigned pointerAddr = 0; + // if (gepType->isPointerTy()) { + // isPointer = true; + // pointerAddr = gepType->getPointerAddressSpace(); + // valueType = gepType->getPointerElementType(); + // } + if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) { - /* - * for user-defined function, quantize the arguments - * */ - for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); - /* - * then quantize the return type - * */ - setQuantizedType(llvmIrCallInstruction, quantizedType); + sourceType = ArrayType::get(quantizedType, + gepInst->getSourceElementType()->getArrayNumElements()); } + // if (isPointer) { + // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + // } + // if (gepType->isDoubleTy() || gepType->isFloatTy()) { + gepInst->setSourceElementType(sourceType); + gepInst->setResultElementType(quantizedType); + // } } + break; + case Instruction::Load: + case Instruction::PHI: + { + setQuantizedType(llvmIrInstruction, quantizedType); } break; - case Instruction::GetElementPtr: - if (auto gepInst = dyn_cast(llvmIrInstruction)) + + case Instruction::Store: { - auto gepType = gepInst->getType(); - auto sourceType = quantizedType; - // bool isPointer = false; - // unsigned pointerAddr = 0; - // if (gepType->isPointerTy()) { - // isPointer = true; - // pointerAddr = gepType->getPointerAddressSpace(); - // valueType = gepType->getPointerElementType(); - // } - if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) - { - sourceType = ArrayType::get(quantizedType, - gepInst->getSourceElementType()->getArrayNumElements()); - } - // if (isPointer) { - // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - // } - // if (gepType->isDoubleTy() || gepType->isFloatTy()) { - gepInst->setSourceElementType(sourceType); - gepInst->setResultElementType(quantizedType); - // } + /* +* If either of the operands is constant, change it to a int value +* */ + setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + quantizeConstant(llvmIrInstruction, quantizedType); } break; - case Instruction::Load: - case Instruction::PHI: - { - setQuantizedType(llvmIrInstruction, quantizedType); - } - break; - case Instruction::Store: - { - /* - * If either of the operands is constant, change it to a int value - * */ - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); - } - break; + /* +* For fmul/fdiv, +* +* if either one of the operands is a constant value, simplify it by multiplying with 10^n, +* then replace the instruction to mul/div; +* +* else substitute this instruction to a pre-implemented function: mulfix/divfix. +* */ + case Instruction::FMul: + case Instruction::FDiv: + { + if (isa(llvmIrInstruction->getOperand(0)) || + isa(llvmIrInstruction->getOperand(1))) + { + simplifyConstant(llvmIrInstruction, quantizedType); + } + else + { + substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); + } + break; + } - /* - * For fmul/fdiv, - * - * if either one of the operands is a constant value, simplify it by multiplying with 10^n, - * then replace the instruction to mul/div; - * - * else substitute this instruction to a pre-implemented function: mulfix/divfix. - * */ - case Instruction::FMul: - case Instruction::FDiv: - { - if (isa(llvmIrInstruction->getOperand(0)) || - isa(llvmIrInstruction->getOperand(1))) + /* +* If either one of the operands is a constant value, quantize it, +* then replace the instruction to the int version. +* */ + case Instruction::FCmp: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FRem: { - simplifyConstant(llvmIrInstruction, quantizedType); + quantizeConstant(llvmIrInstruction, quantizedType); } - else + case Instruction::FNeg: { - substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); + quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); + break; } - break; - } - /* - * If either one of the operands is a constant value, quantize it, - * then replace the instruction to the int version. - * */ - case Instruction::FCmp: - case Instruction::FAdd: - case Instruction::FSub: - case Instruction::FRem: - { - quantizeConstant(llvmIrInstruction, quantizedType); - } - case Instruction::FNeg: - { - quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); - break; - } + // case Instruction::Add: + // case Instruction::Sub: + // case Instruction::Mul: + // case Instruction::UDiv: + // case Instruction::SDiv: + // case Instruction::URem: + // case Instruction::SRem: + // + // case Instruction::Shl: + // case Instruction::LShr: + // case Instruction::AShr: + // case Instruction::And: + // case Instruction::Or: + // case Instruction::Xor: + // + // case Instruction::ICmp: - // case Instruction::Add: - // case Instruction::Sub: - // case Instruction::Mul: - // case Instruction::UDiv: - // case Instruction::SDiv: - // case Instruction::URem: - // case Instruction::SRem: - // - // case Instruction::Shl: - // case Instruction::LShr: - // case Instruction::AShr: - // case Instruction::And: - // case Instruction::Or: - // case Instruction::Xor: - // - // case Instruction::ICmp: + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::SIToFP: + case Instruction::UIToFP: + case Instruction::ZExt: + case Instruction::SExt: + case Instruction::Trunc: + case Instruction::FPExt: + case Instruction::FPTrunc: + case Instruction::BitCast: + break; - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::SIToFP: - case Instruction::UIToFP: - case Instruction::ZExt: - case Instruction::SExt: - case Instruction::Trunc: - case Instruction::FPExt: - case Instruction::FPTrunc: - case Instruction::BitCast: - break; - - case Instruction::Ret: - case Instruction::Switch: - case Instruction::Br: - case Instruction::Select: - case Instruction::IndirectBr: - case Instruction::Invoke: - case Instruction::Resume: - case Instruction::Unreachable: - case Instruction::CleanupRet: - case Instruction::CatchRet: - case Instruction::CatchSwitch: - case Instruction::CallBr: - case Instruction::Fence: - case Instruction::AtomicCmpXchg: - case Instruction::AtomicRMW: - case Instruction::PtrToInt: - case Instruction::IntToPtr: - case Instruction::AddrSpaceCast: - case Instruction::CleanupPad: - case Instruction::CatchPad: - case Instruction::UserOp1: - case Instruction::UserOp2: - case Instruction::VAArg: - case Instruction::ExtractElement: - case Instruction::InsertElement: - case Instruction::ShuffleVector: - case Instruction::ExtractValue: - case Instruction::InsertValue: - case Instruction::LandingPad: - case Instruction::Freeze: - break; - default: - break; + case Instruction::Ret: + case Instruction::Switch: + case Instruction::Br: + case Instruction::Select: + case Instruction::IndirectBr: + case Instruction::Invoke: + case Instruction::Resume: + case Instruction::Unreachable: + case Instruction::CleanupRet: + case Instruction::CatchRet: + case Instruction::CatchSwitch: + case Instruction::CallBr: + case Instruction::Fence: + case Instruction::AtomicCmpXchg: + case Instruction::AtomicRMW: + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::AddrSpaceCast: + case Instruction::CleanupPad: + case Instruction::CatchPad: + case Instruction::UserOp1: + case Instruction::UserOp2: + case Instruction::VAArg: + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + case Instruction::ExtractValue: + case Instruction::InsertValue: + case Instruction::LandingPad: + case Instruction::Freeze: + break; + default: + break; + } } } - } - adaptTypeCast(llvmIrFunction, quantizedType); + handleLoadStoreInstructions(llvmIrFunction, quantizedType); + adaptTypeCast(llvmIrFunction, quantizedType); + + return; + } - return; -} From 7decf9cf0ed772dcd4e13ee57eb9052ae420f8de Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 6 Jul 2024 21:43:43 +0100 Subject: [PATCH 025/213] solved store and load type issue . * dev2. --- ...cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 741 ++++++++---------- 2 files changed, 394 insertions(+), 395 deletions(-) create mode 100644 analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt diff --git a/analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt b/analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt new file mode 100644 index 000000000..fccc0b750 --- /dev/null +++ b/analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt @@ -0,0 +1,48 @@ + +changeset: 1618:22cb3a8af33722eaeb5c723fb55dd826c3b8d13c +char kNewtonVersion[] = "0.3-alpha-1618 (22cb3a8af33722eaeb5c723fb55dd826c3b8d13c) (build 07-06-2024-21:36-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index bac12d2c3..3ec7a21ec 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -30,9 +30,6 @@ */ #include "newton-irPass-LLVMIR-quantization.h" -#include "llvm/Transforms/Utils/Cloning.h" -#include "llvm/Transforms/Utils/ValueMapper.h" -#include "llvm/ADT/SmallVector.h" #include using namespace llvm; @@ -48,30 +45,61 @@ bool isValidFloatingType(Type *type) { } - // Set the quantized type for a given value -void setQuantizedType(Value * inValue, Type * quantizedType) { - auto valueType = inValue->getType(); +void +setQuantizedType(Value * inValue, Type * quantizedType) +{ + llvm::errs() << "Entering setQuantizedType\n"; + if (inValue == nullptr) { + llvm::errs() << "inValue is nullptr\n"; + return; + } + auto valueType = inValue->getType(); unsigned pointerAddr; bool isPointer = false; + + + if (valueType != nullptr) { - if (valueType->isPointerTy()) { + if (valueType->isPointerTy()) + { isPointer = true; + // If the value is a pointer, get the address space and element type pointerAddr = valueType->getPointerAddressSpace(); - valueType = valueType->getPointerElementType(); + valueType = valueType->getPointerElementType(); + } + + // 跳过整数类型和结构体类型 + if (valueType->isIntegerTy() || valueType->isStructTy()) { + llvm::errs() << "Skipping quantization for type: " << *valueType << "\n"; + return; } - if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) + + + if (isValidFloatingType(valueType) || valueType->isArrayTy()) { - if (isPointer) { + // Print the original and new types for debugging + llvm::errs() << "Original type: " << *valueType << "\n"; + llvm::errs() << "New quantized type: " << *quantizedType << "\n"; + // If the value is a pointer, get the address space and element type + if (isPointer) + { inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - } else { + } + else + { + // Otherwise, directly set the type to the quantized type inValue->mutateType(quantizedType); } + }else { + llvm::errs() << "Unsupported type for quantization: " << *valueType << "\n"; + } + } else { + llvm::errs() << "Value type is nullptr\n"; } } - // Quantize constants within an instruction std::unordered_map quantizedValueCache; @@ -102,7 +130,7 @@ void handleLoadStoreInstructions(Function &llvmIrFunction, Type *quantizedType) } } -/*Function *replaceFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { +void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { auto &context = llvmIrFunction.getContext(); std::vector paramTypes; for (auto &arg : llvmIrFunction.args()) { @@ -117,80 +145,34 @@ void handleLoadStoreInstructions(Function &llvmIrFunction, Type *quantizedType) ? quantizedType : llvmIrFunction.getReturnType(); - FunctionType *newFuncType = FunctionType::get(returnType, paramTypes, llvmIrFunction.isVarArg()); - Function *newFunction = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName(), llvmIrFunction.getParent()); - - // Copy the function body to the new function - newFunction->getBasicBlockList().splice(newFunction->begin(), llvmIrFunction.getBasicBlockList()); - - // Update the arguments mapping - auto newArgIter = newFunction->arg_begin(); - for (auto &arg : llvmIrFunction.args()) { - arg.replaceAllUsesWith(&*newArgIter); - newArgIter->takeName(&arg); - ++newArgIter; - } - - return newFunction; -}*/ - - - -// Function to create a new function with quantized return type and parameters -llvm::Function* createNewFunctionWithQuantizedType(llvm::Function &oldFunction, Type *quantizedType) { - // Create a new function type with quantized arguments and return type - std::vector paramTypes; - for (auto &arg : oldFunction.args()) { - if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) { - paramTypes.push_back(quantizedType); - } else { - paramTypes.push_back(arg.getType()); - } - } - - // Determine the return type for the new function - Type *returnType = oldFunction.getReturnType(); - if (returnType->isFloatTy() || returnType->isDoubleTy()) { - returnType = quantizedType; - } - // Create a new function type with the quantized return type and parameters - llvm::FunctionType *newFuncType = llvm::FunctionType::get(returnType, paramTypes, oldFunction.isVarArg()); - // Create a new function with the new function type in the same module as the old function - llvm::Function *newFunction = llvm::Function::Create(newFuncType, oldFunction.getLinkage(), oldFunction.getName(), oldFunction.getParent()); - + FunctionType *funcType = FunctionType::get(returnType, paramTypes, llvmIrFunction.isVarArg()); - return newFunction; + llvmIrFunction.mutateType(PointerType::getUnqual(funcType)); } -// Function to rename the old function to avoid name conflict -void renameOldFunction(llvm::Function &oldFunction) { - std::string oldFunctionName = oldFunction.getName().str(); - oldFunction.setName(oldFunctionName + "_old"); -} - -// Function to replace all calls to the old function with calls to the new function -void replaceCallsToOldFunction(llvm::Function &oldFunction, llvm::Function &newFunction) { - std::vector callsToReplace; - - for (auto &use : oldFunction.uses()) { - if (auto *call = llvm::dyn_cast(use.getUser())) { - callsToReplace.push_back(call); +void handleReturnInstructions(Function &llvmIrFunction, Type *quantizedType) { + for (BasicBlock &llvmIrBasicBlock : llvmIrFunction) { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { + Instruction *llvmIrInstruction = &*itBB++; + if (llvmIrInstruction->getOpcode() == Instruction::Ret) { + if (auto retInst = dyn_cast(llvmIrInstruction)) { + Value *retVal = retInst->getReturnValue(); + if (retVal && (retVal->getType()->isFloatTy() || retVal->getType()->isDoubleTy())) { + IRBuilder<> Builder(retInst); + Value *newRetVal = Builder.CreateFPToSI(retVal, quantizedType); + Builder.CreateRet(newRetVal); + retInst->eraseFromParent(); + } + } + } } } - - // Replace each call to the old function with a call to the new function - for (auto *call : callsToReplace) { - llvm::SmallVector args(call->arg_begin(), call->arg_end()); - llvm::IRBuilder<> builder(call); - llvm::CallInst *newCall = builder.CreateCall(&newFunction, args); - call->replaceAllUsesWith(newCall); - call->eraseFromParent(); - } } + // Quantize constants within an instruction void quantizeConstant(Instruction *inInstruction, Type *quantizedType) { @@ -538,9 +520,6 @@ quantizePredict(CmpInst::Predicate predict) } } - - -std::unordered_set processedInstructions; void quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) { @@ -550,16 +529,6 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "inInstruction is nullptr\n"; return; } - - - // Check if the instruction has already been processed - if (processedInstructions.find(inInstruction) != processedInstructions.end()) { - llvm::errs() << "Instruction already processed: " << *inInstruction << "\n"; - return; - } - processedInstructions.insert(inInstruction); - - IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); @@ -766,356 +735,338 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } } -std::unordered_set processedFunctions; // Main function to perform LLVM IR auto quantization -//void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) { -void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector& functionsToInsert) { - - - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); - llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - - Type * quantizedType; - switch (BIT_WIDTH) - { - case 8: - quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); - break; - case 16: - quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); - break; - case 32: - quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); - break; - case 64: - quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); - break; - default: - flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); - llvm_unreachable("Unknown bit width for quantization"); - return; - } +void +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) +{ + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); + llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - if (processedFunctions.find(llvmIrFunction.getName().str()) != processedFunctions.end()) { - llvm::errs() << "Function already processed: " << llvmIrFunction.getName() << "\n"; + Type * quantizedType; + switch (BIT_WIDTH) + { + case 8: + quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); + break; + case 16: + quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); + break; + case 32: + quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); + break; + case 64: + quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); + break; + default: + flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); + llvm_unreachable("Unknown bit width for quantization"); return; - } - processedFunctions.insert(llvmIrFunction.getName().str()); - - /* // Create a new function with the desired function type - llvm::Function * newFunction = createNewFunctionWithQuantizedType(llvmIrFunction, quantizedType); - - // Handle Load and Store instructions in the old function - handleLoadStoreInstructions(llvmIrFunction, quantizedType); + } - // Replace all calls to the old function with calls to the new function - replaceCallsToOldFunction(llvmIrFunction, *newFunction); + // Change the function signature if it has floating point types + handleFunctionSignature(llvmIrFunction, quantizedType); - // Rename the old function to avoid name conflict - renameOldFunction(llvmIrFunction); - // Adapt type casts in the old function - adaptTypeCast(llvmIrFunction, quantizedType); - // Replace all uses of the old function with the new function - llvmIrFunction.replaceAllUsesWith(newFunction);*/ - // TODO - /* - * generate hardcode function - fixmul and fixdiv - * */ - - //llvm::Function *fixmul = createFixMul(newFunction, quantizedType, functionsToInsert); - llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); +/* + */ +/* + * change the type of this function if it's fp + * *//* - /* -* quantize the arguments type -* */ - /* for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) -{ -auto paramOp = llvmIrFunction.getArg(idx); -setQuantizedType(paramOp, quantizedType); -}*/ + if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) + { + llvmIrFunction.mutateType(quantizedType); + } +*/ + /* + * generate hardcode function - fixmul and fixdiv + * */ + llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); -/* // Continue with the rest of your quantization logic... - for (llvm::BasicBlock &llvmIrBasicBlock : *newFunction) { - for (llvm::BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - llvm::Instruction *llvmIrInstruction = &*itBB++; - static std::unordered_set processedInstructions; - if (processedInstructions.find(llvmIrInstruction) != processedInstructions.end()) { - continue; - } - processedInstructions.insert(llvmIrInstruction);*/ + /* + * quantize the arguments type + * */ + for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) + { + auto paramOp = llvmIrFunction.getArg(idx); + //setQuantizedType(paramOp, quantizedType); + // TODO : When dealing with passing parameter types, make sure that all floating-point types are correctly converted to integer types. + if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) + { + setQuantizedType(paramOp, quantizedType); + } + } - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) -{ -for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) -{ -Instruction * llvmIrInstruction = &*itBB++; -llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; - switch (llvmIrInstruction->getOpcode()) - { - case Instruction::Alloca: - if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; + switch (llvmIrInstruction->getOpcode()) + { + case Instruction::Alloca: + if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) + { + auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); + auto newType = quantizedType; + if (allocaType->getTypeID() == Type::ArrayTyID) { - auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - auto newType = quantizedType; - if (allocaType->getTypeID() == Type::ArrayTyID) - { - newType = ArrayType::get(quantizedType, - allocaType->getArrayNumElements()); - allocaType = allocaType->getArrayElementType(); - } - if (allocaType->isDoubleTy() || allocaType->isFloatTy()) - { - llvmIrAllocaInstruction->setAllocatedType(newType); - } - setQuantizedType(llvmIrAllocaInstruction, newType); + newType = ArrayType::get(quantizedType, + allocaType->getArrayNumElements()); + allocaType = allocaType->getArrayElementType(); } - break; - case Instruction::Call: - if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) + if (allocaType->isDoubleTy() || allocaType->isFloatTy()) + { + llvmIrAllocaInstruction->setAllocatedType(newType); + } + setQuantizedType(llvmIrAllocaInstruction, newType); + } + break; + case Instruction::Call: + if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) + { + Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); + if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + break; + if (!calledFunction->getName().startswith("llvm.dbg.value") && + !calledFunction->getName().startswith("llvm.dbg.declare") && + !calledFunction->getName().startswith("llvm.dbg.label")) { - Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) - break; - if (!calledFunction->getName().startswith("llvm.dbg.value") && - !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) + if (calledFunction->isDeclaration()) { - if (calledFunction->isDeclaration()) + // For library functions + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (calledFunction->getName().str() == "sqrt") { - // For library functions - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (calledFunction->getName().str() == "sqrt") + /* + * if the arg's type is int, convert to fp, + * after the call node, convert to int and shl FRAC_Q/2 + * + * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); + * if (FRAC_Q%2) + * return res*1.414213562; + * else + * return res; + * + * %25 = sitofp i32 %0 to double + * %26 = call double @sqrt(double %25) #3 + * %27 = fptosi double %26 to i32 + * %28 = shl i32 %27, 4 + * */ + auto operand = llvmIrCallInstruction->getOperand(0); + if (operand->getType()->isIntegerTy()) + { + Value * newOperand = Builder.CreateSIToFP( + operand, llvmIrCallInstruction->getType()); + llvmIrCallInstruction->setOperand(0, newOperand); + } + auto cloneInst = llvmIrCallInstruction->clone(); + Value * fptosiInst = Builder.CreateFPToSI( + cloneInst, quantizedType); + Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value * resInst = nullptr; + /* + * if (FRAC_Q%2) then multiply with 1.414213562; + * */ + if (FRAC_Q % 2) { - /* -* if the arg's type is int, convert to fp, -* after the call node, convert to int and shl FRAC_Q/2 -* -* int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); -* if (FRAC_Q%2) -* return res*1.414213562; -* else -* return res; -* -* %25 = sitofp i32 %0 to double -* %26 = call double @sqrt(double %25) #3 -* %27 = fptosi double %26 to i32 -* %28 = shl i32 %27, 4 -* */ - auto operand = llvmIrCallInstruction->getOperand(0); - if (operand->getType()->isIntegerTy()) - { - Value * newOperand = Builder.CreateSIToFP( - operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); - } - auto cloneInst = llvmIrCallInstruction->clone(); - Value * fptosiInst = Builder.CreateFPToSI( - cloneInst, quantizedType); - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value * resInst = nullptr; - /* -* if (FRAC_Q%2) then multiply with 1.414213562; -* */ - if (FRAC_Q % 2) - { - Value * lhsCompensateInst = Builder.CreateSIToFP( - shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), - 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); - } - else - { - resInst = shlInst; - } - llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); + Value * lhsCompensateInst = Builder.CreateSIToFP( + shlInst, llvmIrCallInstruction->getType()); + auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), + 1.414213562); + Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); + resInst = Builder.CreateFPToSI(mulInst, quantizedType); } else { - /* -* for other lib functions, de-quantize the arguments and quantize the return value -* */ + resInst = shlInst; } + llvmIrCallInstruction->replaceAllUsesWith(resInst); + ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); } else { /* -* for user-defined function, quantize the arguments -* */ - for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); - /* -* then quantize the return type -* */ - setQuantizedType(llvmIrCallInstruction, quantizedType); + * for other lib functions, de-quantize the arguments and quantize the return value + * */ } } - } - break; - case Instruction::GetElementPtr: - if (auto gepInst = dyn_cast(llvmIrInstruction)) - { - auto gepType = gepInst->getType(); - auto sourceType = quantizedType; - // bool isPointer = false; - // unsigned pointerAddr = 0; - // if (gepType->isPointerTy()) { - // isPointer = true; - // pointerAddr = gepType->getPointerAddressSpace(); - // valueType = gepType->getPointerElementType(); - // } - if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) + else { - sourceType = ArrayType::get(quantizedType, - gepInst->getSourceElementType()->getArrayNumElements()); + /* + * for user-defined function, quantize the arguments + * */ + for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) + { + setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); + } + quantizeConstant(llvmIrCallInstruction, quantizedType); + /* + * then quantize the return type + * */ + setQuantizedType(llvmIrCallInstruction, quantizedType); } - // if (isPointer) { - // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - // } - // if (gepType->isDoubleTy() || gepType->isFloatTy()) { - gepInst->setSourceElementType(sourceType); - gepInst->setResultElementType(quantizedType); - // } } - break; - case Instruction::Load: - case Instruction::PHI: - { - setQuantizedType(llvmIrInstruction, quantizedType); - } - break; - - case Instruction::Store: - { - /* -* If either of the operands is constant, change it to a int value -* */ - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); } break; - - /* -* For fmul/fdiv, -* -* if either one of the operands is a constant value, simplify it by multiplying with 10^n, -* then replace the instruction to mul/div; -* -* else substitute this instruction to a pre-implemented function: mulfix/divfix. -* */ - case Instruction::FMul: - case Instruction::FDiv: + case Instruction::GetElementPtr: + if (auto gepInst = dyn_cast(llvmIrInstruction)) { - if (isa(llvmIrInstruction->getOperand(0)) || - isa(llvmIrInstruction->getOperand(1))) + auto gepType = gepInst->getType(); + auto sourceType = quantizedType; + // bool isPointer = false; + // unsigned pointerAddr = 0; + // if (gepType->isPointerTy()) { + // isPointer = true; + // pointerAddr = gepType->getPointerAddressSpace(); + // valueType = gepType->getPointerElementType(); + // } + if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) { - simplifyConstant(llvmIrInstruction, quantizedType); + sourceType = ArrayType::get(quantizedType, + gepInst->getSourceElementType()->getArrayNumElements()); } - else - { - substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); - } - break; + // if (isPointer) { + // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + // } + // if (gepType->isDoubleTy() || gepType->isFloatTy()) { + gepInst->setSourceElementType(sourceType); + gepInst->setResultElementType(quantizedType); + // } } + break; + case Instruction::Load: + case Instruction::PHI: + { + setQuantizedType(llvmIrInstruction, quantizedType); + } + break; - /* -* If either one of the operands is a constant value, quantize it, -* then replace the instruction to the int version. -* */ - case Instruction::FCmp: - case Instruction::FAdd: - case Instruction::FSub: - case Instruction::FRem: + case Instruction::Store: + { + /* + * If either of the operands is constant, change it to a int value + * */ + setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + quantizeConstant(llvmIrInstruction, quantizedType); + } + break; + + /* + * For fmul/fdiv, + * + * if either one of the operands is a constant value, simplify it by multiplying with 10^n, + * then replace the instruction to mul/div; + * + * else substitute this instruction to a pre-implemented function: mulfix/divfix. + * */ + case Instruction::FMul: + case Instruction::FDiv: + { + if (isa(llvmIrInstruction->getOperand(0)) || + isa(llvmIrInstruction->getOperand(1))) { - quantizeConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); } - case Instruction::FNeg: + else { - quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); - break; + substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); } + break; + } - // case Instruction::Add: - // case Instruction::Sub: - // case Instruction::Mul: - // case Instruction::UDiv: - // case Instruction::SDiv: - // case Instruction::URem: - // case Instruction::SRem: - // - // case Instruction::Shl: - // case Instruction::LShr: - // case Instruction::AShr: - // case Instruction::And: - // case Instruction::Or: - // case Instruction::Xor: - // - // case Instruction::ICmp: - - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::SIToFP: - case Instruction::UIToFP: - case Instruction::ZExt: - case Instruction::SExt: - case Instruction::Trunc: - case Instruction::FPExt: - case Instruction::FPTrunc: - case Instruction::BitCast: - break; - - case Instruction::Ret: - case Instruction::Switch: - case Instruction::Br: - case Instruction::Select: - case Instruction::IndirectBr: - case Instruction::Invoke: - case Instruction::Resume: - case Instruction::Unreachable: - case Instruction::CleanupRet: - case Instruction::CatchRet: - case Instruction::CatchSwitch: - case Instruction::CallBr: - case Instruction::Fence: - case Instruction::AtomicCmpXchg: - case Instruction::AtomicRMW: - case Instruction::PtrToInt: - case Instruction::IntToPtr: - case Instruction::AddrSpaceCast: - case Instruction::CleanupPad: - case Instruction::CatchPad: - case Instruction::UserOp1: - case Instruction::UserOp2: - case Instruction::VAArg: - case Instruction::ExtractElement: - case Instruction::InsertElement: - case Instruction::ShuffleVector: - case Instruction::ExtractValue: - case Instruction::InsertValue: - case Instruction::LandingPad: - case Instruction::Freeze: - break; - default: - break; + /* + * If either one of the operands is a constant value, quantize it, + * then replace the instruction to the int version. + * */ + case Instruction::FCmp: + case Instruction::FAdd: + case Instruction::FSub: + case Instruction::FRem: + { + quantizeConstant(llvmIrInstruction, quantizedType); + } + case Instruction::FNeg: + { + quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); + break; } - } - } - handleLoadStoreInstructions(llvmIrFunction, quantizedType); - adaptTypeCast(llvmIrFunction, quantizedType); + // case Instruction::Add: + // case Instruction::Sub: + // case Instruction::Mul: + // case Instruction::UDiv: + // case Instruction::SDiv: + // case Instruction::URem: + // case Instruction::SRem: + // + // case Instruction::Shl: + // case Instruction::LShr: + // case Instruction::AShr: + // case Instruction::And: + // case Instruction::Or: + // case Instruction::Xor: + // + // case Instruction::ICmp: - return; + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::SIToFP: + case Instruction::UIToFP: + case Instruction::ZExt: + case Instruction::SExt: + case Instruction::Trunc: + case Instruction::FPExt: + case Instruction::FPTrunc: + case Instruction::BitCast: + break; + + case Instruction::Ret: + case Instruction::Switch: + case Instruction::Br: + case Instruction::Select: + case Instruction::IndirectBr: + case Instruction::Invoke: + case Instruction::Resume: + case Instruction::Unreachable: + case Instruction::CleanupRet: + case Instruction::CatchRet: + case Instruction::CatchSwitch: + case Instruction::CallBr: + case Instruction::Fence: + case Instruction::AtomicCmpXchg: + case Instruction::AtomicRMW: + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::AddrSpaceCast: + case Instruction::CleanupPad: + case Instruction::CatchPad: + case Instruction::UserOp1: + case Instruction::UserOp2: + case Instruction::VAArg: + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + case Instruction::ExtractValue: + case Instruction::InsertValue: + case Instruction::LandingPad: + case Instruction::Freeze: + break; + default: + break; + } + } } + handleLoadStoreInstructions(llvmIrFunction, quantizedType); + adaptTypeCast(llvmIrFunction, quantizedType); + + return; +} From 066f6fdad1a8d2e1d066728dcfa388e3dea8628e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 8 Jul 2024 23:21:54 +0100 Subject: [PATCH 026/213] try to quantize return type but have some problem * dev2. --- ...70cd3b992411d872bc7946f0e905acdffceee2.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 410 ++++++++++-------- 2 files changed, 271 insertions(+), 187 deletions(-) create mode 100644 analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt diff --git a/analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt b/analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt new file mode 100644 index 000000000..a64f6541a --- /dev/null +++ b/analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt @@ -0,0 +1,48 @@ + +changeset: 1619:e770cd3b992411d872bc7946f0e905acdffceee2 +char kNewtonVersion[] = "0.3-alpha-1619 (e770cd3b992411d872bc7946f0e905acdffceee2) (build 07-06-2024-21:43-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 3ec7a21ec..f037ecd05 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1,32 +1,32 @@ /* - Authored 2022. Pei Mu. - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. +Authored 2022. Pei Mu. +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ #include "newton-irPass-LLVMIR-quantization.h" @@ -39,18 +39,20 @@ using namespace llvm; extern "C" { -//check if the type is a floating type -bool isValidFloatingType(Type *type) { +// check if the type is a floating type +bool +isValidFloatingType(Type * type) +{ return type->isFloatTy() || type->isDoubleTy(); } - // Set the quantized type for a given value void setQuantizedType(Value * inValue, Type * quantizedType) { llvm::errs() << "Entering setQuantizedType\n"; - if (inValue == nullptr) { + if (inValue == nullptr) + { llvm::errs() << "inValue is nullptr\n"; return; } @@ -58,8 +60,6 @@ setQuantizedType(Value * inValue, Type * quantizedType) unsigned pointerAddr; bool isPointer = false; - - if (valueType != nullptr) { if (valueType->isPointerTy()) @@ -71,12 +71,12 @@ setQuantizedType(Value * inValue, Type * quantizedType) } // 跳过整数类型和结构体类型 - if (valueType->isIntegerTy() || valueType->isStructTy()) { + if (valueType->isIntegerTy() || valueType->isStructTy()) + { llvm::errs() << "Skipping quantization for type: " << *valueType << "\n"; return; } - if (isValidFloatingType(valueType) || valueType->isArrayTy()) { // Print the original and new types for debugging @@ -92,11 +92,14 @@ setQuantizedType(Value * inValue, Type * quantizedType) // Otherwise, directly set the type to the quantized type inValue->mutateType(quantizedType); } - }else { + } + else + { llvm::errs() << "Unsupported type for quantization: " << *valueType << "\n"; - } - } else { + } + else + { llvm::errs() << "Value type is nullptr\n"; } } @@ -104,24 +107,35 @@ setQuantizedType(Value * inValue, Type * quantizedType) std::unordered_map quantizedValueCache; // Ensure load and store instructions have matching types -void handleLoadStoreInstructions(Function &llvmIrFunction, Type *quantizedType) { +void +handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) +{ llvm::errs() << "Entering handleLoadStoreInstructions\n"; - for (BasicBlock &llvmIrBasicBlock : llvmIrFunction) { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - Instruction *llvmIrInstruction = &*itBB++; - if (llvmIrInstruction->getOpcode() == Instruction::Load) { - if (auto loadInst = dyn_cast(llvmIrInstruction)) { + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + if (llvmIrInstruction->getOpcode() == Instruction::Load) + { + if (auto loadInst = dyn_cast(llvmIrInstruction)) + { auto ptr = loadInst->getPointerOperand(); if (ptr->getType()->getPointerElementType()->isFloatTy() || - ptr->getType()->getPointerElementType()->isDoubleTy()) { + ptr->getType()->getPointerElementType()->isDoubleTy()) + { ptr->mutateType(quantizedType->getPointerTo()); } } - } else if (llvmIrInstruction->getOpcode() == Instruction::Store) { - if (auto storeInst = dyn_cast(llvmIrInstruction)) { + } + else if (llvmIrInstruction->getOpcode() == Instruction::Store) + { + if (auto storeInst = dyn_cast(llvmIrInstruction)) + { auto ptr = storeInst->getPointerOperand(); if (ptr->getType()->getPointerElementType()->isFloatTy() || - ptr->getType()->getPointerElementType()->isDoubleTy()) { + ptr->getType()->getPointerElementType()->isDoubleTy()) + { ptr->mutateType(quantizedType->getPointerTo()); } } @@ -130,104 +144,90 @@ void handleLoadStoreInstructions(Function &llvmIrFunction, Type *quantizedType) } } -void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { - auto &context = llvmIrFunction.getContext(); - std::vector paramTypes; +// Create a quantized function with the same signature as the original function +// Create a quantized function with the same signature as the original function +Function *createQuantizedFunction(Function &llvmIrFunction, Type *quantizedType) { + std::vector params; for (auto &arg : llvmIrFunction.args()) { + // If the argument type is float or double, use the quantized type if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) { - paramTypes.push_back(quantizedType); + params.push_back(quantizedType); + llvm::errs() << "Quantizing parameter: " << arg.getName() << " from " << *arg.getType() << " to " << *quantizedType << "\n"; } else { - paramTypes.push_back(arg.getType()); + params.push_back(arg.getType()); } } + // Determine the return type: if it's float or double, use the quantized type Type *returnType = llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy() ? quantizedType : llvmIrFunction.getReturnType(); + llvm::errs() << "Original Function: " << llvmIrFunction.getName() << "\n"; + llvm::errs() << "Original Return Type: " << *llvmIrFunction.getReturnType() << "\n"; + for (auto &arg : llvmIrFunction.args()) { + llvm::errs() << "Original Arg: " << *arg.getType() << "\n"; + } + llvm::errs() << "New Function: " << llvmIrFunction.getName() + "_quantized" << "\n"; + llvm::errs() << "New Return Type: " << *returnType << "\n"; + for (auto ¶m : params) { + llvm::errs() << "New Arg: " << *param << "\n"; + } - FunctionType *funcType = FunctionType::get(returnType, paramTypes, llvmIrFunction.isVarArg()); - - llvmIrFunction.mutateType(PointerType::getUnqual(funcType)); + // Create a new function type with the modified parameters and return type + FunctionType *newFuncType = FunctionType::get(returnType, params, false); + // Create the new function in the same module with a modified name + Function *newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_quantized", llvmIrFunction.getParent()); + return newFunc; } +// Clone the function body from the original function to the new quantized function +void cloneFunctionBody(Function &oldFunc, Function *newFunc) { + ValueToValueMapTy vmap; + Function::arg_iterator newArgIt = newFunc->arg_begin(); + for (auto &oldArg : oldFunc.args()) { + newArgIt->setName(oldArg.getName()); + vmap[&oldArg] = &*newArgIt++; + } + // Clone the function body into the new function + SmallVector returns; + llvm::CloneFunctionInto(newFunc, &oldFunc, vmap, CloneFunctionChangeType::LocalChangesOnly, returns); +} -void handleReturnInstructions(Function &llvmIrFunction, Type *quantizedType) { - for (BasicBlock &llvmIrBasicBlock : llvmIrFunction) { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - Instruction *llvmIrInstruction = &*itBB++; - if (llvmIrInstruction->getOpcode() == Instruction::Ret) { - if (auto retInst = dyn_cast(llvmIrInstruction)) { - Value *retVal = retInst->getReturnValue(); - if (retVal && (retVal->getType()->isFloatTy() || retVal->getType()->isDoubleTy())) { - IRBuilder<> Builder(retInst); - Value *newRetVal = Builder.CreateFPToSI(retVal, quantizedType); - Builder.CreateRet(newRetVal); - retInst->eraseFromParent(); - } - } +// Replace all uses of the original function with the new quantized function +void replaceFunctionUses(Function &oldFunc, Function *newFunc) { + std::vector users(oldFunc.user_begin(), oldFunc.user_end()); + for (auto *U : users) { + if (CallInst *callInst = dyn_cast(U)) { + std::vector args; + for (auto &arg : callInst->args()) { + args.push_back(arg); } + // Create a new call instruction to the new function + CallInst *newCall = CallInst::Create(newFunc, args, "", callInst); + newCall->setCallingConv(callInst->getCallingConv()); + newCall->setDebugLoc(callInst->getDebugLoc()); + llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; + callInst->replaceAllUsesWith(newCall); + callInst->eraseFromParent(); } } } - +// Handle the function signature change for quantization +void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { + llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; + Function *newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); + cloneFunctionBody(llvmIrFunction, newFunc); + replaceFunctionUses(llvmIrFunction, newFunc); + // Erase the old function from the module + llvmIrFunction.eraseFromParent(); + llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; +} // Quantize constants within an instruction -void quantizeConstant(Instruction *inInstruction, Type *quantizedType) { - llvm::errs() << "Entering quantizeConstant\n"; - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { - Value *inValue = inInstruction->getOperand(idx); - - - // Skip if the operand is not a floating-point constant - if (!isa(inValue)) { - continue; - } - - - auto *constFp = llvm::dyn_cast(inValue); - - - // Check the cache for the quantized value - auto cachedValue = quantizedValueCache.find(constFp); - - if (cachedValue != quantizedValueCache.end()) { - inInstruction->replaceUsesOfWith(inValue, cachedValue->second); - continue; - } - - // Compute the quantized value if not found in cache - Value *newValue = nullptr; - if (inValue->getType()->isFloatTy()) { - // Convert float constant to fixed-point - float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; - int32_t fixedPointValue = round(constValue); - //newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / FRAC_BASE); - } else if (inValue->getType()->isDoubleTy()) { - // Convert double constant to fixed-point and back to double - double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; - int64_t fixedPointValue = round(constValue); - //newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / FRAC_BASE); - } else { - llvm::errs() << "Unknown floating type: " << *inValue->getType() << "\n"; - //assert(false && "unknown floating type"); - continue; - } - - // Cache the quantized value - quantizedValueCache[constFp] = newValue; - - // Replace all uses of the original value with the new quantized value - inInstruction->replaceUsesOfWith(inValue, newValue); - } -} -/*void +void quantizeConstant(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Entering quantizeConstant\n"; @@ -241,35 +241,51 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) continue; } - ConstantFP * constFp = llvm::dyn_cast(inValue); + auto * constFp = llvm::dyn_cast(inValue); + + // Check the cache for the quantized value + auto cachedValue = quantizedValueCache.find(constFp); + if (cachedValue != quantizedValueCache.end()) + { + inInstruction->replaceUsesOfWith(inValue, cachedValue->second); + continue; + } + // Compute the quantized value if not found in cache Value * newValue = nullptr; if (inValue->getType()->isFloatTy()) { // Convert float constant to fixed-point float constValue = constFp->getValueAPF().convertToFloat(); constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); + int32_t fixedPointValue = round(constValue); + // newValue = ConstantInt::get(quantizedType, round(constValue), true); + newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / FRAC_BASE); } else if (inValue->getType()->isDoubleTy()) { - // Convert double constant to fixed-point + // Convert double constant to fixed-point and back to double double constValue = constFp->getValueAPF().convertToDouble(); constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); + int64_t fixedPointValue = round(constValue); + // newValue = ConstantInt::get(quantizedType, round(constValue), true); + newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / FRAC_BASE); } else { + llvm::errs() << "Unknown floating type: " << *inValue->getType() << "\n"; // assert(false && "unknown floating type"); - llvm::errs() << "Unknown floating type\n"; continue; } + // Cache the quantized value + quantizedValueCache[constFp] = newValue; + // Replace all uses of the original value with the new quantized value inInstruction->replaceUsesOfWith(inValue, newValue); } -}*/ +} // Simplify constants in the instruction void @@ -409,13 +425,17 @@ createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vectorgetContext(), "entry", func); llvm::IRBuilder<> builder(entryBB); builder.SetInsertPoint(entryBB); @@ -434,6 +454,8 @@ createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vectorgetContext()); break; } + // Get arguments and create the instructions for the function body + llvm::Function::arg_iterator arg1 = func->arg_begin(); + if (arg1 == func->arg_end()) { + llvm::errs() << "Error: Function arguments not set correctly\n"; + return nullptr; + } - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + + //llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); + //llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Function::arg_iterator arg2 = std::next(func->arg_begin()); + if (arg2 == func->arg_end()) { + llvm::errs() << "Error: Function arguments not set correctly\n"; + return nullptr; + } llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); llvm::Value * mulInst = builder.CreateMul(sext1, sext2); llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); builder.CreateRet(truncInst); + llvm::errs() << "Created function body\n"; + + // Insert the created function into the list functionsToInsert.emplace_back(func); + llvm::errs() << "Inserted fixmul function into the list\n"; + return func; } @@ -592,47 +631,45 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) } case Instruction::FCmp: - // Replace floating-point comparison with integer comparisonm - llvm::errs() << "Handling FCmp\n"; - if (auto fcmp_inst = dyn_cast(inInstruction)) + // Replace floating-point comparison with integer comparisonm + llvm::errs() << "Handling FCmp\n"; + if (auto fcmp_inst = dyn_cast(inInstruction)) + { + CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); + if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) { - CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); - if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) - { - newInst = ConstantInt::getTrue(quantizedType); - } - else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) - { - newInst = ConstantInt::getFalse(quantizedType); - } - else - { - // Convert floating-point operands to integer operands - Value * fpOp0 = fcmp_inst->getOperand(0); - Value * fpOp1 = fcmp_inst->getOperand(1); + newInst = ConstantInt::getTrue(quantizedType); + } + else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) + { + newInst = ConstantInt::getFalse(quantizedType); + } + else + { + // Convert floating-point operands to integer operands + Value * fpOp0 = fcmp_inst->getOperand(0); + Value * fpOp1 = fcmp_inst->getOperand(1); - // Debug output to check types - llvm::errs() << "Entering FCmp case\n"; - llvm::errs() << "Original Operand 0 type: " << *fpOp0->getType() << "\n"; - llvm::errs() << "Original Operand 1 type: " << *fpOp1->getType() << "\n"; + // Debug output to check types + llvm::errs() << "Entering FCmp case\n"; + llvm::errs() << "Original Operand 0 type: " << *fpOp0->getType() << "\n"; + llvm::errs() << "Original Operand 1 type: " << *fpOp1->getType() << "\n"; - Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); - Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); + Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); + Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); - // Debug output to check types after conversion - llvm::errs() << "Converted Operand 0 type: " << *intOp0->getType() << "\n"; - llvm::errs() << "Converted Operand 1 type: " << *intOp1->getType() << "\n"; - llvm::errs() << "Expected quantizedType: " << *quantizedType << "\n"; + // Debug output to check types after conversion + llvm::errs() << "Converted Operand 0 type: " << *intOp0->getType() << "\n"; + llvm::errs() << "Converted Operand 1 type: " << *intOp1->getType() << "\n"; + llvm::errs() << "Expected quantizedType: " << *quantizedType << "\n"; - // Assert to ensure types are correct - assert(intOp0->getType() == intOp1->getType() && "Both operands to ICmp instruction are not of the same type!"); + // Assert to ensure types are correct + assert(intOp0->getType() == intOp1->getType() && "Both operands to ICmp instruction are not of the same type!"); - newInst = Builder.CreateICmp(pred, intOp0, intOp1); - } + newInst = Builder.CreateICmp(pred, intOp0, intOp1); } - break; - - + } + break; /* * Change fneg(a) to `0-a`. @@ -763,28 +800,28 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } - // Change the function signature if it has floating point types + llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; handleFunctionSignature(llvmIrFunction, quantizedType); + llvm::errs() << "Finished calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; - - - -/* - */ -/* + /* + */ + /* * change the type of this function if it's fp - * *//* + * */ + /* - if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) - { - llvmIrFunction.mutateType(quantizedType); - } -*/ + if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) + { + llvmIrFunction.mutateType(quantizedType); + } + */ /* * generate hardcode function - fixmul and fixdiv * */ llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); + llvm::errs() << "Created fixmul function: " << (fixmul ? "Success" : "Failed") << "\n"; /* * quantize the arguments type @@ -792,12 +829,12 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) { auto paramOp = llvmIrFunction.getArg(idx); - //setQuantizedType(paramOp, quantizedType); - // TODO : When dealing with passing parameter types, make sure that all floating-point types are correctly converted to integer types. - if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) - { - setQuantizedType(paramOp, quantizedType); - } + // setQuantizedType(paramOp, quantizedType); + // TODO : When dealing with passing parameter types, make sure that all floating-point types are correctly converted to integer types. + if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) + { + setQuantizedType(paramOp, quantizedType); + } } for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) @@ -1069,4 +1106,3 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } - From 7698840589aec0ed2651f3529663a5107907f88a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 9 Jul 2024 20:30:42 +0100 Subject: [PATCH 027/213] add more debug information * dev2. --- ...ecf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt | 48 +++++ .../newton-irPass-LLVMIR-quantization.cpp | 191 ++++++++++-------- 2 files changed, 151 insertions(+), 88 deletions(-) create mode 100644 analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt diff --git a/analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt b/analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt new file mode 100644 index 000000000..83bcb972c --- /dev/null +++ b/analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt @@ -0,0 +1,48 @@ + +changeset: 1620:7decf9cf0ed772dcd4e13ee57eb9052ae420f8de +char kNewtonVersion[] = "0.3-alpha-1620 (7decf9cf0ed772dcd4e13ee57eb9052ae420f8de) (build 07-08-2024-23:21-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index f037ecd05..3432adb36 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -144,7 +144,7 @@ handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) } } -// Create a quantized function with the same signature as the original function + // Create a quantized function with the same signature as the original function Function *createQuantizedFunction(Function &llvmIrFunction, Type *quantizedType) { std::vector params; @@ -213,14 +213,35 @@ void replaceFunctionUses(Function &oldFunc, Function *newFunc) { } } + + + + + // Handle the function signature change for quantization void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; + // Skip certain functions + std::string functionName = llvmIrFunction.getName().str(); + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label") { + llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; + return; + } + + // 保存原始函数名和模块指针 + std::string originalName = llvmIrFunction.getName().str(); + Module *module = llvmIrFunction.getParent(); + + + Function *newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); cloneFunctionBody(llvmIrFunction, newFunc); replaceFunctionUses(llvmIrFunction, newFunc); + + + // Erase the old function from the module - llvmIrFunction.eraseFromParent(); + //llvmIrFunction.eraseFromParent(); llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } @@ -412,95 +433,62 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) } // Create a fixed-point multiplication function -llvm::Function * -createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector & functionsToInsert) -{ +//llvm::Function *createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector & functionsToInsert) + +// 创建一个固定点乘法函数 +llvm::Function* createFixMul(Module *irModule, Type *quantizedType, std::vector &functionsToInsert) { llvm::errs() << "Entering createFixMul\n"; - /* - * check if this function is exist - * */ + + // 检查 irModule 是否有效 + if (!irModule) { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + std::string fixmulFuncName = "fixmul"; - auto irModule = inFunction->getParent(); - for (auto & function : *irModule) - { - if (function.getName() == fixmulFuncName) - { + for (auto &function : *irModule) { + if (function.getName() == fixmulFuncName) { llvm::errs() << "fixmul already exists\n"; return &function; } } - // Create the function type for fixmul - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - llvm::errs() << "Created fixmul function\n"; + llvm::FunctionType *funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); + llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - // Create the entry block and IR builder - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(inFunction->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); builder.SetInsertPoint(entryBB); - /* - * ((int64_t)x*y)>>FRAC_Q - * - * ===========> - * - * define private i32 @mulfix(i32 %0, i32 %1) { - * %3 = sext i32 %0 to i64 - * %4 = sext i32 %1 to i64 - * %5 = mul nsw i64 %3, %4 - * %6 = ashr i64 %5, 8 - * %7 = trunc i64 %6 to i32 - * ret i32 %7 - * } - * */ - - // Handle different bit widths for higher precision multiplication - Type * higherQuantizedType; - switch (BIT_WIDTH) - { + // 生成 fixed-point 乘法指令 + Type *higherQuantizedType; + switch (BIT_WIDTH) { case 8: - higherQuantizedType = Type::getInt16Ty(inFunction->getContext()); + higherQuantizedType = Type::getInt16Ty(irModule->getContext()); break; case 16: - higherQuantizedType = Type::getInt32Ty(inFunction->getContext()); + higherQuantizedType = Type::getInt32Ty(irModule->getContext()); break; default: - higherQuantizedType = Type::getInt64Ty(inFunction->getContext()); + higherQuantizedType = Type::getInt64Ty(irModule->getContext()); break; } - // Get arguments and create the instructions for the function body - llvm::Function::arg_iterator arg1 = func->arg_begin(); - if (arg1 == func->arg_end()) { - llvm::errs() << "Error: Function arguments not set correctly\n"; - return nullptr; - } - - //llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - //llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Function::arg_iterator arg2 = std::next(func->arg_begin()); - if (arg2 == func->arg_end()) { - llvm::errs() << "Error: Function arguments not set correctly\n"; - return nullptr; - } - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value * mulInst = builder.CreateMul(sext1, sext2); - llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value *sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value *sext2 = builder.CreateSExt(arg2, higherQuantizedType); + llvm::Value *mulInst = builder.CreateMul(sext1, sext2); + llvm::Value *ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value *truncInst = builder.CreateTrunc(ashrInst, quantizedType); builder.CreateRet(truncInst); - llvm::errs() << "Created function body\n"; - - // Insert the created function into the list functionsToInsert.emplace_back(func); - llvm::errs() << "Inserted fixmul function into the list\n"; - - + llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; return func; } + // Substitute hardcoded functions like multiplication with fixed-point versions void substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) @@ -779,6 +767,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; + // Skip certain functions + std::string functionName = llvmIrFunction.getName().str(); + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label") { + llvm::errs() << "Skipping function: " << functionName << "\n"; + return; + } + + Type * quantizedType; switch (BIT_WIDTH) { @@ -800,42 +796,61 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } + // 保存模块指针 + Module *module = llvmIrFunction.getParent(); + if (!module) { + llvm::errs() << "Error: Function does not have a parent module.\n"; + return; + } + + // 处理函数签名 llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; handleFunctionSignature(llvmIrFunction, quantizedType); llvm::errs() << "Finished calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; - /* - */ - /* - * change the type of this function if it's fp - * */ - /* - if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) - { - llvmIrFunction.mutateType(quantizedType); - } - */ /* * generate hardcode function - fixmul and fixdiv * */ - llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); + + llvm::errs() << "Calling createFixMul for function: " << llvmIrFunction.getName() << "\n"; + llvm::errs() << "inFunction address: " << &llvmIrFunction << "\n"; + //lvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); + llvm::Function *fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::errs() << "Created fixmul function: " << (fixmul ? "Success" : "Failed") << "\n"; /* * quantize the arguments type * */ - for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) - { - auto paramOp = llvmIrFunction.getArg(idx); - // setQuantizedType(paramOp, quantizedType); - // TODO : When dealing with passing parameter types, make sure that all floating-point types are correctly converted to integer types. - if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) - { - setQuantizedType(paramOp, quantizedType); - } + + llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; + for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++){ + llvm::errs() << "Quantizing parameter at index " << idx << "\n"; + llvm::Function::arg_iterator argIt = llvmIrFunction.arg_begin(); + std::advance(argIt, idx); + + if (argIt == llvmIrFunction.arg_end()) { + llvm::errs() << "Error: Reached end of arguments while trying to access index " << idx << "\n"; + continue; + } + + llvm::Argument *paramOp = &*argIt; + llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; + if (!paramOp) { + llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; + continue; + } + + if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) { + llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; + setQuantizedType(paramOp, quantizedType); + } else { + llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; } +} + + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { From 6f611897711928b9b7418dc487a7cb63b3716a7a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 9 Jul 2024 22:21:19 +0100 Subject: [PATCH 028/213] Solve the problem of duplicate quantization * dev2. --- ...6f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt | 48 +++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 80 ++++++++++++++----- 2 files changed, 108 insertions(+), 20 deletions(-) create mode 100644 analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt diff --git a/analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt b/analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt new file mode 100644 index 000000000..b4496c781 --- /dev/null +++ b/analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt @@ -0,0 +1,48 @@ + +changeset: 1621:066f6fdad1a8d2e1d066728dcfa388e3dea8628e +char kNewtonVersion[] = "0.3-alpha-1621 (066f6fdad1a8d2e1d066728dcfa388e3dea8628e) (build 07-09-2024-20:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 3432adb36..004dc18cb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -180,6 +180,8 @@ Function *createQuantizedFunction(Function &llvmIrFunction, Type *quantizedType) return newFunc; } + + // Clone the function body from the original function to the new quantized function void cloneFunctionBody(Function &oldFunc, Function *newFunc) { ValueToValueMapTy vmap; @@ -193,7 +195,10 @@ void cloneFunctionBody(Function &oldFunc, Function *newFunc) { llvm::CloneFunctionInto(newFunc, &oldFunc, vmap, CloneFunctionChangeType::LocalChangesOnly, returns); } -// Replace all uses of the original function with the new quantized function + + + + //Replace all uses of the original function with the new quantized function void replaceFunctionUses(Function &oldFunc, Function *newFunc) { std::vector users(oldFunc.user_begin(), oldFunc.user_end()); for (auto *U : users) { @@ -216,7 +221,17 @@ void replaceFunctionUses(Function &oldFunc, Function *newFunc) { +bool isQuantizedFunctionName(const std::string &functionName) { + return functionName.find("_quantized") != std::string::npos; +} + + + +//A list of functions to erase after processing +//std::vector functionsToErase; +//Track processed functions to avoid duplicate processing +std::set processedFunctions; // Handle the function signature change for quantization void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { @@ -228,9 +243,19 @@ void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { return; } - // 保存原始函数名和模块指针 - std::string originalName = llvmIrFunction.getName().str(); - Module *module = llvmIrFunction.getParent(); + if (processedFunctions.find(functionName) != processedFunctions.end()) { + llvm::errs() << "Function already processed: " << functionName << "\n"; + return; + } + + if (isQuantizedFunctionName(functionName)) { + llvm::errs() << "Skipping already quantized function: " << functionName << "\n"; + return; + } + + processedFunctions.insert(functionName); + + @@ -240,12 +265,23 @@ void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { - // Erase the old function from the module - //llvmIrFunction.eraseFromParent(); + // Add the old function to the list of functions to erase + //functionsToErase.push_back(&llvmIrFunction); + llvmIrFunction.replaceAllUsesWith(UndefValue::get(llvmIrFunction.getType())); llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } + +// Function to actually erase functions after processing +//void eraseOldFunctions() { +// for (auto *func : functionsToErase) { +// func->eraseFromParent(); +// } +// functionsToErase.clear(); +//} + + // Quantize constants within an instruction void @@ -833,22 +869,22 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve if (argIt == llvmIrFunction.arg_end()) { llvm::errs() << "Error: Reached end of arguments while trying to access index " << idx << "\n"; continue; - } + } - llvm::Argument *paramOp = &*argIt; - llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; - if (!paramOp) { - llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; - continue; - } + llvm::Argument *paramOp = &*argIt; + llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; + if (!paramOp) { + llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; + continue; + } - if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) { - llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; - setQuantizedType(paramOp, quantizedType); - } else { - llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; + if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) { + llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; + setQuantizedType(paramOp, quantizedType); + } else { + llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; + } } -} @@ -1119,5 +1155,9 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); + + + // Finally, erase old functions + //eraseOldFunctions(); return; -} +} \ No newline at end of file From 4c72375b9c565d124523fd22cda7382fe4dd7614 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 10 Jul 2024 12:43:30 +0100 Subject: [PATCH 029/213] add fixmul to the list of the functions to skip * dev2. --- src/newton/newton-irPass-LLVMIR-quantization.cpp | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 004dc18cb..421549aa2 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -198,7 +198,7 @@ void cloneFunctionBody(Function &oldFunc, Function *newFunc) { - //Replace all uses of the original function with the new quantized function +//Replace all uses of the original function with the new quantized function void replaceFunctionUses(Function &oldFunc, Function *newFunc) { std::vector users(oldFunc.user_begin(), oldFunc.user_end()); for (auto *U : users) { @@ -805,7 +805,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label") { + //if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label") { + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul") { llvm::errs() << "Skipping function: " << functionName << "\n"; return; } From 365f482bc78076ef3d9b200117d948b899bba585 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Thu, 11 Jul 2024 18:58:21 +0100 Subject: [PATCH 030/213] add earse old funtion * dev2. --- ...611897711928b9b7418dc487a7cb63b3716a7a.txt | 48 ++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 15 ++++- .../newton-irPass-LLVMIR-optimizeByRange.h | 66 +++++++++---------- .../newton-irPass-LLVMIR-quantization.cpp | 28 ++++---- .../newton-irPass-LLVMIR-quantization.h | 6 +- 5 files changed, 111 insertions(+), 52 deletions(-) create mode 100644 analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt diff --git a/analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt b/analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt new file mode 100644 index 000000000..7ca810f04 --- /dev/null +++ b/analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt @@ -0,0 +1,48 @@ + +changeset: 1623:6f611897711928b9b7418dc487a7cb63b3716a7a +char kNewtonVersion[] = "0.3-alpha-1623 (6f611897711928b9b7418dc487a7cb63b3716a7a) (build 07-10-2024-12:43-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 8d20edb5f..8d048d3dc 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -41,6 +41,7 @@ #include "newton-irPass-LLVMIR-constantSubstitution.h" #include "newton-irPass-LLVMIR-shrinkTypeByRange.h" #include "newton-irPass-LLVMIR-quantization.h" +#include "newton-irPass-LLVMIR-optimizeByRange.h" #include "newton-irPass-LLVMIR-memoryAlignment.h" #include "newton-irPass-LLVMIR-emitAssume.h" #endif /* __cplusplus */ @@ -69,9 +70,9 @@ #include "llvm/IR/Module.h" #include "llvm/Support/raw_ostream.h" #include "llvm/Support/FileSystem.h" -using namespace llvm; +#include "llvm/IR/Function.h" -extern "C" { +using namespace llvm; // Function to save the IR of a module to a file @@ -491,14 +492,22 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // overloadFunc(Mod, callerMap); + // Finally, erase old functions + eraseOldFunctions(); + + + // 打印处理后的模块IR并保存到文件 saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + + + /* * Dump BC file to a file. * */ dumpIR(N, "output", Mod); llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; } -} + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.h b/src/newton/newton-irPass-LLVMIR-optimizeByRange.h index 5d38eccbf..92a49ad09 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.h +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.h @@ -1,39 +1,39 @@ /* - Authored 2022. Pei Mu. +Authored 2022. Pei Mu. - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ +are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the following +disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ #ifndef NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE #define NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE @@ -50,4 +50,4 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } /* extern "C" */ #endif /* __cplusplus */ -#endif /* NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE */ +#endif /* NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE */ \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 421549aa2..3ef40f8f4 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -228,7 +228,7 @@ bool isQuantizedFunctionName(const std::string &functionName) { //A list of functions to erase after processing -//std::vector functionsToErase; +std::vector functionsToErase; //Track processed functions to avoid duplicate processing std::set processedFunctions; @@ -238,7 +238,7 @@ void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label") { + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label"||functionName == "fixmul") { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; return; } @@ -266,7 +266,7 @@ void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { // Add the old function to the list of functions to erase - //functionsToErase.push_back(&llvmIrFunction); + functionsToErase.push_back(&llvmIrFunction); llvmIrFunction.replaceAllUsesWith(UndefValue::get(llvmIrFunction.getType())); llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } @@ -274,12 +274,15 @@ void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { // Function to actually erase functions after processing -//void eraseOldFunctions() { -// for (auto *func : functionsToErase) { -// func->eraseFromParent(); -// } -// functionsToErase.clear(); -//} +void eraseOldFunctions() { + llvm::errs() << "Entering eraseOldFunctions\n"; + for (auto *func : functionsToErase) { + llvm::errs() << "Erasing old function: " << func->getName() << "\n"; + func->eraseFromParent(); + } + functionsToErase.clear(); + llvm::errs() << "Exiting eraseOldFunctions\n"; +} // Quantize constants within an instruction @@ -805,8 +808,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - //if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label") { - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul") { + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label"|| functionName == "fixmul" ) { llvm::errs() << "Skipping function: " << functionName << "\n"; return; } @@ -1156,9 +1158,5 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); - - - // Finally, erase old functions - //eraseOldFunctions(); return; } \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 8a18ececa..8c867fd1b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -35,10 +35,14 @@ extern "C" { #endif /* __cplusplus */ - +extern std::vector functionsToErase; void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); + +extern +void eraseOldFunctions(); + #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ From b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 12 Jul 2024 12:30:47 +0100 Subject: [PATCH 031/213] fix fmul issue * dev2. --- ...72375b9c565d124523fd22cda7382fe4dd7614.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 515 ++++++++++++------ 2 files changed, 396 insertions(+), 167 deletions(-) create mode 100644 analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt diff --git a/analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt b/analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt new file mode 100644 index 000000000..ee5365648 --- /dev/null +++ b/analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt @@ -0,0 +1,48 @@ + +changeset: 1624:4c72375b9c565d124523fd22cda7382fe4dd7614 +char kNewtonVersion[] = "0.3-alpha-1624 (4c72375b9c565d124523fd22cda7382fe4dd7614) (build 07-11-2024-18:58-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 3ef40f8f4..20579a3ef 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -144,49 +144,57 @@ handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) } } - // Create a quantized function with the same signature as the original function -Function *createQuantizedFunction(Function &llvmIrFunction, Type *quantizedType) { +Function * +createQuantizedFunction(Function & llvmIrFunction, Type * quantizedType) +{ std::vector params; - for (auto &arg : llvmIrFunction.args()) { + for (auto & arg : llvmIrFunction.args()) + { // If the argument type is float or double, use the quantized type - if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) { + if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) + { params.push_back(quantizedType); llvm::errs() << "Quantizing parameter: " << arg.getName() << " from " << *arg.getType() << " to " << *quantizedType << "\n"; - } else { + } + else + { params.push_back(arg.getType()); } } // Determine the return type: if it's float or double, use the quantized type - Type *returnType = llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy() + Type * returnType = llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy() ? quantizedType : llvmIrFunction.getReturnType(); llvm::errs() << "Original Function: " << llvmIrFunction.getName() << "\n"; llvm::errs() << "Original Return Type: " << *llvmIrFunction.getReturnType() << "\n"; - for (auto &arg : llvmIrFunction.args()) { + for (auto & arg : llvmIrFunction.args()) + { llvm::errs() << "Original Arg: " << *arg.getType() << "\n"; } llvm::errs() << "New Function: " << llvmIrFunction.getName() + "_quantized" << "\n"; llvm::errs() << "New Return Type: " << *returnType << "\n"; - for (auto ¶m : params) { + for (auto & param : params) + { llvm::errs() << "New Arg: " << *param << "\n"; } // Create a new function type with the modified parameters and return type - FunctionType *newFuncType = FunctionType::get(returnType, params, false); + FunctionType * newFuncType = FunctionType::get(returnType, params, false); // Create the new function in the same module with a modified name - Function *newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_quantized", llvmIrFunction.getParent()); + Function * newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_quantized", llvmIrFunction.getParent()); return newFunc; } - - // Clone the function body from the original function to the new quantized function -void cloneFunctionBody(Function &oldFunc, Function *newFunc) { - ValueToValueMapTy vmap; +void +cloneFunctionBody(Function & oldFunc, Function * newFunc) +{ + ValueToValueMapTy vmap; Function::arg_iterator newArgIt = newFunc->arg_begin(); - for (auto &oldArg : oldFunc.args()) { + for (auto & oldArg : oldFunc.args()) + { newArgIt->setName(oldArg.getName()); vmap[&oldArg] = &*newArgIt++; } @@ -195,20 +203,22 @@ void cloneFunctionBody(Function &oldFunc, Function *newFunc) { llvm::CloneFunctionInto(newFunc, &oldFunc, vmap, CloneFunctionChangeType::LocalChangesOnly, returns); } - - - -//Replace all uses of the original function with the new quantized function -void replaceFunctionUses(Function &oldFunc, Function *newFunc) { - std::vector users(oldFunc.user_begin(), oldFunc.user_end()); - for (auto *U : users) { - if (CallInst *callInst = dyn_cast(U)) { +// Replace all uses of the original function with the new quantized function +void +replaceFunctionUses(Function & oldFunc, Function * newFunc) +{ + std::vector users(oldFunc.user_begin(), oldFunc.user_end()); + for (auto * U : users) + { + if (CallInst * callInst = dyn_cast(U)) + { std::vector args; - for (auto &arg : callInst->args()) { + for (auto & arg : callInst->args()) + { args.push_back(arg); } // Create a new call instruction to the new function - CallInst *newCall = CallInst::Create(newFunc, args, "", callInst); + CallInst * newCall = CallInst::Create(newFunc, args, "", callInst); newCall->setCallingConv(callInst->getCallingConv()); newCall->setDebugLoc(callInst->getDebugLoc()); llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; @@ -218,65 +228,63 @@ void replaceFunctionUses(Function &oldFunc, Function *newFunc) { } } - - - -bool isQuantizedFunctionName(const std::string &functionName) { +bool +isQuantizedFunctionName(const std::string & functionName) +{ return functionName.find("_quantized") != std::string::npos; } +// A list of functions to erase after processing +std::vector functionsToErase; - -//A list of functions to erase after processing -std::vector functionsToErase; - -//Track processed functions to avoid duplicate processing +// Track processed functions to avoid duplicate processing std::set processedFunctions; // Handle the function signature change for quantization -void handleFunctionSignature(Function &llvmIrFunction, Type *quantizedType) { +void +handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) +{ llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label"||functionName == "fixmul") { + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul") + // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul") + { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; return; } - if (processedFunctions.find(functionName) != processedFunctions.end()) { + if (processedFunctions.find(functionName) != processedFunctions.end()) + { llvm::errs() << "Function already processed: " << functionName << "\n"; return; } - if (isQuantizedFunctionName(functionName)) { + if (isQuantizedFunctionName(functionName)) + { llvm::errs() << "Skipping already quantized function: " << functionName << "\n"; return; } processedFunctions.insert(functionName); - - - - - Function *newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); + Function * newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); cloneFunctionBody(llvmIrFunction, newFunc); replaceFunctionUses(llvmIrFunction, newFunc); - - // Add the old function to the list of functions to erase functionsToErase.push_back(&llvmIrFunction); llvmIrFunction.replaceAllUsesWith(UndefValue::get(llvmIrFunction.getType())); llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } - - // Function to actually erase functions after processing -void eraseOldFunctions() { +void +eraseOldFunctions() +{ llvm::errs() << "Entering eraseOldFunctions\n"; - for (auto *func : functionsToErase) { + for (auto * func : functionsToErase) + { llvm::errs() << "Erasing old function: " << func->getName() << "\n"; func->eraseFromParent(); } @@ -284,7 +292,6 @@ void eraseOldFunctions() { llvm::errs() << "Exiting eraseOldFunctions\n"; } - // Quantize constants within an instruction void @@ -347,19 +354,55 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) } } -// Simplify constants in the instruction void -simplifyConstant(Instruction * inInstruction, Type * quantizedType) +handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) +{ + llvm::errs() << "Handling FloatIntMul\n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + // 获取操作数 + Value * lhs = llvmIrInstruction->getOperand(0); + Value * rhs = llvmIrInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + // 确保左操作数是浮点数,右操作数是整数 + if (!lhs->getType()->isFloatTy() && !lhs->getType()->isDoubleTy()) + { + std::swap(lhs, rhs); + } + + if (lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy()) + { + if (rhs->getType()->isIntegerTy()) + { + llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Replaced with call to floatIntMul\n"; + } + else + { + llvm::errs() << "RHS is not an integer\n"; + } + } + else + { + llvm::errs() << "LHS is not a float\n"; + } + + llvm::errs() << "Finished handling FloatIntMul\n"; +} + +void +simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) + { llvm::errs() << "Entering simplifyConstant\n"; auto checkDecimal = [](float decimalNum) { int digits = 0; - /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - - // Ensure the decimal number can be represented within the fixed-point range while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { decimalNum *= 10; // Scale decimal to avoid precision issues @@ -368,23 +411,16 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) return decimalNum; }; - auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { - /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ + auto compensateFP = [inInstruction, quantizedType, floatIntMul](float quantizedNum, float decimalNum) { float compensateNum = quantizedNum / decimalNum; + // Value * constOperand, *nonConstOperand; + // + // unsigned constIdx, nonConstIdx; + Value * constOperand = nullptr; + Value * nonConstOperand = nullptr; + unsigned constIdx = 0; + unsigned nonConstIdx = 0; - Value * constOperand, *nonConstOperand; - unsigned constIdx, nonConstIdx; if (isa(inInstruction->getOperand(0))) { constIdx = 0; @@ -400,60 +436,86 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) nonConstOperand = inInstruction->getOperand(0); } + // auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); + // auto quantizeNumValue = ConstantFP::get(nonConstOperand->getType(), quantizedNum); auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); if (compensateNum == 1) { - // If the compensation factor is 1, directly set the quantized value + llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; + llvm::errs() << "Original Instruction: " << *inInstruction << "\n"; + llvm::errs() << "Quantized value: " << *quantizeNumValue << "\n"; inInstruction->setOperand(constIdx, quantizeNumValue); - } - else - { - // Otherwise, apply compensation to the fixed-point arithmetic - auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + + IRBuilder<> Builder(inInstruction); + + if (nonConstOperand->getType()->isFloatTy() || nonConstOperand->getType()->isDoubleTy()) + { + // 调用 handleFloatIntMul 处理浮点数和整数相乘 + llvm::errs() << "Calling handleFloatIntMul\n"; + llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {nonConstOperand, quantizeNumValue}); + inInstruction->replaceAllUsesWith(callInst); + } + else + { + // Replace the original fmul instruction with integer mul + llvm::errs() << "Replacing original fmul instruction with integer mul\n"; + Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); + inInstruction->replaceAllUsesWith(newMulValue); + } + inInstruction->eraseFromParent(); + } + else{ + llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; + auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + + Value * newFirstInst = nullptr; Value * newSecondInst = nullptr; auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) { - // Replace floating-point multiplication with fixed-point equivalent - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { - // Replace floating-point division with fixed-point equivalent (divisor is constant) - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); + newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { - // Handle fixed-point division (constant dividend) - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + newFirstInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); } - inInstruction->replaceAllUsesWith(newSecondInst); - inInstruction->removeFromParent(); + if (newSecondInst) + { + inInstruction->replaceAllUsesWith(newSecondInst); + inInstruction->eraseFromParent(); + } + else + { + llvm::errs() << "Failed to create new compensated instruction\n"; + } } }; for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { Value * inValue = inInstruction->getOperand(idx); - if (!isa(inValue)) { - // Skip non-floating-point constants continue; } - ConstantFP * constFp = llvm::dyn_cast(inValue); + ConstantFP * constFp = dyn_cast(inValue); Value * newValue = nullptr; + if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -469,39 +531,114 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) assert(false && "unknown floating type"); } } + llvm::errs() << "Exiting simplifyConstant\n"; } -// Create a fixed-point multiplication function -//llvm::Function *createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector & functionsToInsert) +void +substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) +{ + llvm::errs() << "Entering substituteHardcodeFunc\n"; + llvm::errs() << "Original Instruction: " << *inInstruction << "\n"; + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + + Value * lhs = inInstruction->getOperand(0); + Value * rhs = inInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + llvm::CallInst * callInst = Builder.CreateCall(func, {lhs, rhs}); + llvm::errs() << "Created call instruction: " << *callInst << "\n"; + + inInstruction->replaceAllUsesWith(callInst); + inInstruction->removeFromParent(); + llvm::errs() << "Exiting substituteHardcodeFunc\n"; +} + +llvm::Function * +createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFloatIntMul\n"; + + // 检查 irModule 是否有效 + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string floatIntMulFuncName = "floatIntMul"; + for (auto & function : *irModule) + { + if (function.getName() == floatIntMulFuncName) + { + llvm::errs() << "floatIntMul already exists\n"; + return &function; + } + } + + llvm::FunctionType * funcType = llvm::FunctionType::get(intType, {floatType, intType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, floatIntMulFuncName, irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + builder.SetInsertPoint(entryBB); -// 创建一个固定点乘法函数 -llvm::Function* createFixMul(Module *irModule, Type *quantizedType, std::vector &functionsToInsert) { + // Get function arguments + + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * floatArg = &*arg1; + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * intArg = &*arg2; + + // Generate float * int multiplication instruction + llvm::Value * intToFloat = builder.CreateSIToFP(intArg, floatType); + llvm::Value * mulInst = builder.CreateFMul(floatArg, intToFloat); + llvm::Value * floatToInt = builder.CreateFPToSI(mulInst, intType); + + builder.CreateRet(floatToInt); + + functionsToInsert.emplace_back(func); + llvm::errs() << "Created floatIntMul function: " << func->getName() << "\n"; + return func; +} + +// Create a fixed-point multiplication function +llvm::Function * +createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ llvm::errs() << "Entering createFixMul\n"; // 检查 irModule 是否有效 - if (!irModule) { + if (!irModule) + { llvm::errs() << "Error: irModule is nullptr\n"; return nullptr; } std::string fixmulFuncName = "fixmul"; - for (auto &function : *irModule) { - if (function.getName() == fixmulFuncName) { + for (auto & function : *irModule) + { + if (function.getName() == fixmulFuncName) + { llvm::errs() << "fixmul already exists\n"; return &function; } } - llvm::FunctionType *funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); builder.SetInsertPoint(entryBB); // 生成 fixed-point 乘法指令 - Type *higherQuantizedType; - switch (BIT_WIDTH) { + Type * higherQuantizedType; + switch (BIT_WIDTH) + { case 8: higherQuantizedType = Type::getInt16Ty(irModule->getContext()); break; @@ -513,13 +650,13 @@ llvm::Function* createFixMul(Module *irModule, Type *quantizedType, std::vector< break; } - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value *sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value *sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value *mulInst = builder.CreateMul(sext1, sext2); - llvm::Value *ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value *truncInst = builder.CreateTrunc(ashrInst, quantizedType); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); builder.CreateRet(truncInst); functionsToInsert.emplace_back(func); @@ -527,25 +664,6 @@ llvm::Function* createFixMul(Module *irModule, Type *quantizedType, std::vector< return func; } - -// Substitute hardcoded functions like multiplication with fixed-point versions -void -substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) -{ - llvm::errs() << "Entering substituteHardcodeFunc\n"; - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - // Value * newInst = nullptr; - - llvm::CallInst * callInst = Builder.CreateCall(func, {inInstruction->getOperand(0), inInstruction->getOperand(1)}); - // InlineFunctionInfo inlineFuncInfo; - // llvm::InlineFunction(*callInst, inlineFuncInfo); - - inInstruction->replaceAllUsesWith(callInst); - inInstruction->removeFromParent(); -} - // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -634,21 +752,6 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) break; } - case Instruction::FMul: - { - llvm::errs() << "Handling FMul\n"; - // Replace floating-point multiplication with integer multiplication - newInst = Builder.CreateMul(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FDiv: - { - llvm::errs() << "Handling FDiv\n"; - // Replace floating-point division with integer division - newInst = Builder.CreateSDiv(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FRem: { llvm::errs() << "Handling FRem\n"; @@ -799,6 +902,63 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } } +void +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) +{ + llvm::errs() << "Handling FMul\n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + // Ensure operands are correctly converted to fixed-point integers + Value * lhs = llvmIrInstruction->getOperand(0); + Value * rhs = llvmIrInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + // If either operand is a float, convert both to fixed-point + if (lhsIsFloat) + { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + if (rhsIsFloat) + { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } + + // Ensure both operands are now integers + bool lhsIsInteger = lhs->getType()->isIntegerTy(); + bool rhsIsInteger = rhs->getType()->isIntegerTy(); + + if (lhsIsInteger && rhsIsInteger) + { + // Handle constant simplification if one of the operands is a constant + if (isa(lhs) || isa(rhs)) + { + llvm::errs() << "One of the operands is a constant, simplifying...\n"; + simplifyConstant(llvmIrInstruction, quantizedType, floatIntMul); + } + else + { + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); + } + } + else + { + llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; + } + + llvm::errs() << "Finished handling FMul\n"; +} + // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) @@ -808,12 +968,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label"|| functionName == "fixmul" ) { + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul") + // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) + { llvm::errs() << "Skipping function: " << functionName << "\n"; return; } - Type * quantizedType; switch (BIT_WIDTH) { @@ -835,68 +996,84 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } - // 保存模块指针 - Module *module = llvmIrFunction.getParent(); - if (!module) { + // Save the parent module + Module * module = llvmIrFunction.getParent(); + if (!module) + { llvm::errs() << "Error: Function does not have a parent module.\n"; return; } - // 处理函数签名 + // Ensure types are correctly defined + Type* floatType = Type::getFloatTy(llvmIrFunction.getContext()); + Type* intType = Type::getInt32Ty(llvmIrFunction.getContext()); + + // Deal with function signature llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; handleFunctionSignature(llvmIrFunction, quantizedType); llvm::errs() << "Finished calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; - - /* * generate hardcode function - fixmul and fixdiv * */ llvm::errs() << "Calling createFixMul for function: " << llvmIrFunction.getName() << "\n"; llvm::errs() << "inFunction address: " << &llvmIrFunction << "\n"; - //lvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); - llvm::Function *fixmul = createFixMul(module, quantizedType, functionsToInsert); + llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::errs() << "Created fixmul function: " << (fixmul ? "Success" : "Failed") << "\n"; + // Create floatIntMul function + llvm::errs() << "Calling createFloatIntMul for function: " << llvmIrFunction.getName() << "\n"; + //llvm::Function * floatIntMul = createFloatIntMul(llvmIrFunction.getParent(), intType, floatType, functionsToInsert); + llvm::Function *floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); + + llvm::errs() + << "Created floatIntMul function: " << (floatIntMul ? "Success" : "Failed") << "\n"; + /* * quantize the arguments type * */ llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; - for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++){ + for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) + { llvm::errs() << "Quantizing parameter at index " << idx << "\n"; llvm::Function::arg_iterator argIt = llvmIrFunction.arg_begin(); std::advance(argIt, idx); - if (argIt == llvmIrFunction.arg_end()) { + if (argIt == llvmIrFunction.arg_end()) + { llvm::errs() << "Error: Reached end of arguments while trying to access index " << idx << "\n"; continue; } - llvm::Argument *paramOp = &*argIt; + llvm::Argument * paramOp = &*argIt; llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; - if (!paramOp) { + if (!paramOp) + { llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; continue; } - if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) { + if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) + { llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; setQuantizedType(paramOp, quantizedType); - } else { + } + else + { llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; } } - - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { Instruction * llvmIrInstruction = &*itBB++; llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; + + IRBuilder<> Builder(llvmIrInstruction); switch (llvmIrInstruction->getOpcode()) { case Instruction::Alloca: @@ -1059,12 +1236,16 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * else substitute this instruction to a pre-implemented function: mulfix/divfix. * */ case Instruction::FMul: + llvm::errs() << "Found FMul instruction.\n"; + handleFMul(llvmIrInstruction, quantizedType, fixmul, floatIntMul); + break; + case Instruction::FDiv: { if (isa(llvmIrInstruction->getOperand(0)) || isa(llvmIrInstruction->getOperand(1))) { - simplifyConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType, floatIntMul); } else { From 9daab14572516b4d365bfcbc003379e336a79c0b Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 12 Jul 2024 12:55:21 +0100 Subject: [PATCH 032/213] fix fneg issue * dev2. --- ...5f482bc78076ef3d9b200117d948b899bba585.txt | 48 ++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 88 ++++++++++++++++++- 2 files changed, 132 insertions(+), 4 deletions(-) create mode 100644 analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt diff --git a/analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt b/analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt new file mode 100644 index 000000000..a63b397ad --- /dev/null +++ b/analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt @@ -0,0 +1,48 @@ + +changeset: 1625:365f482bc78076ef3d9b200117d948b899bba585 +char kNewtonVersion[] = "0.3-alpha-1625 (365f482bc78076ef3d9b200117d948b899bba585) (build 07-12-2024-12:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 20579a3ef..47de39281 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -411,6 +411,8 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f return decimalNum; }; + + auto compensateFP = [inInstruction, quantizedType, floatIntMul](float quantizedNum, float decimalNum) { float compensateNum = quantizedNum / decimalNum; // Value * constOperand, *nonConstOperand; @@ -735,23 +737,47 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; } +// case Instruction::FSub: +// { +// llvm::errs() << "Handling FSub\n"; +// Value * op0 = inInstruction->getOperand(0); +// Value * op1 = inInstruction->getOperand(1); +// if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) +// { +// op0 = Builder.CreateFPToSI(op0, quantizedType); +// } +// if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) +// { +// op1 = Builder.CreateFPToSI(op1, quantizedType); +// } +// newInst = Builder.CreateSub(op0, op1); +// break; +// } + case Instruction::FSub: { llvm::errs() << "Handling FSub\n"; Value * op0 = inInstruction->getOperand(0); Value * op1 = inInstruction->getOperand(1); + llvm::errs() << "Original Operand 0: " << *op0 << "\n"; + llvm::errs() << "Original Operand 1: " << *op1 << "\n"; if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { op0 = Builder.CreateFPToSI(op0, quantizedType); + llvm::errs() << "Converted Operand 0 to fixed-point: " << *op0 << "\n"; } if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { op1 = Builder.CreateFPToSI(op1, quantizedType); + llvm::errs() << "Converted Operand 1 to fixed-point: " << *op1 << "\n"; } newInst = Builder.CreateSub(op0, op1); + llvm::errs() << "Created Sub instruction: " << *newInst << "\n"; break; } + + case Instruction::FRem: { llvm::errs() << "Handling FRem\n"; @@ -808,9 +834,16 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Handling FNeg\n"; // Replace floating-point negation with integer negation + Value * op = inInstruction->getOperand(0); + if (op->getType()->isFloatTy() || op->getType()->isDoubleTy()) + { + op = Builder.CreateFPToSI(op, quantizedType); + } auto constZero = ConstantInt::get(quantizedType, 0, true); - newInst = Builder.CreateSub(constZero, inInstruction->getOperand(0)); + newInst = Builder.CreateSub(constZero, op); break; + + } default: llvm::errs() << "Unhandled floating point instruction\n"; @@ -844,16 +877,63 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: +// case Instruction::UIToFP: +// { +// auto sourceOp = llvmIrInstruction->getOperand(0); +// IRBuilder<> Builder(llvmIrInstruction); +// +// // 如果源操作数类型和指令类型相同,直接替换 +// if (sourceOp->getType() == llvmIrInstruction->getType()) +// { +// llvmIrInstruction->replaceAllUsesWith(sourceOp); +// llvmIrInstruction->removeFromParent(); +// } +// else +// { +// llvm::errs() << "Handling UIToFP: Source type does not match instruction type, adjusting types\n"; +// if (sourceOp->getType()->isIntegerTy()) +// { +// // 如果源操作数是整数类型,转换为浮点类型 +// sourceOp = Builder.CreateSIToFP(sourceOp, llvmIrInstruction->getType()); +// } +// else if (sourceOp->getType()->isFloatTy() || sourceOp->getType()->isDoubleTy()) +// { +// // 如果源操作数是浮点类型,转换为整数类型 +// sourceOp = Builder.CreateFPToSI(sourceOp, quantizedType); +// } +// +// // 创建新的 UIToFP 指令 +// Value * newInst = Builder.CreateUIToFP(sourceOp, llvmIrInstruction->getType()); +// llvmIrInstruction->replaceAllUsesWith(newInst); +// llvmIrInstruction->removeFromParent(); +// } +// break; +// } + + case Instruction::UIToFP: { auto sourceOp = llvmIrInstruction->getOperand(0); - if (sourceOp->getType() == llvmIrInstruction->getType()) + IRBuilder<> Builder(llvmIrInstruction); + + if (sourceOp->getType()->isIntegerTy()) { - // Replace with source if types match - llvmIrInstruction->replaceAllUsesWith(sourceOp); + // 将整数类型转换为浮点类型 + Value * newInst = Builder.CreateSIToFP(sourceOp, llvmIrInstruction->getType()); + llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->removeFromParent(); } + else if (sourceOp->getType()->isFloatTy() || sourceOp->getType()->isDoubleTy()) + { + // 将浮点类型转换为整数类型 + Value * newInst = Builder.CreateFPToSI(sourceOp, quantizedType); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); + } + break; } + + break; // case Instruction::ZExt: // case Instruction::SExt: From b4af9cb59fdfd1dd4ec1719e5953e9d0bcb0cef6 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 12 Jul 2024 22:17:57 +0100 Subject: [PATCH 033/213] add quantize global variable * dev2. --- ...30bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt | 48 ++++ .../newton-irPass-LLVMIR-quantization.cpp | 248 ++++++++++++++---- 2 files changed, 243 insertions(+), 53 deletions(-) create mode 100644 analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt diff --git a/analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt b/analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt new file mode 100644 index 000000000..6ef19480f --- /dev/null +++ b/analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt @@ -0,0 +1,48 @@ + +changeset: 1626:b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc +char kNewtonVersion[] = "0.3-alpha-1626 (b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc) (build 07-12-2024-12:55-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 47de39281..6f2b05a49 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -124,7 +124,20 @@ handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) if (ptr->getType()->getPointerElementType()->isFloatTy() || ptr->getType()->getPointerElementType()->isDoubleTy()) { - ptr->mutateType(quantizedType->getPointerTo()); + // 如果是全局变量,确保它的类型已经被转换 + if (isa(ptr)) + { + auto globalVar = dyn_cast(ptr); + if (globalVar->getType()->getElementType()->isFloatTy() || + globalVar->getType()->getElementType()->isDoubleTy()) + { + ptr->mutateType(quantizedType->getPointerTo()); + } + } + else + { + ptr->mutateType(quantizedType->getPointerTo()); + } } } } @@ -136,7 +149,17 @@ handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) if (ptr->getType()->getPointerElementType()->isFloatTy() || ptr->getType()->getPointerElementType()->isDoubleTy()) { - ptr->mutateType(quantizedType->getPointerTo()); + // 如果是全局变量,确保它的类型已经被转换 + if (isa(ptr)) { + auto globalVar = dyn_cast(ptr); + if (globalVar->getType()->getElementType()->isFloatTy() || + globalVar->getType()->getElementType()->isDoubleTy()) { + ptr->mutateType(quantizedType->getPointerTo()); + } + } + else { + ptr->mutateType(quantizedType->getPointerTo()); + } } } } @@ -247,8 +270,8 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul") - // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul") + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul"|| functionName == "sqrt") + { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; return; @@ -278,6 +301,29 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } + +// A list of global variables to erase after processing +std::vector globalsToErase; + + + +// Function to actually erase global variables after processing +void eraseOldGlobals() { + llvm::errs() << "Entering eraseOldGlobals\n"; + std::set uniqueGlobals(globalsToErase.begin(), globalsToErase.end()); + for (auto *global : uniqueGlobals) { + if (global) { + llvm::errs() << "Erasing old global variable: " << global->getName() << "\n"; + global->eraseFromParent(); + } else { + llvm::errs() << "Skipping null global variable\n"; + } + } + globalsToErase.clear(); + llvm::errs() << "Exiting eraseOldGlobals\n"; +} + + // Function to actually erase functions after processing void eraseOldFunctions() @@ -292,6 +338,95 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } + +// Function to update global variables from floating-point to integer types +//void updateGlobalVariables(llvm::Module *module, llvm::Type *quantizedType) { +// for (llvm::GlobalVariable &globalVar : module->globals()) { +// if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) { +// llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; +// +// // Create the new integer type pointer +// llvm::Type* newType = quantizedType->getPointerTo(); +// +// // Update the initializer of the global variable +// llvm::Constant *newInitializer = nullptr; +// if (llvm::Constant *init = globalVar.getInitializer()) { +// if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { +// double value = constFp->getValueAPF().convertToDouble(); +// int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); +// newInitializer = llvm::ConstantInt::get(quantizedType, quantizedValue); +// } +// } +// +// // Create a new global variable with the updated type and initializer +// llvm::GlobalVariable *newGlobalVar = new llvm::GlobalVariable( +// *module, +// quantizedType, +// globalVar.isConstant(), +// globalVar.getLinkage(), +// newInitializer, +// globalVar.getName() + "_quantized", +// nullptr, +// globalVar.getThreadLocalMode(), +// globalVar.getType()->getAddressSpace(), +// globalVar.isExternallyInitialized() +// ); +// +// // Replace all uses of the old global variable with the new one +// globalVar.replaceAllUsesWith(newGlobalVar); +// +// // Add the old global variable to the list of globals to erase +// globalsToErase.push_back(&globalVar); +// } +// } +//} + +void updateGlobalVariables(Module *module, Type *quantizedType) { + llvm::errs() << "Updating global variables\n"; + + for (GlobalVariable &globalVar : module->globals()) { + if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) { + llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; + + // Create the new integer type pointer + Type *newType = quantizedType->getPointerTo(); + + // Update the initializer of the global variable + if (llvm::Constant *init = globalVar.getInitializer()) { + if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { + double value = constFp->getValueAPF().convertToDouble(); + int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); + globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); + } + } + + // Check if a quantized version of the global variable already exists + std::string quantizedName = globalVar.getName().str() + "_quantized"; + if (GlobalVariable *existingGlobalVar = module->getNamedGlobal(quantizedName)) { + // Replace all uses of the old global variable with the existing quantized one + globalVar.replaceAllUsesWith(existingGlobalVar); + } else { + // Create a new global variable with the updated type and initializer + GlobalVariable *newGlobalVar = new GlobalVariable( + *module, + quantizedType, + globalVar.isConstant(), + globalVar.getLinkage(), + globalVar.getInitializer(), + quantizedName + ); + + // Replace all uses of the old global variable with the new one + globalVar.replaceAllUsesWith(newGlobalVar); + } + + // Add the old global variable to the list of globals to erase + globalsToErase.push_back(&globalVar); + } + } +} + + // Quantize constants within an instruction void @@ -354,47 +489,47 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) } } -void -handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) -{ - llvm::errs() << "Handling FloatIntMul\n"; - llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; - IRBuilder<> Builder(llvmIrInstruction); - - // 获取操作数 - Value * lhs = llvmIrInstruction->getOperand(0); - Value * rhs = llvmIrInstruction->getOperand(1); - - llvm::errs() << "LHS: " << *lhs << "\n"; - llvm::errs() << "RHS: " << *rhs << "\n"; - - // 确保左操作数是浮点数,右操作数是整数 - if (!lhs->getType()->isFloatTy() && !lhs->getType()->isDoubleTy()) - { - std::swap(lhs, rhs); - } - - if (lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy()) - { - if (rhs->getType()->isIntegerTy()) - { - llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Replaced with call to floatIntMul\n"; - } - else - { - llvm::errs() << "RHS is not an integer\n"; - } - } - else - { - llvm::errs() << "LHS is not a float\n"; - } - - llvm::errs() << "Finished handling FloatIntMul\n"; -} +//void +//handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) +//{ +// llvm::errs() << "Handling FloatIntMul\n"; +// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; +// IRBuilder<> Builder(llvmIrInstruction); +// +// // 获取操作数 +// Value * lhs = llvmIrInstruction->getOperand(0); +// Value * rhs = llvmIrInstruction->getOperand(1); +// +// llvm::errs() << "LHS: " << *lhs << "\n"; +// llvm::errs() << "RHS: " << *rhs << "\n"; +// +// // 确保左操作数是浮点数,右操作数是整数 +// if (!lhs->getType()->isFloatTy() && !lhs->getType()->isDoubleTy()) +// { +// std::swap(lhs, rhs); +// } +// +// if (lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy()) +// { +// if (rhs->getType()->isIntegerTy()) +// { +// llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {lhs, rhs}); +// llvmIrInstruction->replaceAllUsesWith(callInst); +// llvmIrInstruction->eraseFromParent(); +// llvm::errs() << "Replaced with call to floatIntMul\n"; +// } +// else +// { +// llvm::errs() << "RHS is not an integer\n"; +// } +// } +// else +// { +// llvm::errs() << "LHS is not a float\n"; +// } +// +// llvm::errs() << "Finished handling FloatIntMul\n"; +//} void simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) @@ -1046,14 +1181,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - // Skip certain functions - std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul") - // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) - { - llvm::errs() << "Skipping function: " << functionName << "\n"; - return; - } +// // Skip certain functions +// std::string functionName = llvmIrFunction.getName().str(); +// if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul") +// // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) +// { +// llvm::errs() << "Skipping function: " << functionName << "\n"; +// return; +// } Type * quantizedType; switch (BIT_WIDTH) @@ -1093,6 +1228,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleFunctionSignature(llvmIrFunction, quantizedType); llvm::errs() << "Finished calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; + + + // Update global variable type to integer type + llvm::errs() << "Calling handleGlobalVariable for function: " << llvmIrFunction.getName() << "\n"; + // Update global variables + updateGlobalVariables(module, quantizedType); + /* * generate hardcode function - fixmul and fixdiv * */ From 572baf5a9257a2a739d75901e62a8dd2b7b83521 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 13 Jul 2024 11:47:42 +0100 Subject: [PATCH 034/213] fix some issue * dev2. --- ...aab14572516b4d365bfcbc003379e336a79c0b.txt | 48 ++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 2 +- .../newton-irPass-LLVMIR-quantization.cpp | 214 +++++++++--------- .../newton-irPass-LLVMIR-quantization.h | 4 +- 4 files changed, 160 insertions(+), 108 deletions(-) create mode 100644 analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt diff --git a/analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt b/analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt new file mode 100644 index 000000000..cfe3f099a --- /dev/null +++ b/analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt @@ -0,0 +1,48 @@ + +changeset: 1627:9daab14572516b4d365bfcbc003379e336a79c0b +char kNewtonVersion[] = "0.3-alpha-1627 (9daab14572516b4d365bfcbc003379e336a79c0b) (build 07-12-2024-22:17-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 8d048d3dc..d26c490d7 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -495,7 +495,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // Finally, erase old functions eraseOldFunctions(); - + eraseOldGlobals(); // 打印处理后的模块IR并保存到文件 saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 6f2b05a49..4c62aa01e 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -150,14 +150,17 @@ handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) ptr->getType()->getPointerElementType()->isDoubleTy()) { // 如果是全局变量,确保它的类型已经被转换 - if (isa(ptr)) { + if (isa(ptr)) + { auto globalVar = dyn_cast(ptr); if (globalVar->getType()->getElementType()->isFloatTy() || - globalVar->getType()->getElementType()->isDoubleTy()) { + globalVar->getType()->getElementType()->isDoubleTy()) + { ptr->mutateType(quantizedType->getPointerTo()); } } - else { + else + { ptr->mutateType(quantizedType->getPointerTo()); } } @@ -270,7 +273,7 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul"|| functionName == "sqrt") + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; @@ -301,21 +304,24 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } - // A list of global variables to erase after processing std::vector globalsToErase; - - // Function to actually erase global variables after processing -void eraseOldGlobals() { +void +eraseOldGlobals() +{ llvm::errs() << "Entering eraseOldGlobals\n"; std::set uniqueGlobals(globalsToErase.begin(), globalsToErase.end()); - for (auto *global : uniqueGlobals) { - if (global) { + for (auto * global : uniqueGlobals) + { + if (global) + { llvm::errs() << "Erasing old global variable: " << global->getName() << "\n"; global->eraseFromParent(); - } else { + } + else + { llvm::errs() << "Skipping null global variable\n"; } } @@ -323,7 +329,6 @@ void eraseOldGlobals() { llvm::errs() << "Exiting eraseOldGlobals\n"; } - // Function to actually erase functions after processing void eraseOldFunctions() @@ -338,9 +343,8 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } - // Function to update global variables from floating-point to integer types -//void updateGlobalVariables(llvm::Module *module, llvm::Type *quantizedType) { +// void updateGlobalVariables(llvm::Module *module, llvm::Type *quantizedType) { // for (llvm::GlobalVariable &globalVar : module->globals()) { // if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) { // llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; @@ -381,20 +385,26 @@ eraseOldFunctions() // } //} -void updateGlobalVariables(Module *module, Type *quantizedType) { +void +updateGlobalVariables(Module * module, Type * quantizedType) +{ llvm::errs() << "Updating global variables\n"; - for (GlobalVariable &globalVar : module->globals()) { - if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) { + for (GlobalVariable & globalVar : module->globals()) + { + if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) + { llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; // Create the new integer type pointer - Type *newType = quantizedType->getPointerTo(); + Type * newType = quantizedType->getPointerTo(); // Update the initializer of the global variable - if (llvm::Constant *init = globalVar.getInitializer()) { - if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { - double value = constFp->getValueAPF().convertToDouble(); + if (llvm::Constant * init = globalVar.getInitializer()) + { + if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) + { + double value = constFp->getValueAPF().convertToDouble(); int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); } @@ -402,19 +412,21 @@ void updateGlobalVariables(Module *module, Type *quantizedType) { // Check if a quantized version of the global variable already exists std::string quantizedName = globalVar.getName().str() + "_quantized"; - if (GlobalVariable *existingGlobalVar = module->getNamedGlobal(quantizedName)) { + if (GlobalVariable * existingGlobalVar = module->getNamedGlobal(quantizedName)) + { // Replace all uses of the old global variable with the existing quantized one globalVar.replaceAllUsesWith(existingGlobalVar); - } else { + } + else + { // Create a new global variable with the updated type and initializer - GlobalVariable *newGlobalVar = new GlobalVariable( + GlobalVariable * newGlobalVar = new GlobalVariable( *module, quantizedType, globalVar.isConstant(), globalVar.getLinkage(), globalVar.getInitializer(), - quantizedName - ); + quantizedName); // Replace all uses of the old global variable with the new one globalVar.replaceAllUsesWith(newGlobalVar); @@ -426,7 +438,6 @@ void updateGlobalVariables(Module *module, Type *quantizedType) { } } - // Quantize constants within an instruction void @@ -489,8 +500,8 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) } } -//void -//handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) +// void +// handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) //{ // llvm::errs() << "Handling FloatIntMul\n"; // llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -529,7 +540,7 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // } // // llvm::errs() << "Finished handling FloatIntMul\n"; -//} +// } void simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) @@ -546,8 +557,6 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f return decimalNum; }; - - auto compensateFP = [inInstruction, quantizedType, floatIntMul](float quantizedNum, float decimalNum) { float compensateNum = quantizedNum / decimalNum; // Value * constOperand, *nonConstOperand; @@ -584,7 +593,6 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f llvm::errs() << "Quantized value: " << *quantizeNumValue << "\n"; inInstruction->setOperand(constIdx, quantizeNumValue); - IRBuilder<> Builder(inInstruction); if (nonConstOperand->getType()->isFloatTy() || nonConstOperand->getType()->isDoubleTy()) @@ -598,12 +606,13 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f { // Replace the original fmul instruction with integer mul llvm::errs() << "Replacing original fmul instruction with integer mul\n"; - Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); + Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); inInstruction->replaceAllUsesWith(newMulValue); } inInstruction->eraseFromParent(); } - else{ + else + { llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); IRBuilder<> Builder(inInstruction); @@ -872,22 +881,22 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); break; } -// case Instruction::FSub: -// { -// llvm::errs() << "Handling FSub\n"; -// Value * op0 = inInstruction->getOperand(0); -// Value * op1 = inInstruction->getOperand(1); -// if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) -// { -// op0 = Builder.CreateFPToSI(op0, quantizedType); -// } -// if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) -// { -// op1 = Builder.CreateFPToSI(op1, quantizedType); -// } -// newInst = Builder.CreateSub(op0, op1); -// break; -// } + // case Instruction::FSub: + // { + // llvm::errs() << "Handling FSub\n"; + // Value * op0 = inInstruction->getOperand(0); + // Value * op1 = inInstruction->getOperand(1); + // if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + // { + // op0 = Builder.CreateFPToSI(op0, quantizedType); + // } + // if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + // { + // op1 = Builder.CreateFPToSI(op1, quantizedType); + // } + // newInst = Builder.CreateSub(op0, op1); + // break; + // } case Instruction::FSub: { @@ -911,8 +920,6 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) break; } - - case Instruction::FRem: { llvm::errs() << "Handling FRem\n"; @@ -975,10 +982,8 @@ quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) op = Builder.CreateFPToSI(op, quantizedType); } auto constZero = ConstantInt::get(quantizedType, 0, true); - newInst = Builder.CreateSub(constZero, op); + newInst = Builder.CreateSub(constZero, op); break; - - } default: llvm::errs() << "Unhandled floating point instruction\n"; @@ -1012,43 +1017,42 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: -// case Instruction::UIToFP: -// { -// auto sourceOp = llvmIrInstruction->getOperand(0); -// IRBuilder<> Builder(llvmIrInstruction); -// -// // 如果源操作数类型和指令类型相同,直接替换 -// if (sourceOp->getType() == llvmIrInstruction->getType()) -// { -// llvmIrInstruction->replaceAllUsesWith(sourceOp); -// llvmIrInstruction->removeFromParent(); -// } -// else -// { -// llvm::errs() << "Handling UIToFP: Source type does not match instruction type, adjusting types\n"; -// if (sourceOp->getType()->isIntegerTy()) -// { -// // 如果源操作数是整数类型,转换为浮点类型 -// sourceOp = Builder.CreateSIToFP(sourceOp, llvmIrInstruction->getType()); -// } -// else if (sourceOp->getType()->isFloatTy() || sourceOp->getType()->isDoubleTy()) -// { -// // 如果源操作数是浮点类型,转换为整数类型 -// sourceOp = Builder.CreateFPToSI(sourceOp, quantizedType); -// } -// -// // 创建新的 UIToFP 指令 -// Value * newInst = Builder.CreateUIToFP(sourceOp, llvmIrInstruction->getType()); -// llvmIrInstruction->replaceAllUsesWith(newInst); -// llvmIrInstruction->removeFromParent(); -// } -// break; -// } - + // case Instruction::UIToFP: + // { + // auto sourceOp = llvmIrInstruction->getOperand(0); + // IRBuilder<> Builder(llvmIrInstruction); + // + // // 如果源操作数类型和指令类型相同,直接替换 + // if (sourceOp->getType() == llvmIrInstruction->getType()) + // { + // llvmIrInstruction->replaceAllUsesWith(sourceOp); + // llvmIrInstruction->removeFromParent(); + // } + // else + // { + // llvm::errs() << "Handling UIToFP: Source type does not match instruction type, adjusting types\n"; + // if (sourceOp->getType()->isIntegerTy()) + // { + // // 如果源操作数是整数类型,转换为浮点类型 + // sourceOp = Builder.CreateSIToFP(sourceOp, llvmIrInstruction->getType()); + // } + // else if (sourceOp->getType()->isFloatTy() || sourceOp->getType()->isDoubleTy()) + // { + // // 如果源操作数是浮点类型,转换为整数类型 + // sourceOp = Builder.CreateFPToSI(sourceOp, quantizedType); + // } + // + // // 创建新的 UIToFP 指令 + // Value * newInst = Builder.CreateUIToFP(sourceOp, llvmIrInstruction->getType()); + // llvmIrInstruction->replaceAllUsesWith(newInst); + // llvmIrInstruction->removeFromParent(); + // } + // break; + // } case Instruction::UIToFP: { - auto sourceOp = llvmIrInstruction->getOperand(0); + auto sourceOp = llvmIrInstruction->getOperand(0); IRBuilder<> Builder(llvmIrInstruction); if (sourceOp->getType()->isIntegerTy()) @@ -1068,7 +1072,6 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) break; } - break; // case Instruction::ZExt: // case Instruction::SExt: @@ -1181,14 +1184,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; -// // Skip certain functions -// std::string functionName = llvmIrFunction.getName().str(); -// if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul") -// // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) -// { -// llvm::errs() << "Skipping function: " << functionName << "\n"; -// return; -// } + // Skip certain functions + std::string functionName = llvmIrFunction.getName().str(); + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") + // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) + { + llvm::errs() << "Skipping function: " << functionName << "\n"; + return; + } Type * quantizedType; switch (BIT_WIDTH) @@ -1220,16 +1223,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } // Ensure types are correctly defined - Type* floatType = Type::getFloatTy(llvmIrFunction.getContext()); - Type* intType = Type::getInt32Ty(llvmIrFunction.getContext()); + Type * floatType = Type::getFloatTy(llvmIrFunction.getContext()); + Type * intType = Type::getInt32Ty(llvmIrFunction.getContext()); // Deal with function signature llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; handleFunctionSignature(llvmIrFunction, quantizedType); llvm::errs() << "Finished calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; - - // Update global variable type to integer type llvm::errs() << "Calling handleGlobalVariable for function: " << llvmIrFunction.getName() << "\n"; // Update global variables @@ -1244,10 +1245,11 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::errs() << "Created fixmul function: " << (fixmul ? "Success" : "Failed") << "\n"; - // Create floatIntMul function + // generate hardcode function - floatIntMul function llvm::errs() << "Calling createFloatIntMul for function: " << llvmIrFunction.getName() << "\n"; - //llvm::Function * floatIntMul = createFloatIntMul(llvmIrFunction.getParent(), intType, floatType, functionsToInsert); - llvm::Function *floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); + // llvm::Function * floatIntMul = createFloatIntMul(llvmIrFunction.getParent(), intType, floatType, functionsToInsert); + llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); + // llvm::Function* floatIntMul = createFloatIntMul(module, quantizedType, floatType, functionsToInsert); llvm::errs() << "Created floatIntMul function: " << (floatIntMul ? "Success" : "Failed") << "\n"; diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 8c867fd1b..62f5a6ab2 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -36,13 +36,15 @@ extern "C" { #endif /* __cplusplus */ extern std::vector functionsToErase; +extern std::vector globalsToErase; void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); extern void eraseOldFunctions(); - +extern +void eraseOldGlobals(); #ifdef __cplusplus } /* extern "C" */ #endif /* __cplusplus */ From 82a2b0f7f25b26215f1edaea1aaf0d327901191c Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 16 Jul 2024 11:33:21 +0100 Subject: [PATCH 035/213] fix function name issue * dev2. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 18 ++++++++++++++++-- 1 file changed, 16 insertions(+), 2 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index d26c490d7..00251fad0 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -88,7 +88,18 @@ void saveModuleIR(llvm::Module &M, const std::string &fileName) { file.close(); } - +void removeQuantizedSuffixInModule(llvm::Module &M) { + for (auto &F : M) { + if (F.hasName()) { + std::string FuncName = F.getName().str(); + size_t pos = FuncName.find("_quantized"); + if (pos != std::string::npos) { + FuncName.erase(pos, 10); // Remove "_quantized" + F.setName(FuncName); + } + } + } +} void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) @@ -497,7 +508,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl eraseOldGlobals(); - // 打印处理后的模块IR并保存到文件 + // Perform text replacement to remove "_quantized" suffixes + removeQuantizedSuffixInModule(*Mod); + + // Save the optimized IR to a file saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); From c2128b78afdac2805a0e2dc277c4e54c7f5acb2e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 17 Jul 2024 22:20:04 +0100 Subject: [PATCH 036/213] update makefile * dev2. --- applications/newton/llvm-ir/performance_test/Makefile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 820f1afb6..fc20ab602 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -59,7 +59,7 @@ endif endif # ENABLE_OVERLOAD=true ENABLE_BUILTIN_ASSUME=true make ... -NEWTON_FLAG=--llvm-ir-liveness-check +NEWTON_FLAG=--llvm-ir-liveness-check --llvm-ir-auto-quantization ifdef ENABLE_OVERLOAD NEWTON_FLAG+=--llvm-ir-enable-overload endif From 2de0b2082ea8d7560ef1776838debac2236b308b Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Thu, 18 Jul 2024 14:59:33 +0100 Subject: [PATCH 037/213] update MadwickAHRS.c to have more print logs * dev2. --- ...a2b0f7f25b26215f1edaea1aaf0d327901191c.txt | 48 +++++++++++++++++++ .../newton/llvm-ir/c-files/MadgwickAHRS.c | 35 +++++++++++++- 2 files changed, 82 insertions(+), 1 deletion(-) create mode 100644 analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt diff --git a/analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt b/analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt new file mode 100644 index 000000000..7804f9673 --- /dev/null +++ b/analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt @@ -0,0 +1,48 @@ + +changeset: 1630:82a2b0f7f25b26215f1edaea1aaf0d327901191c +char kNewtonVersion[] = "0.3-alpha-1630 (82a2b0f7f25b26215f1edaea1aaf0d327901191c) (build 07-17-2024-22:20-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index 944df8c62..7af26d46d 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -16,6 +16,7 @@ // Header files #include "MadgwickAHRS.h" +#include // for printf //--------------------------------------------------------------------------------------------------- // Definitions @@ -70,6 +71,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float float q1 = *q1_ptr; float q2 = *q2_ptr; float q3 = *q3_ptr; + printf("Initial quaternion: q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); #ifdef ASSUME __builtin_assume(ax > lowerBound && ax < upperBound); __builtin_assume(ay > lowerBound && ay < upperBound); @@ -93,6 +95,8 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + printf("qDot values: qDot1=%f, qDot2=%f, qDot3=%f, qDot4=%f\n", qDot1, qDot2, qDot3, qDot4); + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -103,12 +107,16 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float ay *= recipNorm; az *= recipNorm; + printf("Normalized accelerometer: ax=%f, ay=%f, az=%f\n", ax, ay, az); + // Normalise magnetometer measurement recipNorm = invSqrt(mx * mx + my * my + mz * mz); mx *= recipNorm; my *= recipNorm; mz *= recipNorm; + printf("Normalized magnetometer: mx=%f, my=%f, mz=%f\n", mx, my, mz); + // Auxiliary variables to avoid repeated arithmetic _2q0mx = 2.0f * q0 * mx; _2q0my = 2.0f * q0 * my; @@ -139,6 +147,8 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float _4bx = 2.0f * _2bx; _4bz = 2.0f * _2bz; + printf("hx=%f, hy=%f, _2bx=%f, _2bz=%f, _4bx=%f, _4bz=%f\n", hx, hy, _2bx, _2bz, _4bx, _4bz); + // Gradient decent algorithm corrective step s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); @@ -150,6 +160,8 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float s2 *= recipNorm; s3 *= recipNorm; + printf("s values: s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); + // Apply feedback step qDot1 -= beta * s0; qDot2 -= beta * s1; @@ -163,6 +175,10 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float q2 += qDot3 * (1.0f / sampleFreq); q3 += qDot4 * (1.0f / sampleFreq); + printf("Updated quaternion (before normalization): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", @@ -171,6 +187,8 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; + + printf("Normalized quaternion: q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); *q0_ptr = q0; *q1_ptr = q1; *q2_ptr = q2; @@ -188,6 +206,9 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo float q1 = *q1_ptr; float q2 = *q2_ptr; float q3 = *q3_ptr; + + printf("Initial quaternion (IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; @@ -199,6 +220,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + printf("qDot values (IMU): qDot1=%f, qDot2=%f, qDot3=%f, qDot4=%f\n", qDot1, qDot2, qDot3, qDot4); + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -206,7 +229,9 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo recipNorm = invSqrt(ax * ax + ay * ay + az * az); ax *= recipNorm; ay *= recipNorm; - az *= recipNorm; + az *= recipNorm; + + printf("Normalized accelerometer (IMU): ax=%f, ay=%f, az=%f\n", ax, ay, az); // Auxiliary variables to avoid repeated arithmetic _2q0 = 2.0f * q0; @@ -234,6 +259,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo s2 *= recipNorm; s3 *= recipNorm; + printf("s values (IMU): s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); + // Apply feedback step qDot1 -= beta * s0; qDot2 -= beta * s1; @@ -247,12 +274,18 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo q2 += qDot3 * (1.0f / sampleFreq); q3 += qDot4 * (1.0f / sampleFreq); + printf("Updated quaternion (before normalization, IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + + + // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); q0 *= recipNorm; q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; + + printf("Normalized quaternion (IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); *q0_ptr = q0; *q1_ptr = q1; *q2_ptr = q2; From 223a41a57e9c7de1fc56eb069a7f0f64e632dec4 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 19 Jul 2024 21:08:38 +0100 Subject: [PATCH 038/213] add dequantization pass * dev2. --- src/newton/Makefile | 9 + .../newton-irPass-LLVMIR-dequantization.cpp | 217 ++++++++++++++++++ .../newton-irPass-LLVMIR-dequantization.h | 26 +++ 3 files changed, 252 insertions(+) create mode 100644 src/newton/newton-irPass-LLVMIR-dequantization.cpp create mode 100644 src/newton/newton-irPass-LLVMIR-dequantization.h diff --git a/src/newton/Makefile b/src/newton/Makefile index 31e249390..42c9a7d43 100644 --- a/src/newton/Makefile +++ b/src/newton/Makefile @@ -100,6 +100,8 @@ SOURCES =\ newton-irPass-LLVMIR-constantSubstitution.cpp\ newton-irPass-LLVMIR-shrinkTypeByRange.cpp\ newton-irPass-LLVMIR-quantization.cpp\ + + newton-irPass-LLVMIR-dequantization.cpp\ newton-irPass-LLVMIR-memoryAlignment.cpp\ newton-irPass-LLVMIR-emitAssume.cpp\ @@ -137,6 +139,7 @@ OBJS =\ newton-irPass-LLVMIR-constantSubstitution.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION)\ + newton-irPass-LLVMIR-dequantization.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION)\ newton-irPass-invariantSignalAnnotation.$(OBJECTEXTENSION)\ newton-irPass-piGroupsSignalAnnotation.$(OBJECTEXTENSION)\ @@ -186,6 +189,7 @@ CGIOBJS =\ newton-irPass-LLVMIR-constantSubstitution.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION)\ + newton-irPass-LLVMIR-dequantization.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION)\ newton-irPass-invariantSignalAnnotation.$(OBJECTEXTENSION)\ newton-irPass-piGroupsSignalAnnotation.$(OBJECTEXTENSION)\ @@ -234,6 +238,7 @@ LIBNEWTONOBJS =\ newton-irPass-LLVMIR-constantSubstitution.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION)\ + newton-irPass-LLVMIR-dequantization.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION)\ newton-irPass-invariantSignalAnnotation.$(OBJECTEXTENSION)\ newton-irPass-piGroupsSignalAnnotation.$(OBJECTEXTENSION)\ @@ -370,6 +375,10 @@ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-quant $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< +newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-dequantization.cpp + $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< + $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< + newton-irPass-LLVMIR-memoryAlignment.$(OBJECTEXTENSION): newton-irPass-LLVMIR-memoryAlignment.cpp $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< diff --git a/src/newton/newton-irPass-LLVMIR-dequantization.cpp b/src/newton/newton-irPass-LLVMIR-dequantization.cpp new file mode 100644 index 000000000..7b593a0b1 --- /dev/null +++ b/src/newton/newton-irPass-LLVMIR-dequantization.cpp @@ -0,0 +1,217 @@ +#include "newton-irPass-LLVMIR-dequantization.h" +#include +using namespace llvm; + +#define FRAC_Q 16 +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 + +extern "C" { + +// Create a dequantized function with the same signature as the original function +Function * +createDequantizedFunction(Function & llvmIrFunction, Type * floatType) +{ + std::vector params; + for (auto & arg : llvmIrFunction.args()) + { + if (isQuantizedType(arg.getType())) + { + params.push_back(floatType); + llvm::errs() << "Dequantizing parameter: " << arg.getName() << " from " << *arg.getType() << " to " << *floatType << "\n"; + } + else + { + params.push_back(arg.getType()); + } + } + + Type * returnType = isQuantizedType(llvmIrFunction.getReturnType()) ? floatType : llvmIrFunction.getReturnType(); + FunctionType * newFuncType = FunctionType::get(returnType, params, false); + Function * newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_dequantized", llvmIrFunction.getParent()); + return newFunc; +} + +// Clone the function body from the original function to the new dequantized function +void +cloneFunctionBody(Function & oldFunc, Function * newFunc) +{ + ValueToValueMapTy vmap; + Function::arg_iterator newArgIt = newFunc->arg_begin(); + for (auto & oldArg : oldFunc.args()) + { + newArgIt->setName(oldArg.getName()); + vmap[&oldArg] = &*newArgIt++; + } + SmallVector returns; + llvm::CloneFunctionInto(newFunc, &oldFunc, vmap, CloneFunctionChangeType::LocalChangesOnly, returns); +} + +// Replace all uses of the original function with the new dequantized function +void +replaceFunctionUses(Function & oldFunc, Function * newFunc) +{ + std::vector users(oldFunc.user_begin(), oldFunc.user_end()); + for (auto * U : users) + { + if (CallInst * callInst = dyn_cast(U)) + { + std::vector args; + for (auto & arg : callInst->args()) + { + args.push_back(arg); + } + CallInst * newCall = CallInst::Create(newFunc, args, "", callInst); + newCall->setCallingConv(callInst->getCallingConv()); + newCall->setDebugLoc(callInst->getDebugLoc()); + llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; + callInst->replaceAllUsesWith(newCall); + callInst->eraseFromParent(); + } + } +} + +// Set the dequantized type for a given value +void +setDequantizedType(Value * inValue, Type * floatType) +{ + llvm::errs() << "Entering setDequantizedType\n"; + if (inValue == nullptr) + { + llvm::errs() << "inValue is nullptr\n"; + return; + } + auto valueType = inValue->getType(); + unsigned pointerAddr; + bool isPointer = false; + + if (valueType != nullptr) + { + if (valueType->isPointerTy()) + { + isPointer = true; + pointerAddr = valueType->getPointerAddressSpace(); + valueType = valueType->getPointerElementType(); + } + + if (isQuantizedType(valueType) || valueType->isArrayTy()) + { + llvm::errs() << "Original type: " << *valueType << "\n"; + llvm::errs() << "New dequantized type: " << *floatType << "\n"; + if (isPointer) + { + inValue->mutateType(floatType->getPointerTo(pointerAddr)); + } + else + { + inValue->mutateType(floatType); + } + } + else + { + llvm::errs() << "Unsupported type for dequantization: " << *valueType << "\n"; + } + } + else + { + llvm::errs() << "Value type is nullptr\n"; + } +} +} + +// Adapt type casts to the dequantized type +void adaptTypeCast(Function &llvmIrFunction, Type *floatType) { + llvm::errs() << "Entering adaptTypeCast\n"; + for (BasicBlock &llvmIrBasicBlock : llvmIrFunction) { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { + Instruction *llvmIrInstruction = &*itBB++; + switch (llvmIrInstruction->getOpcode()) { + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::UIToFP: + case Instruction::SIToFP: + { + IRBuilder<> Builder(llvmIrInstruction); + Value *operand = llvmIrInstruction->getOperand(0); + Value *newInst = nullptr; + + if (llvmIrInstruction->getOpcode() == Instruction::FPToUI || llvmIrInstruction->getOpcode() == Instruction::FPToSI) { + if (isQuantizedType(operand->getType())) { + newInst = Builder.CreateSIToFP(operand, floatType); + } + } else if (llvmIrInstruction->getOpcode() == Instruction::UIToFP || llvmIrInstruction->getOpcode() == Instruction::SIToFP) { + if (isQuantizedType(llvmIrInstruction->getType())) { + newInst = Builder.CreateSIToFP(operand, floatType); + } + } + + if (newInst) { + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + } + break; + } + case Instruction::FPExt: + case Instruction::FPTrunc: + { + IRBuilder<> Builder(llvmIrInstruction); + Value *operand = llvmIrInstruction->getOperand(0); + Value *newInst = nullptr; + + if (isQuantizedType(operand->getType())) { + newInst = Builder.CreateSIToFP(operand, floatType); + } else { + newInst = Builder.CreateFPCast(operand, floatType); + } + + if (newInst) { + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + } + break; + } + case Instruction::BitCast: + { + IRBuilder<> Builder(llvmIrInstruction); + Value *operand = llvmIrInstruction->getOperand(0); + Value *newInst = Builder.CreateBitCast(operand, floatType); + + if (newInst) { + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + } + break; + } + default: + break; + } + } + } + llvm::errs() << "Exiting adaptTypeCast\n"; +} + +// Main function to perform LLVM IR auto dequantization +void +irPassLLVMIRAutoDequantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) +{ + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto dequantization.\n"); + llvm::errs() << "Entering irPassLLVMIRAutoDequantization\n"; + + std::string functionName = llvmIrFunction.getName().str(); + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") + { + llvm::errs() << "Skipping function: " << functionName << "\n"; + return; + } + + Type * floatType = Type::getFloatTy(llvmIrFunction.getContext()); + + Function * newFunc = createDequantizedFunction(llvmIrFunction, floatType); + cloneFunctionBody(llvmIrFunction, newFunc); + replaceFunctionUses(llvmIrFunction, newFunc); + + llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; + + handleLoadStoreInstructions(llvmIrFunction, floatType); + llvm::errs() << "Exiting irPassLLVMIRAutoDequantization\n"; +} diff --git a/src/newton/newton-irPass-LLVMIR-dequantization.h b/src/newton/newton-irPass-LLVMIR-dequantization.h new file mode 100644 index 000000000..a5e4e2166 --- /dev/null +++ b/src/newton/newton-irPass-LLVMIR-dequantization.h @@ -0,0 +1,26 @@ +// +// Created by 13862 on 2024/7/19. +// + + + +#include "newton-irPass-LLVMIR-rangeAnalysis.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ +extern std::vector functionsToErase; +extern std::vector globalsToErase; +void +irPassLLVMIRAutoDequantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); + + + +extern + void eraseOldFunctions(); +extern + void eraseOldGlobals(); +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ \ No newline at end of file From ad3ceaf79588bd25a04d012ea9f62f69b3147f65 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 23 Jul 2024 17:28:00 +0100 Subject: [PATCH 039/213] update test files * dev2. --- ...e0b2082ea8d7560ef1776838debac2236b308b.txt | 48 +++++++ .../c-files/floating_point_operations.c | 33 +++++ .../c-files/test_floating_point_operations.c | 120 ++++++++++++++++++ 3 files changed, 201 insertions(+) create mode 100644 analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt create mode 100644 applications/newton/llvm-ir/c-files/floating_point_operations.c create mode 100644 applications/newton/llvm-ir/c-files/test_floating_point_operations.c diff --git a/analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt b/analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt new file mode 100644 index 000000000..d5d534b86 --- /dev/null +++ b/analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt @@ -0,0 +1,48 @@ + +changeset: 1632:2de0b2082ea8d7560ef1776838debac2236b308b +char kNewtonVersion[] = "0.3-alpha-1632 (2de0b2082ea8d7560ef1776838debac2236b308b) (build 07-18-2024-15:49-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.c b/applications/newton/llvm-ir/c-files/floating_point_operations.c new file mode 100644 index 000000000..71caeadda --- /dev/null +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.c @@ -0,0 +1,33 @@ + +#include +#include "floating_point_operations.h" + + + + + +// Function to perform basic floating point operations +// Function to perform addition +float perform_addition(float a, float b) { + + return a + b; +} + + + +//// Function to perform subtraction +float perform_subtraction(float a, float b) { + return a - b; +} +// +//// Function to perform multiplication +float perform_multiplication(float a, float b) { + //printf("Performing multiplication\ a: %f, b: %f\n", a, b); + return a * b; +} +// +//// Function to perform division +//float perform_division(float a, float b) { +// //printf("Performing division\ a: %f, b: %f\n", a, b); +// return a / b; +//} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_floating_point_operations.c b/applications/newton/llvm-ir/c-files/test_floating_point_operations.c new file mode 100644 index 000000000..1127e71ad --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_floating_point_operations.c @@ -0,0 +1,120 @@ +#include +#include + +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 + + + +// Quantization function +int quantize(float value, int frac_base) { + return round(value * frac_base); +} + +// Dequantization function +float dequantize(int quantized_value, int frac_base) { + return (float)quantized_value / frac_base; +} + +extern int perform_addition(float a, float b); +extern int perform_subtraction(float a, float b); +//extern int perform_multiplication(int a, int b); + +int main() { + float num1 = 8.10; + printf("num1: %f\n", num1); + float num2 = 2.25; + printf("num2: %f\n", num2); + printf("FRAC_BASE: %d\n", FRAC_BASE); + + int a = 10; + int b = 3; + + float num3 = 0; + float num4 = 0; + float num5 = 0; + float num6 = 0; + + int quantized_num1 = quantize(num1, FRAC_BASE); + int quantized_num2 = quantize(num2, FRAC_BASE); + + int addition_result = perform_addition(num1, num2); + float actual_addition_result = dequantize(addition_result, FRAC_BASE); + + printf("Addition Result after quantization: %d\n", addition_result); + printf("expected Addition Result after dequantization: %f\n",dequantize(quantized_num1 + quantized_num2, FRAC_BASE)); + printf("Actual Addition Result after dequantization: %f\n", actual_addition_result); + + + // Perform original operations to compare results later + float original_addition = num1 + num2; + float original_subtraction = num1 - num2; + float original_multiplication = num1 * num2; + float original_division = num1 / num2; + printf("Original Addition: %f\n", original_addition); + printf("Original Subtraction: %f\n", original_subtraction); + + int subtraction_result = perform_subtraction(num1, num2); + float actual_subtraction_result = dequantize(subtraction_result, FRAC_BASE); + printf("Subtraction Result after quantization: %d\n", subtraction_result); + printf("Actual Subtraction Result after dequantization: %f\n", actual_subtraction_result); + + // Quantization of input numbers + + + //int subtraction_result = perform_subtraction(a, b); + //printf("Subtraction Result: %d\n", subtraction_result); + + +// // Perform operations on the quantized numbers +// float addition = perform_addition(num1, num2); +// +// float subtraction = perform_subtraction(num1, num2); +// +// float multiplication = perform_multiplication(num1, num2); +// +// float division = perform_division(num1, num2); +// +// +// // Dequantization of actual results +// float my_dequantized_addition = dequantize(addition, FRAC_BASE); +// float my_dequantized_subtraction = dequantize(subtraction, FRAC_BASE); +// float my_dequantized_multiplication = dequantize( multiplication, FRAC_BASE * FRAC_BASE); +// float my_dequantized_division = division; +// +// +// num3 = (my_dequantized_addition + my_dequantized_subtraction) / 2; +// num4 = (my_dequantized_addition -my_dequantized_subtraction) / 2; +// printf("num3: %f\n", num3); +// printf("num4: %f\n", num4); +// +// +// num5 = sqrt(my_dequantized_multiplication*my_dequantized_division); +// printf("xdeduced : %f\n", num5); +// num6 = sqrt(my_dequantized_multiplication/my_dequantized_division); +// printf("num6: %f\n", num6); +// +// +// +// + +// printf("Expected after Dequantized Addition: %f\n", dequantize(quantized_num1 + quantized_num2, FRAC_BASE)); +// printf("Actual after Dequantized Addition: %f\n", my_dequantized_addition); +// +// printf("Original Subtraction: %f\n", original_subtraction); +// printf("Expected after Dequantized Subtraction: %f\n", dequantize(quantized_num1 - quantized_num2, FRAC_BASE)); +// printf("Actual after Dequantized Subtraction: %f\n", my_dequantized_subtraction); +// +// printf("Original Multiplication: %f\n", original_multiplication); +// +// printf("Expected after Dequantized Multiplication: %f\n", dequantize((int)((long long)quantized_num1 * quantized_num2), FRAC_BASE * FRAC_BASE)); +// //printf("Expected after Dequantized Multiplication: %f\n", dequantize((quantized_num1 * quantized_num2), (FRAC_BASE * FRAC_BASE))); +// printf("Actual after Dequantized Multiplication: %f\n", my_dequantized_multiplication); +// +// printf("Original Division: %f\n", original_division); +// printf("Expected after Dequantized Division: %f\n", (float)quantized_num1 / quantized_num2 ); +// printf("Actual after Dequantized Division: %f\n", my_dequantized_division); + + return 0; +} \ No newline at end of file From 7bbf84ffed653927aa206d3bfcd3fe4573b67469 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 23 Jul 2024 17:30:00 +0100 Subject: [PATCH 040/213] big update * dev2. --- ...3a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt | 48 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 37 +- .../newton-irPass-LLVMIR-quantization.cpp | 1147 ++++++++++------- 3 files changed, 769 insertions(+), 463 deletions(-) create mode 100644 analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt diff --git a/analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt b/analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt new file mode 100644 index 000000000..4eed7dd8e --- /dev/null +++ b/analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt @@ -0,0 +1,48 @@ + +changeset: 1633:223a41a57e9c7de1fc56eb069a7f0f64e632dec4 +char kNewtonVersion[] = "0.3-alpha-1633 (223a41a57e9c7de1fc56eb069a7f0f64e632dec4) (build 07-23-2024-17:28-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 00251fad0..65a632f3e 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -101,6 +101,37 @@ void removeQuantizedSuffixInModule(llvm::Module &M) { } } +void finalCorrectionPass(Module &M, Type *quantizedType) { + llvm::errs() << "Entering finalCorrectionPass\n"; + for (Function &F : M) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + // Correct Load Instructions + if (auto *loadInst = dyn_cast(&I)) { + if (loadInst->getType() != quantizedType) { + llvm::errs() << "Correcting load instruction: " << *loadInst << "\n"; + loadInst->mutateType(quantizedType); + } + } + + // Correct Store Instructions + if (auto *storeInst = dyn_cast(&I)) { + if (storeInst->getValueOperand()->getType() != quantizedType) { + llvm::errs() << "Correcting store instruction: " << *storeInst << "\n"; + storeInst->getValueOperand()->mutateType(quantizedType); + } + if (storeInst->getPointerOperand()->getType()->getPointerElementType() != quantizedType) { + llvm::errs() << "Correcting store pointer operand: " << *storeInst << "\n"; + storeInst->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + } + } + } + } + } + llvm::errs() << "Exiting finalCorrectionPass\n"; +} + + void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { @@ -512,7 +543,11 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl removeQuantizedSuffixInModule(*Mod); // Save the optimized IR to a file - saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + + //finalCorrectionPass(*Mod, quantizedType); + finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 4c62aa01e..8f7396b9f 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -33,7 +33,7 @@ POSSIBILITY OF SUCH DAMAGE. #include using namespace llvm; -#define FRAC_Q 16 +#define FRAC_Q 10 #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 @@ -106,65 +106,97 @@ setQuantizedType(Value * inValue, Type * quantizedType) // Quantize constants within an instruction std::unordered_map quantizedValueCache; -// Ensure load and store instructions have matching types -void -handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) -{ - llvm::errs() << "Entering handleLoadStoreInstructions\n"; - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) - { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) - { - Instruction * llvmIrInstruction = &*itBB++; - if (llvmIrInstruction->getOpcode() == Instruction::Load) - { - if (auto loadInst = dyn_cast(llvmIrInstruction)) - { - auto ptr = loadInst->getPointerOperand(); - if (ptr->getType()->getPointerElementType()->isFloatTy() || - ptr->getType()->getPointerElementType()->isDoubleTy()) - { - // 如果是全局变量,确保它的类型已经被转换 - if (isa(ptr)) - { - auto globalVar = dyn_cast(ptr); - if (globalVar->getType()->getElementType()->isFloatTy() || - globalVar->getType()->getElementType()->isDoubleTy()) - { - ptr->mutateType(quantizedType->getPointerTo()); - } - } - else - { - ptr->mutateType(quantizedType->getPointerTo()); - } - } - } - } - else if (llvmIrInstruction->getOpcode() == Instruction::Store) - { - if (auto storeInst = dyn_cast(llvmIrInstruction)) - { - auto ptr = storeInst->getPointerOperand(); - if (ptr->getType()->getPointerElementType()->isFloatTy() || - ptr->getType()->getPointerElementType()->isDoubleTy()) - { - // 如果是全局变量,确保它的类型已经被转换 - if (isa(ptr)) - { - auto globalVar = dyn_cast(ptr); - if (globalVar->getType()->getElementType()->isFloatTy() || - globalVar->getType()->getElementType()->isDoubleTy()) - { - ptr->mutateType(quantizedType->getPointerTo()); - } - } - else - { - ptr->mutateType(quantizedType->getPointerTo()); - } - } - } + //Ensure load and store instructions have matching types +// void +// handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) +//{ +// llvm::errs() << "Entering handleLoadStoreInstructions\n"; +// for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) +// { +// for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) +// { +// Instruction * llvmIrInstruction = &*itBB++; +// if (llvmIrInstruction->getOpcode() == Instruction::Load) +// { +// if (auto loadInst = dyn_cast(llvmIrInstruction)) +// { +// auto ptr = loadInst->getPointerOperand(); +// if (ptr->getType()->getPointerElementType()->isFloatTy() || +// ptr->getType()->getPointerElementType()->isDoubleTy()) +// { +// // 如果是全局变量,确保它的类型已经被转换 +// if (auto globalVar = dyn_cast(ptr)) +// { +// if (globalVar->getType()->getElementType()->isFloatTy() || +// globalVar->getType()->getElementType()->isDoubleTy()) +// { +// globalVar->mutateType(quantizedType->getPointerTo()); +// } +// } +// else +// { +// ptr->mutateType(quantizedType->getPointerTo()); +// } +// } +// } +// } +// else if (llvmIrInstruction->getOpcode() == Instruction::Store) +// { +// if (auto storeInst = dyn_cast(llvmIrInstruction)) +// { +// auto ptr = storeInst->getPointerOperand(); +// if (ptr->getType()->getPointerElementType()->isFloatTy() || +// ptr->getType()->getPointerElementType()->isDoubleTy()) +// { +// // 如果是全局变量,确保它的类型已经被转换 +// if (auto globalVar = dyn_cast(ptr)) +// { +// if (globalVar->getType()->getElementType()->isFloatTy() || +// globalVar->getType()->getElementType()->isDoubleTy()) +// { +// globalVar->mutateType(quantizedType->getPointerTo()); +// } +// } +// else +// { +// ptr->mutateType(quantizedType->getPointerTo()); +// } +// } +// } +// } +// } +// } +//} + +void fixLoadStoreTypes(Function &F, Type *quantizedType) { + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (auto *loadInst = dyn_cast(&I)) { + IRBuilder<> Builder(loadInst); + + // 确保load指令使用量化类型 + auto *pointerOperand = loadInst->getPointerOperand(); + pointerOperand->mutateType(quantizedType->getPointerTo()); + + // 创建新的load指令,并替换旧的load指令 + auto *newLoad = Builder.CreateLoad(quantizedType, pointerOperand); + loadInst->replaceAllUsesWith(newLoad); + loadInst->eraseFromParent(); + } else if (auto *storeInst = dyn_cast(&I)) { + IRBuilder<> Builder(storeInst); + + // 确保store指令使用量化类型 + auto *valueOperand = storeInst->getValueOperand(); + auto *pointerOperand = storeInst->getPointerOperand(); + + // 转换store值为量化类型 + auto *newValue = Builder.CreateFPToSI(valueOperand, quantizedType); + pointerOperand->mutateType(quantizedType->getPointerTo()); + + // 创建新的store指令,并替换旧的store指令 + auto *newStore = Builder.CreateStore(newValue, pointerOperand); + storeInst->replaceAllUsesWith(newStore); + storeInst->eraseFromParent(); } } } @@ -177,39 +209,12 @@ createQuantizedFunction(Function & llvmIrFunction, Type * quantizedType) std::vector params; for (auto & arg : llvmIrFunction.args()) { - // If the argument type is float or double, use the quantized type - if (arg.getType()->isFloatTy() || arg.getType()->isDoubleTy()) - { - params.push_back(quantizedType); - llvm::errs() << "Quantizing parameter: " << arg.getName() << " from " << *arg.getType() << " to " << *quantizedType << "\n"; - } - else - { - params.push_back(arg.getType()); - } - } - - // Determine the return type: if it's float or double, use the quantized type - Type * returnType = llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy() - ? quantizedType - : llvmIrFunction.getReturnType(); - llvm::errs() << "Original Function: " << llvmIrFunction.getName() << "\n"; - llvm::errs() << "Original Return Type: " << *llvmIrFunction.getReturnType() << "\n"; - for (auto & arg : llvmIrFunction.args()) - { - llvm::errs() << "Original Arg: " << *arg.getType() << "\n"; - } - llvm::errs() << "New Function: " << llvmIrFunction.getName() + "_quantized" << "\n"; - llvm::errs() << "New Return Type: " << *returnType << "\n"; - for (auto & param : params) - { - llvm::errs() << "New Arg: " << *param << "\n"; + params.push_back(arg.getType()); // 保持原参数类型 } - // Create a new function type with the modified parameters and return type + Type * returnType = quantizedType; // 返回量化后的整数类型 FunctionType * newFuncType = FunctionType::get(returnType, params, false); - // Create the new function in the same module with a modified name - Function * newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_quantized", llvmIrFunction.getParent()); + Function * newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_quantized", llvmIrFunction.getParent()); return newFunc; } @@ -273,6 +278,7 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); + // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" ||functionName == "printf") if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") { @@ -294,7 +300,33 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) processedFunctions.insert(functionName); + // 调试信息:输出原始函数参数类型 + llvm::errs() << "Original function name: " << llvmIrFunction.getName() << "\n"; + for (auto & arg : llvmIrFunction.args()) + { + llvm::errs() << "Original argument type: " << *arg.getType() << "\n"; + } + + // 保留原参数类型 + std::vector params; + for (auto & arg : llvmIrFunction.args()) + { + params.push_back(arg.getType()); + } + + // 修改返回类型为量化后的类型 + Type * returnType = quantizedType; + FunctionType * newFuncType = FunctionType::get(returnType, params, false); + Function * newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); + + // 调试信息:输出新函数参数类型 + llvm::errs() << "New function name: " << newFunc->getName() << "\n"; + for (auto & arg : newFunc->args()) + { + llvm::errs() << "New argument type: " << *arg.getType() << "\n"; + } + cloneFunctionBody(llvmIrFunction, newFunc); replaceFunctionUses(llvmIrFunction, newFunc); @@ -304,6 +336,58 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +{ + /* + * if the arg's type is int, convert to fp, + * after the call node, convert to int and shl FRAC_Q/2 + * + * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); + * if (FRAC_Q%2) + * return res*1.414213562; + * else + * return res; + * + * %25 = sitofp i32 %0 to double + * %26 = call double @sqrt(double %25) #3 + * %27 = fptosi double %26 to i32 + * %28 = shl i32 %27, 4 + * */ + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + if (operand->getType()->isIntegerTy()) + { + Value * newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); + llvmIrCallInstruction->setOperand(0, newOperand); + } + + auto cloneInst = llvmIrCallInstruction->clone(); + Value * fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); + Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value * resInst = nullptr; + + /* + * if (FRAC_Q%2) then multiply with 1.414213562; + * */ + + if (FRAC_Q % 2) + { + Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); + auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); + Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); + resInst = Builder.CreateFPToSI(mulInst, quantizedType); + } + else + { + resInst = shlInst; + } + + llvmIrCallInstruction->replaceAllUsesWith(resInst); + ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); +} + // A list of global variables to erase after processing std::vector globalsToErase; @@ -440,8 +524,8 @@ updateGlobalVariables(Module * module, Type * quantizedType) // Quantize constants within an instruction -void -quantizeConstant(Instruction * inInstruction, Type * quantizedType) + void + quantizeConstant(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Entering quantizeConstant\n"; for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) @@ -498,7 +582,44 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // Replace all uses of the original value with the new quantized value inInstruction->replaceUsesOfWith(inValue, newValue); } -} + } + +// 量化常量: +//void +//quantizeConstant(Instruction * inInstruction, Type * quantizedType) +//{ +// for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) +// { +// Value * inValue = inInstruction->getOperand(idx); +// +// if (!isa(inValue)) +// { +// continue; +// } +// +// ConstantFP * constFp = llvm::dyn_cast(inValue); +// Value * newValue = nullptr; +// +// if (inValue->getType()->isFloatTy()) +// { +// float constValue = constFp->getValueAPF().convertToFloat(); +// constValue *= FRAC_BASE; +// newValue = ConstantInt::get(quantizedType, round(constValue), true); +// } +// else if (inValue->getType()->isDoubleTy()) +// { +// double constValue = constFp->getValueAPF().convertToDouble(); +// constValue *= FRAC_BASE; +// newValue = ConstantInt::get(quantizedType, round(constValue), true); +// } +// else +// { +// assert(false && "unknown floating type"); +// } +// +// inInstruction->replaceUsesOfWith(inValue, newValue); +// } +//} // void // handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) @@ -708,7 +829,7 @@ createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vect { llvm::errs() << "Entering createFloatIntMul\n"; - // 检查 irModule 是否有效 + // Check if irModule is valid if (!irModule) { llvm::errs() << "Error: irModule is nullptr\n"; @@ -757,7 +878,7 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector builder(entryBB); builder.SetInsertPoint(entryBB); - // 生成 fixed-point 乘法指令 + // Create fixed-point multiplication instruction Type * higherQuantizedType; switch (BIT_WIDTH) { @@ -835,15 +956,6 @@ quantizePredict(CmpInst::Predicate predict) case FCmpInst::FCMP_ONE: // not equal case FCmpInst::FCMP_UNE: return ICmpInst::ICMP_NE; - - /* - case FCmpInst::FCMP_ORD: // ordered (no NaNs) - // For ordered, we map it to ICMP_NE, assuming integers cannot be NaN - return ICmpInst::ICMP_NE; - case FCmpInst::FCMP_UNO: // unordered (at least one NaN) - // For unordered, there is no direct integer equivalent, map to ICMP_EQ for safety - return ICmpInst::ICMP_EQ;*/ - default: llvm::errs() << "Unhandled floating point predicate\n"; return ICmpInst::ICMP_EQ; // Default to equal for safety @@ -851,158 +963,386 @@ quantizePredict(CmpInst::Predicate predict) } void -quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) +dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * quantizedType) { - llvm::errs() << "Entering quantizeSimpleFPInstruction\n"; - if (!inInstruction) - { - llvm::errs() << "inInstruction is nullptr\n"; - return; - } - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; + IRBuilder<> Builder(llvmIrCallInstruction); + llvm::Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - // Ensure the quantized type is not null - if (!quantizedType) + if (!calledFunction) { - llvm::errs() << "Error: Quantized type is nullptr\n"; + llvm::errs() << "Error: Called function is null.\n"; return; } - llvm::errs() << "Instruction Opcode: " << inInstruction->getOpcodeName() << "\n"; - switch (inInstruction->getOpcode()) + llvm::errs() << "De-quantizing arguments for library function call\n"; + + // Create a vector to store de-quantized arguments + std::vector dequantizedArgs; + + // Iterate through the call instruction's operands (arguments) + for (unsigned i = 0; i < llvmIrCallInstruction->getNumArgOperands(); ++i) { - case Instruction::FAdd: + Value * arg = llvmIrCallInstruction->getArgOperand(i); + + if (arg->getType()->isIntegerTy()) { - llvm::errs() << "Handling FAdd\n"; - // Replace floating-point addition with integer addition - newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; + // Convert integer arguments back to floating-point + Value * dequantizedArg = Builder.CreateSIToFP(arg, llvmIrCallInstruction->getFunctionType()->getParamType(i)); + dequantizedArgs.push_back(dequantizedArg); } - // case Instruction::FSub: - // { - // llvm::errs() << "Handling FSub\n"; - // Value * op0 = inInstruction->getOperand(0); - // Value * op1 = inInstruction->getOperand(1); - // if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - // { - // op0 = Builder.CreateFPToSI(op0, quantizedType); - // } - // if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - // { - // op1 = Builder.CreateFPToSI(op1, quantizedType); - // } - // newInst = Builder.CreateSub(op0, op1); - // break; - // } - - case Instruction::FSub: + else { - llvm::errs() << "Handling FSub\n"; - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - llvm::errs() << "Original Operand 0: " << *op0 << "\n"; - llvm::errs() << "Original Operand 1: " << *op1 << "\n"; - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - { - op0 = Builder.CreateFPToSI(op0, quantizedType); - llvm::errs() << "Converted Operand 0 to fixed-point: " << *op0 << "\n"; - } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - op1 = Builder.CreateFPToSI(op1, quantizedType); - llvm::errs() << "Converted Operand 1 to fixed-point: " << *op1 << "\n"; - } - newInst = Builder.CreateSub(op0, op1); - llvm::errs() << "Created Sub instruction: " << *newInst << "\n"; - break; + // If not an integer, keep the original argument + dequantizedArgs.push_back(arg); } + } + + // Create a new call instruction with de-quantized arguments + CallInst * newCall = CallInst::Create(calledFunction, dequantizedArgs, "", llvmIrCallInstruction); + newCall->setCallingConv(llvmIrCallInstruction->getCallingConv()); + newCall->setDebugLoc(llvmIrCallInstruction->getDebugLoc()); + + llvm::errs() << "Quantizing return value of the library function call\n"; + // Quantize the return value of the call instruction + if (newCall->getType()->isFloatingPointTy()) + { + Value * quantizedReturnValue = Builder.CreateFPToSI(newCall, quantizedType); + llvmIrCallInstruction->replaceAllUsesWith(quantizedReturnValue); + } + else + { + llvmIrCallInstruction->replaceAllUsesWith(newCall); + } + + // Remove the old call instruction + llvmIrCallInstruction->eraseFromParent(); +} + +//void +//handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) +//{ +// llvm::errs() << "Handling FMul\n"; +// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; +// IRBuilder<> Builder(llvmIrInstruction); +// +// // Ensure operands are correctly converted to fixed-point integers +// Value * lhs = llvmIrInstruction->getOperand(0); +// Value * rhs = llvmIrInstruction->getOperand(1); +// +// llvm::errs() << "LHS: " << *lhs << "\n"; +// llvm::errs() << "RHS: " << *rhs << "\n"; +// +// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); +// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); +// +// // If either operand is a float, convert both to fixed-point +// if (lhsIsFloat) +// { +// lhs = Builder.CreateFPToSI(lhs, quantizedType); +// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; +// } +// if (rhsIsFloat) +// { +// rhs = Builder.CreateFPToSI(rhs, quantizedType); +// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; +// } +// +// // Ensure both operands are now integers +// bool lhsIsInteger = lhs->getType()->isIntegerTy(); +// bool rhsIsInteger = rhs->getType()->isIntegerTy(); +// +// if (lhsIsInteger && rhsIsInteger) +// { +// // Handle constant simplification if one of the operands is a constant +// if (isa(lhs) || isa(rhs)) +// { +// llvm::errs() << "One of the operands is a constant, simplifying...\n"; +// simplifyConstant(llvmIrInstruction, quantizedType, floatIntMul); +// } +// else +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); +// llvmIrInstruction->replaceAllUsesWith(callInst); +// llvmIrInstruction->eraseFromParent(); +// } +// } +// else +// { +// llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; +// } +// +// llvm::errs() << "Finished handling FMul\n"; +//} + +void handleFMul(Instruction * inInstruction, Type * quantizedType,Function * fixmul, Function * floatIntMul) +{ + llvm::errs() << "Handling FMul\n"; + IRBuilder<> Builder(inInstruction); + + Value * lhs = inInstruction->getOperand(0); + Value * rhs = inInstruction->getOperand(1); + + bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - case Instruction::FRem: + + bool lhsIsInteger = lhs->getType()->isIntegerTy(); + bool rhsIsInteger = rhs->getType()->isIntegerTy(); + if (lhsIsInteger && rhsIsInteger) + { + // Handle constant simplification if one of the operands is a constant + if (isa(lhs) || isa(rhs)) { - llvm::errs() << "Handling FRem\n"; - // Replace floating-point remainder with integer remainder - newInst = Builder.CreateSRem(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; + llvm::errs() << "One of the operands is a constant, simplifying...\n"; + simplifyConstant(inInstruction, quantizedType, floatIntMul); } + else + { + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + inInstruction->replaceAllUsesWith(callInst); + inInstruction->eraseFromParent(); + } + } + else + { + llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; + } - case Instruction::FCmp: - // Replace floating-point comparison with integer comparisonm - llvm::errs() << "Handling FCmp\n"; - if (auto fcmp_inst = dyn_cast(inInstruction)) - { - CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); - if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) - { - newInst = ConstantInt::getTrue(quantizedType); - } - else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) - { - newInst = ConstantInt::getFalse(quantizedType); - } - else - { - // Convert floating-point operands to integer operands - Value * fpOp0 = fcmp_inst->getOperand(0); - Value * fpOp1 = fcmp_inst->getOperand(1); + Value * newInst = Builder.CreateMul(lhs, rhs); + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FMul\n"; +} - // Debug output to check types - llvm::errs() << "Entering FCmp case\n"; - llvm::errs() << "Original Operand 0 type: " << *fpOp0->getType() << "\n"; - llvm::errs() << "Original Operand 1 type: " << *fpOp1->getType() << "\n"; - Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); - Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); - // Debug output to check types after conversion - llvm::errs() << "Converted Operand 0 type: " << *intOp0->getType() << "\n"; - llvm::errs() << "Converted Operand 1 type: " << *intOp1->getType() << "\n"; - llvm::errs() << "Expected quantizedType: " << *quantizedType << "\n"; +void +handleFAdd(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + + //quantizeConstant(inInstruction, quantizedType); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + + Value * newInst = Builder.CreateAdd(op0, op1); + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FSub\n"; +} + + - // Assert to ensure types are correct - assert(intOp0->getType() == intOp1->getType() && "Both operands to ICmp instruction are not of the same type!"); - newInst = Builder.CreateICmp(pred, intOp0, intOp1); - } - } - break; - /* - * Change fneg(a) to `0-a`. - * */ - case Instruction::FNeg: +void +handleFSub(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + +// if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) +// { +// op0 = Builder.CreateFPToSI(op0, quantizedType); +// } +// if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) +// { +// op1 = Builder.CreateFPToSI(op1, quantizedType); +// } + Value * newInst = Builder.CreateSub(op0, op1); + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FSub\n"; +} + +void +handleFRem(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FRem\n"; + IRBuilder<> Builder(inInstruction); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + { + op0 = Builder.CreateFPToSI(op0, quantizedType); + } + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + op1 = Builder.CreateFPToSI(op1, quantizedType); + } + Value * newInst = Builder.CreateSRem(op0, op1); + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FRem\n"; +} + +void +handleFCmp(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FCmp\n"; + IRBuilder<> Builder(inInstruction); + + if (auto fcmp_inst = dyn_cast(inInstruction)) + { + CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); + if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) { - llvm::errs() << "Handling FNeg\n"; - // Replace floating-point negation with integer negation - Value * op = inInstruction->getOperand(0); - if (op->getType()->isFloatTy() || op->getType()->isDoubleTy()) - { - op = Builder.CreateFPToSI(op, quantizedType); - } - auto constZero = ConstantInt::get(quantizedType, 0, true); - newInst = Builder.CreateSub(constZero, op); - break; + Value * newInst = ConstantInt::getTrue(quantizedType); + inInstruction->replaceAllUsesWith(newInst); } - default: - llvm::errs() << "Unhandled floating point instruction\n"; - break; - } + else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) + { + Value * newInst = ConstantInt::getFalse(quantizedType); + inInstruction->replaceAllUsesWith(newInst); + } + else + { + Value * fpOp0 = fcmp_inst->getOperand(0); + Value * fpOp1 = fcmp_inst->getOperand(1); + + Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); + Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); - if (newInst) + Value * newInst = Builder.CreateICmp(pred, intOp0, intOp1); + inInstruction->replaceAllUsesWith(newInst); + } + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FCmp\n"; + } +} +void +handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) { - llvm::errs() << "Replacing instruction with newInst\n"; - inInstruction->replaceAllUsesWith(newInst); - inInstruction->removeFromParent(); + auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); + + // 检查是否需要转换类型 + if (allocaType->isFloatTy() || allocaType->isDoubleTy()) + { + llvm::errs() << "Original alloca type: " << *allocaType << "\n"; + llvm::errs() << "New quantized alloca type: " << *quantizedType << "\n"; + + // 设置新的量化类型 + llvmIrAllocaInstruction->setAllocatedType(quantizedType); + } } - else +} + +// void handleStore(Instruction *llvmIrInstruction, Type *quantizedType) { +// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) { +// IRBuilder<> Builder(llvmIrStoreInstruction); +// +// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); +// if (valueType->isFloatTy() || valueType->isDoubleTy()) { +// llvm::errs() << "Original store value type: " << *valueType << "\n"; +// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; +// +// // 转换存储的值为量化后的类型 +// auto quantizedValue = Builder.CreateFPToSI(llvmIrStoreInstruction->getValueOperand(), quantizedType); +// llvmIrStoreInstruction->setOperand(0, quantizedValue); +// } +// +// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); +// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { +// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; +// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; +// +// // 设置新的量化类型 +// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); +// } +// } +// } + +void +handleStore(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) { - llvm::errs() << "No new instruction created\n"; + IRBuilder<> Builder(llvmIrStoreInstruction); + + auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); + if (valueType->isFloatTy() || valueType->isDoubleTy()) + { + llvm::errs() << "Original store value type: " << *valueType << "\n"; + llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; + + // 乘以 FRAC_BASE 然后转换为整数类型 + auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); + quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); + llvmIrStoreInstruction->setOperand(0, quantizedValue); + } + + auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + { + llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; + llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; + + // 设置新的量化类型 + llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + } + } +} + +//void +//handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) +// { +// IRBuilder<> Builder(llvmIrLoadInstruction); +// +// auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); +// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) +// { +// llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; +// llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; +// +// // 加载整数值,并转换为浮点数进行后续计算 +// llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); +// auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); +// auto fpValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); +// +// // 除以 FRAC_BASE 还原浮点数 +// fpValue = Builder.CreateFDiv(fpValue, ConstantFP::get(Type::getFloatTy(llvmIrInstruction->getContext()), FRAC_BASE)); +// llvmIrLoadInstruction->replaceAllUsesWith(fpValue); +// llvmIrLoadInstruction->eraseFromParent(); +// } +// } +//} + +void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { + if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) { + IRBuilder<> Builder(llvmIrLoadInstruction); + + auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { + llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; + llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; + + // Update the type of the pointer operand to the quantized type + llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + + // Load the value as an integer + auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); + + // Replace all uses of the original load instruction with the new loaded integer value + llvmIrLoadInstruction->replaceAllUsesWith(loadedValue); + + // Remove the old load instruction + llvmIrLoadInstruction->eraseFromParent(); + } } } -// Adapt type casts to the quantized type + void adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { @@ -1012,73 +1352,32 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { Instruction * llvmIrInstruction = &*itBB++; + llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; + switch (llvmIrInstruction->getOpcode()) { + case Instruction::Alloca: + handleAlloca(llvmIrInstruction, quantizedType); + break; + case Instruction::Store: + handleStore(llvmIrInstruction, quantizedType); + break; + case Instruction::Load: + handleLoad(llvmIrInstruction, quantizedType); + break; case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: - // case Instruction::UIToFP: - // { - // auto sourceOp = llvmIrInstruction->getOperand(0); - // IRBuilder<> Builder(llvmIrInstruction); - // - // // 如果源操作数类型和指令类型相同,直接替换 - // if (sourceOp->getType() == llvmIrInstruction->getType()) - // { - // llvmIrInstruction->replaceAllUsesWith(sourceOp); - // llvmIrInstruction->removeFromParent(); - // } - // else - // { - // llvm::errs() << "Handling UIToFP: Source type does not match instruction type, adjusting types\n"; - // if (sourceOp->getType()->isIntegerTy()) - // { - // // 如果源操作数是整数类型,转换为浮点类型 - // sourceOp = Builder.CreateSIToFP(sourceOp, llvmIrInstruction->getType()); - // } - // else if (sourceOp->getType()->isFloatTy() || sourceOp->getType()->isDoubleTy()) - // { - // // 如果源操作数是浮点类型,转换为整数类型 - // sourceOp = Builder.CreateFPToSI(sourceOp, quantizedType); - // } - // - // // 创建新的 UIToFP 指令 - // Value * newInst = Builder.CreateUIToFP(sourceOp, llvmIrInstruction->getType()); - // llvmIrInstruction->replaceAllUsesWith(newInst); - // llvmIrInstruction->removeFromParent(); - // } - // break; - // } - case Instruction::UIToFP: { - auto sourceOp = llvmIrInstruction->getOperand(0); - IRBuilder<> Builder(llvmIrInstruction); - - if (sourceOp->getType()->isIntegerTy()) + auto sourceOp = llvmIrInstruction->getOperand(0); + if (sourceOp->getType() == llvmIrInstruction->getType()) { - // 将整数类型转换为浮点类型 - Value * newInst = Builder.CreateSIToFP(sourceOp, llvmIrInstruction->getType()); - llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->replaceAllUsesWith(sourceOp); llvmIrInstruction->removeFromParent(); } - else if (sourceOp->getType()->isFloatTy() || sourceOp->getType()->isDoubleTy()) - { - // 将浮点类型转换为整数类型 - Value * newInst = Builder.CreateFPToSI(sourceOp, quantizedType); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); - } - break; } - break; - // case Instruction::ZExt: - // case Instruction::SExt: - // case Instruction::Trunc: - /* - * since the src type changed, adapt the new instruction - * */ case Instruction::FPExt: case Instruction::FPTrunc: { @@ -1088,15 +1387,11 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) Value * newInst = nullptr; if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) { - // Handle integer to float cast - newInst = Builder.CreateSIToFP( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + newInst = Builder.CreateSIToFP(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); } else { - // Handle float cast - newInst = Builder.CreateFPCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + newInst = Builder.CreateFPCast(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); } llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->removeFromParent(); @@ -1107,8 +1402,7 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) IRBuilder<> Builder(llvmIrInstruction); Instruction * insertPoint = llvmIrInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newInst = Builder.CreateBitCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + Value * newInst = Builder.CreateBitCast(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->removeFromParent(); break; @@ -1118,64 +1412,6 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } llvm::errs() << "Exiting adaptTypeCast\n"; } -} - -void -handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) -{ - llvm::errs() << "Handling FMul\n"; - llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; - IRBuilder<> Builder(llvmIrInstruction); - - // Ensure operands are correctly converted to fixed-point integers - Value * lhs = llvmIrInstruction->getOperand(0); - Value * rhs = llvmIrInstruction->getOperand(1); - - llvm::errs() << "LHS: " << *lhs << "\n"; - llvm::errs() << "RHS: " << *rhs << "\n"; - - bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); - bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - - // If either operand is a float, convert both to fixed-point - if (lhsIsFloat) - { - lhs = Builder.CreateFPToSI(lhs, quantizedType); - llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; - } - if (rhsIsFloat) - { - rhs = Builder.CreateFPToSI(rhs, quantizedType); - llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; - } - - // Ensure both operands are now integers - bool lhsIsInteger = lhs->getType()->isIntegerTy(); - bool rhsIsInteger = rhs->getType()->isIntegerTy(); - - if (lhsIsInteger && rhsIsInteger) - { - // Handle constant simplification if one of the operands is a constant - if (isa(lhs) || isa(rhs)) - { - llvm::errs() << "One of the operands is a constant, simplifying...\n"; - simplifyConstant(llvmIrInstruction, quantizedType, floatIntMul); - } - else - { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - } - } - else - { - llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; - } - - llvm::errs() << "Finished handling FMul\n"; -} // Main function to perform LLVM IR auto quantization void @@ -1243,53 +1479,54 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::errs() << "Calling createFixMul for function: " << llvmIrFunction.getName() << "\n"; llvm::errs() << "inFunction address: " << &llvmIrFunction << "\n"; llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - llvm::errs() << "Created fixmul function: " << (fixmul ? "Success" : "Failed") << "\n"; // generate hardcode function - floatIntMul function - llvm::errs() << "Calling createFloatIntMul for function: " << llvmIrFunction.getName() << "\n"; // llvm::Function * floatIntMul = createFloatIntMul(llvmIrFunction.getParent(), intType, floatType, functionsToInsert); llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); // llvm::Function* floatIntMul = createFloatIntMul(module, quantizedType, floatType, functionsToInsert); - llvm::errs() - << "Created floatIntMul function: " << (floatIntMul ? "Success" : "Failed") << "\n"; - /* * quantize the arguments type * */ - llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; - for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) - { - llvm::errs() << "Quantizing parameter at index " << idx << "\n"; - llvm::Function::arg_iterator argIt = llvmIrFunction.arg_begin(); - std::advance(argIt, idx); - - if (argIt == llvmIrFunction.arg_end()) - { - llvm::errs() << "Error: Reached end of arguments while trying to access index " << idx << "\n"; - continue; - } - - llvm::Argument * paramOp = &*argIt; - llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; - if (!paramOp) - { - llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; - continue; - } - - if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) - { - llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; - setQuantizedType(paramOp, quantizedType); - } - else - { - llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; - } - } - + // llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; + // for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) + // { + // llvm::errs() << "Quantizing parameter at index " << idx << "\n"; + // llvm::Function::arg_iterator argIt = llvmIrFunction.arg_begin(); + // std::advance(argIt, idx); + // + // if (argIt == llvmIrFunction.arg_end()) + // { + // llvm::errs() << "Error: Reached end of arguments while trying to access index " << idx << "\n"; + // continue; + // } + // + // llvm::Argument * paramOp = &*argIt; + // llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; + // if (!paramOp) + // { + // llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; + // continue; + // } + // + // if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) + // { + // llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; + // setQuantizedType(paramOp, quantizedType); + // } + // else + // { + // llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; + // } + // } + // for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) + // { + // auto paramOp = llvmIrFunction.getArg(idx); + // setQuantizedType(paramOp, quantizedType); + // } + + // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) @@ -1301,22 +1538,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve switch (llvmIrInstruction->getOpcode()) { case Instruction::Alloca: - if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) - { - auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - auto newType = quantizedType; - if (allocaType->getTypeID() == Type::ArrayTyID) - { - newType = ArrayType::get(quantizedType, - allocaType->getArrayNumElements()); - allocaType = allocaType->getArrayElementType(); - } - if (allocaType->isDoubleTy() || allocaType->isFloatTy()) - { - llvmIrAllocaInstruction->setAllocatedType(newType); - } - setQuantizedType(llvmIrAllocaInstruction, newType); - } + handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) @@ -1337,57 +1559,15 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve Value * newInst = nullptr; if (calledFunction->getName().str() == "sqrt") { - /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sqrt(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ - auto operand = llvmIrCallInstruction->getOperand(0); - if (operand->getType()->isIntegerTy()) - { - Value * newOperand = Builder.CreateSIToFP( - operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); - } - auto cloneInst = llvmIrCallInstruction->clone(); - Value * fptosiInst = Builder.CreateFPToSI( - cloneInst, quantizedType); - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value * resInst = nullptr; - /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ - if (FRAC_Q % 2) - { - Value * lhsCompensateInst = Builder.CreateSIToFP( - shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), - 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); - } - else - { - resInst = shlInst; - } - llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); + // For sqrt + handleSqrtCall(llvmIrCallInstruction, quantizedType); } else { /* * for other lib functions, de-quantize the arguments and quantize the return value * */ + dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); } } else @@ -1395,15 +1575,15 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * for user-defined function, quantize the arguments * */ - for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); - /* - * then quantize the return type - * */ - setQuantizedType(llvmIrCallInstruction, quantizedType); + // for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) + // { + // setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); + // } + // quantizeConstant(llvmIrCallInstruction, quantizedType); + // /* + // * then quantize the return type + // * */ + // setQuantizedType(llvmIrCallInstruction, quantizedType); } } } @@ -1435,6 +1615,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } break; case Instruction::Load: + handleLoad(llvmIrInstruction, quantizedType); + break; case Instruction::PHI: { setQuantizedType(llvmIrInstruction, quantizedType); @@ -1446,8 +1628,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * If either of the operands is constant, change it to a int value * */ - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); + handleStore(llvmIrInstruction, quantizedType); } break; @@ -1483,15 +1664,54 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * then replace the instruction to the int version. * */ case Instruction::FCmp: + handleFCmp(llvmIrInstruction, quantizedType); + break; case Instruction::FAdd: + handleFAdd(llvmIrInstruction, quantizedType); + break; case Instruction::FSub: + handleFSub(llvmIrInstruction, quantizedType); + break; case Instruction::FRem: - { - quantizeConstant(llvmIrInstruction, quantizedType); - } + handleFRem(llvmIrInstruction, quantizedType); + break; + { + // quantizeConstant(llvmIrInstruction, quantizedType); + // 量化浮点常量 + for (size_t idx = 0; idx < llvmIrInstruction->getNumOperands(); idx++) + { + Value * inValue = llvmIrInstruction->getOperand(idx); + + if (isa(inValue)) + { + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; + + if (inValue->getType()->isFloatTy()) + { + float constValue = constFp->getValueAPF().convertToFloat(); + constValue *= FRAC_BASE; + newValue = ConstantInt::get(quantizedType, round(constValue), true); + } + else if (inValue->getType()->isDoubleTy()) + { + double constValue = constFp->getValueAPF().convertToDouble(); + constValue *= FRAC_BASE; + newValue = ConstantInt::get(quantizedType, round(constValue), true); + } + else + { + assert(false && "unknown floating type"); + } + + llvmIrInstruction->setOperand(idx, newValue); + } + } + } case Instruction::FNeg: + { - quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); + // quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); break; } @@ -1516,6 +1736,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FPToSI: case Instruction::SIToFP: case Instruction::UIToFP: + case Instruction::ZExt: case Instruction::SExt: case Instruction::Trunc: @@ -1560,8 +1781,10 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } } } - handleLoadStoreInstructions(llvmIrFunction, quantizedType); + //handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); + //fixLoadStoreTypes(llvmIrFunction, quantizedType); return; +} } \ No newline at end of file From 675b2689bdc004f9c12d3f827b1313550af254ba Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 23 Jul 2024 22:10:04 +0100 Subject: [PATCH 041/213] fix fdiv issue * dev2. --- ...3ceaf79588bd25a04d012ea9f62f69b3147f65.txt | 48 +++ .../newton-irPass-LLVMIR-quantization.cpp | 282 ++++++++++++------ 2 files changed, 238 insertions(+), 92 deletions(-) create mode 100644 analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt diff --git a/analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt b/analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt new file mode 100644 index 000000000..8dae3ab97 --- /dev/null +++ b/analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt @@ -0,0 +1,48 @@ + +changeset: 1634:ad3ceaf79588bd25a04d012ea9f62f69b3147f65 +char kNewtonVersion[] = "0.3-alpha-1634 (ad3ceaf79588bd25a04d012ea9f62f69b3147f65) (build 07-23-2024-17:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 8f7396b9f..f7d18599c 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -106,7 +106,7 @@ setQuantizedType(Value * inValue, Type * quantizedType) // Quantize constants within an instruction std::unordered_map quantizedValueCache; - //Ensure load and store instructions have matching types +// Ensure load and store instructions have matching types // void // handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) //{ @@ -168,33 +168,40 @@ std::unordered_map quantizedValueCache; // } //} -void fixLoadStoreTypes(Function &F, Type *quantizedType) { - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { - if (auto *loadInst = dyn_cast(&I)) { +void +fixLoadStoreTypes(Function & F, Type * quantizedType) +{ + for (BasicBlock & BB : F) + { + for (Instruction & I : BB) + { + if (auto * loadInst = dyn_cast(&I)) + { IRBuilder<> Builder(loadInst); // 确保load指令使用量化类型 - auto *pointerOperand = loadInst->getPointerOperand(); + auto * pointerOperand = loadInst->getPointerOperand(); pointerOperand->mutateType(quantizedType->getPointerTo()); // 创建新的load指令,并替换旧的load指令 - auto *newLoad = Builder.CreateLoad(quantizedType, pointerOperand); + auto * newLoad = Builder.CreateLoad(quantizedType, pointerOperand); loadInst->replaceAllUsesWith(newLoad); loadInst->eraseFromParent(); - } else if (auto *storeInst = dyn_cast(&I)) { + } + else if (auto * storeInst = dyn_cast(&I)) + { IRBuilder<> Builder(storeInst); // 确保store指令使用量化类型 - auto *valueOperand = storeInst->getValueOperand(); - auto *pointerOperand = storeInst->getPointerOperand(); + auto * valueOperand = storeInst->getValueOperand(); + auto * pointerOperand = storeInst->getPointerOperand(); // 转换store值为量化类型 - auto *newValue = Builder.CreateFPToSI(valueOperand, quantizedType); + auto * newValue = Builder.CreateFPToSI(valueOperand, quantizedType); pointerOperand->mutateType(quantizedType->getPointerTo()); // 创建新的store指令,并替换旧的store指令 - auto *newStore = Builder.CreateStore(newValue, pointerOperand); + auto * newStore = Builder.CreateStore(newValue, pointerOperand); storeInst->replaceAllUsesWith(newStore); storeInst->eraseFromParent(); } @@ -524,8 +531,8 @@ updateGlobalVariables(Module * module, Type * quantizedType) // Quantize constants within an instruction - void - quantizeConstant(Instruction * inInstruction, Type * quantizedType) +void +quantizeConstant(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Entering quantizeConstant\n"; for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) @@ -582,11 +589,11 @@ updateGlobalVariables(Module * module, Type * quantizedType) // Replace all uses of the original value with the new quantized value inInstruction->replaceUsesOfWith(inValue, newValue); } - } +} // 量化常量: -//void -//quantizeConstant(Instruction * inInstruction, Type * quantizedType) +// void +// quantizeConstant(Instruction * inInstruction, Type * quantizedType) //{ // for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) // { @@ -804,24 +811,17 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f void substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) { - llvm::errs() << "Entering substituteHardcodeFunc\n"; - llvm::errs() << "Original Instruction: " << *inInstruction << "\n"; IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); + // Value * newInst = nullptr; - Value * lhs = inInstruction->getOperand(0); - Value * rhs = inInstruction->getOperand(1); - - llvm::errs() << "LHS: " << *lhs << "\n"; - llvm::errs() << "RHS: " << *rhs << "\n"; - - llvm::CallInst * callInst = Builder.CreateCall(func, {lhs, rhs}); - llvm::errs() << "Created call instruction: " << *callInst << "\n"; + llvm::CallInst * callInst = Builder.CreateCall(func, {inInstruction->getOperand(0), inInstruction->getOperand(1)}); + // InlineFunctionInfo inlineFuncInfo; + // llvm::InlineFunction(*callInst, inlineFuncInfo); inInstruction->replaceAllUsesWith(callInst); inInstruction->removeFromParent(); - llvm::errs() << "Exiting substituteHardcodeFunc\n"; } llvm::Function * @@ -931,6 +931,68 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFixDiv\n"; + + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixdivFuncName = "fixdiv"; + for (auto & function : *irModule) + { + if (function.getName() == fixdivFuncName) + { + llvm::errs() << "fixdiv already exists\n"; + return &function; + } + } + + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixdivFuncName, irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + builder.SetInsertPoint(entryBB); + + // Create fixed-point division instruction + Type * higherQuantizedType; + switch (BIT_WIDTH) + { + case 8: + higherQuantizedType = Type::getInt16Ty(irModule->getContext()); + break; + case 16: + higherQuantizedType = Type::getInt32Ty(irModule->getContext()); + break; + default: + higherQuantizedType = Type::getInt64Ty(irModule->getContext()); + break; + } + + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + + // Multiply numerator by FRAC_BASE before division to maintain precision + llvm::Value * mulInst = builder.CreateMul(sext1, ConstantInt::get(higherQuantizedType, FRAC_BASE)); + llvm::Value * divInst = builder.CreateSDiv(mulInst, sext2); + llvm::Value * truncInst = builder.CreateTrunc(divInst, quantizedType); + builder.CreateRet(truncInst); + + functionsToInsert.emplace_back(func); + llvm::errs() << "Created fixdiv function: " << func->getName() << "\n"; + return func; +} + // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -1018,8 +1080,8 @@ dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * qu llvmIrCallInstruction->eraseFromParent(); } -//void -//handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) +// void +// handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) //{ // llvm::errs() << "Handling FMul\n"; // llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -1073,9 +1135,48 @@ dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * qu // } // // llvm::errs() << "Finished handling FMul\n"; -//} +// } -void handleFMul(Instruction * inInstruction, Type * quantizedType,Function * fixmul, Function * floatIntMul) +void +handleFAdd(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + + // quantizeConstant(inInstruction, quantizedType); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + Value * newInst = Builder.CreateAdd(op0, op1); + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FSub\n"; +} + +void +handleFSub(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + // if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + // { + // op0 = Builder.CreateFPToSI(op0, quantizedType); + // } + // if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + // { + // op1 = Builder.CreateFPToSI(op1, quantizedType); + // } + Value * newInst = Builder.CreateSub(op0, op1); + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FSub\n"; +} + +void +handleFMul(Instruction * inInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) { llvm::errs() << "Handling FMul\n"; IRBuilder<> Builder(inInstruction); @@ -1086,7 +1187,6 @@ void handleFMul(Instruction * inInstruction, Type * quantizedType,Function * fix bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); if (lhsIsInteger && rhsIsInteger) @@ -1110,55 +1210,55 @@ void handleFMul(Instruction * inInstruction, Type * quantizedType,Function * fix llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; } - Value * newInst = Builder.CreateMul(lhs, rhs); + // Value * newInst = Builder.CreateMul(lhs, rhs); + Value * newInst = Builder.CreateCall(fixmul, {lhs, rhs}); inInstruction->replaceAllUsesWith(newInst); inInstruction->eraseFromParent(); llvm::errs() << "Finished handling FMul\n"; } - - void -handleFAdd(Instruction * inInstruction, Type * quantizedType) +handleFDiv(Instruction * inInstruction, Type * quantizedType, Function * fixdiv) { - llvm::errs() << "Handling FSub\n"; + llvm::errs() << "Handling FMul\n"; IRBuilder<> Builder(inInstruction); - //quantizeConstant(inInstruction, quantizedType); - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - - - Value * newInst = Builder.CreateAdd(op0, op1); - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FSub\n"; -} - - - - + Value * lhs = inInstruction->getOperand(0); + Value * rhs = inInstruction->getOperand(1); -void -handleFSub(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FSub\n"; - IRBuilder<> Builder(inInstruction); - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); + // bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + // bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + // + // + // bool lhsIsInteger = lhs->getType()->isIntegerTy(); + // bool rhsIsInteger = rhs->getType()->isIntegerTy(); + // if (lhsIsInteger && rhsIsInteger) + // { + // // Handle constant simplification if one of the operands is a constant + // if (isa(lhs) || isa(rhs)) + // { + // llvm::errs() << "One of the operands is a constant, simplifying...\n"; + // //simplifyConstant(inInstruction, quantizedType, floatIntMul); + // } + // else + // { + // // Call fixdiv with the converted fixed-point integers + // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + // llvm::CallInst * callInst = Builder.CreateCall(fixdiv, {lhs, rhs}); + // inInstruction->replaceAllUsesWith(callInst); + // inInstruction->eraseFromParent(); + // } + // } + // else + // { + // llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; + // } -// if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) -// { -// op0 = Builder.CreateFPToSI(op0, quantizedType); -// } -// if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) -// { -// op1 = Builder.CreateFPToSI(op1, quantizedType); -// } - Value * newInst = Builder.CreateSub(op0, op1); + // Value * newInst = Builder.CreateMul(lhs, rhs); + Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); inInstruction->replaceAllUsesWith(newInst); inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FSub\n"; + llvm::errs() << "Finished handling FMul\n"; } void @@ -1292,8 +1392,8 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } -//void -//handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +// void +// handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) //{ // if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) // { @@ -1316,14 +1416,18 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) // llvmIrLoadInstruction->eraseFromParent(); // } // } -//} +// } -void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { - if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) { +void +handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) + { IRBuilder<> Builder(llvmIrLoadInstruction); auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); - if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + { llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; @@ -1342,7 +1446,6 @@ void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { } } - void adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { @@ -1422,7 +1525,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" || functionName == "fixdiv") // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) { llvm::errs() << "Skipping function: " << functionName << "\n"; @@ -1478,12 +1581,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::errs() << "Calling createFixMul for function: " << llvmIrFunction.getName() << "\n"; llvm::errs() << "inFunction address: " << &llvmIrFunction << "\n"; + + // 先创建 fixmul 和 fixdiv 函数 + + llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); // generate hardcode function - floatIntMul function - // llvm::Function * floatIntMul = createFloatIntMul(llvmIrFunction.getParent(), intType, floatType, functionsToInsert); llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); - // llvm::Function* floatIntMul = createFloatIntMul(module, quantizedType, floatType, functionsToInsert); /* * quantize the arguments type @@ -1638,24 +1743,17 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * if either one of the operands is a constant value, simplify it by multiplying with 10^n, * then replace the instruction to mul/div; * - * else substitute this instruction to a pre-implemented function: mulfix/divfix. + * else substitute this instruction to a pre-implemented function: mulfix/fixdiv. * */ case Instruction::FMul: llvm::errs() << "Found FMul instruction.\n"; - handleFMul(llvmIrInstruction, quantizedType, fixmul, floatIntMul); + handleFMul(llvmIrInstruction, quantizedType, fixmul, nullptr); break; case Instruction::FDiv: { - if (isa(llvmIrInstruction->getOperand(0)) || - isa(llvmIrInstruction->getOperand(1))) - { - simplifyConstant(llvmIrInstruction, quantizedType, floatIntMul); - } - else - { - substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); - } + llvm::errs() << "Found FDiv instruction.\n"; + handleFDiv(llvmIrInstruction, quantizedType, fixdiv); break; } @@ -1781,9 +1879,9 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } } } - //handleLoadStoreInstructions(llvmIrFunction, quantizedType); + // handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); - //fixLoadStoreTypes(llvmIrFunction, quantizedType); + // fixLoadStoreTypes(llvmIrFunction, quantizedType); return; } From 8caeff3e473a731bc92f6f2ce2410f4180b5c163 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 24 Jul 2024 09:30:09 +0100 Subject: [PATCH 042/213] save before handle constant operation * dev2. --- ...bf84ffed653927aa206d3bfcd3fe4573b67469.txt | 48 ++++ .../newton-irPass-LLVMIR-dequantization.cpp | 209 ++++++++++++++++-- 2 files changed, 237 insertions(+), 20 deletions(-) create mode 100644 analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt diff --git a/analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt b/analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt new file mode 100644 index 000000000..e9094dedd --- /dev/null +++ b/analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt @@ -0,0 +1,48 @@ + +changeset: 1635:7bbf84ffed653927aa206d3bfcd3fe4573b67469 +char kNewtonVersion[] = "0.3-alpha-1635 (7bbf84ffed653927aa206d3bfcd3fe4573b67469) (build 07-23-2024-22:10-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-dequantization.cpp b/src/newton/newton-irPass-LLVMIR-dequantization.cpp index 7b593a0b1..7c6ae7532 100644 --- a/src/newton/newton-irPass-LLVMIR-dequantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-dequantization.cpp @@ -8,6 +8,135 @@ using namespace llvm; extern "C" { +// Handle load and store instructions +void +handleLoadStoreInstructions(Function & llvmIrFunction, Type * floatType) +{ + llvm::errs() << "Entering handleLoadStoreInstructions\n"; + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + if (llvmIrInstruction->getOpcode() == Instruction::Load) + { + if (auto loadInst = dyn_cast(llvmIrInstruction)) + { + auto ptr = loadInst->getPointerOperand(); + if (isQuantizedType(ptr->getType()->getPointerElementType())) + { + ptr->mutateType(floatType->getPointerTo()); + } + } + } + else if (llvmIrInstruction->getOpcode() == Instruction::Store) + { + if (auto storeInst = dyn_cast(llvmIrInstruction)) + { + auto ptr = storeInst->getPointerOperand(); + if (isQuantizedType(ptr->getType()->getPointerElementType())) + { + ptr->mutateType(floatType->getPointerTo()); + } + } + } + } + } + llvm::errs() << "Exiting handleLoadStoreInstructions\n"; +} + +// Dequantize constants within an instruction +void +dequantizeConstant(Instruction * inInstruction, Type * floatType) +{ + llvm::errs() << "Entering dequantizeConstant\n"; + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { + Value * inValue = inInstruction->getOperand(idx); + + if (!isa(inValue)) + { + continue; + } + + auto * constInt = dyn_cast(inValue); + Value * newValue = nullptr; + + if (inValue->getType()->isIntegerTy()) + { + // Convert fixed-point to float + int64_t fixedPointValue = constInt->getSExtValue(); + double floatValue = static_cast(fixedPointValue) / FRAC_BASE; + newValue = ConstantFP::get(floatType, floatValue); + } + + if (newValue) + { + inInstruction->replaceUsesOfWith(inValue, newValue); + } + } + llvm::errs() << "Exiting dequantizeConstant\n"; +} + +// Handle floating-point instructions and dequantize them +void +handleFPInstructions(Instruction * inInstruction, Type * floatType) +{ + llvm::errs() << "Entering handleFPInstructions\n"; + IRBuilder<> Builder(inInstruction); + switch (inInstruction->getOpcode()) + { + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::UDiv: + case Instruction::SDiv: + case Instruction::URem: + case Instruction::SRem: + { + Value * lhs = inInstruction->getOperand(0); + Value * rhs = inInstruction->getOperand(1); + if (isQuantizedType(lhs->getType())) + { + // Convert integer to float + lhs = Builder.CreateSIToFP(lhs, floatType); + } + if (isQuantizedType(rhs->getType())) + { + rhs = Builder.CreateSIToFP(rhs, floatType); + } + Instruction * newInst = nullptr; + switch (inInstruction->getOpcode()) + { + case Instruction::Add: + newInst = BinaryOperator::CreateFAdd(lhs, rhs, "", inInstruction); + break; + case Instruction::Sub: + newInst = BinaryOperator::CreateFSub(lhs, rhs, "", inInstruction); + break; + case Instruction::Mul: + newInst = BinaryOperator::CreateFMul(lhs, rhs, "", inInstruction); + break; + case Instruction::UDiv: + case Instruction::SDiv: + newInst = BinaryOperator::CreateFDiv(lhs, rhs, "", inInstruction); + break; + case Instruction::URem: + case Instruction::SRem: + newInst = BinaryOperator::CreateFRem(lhs, rhs, "", inInstruction); + break; + } + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + break; + } + default: + llvm::errs() << "Unhandled instruction: " << *inInstruction << "\n"; + break; + } + llvm::errs() << "Exiting handleFPInstructions\n"; +} + // Create a dequantized function with the same signature as the original function Function * createDequantizedFunction(Function & llvmIrFunction, Type * floatType) @@ -17,11 +146,13 @@ createDequantizedFunction(Function & llvmIrFunction, Type * floatType) { if (isQuantizedType(arg.getType())) { + // Change quantized type to float params.push_back(floatType); llvm::errs() << "Dequantizing parameter: " << arg.getName() << " from " << *arg.getType() << " to " << *floatType << "\n"; } else { + // Keep original type params.push_back(arg.getType()); } } @@ -67,6 +198,7 @@ replaceFunctionUses(Function & oldFunc, Function * newFunc) llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; callInst->replaceAllUsesWith(newCall); callInst->eraseFromParent(); + callInst->eraseFromParent(); } } } @@ -120,32 +252,43 @@ setDequantizedType(Value * inValue, Type * floatType) } // Adapt type casts to the dequantized type -void adaptTypeCast(Function &llvmIrFunction, Type *floatType) { +void +adaptTypeCast(Function & llvmIrFunction, Type * floatType) +{ llvm::errs() << "Entering adaptTypeCast\n"; - for (BasicBlock &llvmIrBasicBlock : llvmIrFunction) { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - Instruction *llvmIrInstruction = &*itBB++; - switch (llvmIrInstruction->getOpcode()) { + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + switch (llvmIrInstruction->getOpcode()) + { case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::UIToFP: case Instruction::SIToFP: { IRBuilder<> Builder(llvmIrInstruction); - Value *operand = llvmIrInstruction->getOperand(0); - Value *newInst = nullptr; + Value * operand = llvmIrInstruction->getOperand(0); + Value * newInst = nullptr; - if (llvmIrInstruction->getOpcode() == Instruction::FPToUI || llvmIrInstruction->getOpcode() == Instruction::FPToSI) { - if (isQuantizedType(operand->getType())) { + if (llvmIrInstruction->getOpcode() == Instruction::FPToUI || llvmIrInstruction->getOpcode() == Instruction::FPToSI) + { + if (isQuantizedType(operand->getType())) + { newInst = Builder.CreateSIToFP(operand, floatType); } - } else if (llvmIrInstruction->getOpcode() == Instruction::UIToFP || llvmIrInstruction->getOpcode() == Instruction::SIToFP) { - if (isQuantizedType(llvmIrInstruction->getType())) { + } + else if (llvmIrInstruction->getOpcode() == Instruction::UIToFP || llvmIrInstruction->getOpcode() == Instruction::SIToFP) + { + if (isQuantizedType(llvmIrInstruction->getType())) + { newInst = Builder.CreateSIToFP(operand, floatType); } } - if (newInst) { + if (newInst) + { llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); } @@ -155,16 +298,20 @@ void adaptTypeCast(Function &llvmIrFunction, Type *floatType) { case Instruction::FPTrunc: { IRBuilder<> Builder(llvmIrInstruction); - Value *operand = llvmIrInstruction->getOperand(0); - Value *newInst = nullptr; + Value * operand = llvmIrInstruction->getOperand(0); + Value * newInst = nullptr; - if (isQuantizedType(operand->getType())) { + if (isQuantizedType(operand->getType())) + { newInst = Builder.CreateSIToFP(operand, floatType); - } else { + } + else + { newInst = Builder.CreateFPCast(operand, floatType); } - if (newInst) { + if (newInst) + { llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); } @@ -173,10 +320,11 @@ void adaptTypeCast(Function &llvmIrFunction, Type *floatType) { case Instruction::BitCast: { IRBuilder<> Builder(llvmIrInstruction); - Value *operand = llvmIrInstruction->getOperand(0); - Value *newInst = Builder.CreateBitCast(operand, floatType); + Value * operand = llvmIrInstruction->getOperand(0); + Value * newInst = Builder.CreateBitCast(operand, floatType); - if (newInst) { + if (newInst) + { llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); } @@ -213,5 +361,26 @@ irPassLLVMIRAutoDequantization(State * N, llvm::Function & llvmIrFunction, std:: llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; handleLoadStoreInstructions(llvmIrFunction, floatType); + + for (BasicBlock & BB : llvmIrFunction) + { + for (BasicBlock::iterator it = BB.begin(); it != BB.end();) + { + Instruction * I = &*it++; + if (isQuantizedType(I->getType())) + { + setDequantizedType(I, floatType); + } + else + { + handleFPInstructions(I, floatType); + dequantizeConstant(I, floatType); + } + } + } + + adaptTypeCast(llvmIrFunction, floatType); + llvm::errs() << "Exiting irPassLLVMIRAutoDequantization\n"; } +} \ No newline at end of file From 6f36179b480bc297449afd40c11c44e3243df14d Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 24 Jul 2024 14:28:04 +0100 Subject: [PATCH 043/213] solve constant issue * dev2. --- ...5b2689bdc004f9c12d3f827b1313550af254ba.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt diff --git a/analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt b/analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt new file mode 100644 index 000000000..d2545cb63 --- /dev/null +++ b/analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt @@ -0,0 +1,48 @@ + +changeset: 1636:675b2689bdc004f9c12d3f827b1313550af254ba +char kNewtonVersion[] = "0.3-alpha-1636 (675b2689bdc004f9c12d3f827b1313550af254ba) (build 07-24-2024-09:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 778ed32c397fc92a7229a442d6d70dfdf8cea747 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 24 Jul 2024 16:45:04 +0100 Subject: [PATCH 044/213] save before handle more constant issue * dev2. --- ...aeff3e473a731bc92f6f2ce2410f4180b5c163.txt | 48 +++++++++++++++++ .../{MadgwickAHRS.c => MadgwickAHRS.cpp} | 51 ++++++++++++------- 2 files changed, 81 insertions(+), 18 deletions(-) create mode 100644 analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt rename applications/newton/llvm-ir/c-files/{MadgwickAHRS.c => MadgwickAHRS.cpp} (86%) diff --git a/analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt b/analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt new file mode 100644 index 000000000..a77433838 --- /dev/null +++ b/analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt @@ -0,0 +1,48 @@ + +changeset: 1637:8caeff3e473a731bc92f6f2ce2410f4180b5c163 +char kNewtonVersion[] = "0.3-alpha-1637 (8caeff3e473a731bc92f6f2ce2410f4180b5c163) (build 07-24-2024-14:28-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp similarity index 86% rename from applications/newton/llvm-ir/c-files/MadgwickAHRS.c rename to applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp index 7af26d46d..9fd6924bc 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp @@ -36,7 +36,7 @@ //--------------------------------------------------------------------------------------------------- // Variable definitions -volatile float beta = betaDef; // 2 * proportional gain (Kp) +volatile float beta = betaDef; // 2 * proportional gain (Kp) // 2 * proportional gain (Kp) //volatile float q0 = 0.64306622f, q1 = 0.02828862f, q2 = -0.00567953f, q3 = -0.76526684f; // quaternion of sensor frame relative to auxiliary frame //--------------------------------------------------------------------------------------------------- @@ -59,6 +59,11 @@ float invSqrt(float x) { return y; } + + + + + //==================================================================================================== // Functions @@ -71,7 +76,9 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float float q1 = *q1_ptr; float q2 = *q2_ptr; float q3 = *q3_ptr; - printf("Initial quaternion: q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + + + #ifdef ASSUME __builtin_assume(ax > lowerBound && ax < upperBound); __builtin_assume(ay > lowerBound && ay < upperBound); @@ -95,7 +102,8 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); - printf("qDot values: qDot1=%f, qDot2=%f, qDot3=%f, qDot4=%f\n", qDot1, qDot2, qDot3, qDot4); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -107,7 +115,6 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float ay *= recipNorm; az *= recipNorm; - printf("Normalized accelerometer: ax=%f, ay=%f, az=%f\n", ax, ay, az); // Normalise magnetometer measurement recipNorm = invSqrt(mx * mx + my * my + mz * mz); @@ -115,7 +122,10 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float my *= recipNorm; mz *= recipNorm; - printf("Normalized magnetometer: mx=%f, my=%f, mz=%f\n", mx, my, mz); + + //printf("Normalized magnetometer: mx=%f, my=%f, mz=%f\n", mx, my, mz); + + // Auxiliary variables to avoid repeated arithmetic _2q0mx = 2.0f * q0 * mx; @@ -147,7 +157,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float _4bx = 2.0f * _2bx; _4bz = 2.0f * _2bz; - printf("hx=%f, hy=%f, _2bx=%f, _2bz=%f, _4bx=%f, _4bz=%f\n", hx, hy, _2bx, _2bz, _4bx, _4bz); + //printf("hx=%f, hy=%f, _2bx=%f, _2bz=%f, _4bx=%f, _4bz=%f\n", hx, hy, _2bx, _2bz, _4bx, _4bz); // Gradient decent algorithm corrective step s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); @@ -160,7 +170,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float s2 *= recipNorm; s3 *= recipNorm; - printf("s values: s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); + //printf("s values: s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); // Apply feedback step qDot1 -= beta * s0; @@ -175,7 +185,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float q2 += qDot3 * (1.0f / sampleFreq); q3 += qDot4 * (1.0f / sampleFreq); - printf("Updated quaternion (before normalization): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + //printf("Updated quaternion (before normalization): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); @@ -183,12 +193,16 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", // q0, q1, q2, q3, recipNorm); + + q0 *= recipNorm; q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; - printf("Normalized quaternion: q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + //printf("Normalized quaternion: q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + + *q0_ptr = q0; *q1_ptr = q1; *q2_ptr = q2; @@ -207,7 +221,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo float q2 = *q2_ptr; float q3 = *q3_ptr; - printf("Initial quaternion (IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + //printf("Initial quaternion (IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); + float recipNorm; float s0, s1, s2, s3; @@ -220,7 +235,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); - printf("qDot values (IMU): qDot1=%f, qDot2=%f, qDot3=%f, qDot4=%f\n", qDot1, qDot2, qDot3, qDot4); + //printf("qDot values (IMU): qDot1=%f, qDot2=%f, qDot3=%f, qDot4=%f\n", qDot1, qDot2, qDot3, qDot4); + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -231,7 +247,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo ay *= recipNorm; az *= recipNorm; - printf("Normalized accelerometer (IMU): ax=%f, ay=%f, az=%f\n", ax, ay, az); + //printf("Normalized accelerometer (IMU): ax=%f, ay=%f, az=%f\n", ax, ay, az); + // Auxiliary variables to avoid repeated arithmetic _2q0 = 2.0f * q0; @@ -259,7 +276,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo s2 *= recipNorm; s3 *= recipNorm; - printf("s values (IMU): s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); + //printf("s values (IMU): s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); + // Apply feedback step qDot1 -= beta * s0; @@ -274,9 +292,6 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo q2 += qDot3 * (1.0f / sampleFreq); q3 += qDot4 * (1.0f / sampleFreq); - printf("Updated quaternion (before normalization, IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); - - // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); @@ -285,8 +300,8 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo q2 *= recipNorm; q3 *= recipNorm; - printf("Normalized quaternion (IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); - *q0_ptr = q0; + + *q1_ptr = q1; *q2_ptr = q2; *q3_ptr = q3; From 8b0f1a3d9caa6662cb7f24cca1a942003db0156d Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 24 Jul 2024 16:52:31 +0100 Subject: [PATCH 045/213] save before change * dev2. --- ...36179b480bc297449afd40c11c44e3243df14d.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt diff --git a/analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt b/analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt new file mode 100644 index 000000000..c1494df3b --- /dev/null +++ b/analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt @@ -0,0 +1,48 @@ + +changeset: 1638:6f36179b480bc297449afd40c11c44e3243df14d +char kNewtonVersion[] = "0.3-alpha-1638 (6f36179b480bc297449afd40c11c44e3243df14d) (build 07-24-2024-16:45-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 1060ebed7ea85969a8ea83c441a96d30d756ebc6 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 26 Jul 2024 13:07:58 +0100 Subject: [PATCH 046/213] update some test files * dev2. --- ...8ed32c397fc92a7229a442d6d70dfdf8cea747.txt | 48 ++++++++ applications/newton/llvm-ir/build_and_run.sh | 41 +++++++ .../c-files/floating_point_operations.c | 113 +++++++++++++++--- .../c-files/floating_point_operations.h | 23 ++++ 4 files changed, 208 insertions(+), 17 deletions(-) create mode 100644 analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt create mode 100755 applications/newton/llvm-ir/build_and_run.sh create mode 100644 applications/newton/llvm-ir/c-files/floating_point_operations.h diff --git a/analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt b/analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt new file mode 100644 index 000000000..0b2286165 --- /dev/null +++ b/analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt @@ -0,0 +1,48 @@ + +changeset: 1639:778ed32c397fc92a7229a442d6d70dfdf8cea747 +char kNewtonVersion[] = "0.3-alpha-1639 (778ed32c397fc92a7229a442d6d70dfdf8cea747) (build 07-24-2024-16:52-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/build_and_run.sh b/applications/newton/llvm-ir/build_and_run.sh new file mode 100755 index 000000000..bb97c39c2 --- /dev/null +++ b/applications/newton/llvm-ir/build_and_run.sh @@ -0,0 +1,41 @@ +#!/bin/bash + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/floating_point_operations.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd /home/xyf/CoSense/src/newton && ./newton-linux-EN --llvm-ir=/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization /home/xyf/CoSense/applications/newton/sensors/test.nt + +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +llvm-dis /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll + +# Step 4: Optimize the generated LLVM IR file +echo "Step 4: Optimize the generated LLVM IR file" +opt /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll --simplifycfg --instsimplify -O3 -Os -S -o /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace -o /home/xyf/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc /home/xyf/CoSense/applications/newton/llvm-ir/out.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +echo "Step 7: Compile the assembly file to object file" +clang -c /home/xyf/CoSense/applications/newton/llvm-ir/out.s -o /home/xyf/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/libout.a /home/xyf/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang /home/xyf/CoSense/applications/newton/llvm-ir/c-files/test_floating_point_operations.c -no-pie -L/home/xyf/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o /home/xyf/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +/home/xyf/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.c b/applications/newton/llvm-ir/c-files/floating_point_operations.c index 71caeadda..170a0c4bd 100644 --- a/applications/newton/llvm-ir/c-files/floating_point_operations.c +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.c @@ -1,14 +1,12 @@ #include #include "floating_point_operations.h" +#include - - - -// Function to perform basic floating point operations -// Function to perform addition -float perform_addition(float a, float b) { + //Function to perform basic floating point operations + //Function to perform addition + float perform_addition(float a, float b) { return a + b; } @@ -16,18 +14,99 @@ float perform_addition(float a, float b) { //// Function to perform subtraction -float perform_subtraction(float a, float b) { + float perform_subtraction(float a, float b) { return a - b; -} -// -//// Function to perform multiplication -float perform_multiplication(float a, float b) { + } +//// +////// Function to perform multiplication + float perform_multiplication(float a, float b) { //printf("Performing multiplication\ a: %f, b: %f\n", a, b); return a * b; -} + } + + float perform_division(float a, float b) { + return a / b; + } + +//float +//perform_constant_multiplication(float a) +//{ +// return a * 0.21; +//} +// +// float +// perform_constant_division(float a) +//{ +// return a / 0.21; +//} // -//// Function to perform division -//float perform_division(float a, float b) { -// //printf("Performing division\ a: %f, b: %f\n", a, b); -// return a / b; -//} \ No newline at end of file +//float +//perform_operations(float a, float b, float c, float d) +//{ +// float result = a * b; // First multiplication +// result = result + c; // Addition +// result = result - d; // Subtraction +// result = result * 1.5f; // Second multiplication with a constant +// return result; +//} + + +//float computePolynomial(float x) { +// float result = 0.0; +// for (int i = 0; i < 1000; ++i) { +// result += 0.3 * x * x * x + 0.2 * x * x + 0.5 * x + 0.7; +// } +// return result; +//} +#define PI 3.14159265358979323846 + + +//void fft(float* real, float* imag, int n) { +// int i, j, k, m, m_max; +// float t_real, t_imag, u_real, u_imag, w_real, w_imag, theta; +// +// // Bit-reverse +// for (i = 0, j = 0; i < n; ++i) { +// if (j > i) { +// t_real = real[j]; +// real[j] = real[i]; +// real[i] = t_real; +// t_imag = imag[j]; +// imag[j] = imag[i]; +// imag[i] = t_imag; +// } +// for (k = n >> 1; (j ^= k) < k; k >>= 1); +// } +// +// // FFT +// for (m = 1; (m_max = m << 1) <= n; m = m_max) { +// theta = -2.0 * PI / m_max; +// w_real = 1.0; +// w_imag = 0.0; +// u_real = cos(theta); +// u_imag = sin(theta); +// +// for (k = 0; k < m; ++k) { +// for (i = k; i < n; i += m_max) { +// j = i + m; +// t_real = w_real * real[j] - w_imag * imag[j]; +// t_imag = w_real * imag[j] + w_imag * real[j]; +// real[j] = real[i] - t_real; +// imag[j] = imag[i] - t_imag; +// real[i] += t_real; +// imag[i] += t_imag; +// } +// t_real = w_real; +// w_real = t_real * u_real - w_imag * u_imag; +// w_imag = t_real * u_imag + w_imag * u_real; +// } +// } +//} + +double polynomial(double x) { + // Evaluate a polynomial: P(x) = 3x^4 - 5x^3 + 2x^2 - x + 7 +// double result = 5.51*x*x*x*x - 0.21*x*x*x + 0.3*x*x + x; +// return result; + double result = x*x*x*x; + return result; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.h b/applications/newton/llvm-ir/c-files/floating_point_operations.h new file mode 100644 index 000000000..5ad4901c5 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.h @@ -0,0 +1,23 @@ +// +// Created by 13862 on 2024/7/20. +// + +#ifndef COSENSE_FLOATING_POINT_OPERATIONS_H +#define COSENSE_FLOATING_POINT_OPERATIONS_H + + +//declare global variables +//extern float param1_global; +//extern float param2_global; + +float perform_addition(float a, float b); + +float perform_subtraction(float a, float b); +float perform_multiplication(float a, float b); +float perform_division(float a, float b); + + + + + +#endif // COSENSE_FLOATING_POINT_OPERATIONS_H From 8e8315709192bf3eda7c9bc2be1346910b1b517e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 27 Jul 2024 10:35:53 +0100 Subject: [PATCH 047/213] update script * dev2. --- ...0f1a3d9caa6662cb7f24cca1a942003db0156d.txt | 48 +++++++++++++++++++ applications/newton/llvm-ir/build_and_run.sh | 23 +++++---- 2 files changed, 61 insertions(+), 10 deletions(-) create mode 100644 analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt diff --git a/analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt b/analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt new file mode 100644 index 000000000..a6eee71fc --- /dev/null +++ b/analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt @@ -0,0 +1,48 @@ + +changeset: 1640:8b0f1a3d9caa6662cb7f24cca1a942003db0156d +char kNewtonVersion[] = "0.3-alpha-1640 (8b0f1a3d9caa6662cb7f24cca1a942003db0156d) (build 07-26-2024-13:07-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/build_and_run.sh b/applications/newton/llvm-ir/build_and_run.sh index bb97c39c2..c77718667 100755 --- a/applications/newton/llvm-ir/build_and_run.sh +++ b/applications/newton/llvm-ir/build_and_run.sh @@ -1,41 +1,44 @@ #!/bin/bash +# Get the current user's home directory +USER_HOME=$HOME + # Step 1: Generate LLVM IR file echo "Step 1: Generate LLVM IR file" -clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/floating_point_operations.c +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/floating_point_operations.c # Step 2: Use newton for optimization and quantization echo "Step 2: Use newton for optimization and quantization" -cd /home/xyf/CoSense/src/newton && ./newton-linux-EN --llvm-ir=/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization /home/xyf/CoSense/applications/newton/sensors/test.nt +cd $USER_HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $USER_HOME/CoSense/applications/newton/sensors/test.nt # Step 3: Convert generated bytecode file to LLVM IR file echo "Step 3: Convert generated bytecode file to LLVM IR file" -llvm-dis /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll +llvm-dis $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -opt /home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll --simplifycfg --instsimplify -O3 -Os -S -o /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace +opt $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" -llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace -o /home/xyf/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly echo "Step 6: Compile the bitcode file to assembly" -llc /home/xyf/CoSense/applications/newton/llvm-ir/out.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/out.s +llc $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.s # Step 7: Compile the assembly file to object file echo "Step 7: Compile the assembly file to object file" -clang -c /home/xyf/CoSense/applications/newton/llvm-ir/out.s -o /home/xyf/CoSense/applications/newton/llvm-ir/out.o +clang -c $USER_HOME/CoSense/applications/newton/llvm-ir/out.s -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.o # Step 8: Package the object file into a static library echo "Step 8: Package the object file into a static library" -ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/libout.a /home/xyf/CoSense/applications/newton/llvm-ir/out.o +ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang /home/xyf/CoSense/applications/newton/llvm-ir/c-files/test_floating_point_operations.c -no-pie -L/home/xyf/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o /home/xyf/CoSense/applications/newton/llvm-ir/main_out -lm +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_floating_point_operations.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" -/home/xyf/CoSense/applications/newton/llvm-ir/main_out +$USER_HOME/CoSense/applications/newton/llvm-ir/main_out From 37f720e35e3cfdae69167d152d6069da2199b66c Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Thu, 1 Aug 2024 14:25:21 +0100 Subject: [PATCH 048/213] fix some issue * dev2. --- ...60ebed7ea85969a8ea83c441a96d30d756ebc6.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 780 +++++++++++++----- 2 files changed, 603 insertions(+), 225 deletions(-) create mode 100644 analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt diff --git a/analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt b/analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt new file mode 100644 index 000000000..e37cbb806 --- /dev/null +++ b/analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt @@ -0,0 +1,48 @@ + +changeset: 1641:1060ebed7ea85969a8ea83c441a96d30d756ebc6 +char kNewtonVersion[] = "0.3-alpha-1641 (1060ebed7ea85969a8ea83c441a96d30d756ebc6) (build 07-27-2024-10:35-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index f7d18599c..9d3d40311 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -286,7 +286,7 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" ||functionName == "printf") - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt"||functionName =="sin") { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; @@ -361,9 +361,11 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) * %27 = fptosi double %26 to i32 * %28 = shl i32 %27, 4 * */ + IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); + llvm::errs() << "Operand type: " << *operand->getType() << "\n"; if (operand->getType()->isIntegerTy()) { Value * newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); @@ -371,6 +373,7 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) } auto cloneInst = llvmIrCallInstruction->clone(); + llvm::errs() << "Cloned instruction: " << *cloneInst << "\n"; Value * fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); Value * resInst = nullptr; @@ -385,6 +388,68 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); resInst = Builder.CreateFPToSI(mulInst, quantizedType); + llvm::errs() << "Compensation applied\n"; + } + else + { + resInst = shlInst; + } + + llvmIrCallInstruction->replaceAllUsesWith(resInst); + ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); + llvm::errs() << "Exiting handleSinCall\n"; +} +} + + +void handleSinCall(CallInst *llvmIrCallInstruction, Type *quantizedType) +{ + + // Debugging output + llvm::errs() << "Entering handleSinCall\n"; + /* + * if the arg's type is int, convert to fp, + * after the call node, convert to int and shl FRAC_Q/2 + * + * int32_t res = (int32_t)sin(x)<<(FRAC_Q/2); + * if (FRAC_Q%2) + * return res*1.414213562; + * else + * return res; + * + * %25 = sitofp i32 %0 to double + * %26 = call double @sin(double %25) #3 + * %27 = fptosi double %26 to i32 + * %28 = shl i32 %27, 4 + * */ + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + llvm::errs() << "Operand type: " << *operand->getType() << "\n"; + + if (operand->getType()->isIntegerTy()) + { + Value *newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); + llvmIrCallInstruction->setOperand(0, newOperand); + } + + auto cloneInst = llvmIrCallInstruction->clone(); + llvm::errs() << "Cloned instruction: " << *cloneInst << "\n"; + Value *fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); + Value *shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value *resInst = nullptr; + + /* + * if (FRAC_Q%2) then multiply with 1.414213562; + * */ + + if (FRAC_Q % 2) + { + Value *lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); + auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); + Value *mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); + resInst = Builder.CreateFPToSI(mulInst, quantizedType); + llvm::errs() << "Compensation applied\n"; } else { @@ -393,6 +458,7 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) llvmIrCallInstruction->replaceAllUsesWith(resInst); ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); + llvm::errs() << "Exiting handleSinCall\n"; } // A list of global variables to erase after processing @@ -671,10 +737,52 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // } void -simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) +handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) +{ + llvm::errs() << "Handling FloatIntMul\n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + // 获取操作数 + Value * lhs = llvmIrInstruction->getOperand(0); + Value * rhs = llvmIrInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + // 确保左操作数是浮点数,右操作数是整数 + if (!lhs->getType()->isFloatTy() && !lhs->getType()->isDoubleTy()) + { + std::swap(lhs, rhs); + } + + if (lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy()) + { + if (rhs->getType()->isIntegerTy()) + { + llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Replaced with call to floatIntMul\n"; + } + else + { + llvm::errs() << "RHS is not an integer\n"; + } + } + else + { + llvm::errs() << "LHS is not a float\n"; + } + llvm::errs() << "Finished handling FloatIntMul\n"; +} + +void +simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) { llvm::errs() << "Entering simplifyConstant\n"; + auto checkDecimal = [](float decimalNum) { int digits = 0; while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) @@ -686,10 +794,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f }; auto compensateFP = [inInstruction, quantizedType, floatIntMul](float quantizedNum, float decimalNum) { - float compensateNum = quantizedNum / decimalNum; - // Value * constOperand, *nonConstOperand; - // - // unsigned constIdx, nonConstIdx; + float compensateNum = quantizedNum / decimalNum; Value * constOperand = nullptr; Value * nonConstOperand = nullptr; unsigned constIdx = 0; @@ -710,8 +815,6 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f nonConstOperand = inInstruction->getOperand(0); } - // auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - // auto quantizeNumValue = ConstantFP::get(nonConstOperand->getType(), quantizedNum); auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); if (compensateNum == 1) @@ -725,19 +828,18 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f if (nonConstOperand->getType()->isFloatTy() || nonConstOperand->getType()->isDoubleTy()) { - // 调用 handleFloatIntMul 处理浮点数和整数相乘 llvm::errs() << "Calling handleFloatIntMul\n"; llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {nonConstOperand, quantizeNumValue}); inInstruction->replaceAllUsesWith(callInst); } else { - // Replace the original fmul instruction with integer mul llvm::errs() << "Replacing original fmul instruction with integer mul\n"; Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); inInstruction->replaceAllUsesWith(newMulValue); } inInstruction->eraseFromParent(); + // inInstruction->setOperand(constIdx, quantizeNumValue); } else { @@ -753,7 +855,8 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f if (instOpCode == Instruction::FMul) { - newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + llvm::errs() << "Created Mul instruction: " << *newFirstInst << "\n"; newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) @@ -787,9 +890,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * f continue; } - ConstantFP * constFp = dyn_cast(inValue); - Value * newValue = nullptr; - + ConstantFP * constFp = dyn_cast(inValue); if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -1080,63 +1181,6 @@ dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * qu llvmIrCallInstruction->eraseFromParent(); } -// void -// handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) -//{ -// llvm::errs() << "Handling FMul\n"; -// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; -// IRBuilder<> Builder(llvmIrInstruction); -// -// // Ensure operands are correctly converted to fixed-point integers -// Value * lhs = llvmIrInstruction->getOperand(0); -// Value * rhs = llvmIrInstruction->getOperand(1); -// -// llvm::errs() << "LHS: " << *lhs << "\n"; -// llvm::errs() << "RHS: " << *rhs << "\n"; -// -// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); -// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); -// -// // If either operand is a float, convert both to fixed-point -// if (lhsIsFloat) -// { -// lhs = Builder.CreateFPToSI(lhs, quantizedType); -// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; -// } -// if (rhsIsFloat) -// { -// rhs = Builder.CreateFPToSI(rhs, quantizedType); -// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; -// } -// -// // Ensure both operands are now integers -// bool lhsIsInteger = lhs->getType()->isIntegerTy(); -// bool rhsIsInteger = rhs->getType()->isIntegerTy(); -// -// if (lhsIsInteger && rhsIsInteger) -// { -// // Handle constant simplification if one of the operands is a constant -// if (isa(lhs) || isa(rhs)) -// { -// llvm::errs() << "One of the operands is a constant, simplifying...\n"; -// simplifyConstant(llvmIrInstruction, quantizedType, floatIntMul); -// } -// else -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); -// llvmIrInstruction->replaceAllUsesWith(callInst); -// llvmIrInstruction->eraseFromParent(); -// } -// } -// else -// { -// llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; -// } -// -// llvm::errs() << "Finished handling FMul\n"; -// } - void handleFAdd(Instruction * inInstruction, Type * quantizedType) { @@ -1175,90 +1219,266 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FSub\n"; } +float +checkDecimal(float decimalNum) +{ + int digits = 0; + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) + { + decimalNum *= 10; // Scale decimal to avoid precision issues + digits++; + } + return decimalNum; +} + +void +compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, Type * quantizedType) +{ + float compensateNum = quantizedNum / decimalNum; + Value * constOperand = nullptr; + Value * nonConstOperand = nullptr; + unsigned constIdx = 0; + unsigned nonConstIdx = 0; + + if (isa(inInstruction->getOperand(0))) + { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); + nonConstOperand = inInstruction->getOperand(1); + } + else + { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); + nonConstOperand = inInstruction->getOperand(0); + } + + //uto quantizeNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); + + llvm::errs() << "Compensate Num: " << compensateNum << "\n"; + llvm::errs() << "Quantized Num Value: " << quantizedNum << "\n"; + llvm::errs() << "Instruction Opcode: " << inInstruction->getOpcodeName() << "\n"; + + if (compensateNum == 1) + { + llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; + inInstruction->setOperand(constIdx, quantizeNumValue); + } + else + { + llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; + auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + + Value * newFirstInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + + if (instOpCode == Instruction::FMul) + { + llvm::errs() << "Handling FMul instruction\n"; + newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) + { + llvm::errs() << "Handling FDiv instruction with constant denominator\n"; + newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) + { + llvm::errs() << "Handling FDiv instruction with constant numerator\n"; + newFirstInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); + } + + if (newSecondInst) + { + llvm::errs() << "Replacing old instruction with compensated instruction\n"; + inInstruction->replaceAllUsesWith(newSecondInst); + inInstruction->eraseFromParent(); + } + else + { + llvm::errs() << "Failed to create new compensated instruction\n"; + } + } +} + + + void -handleFMul(Instruction * inInstruction, Type * quantizedType, Function * fixmul, Function * floatIntMul) +handleConstant(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling constant operand\n"; + + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { + Value * inValue = inInstruction->getOperand(idx); + if (!isa(inValue)) + { + continue; + } + + ConstantFP * constFp = dyn_cast(inValue); + + if (inValue->getType()->isFloatTy()) + { + float constValue = constFp->getValueAPF().convertToFloat(); + llvm::errs() << "Original float constant: " << constValue << "\n"; + float decimalValue = checkDecimal(constValue); + llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; + if (decimalValue == constValue) + { + // 如果小数部分已经是整数,则直接量化 + auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); + auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); + llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; + inInstruction->setOperand(idx, quantizedConst); + } + else + { + compensateFP(inInstruction, decimalValue, constValue, quantizedType); + } + } + else if (inValue->getType()->isDoubleTy()) + { + double constValue = constFp->getValueAPF().convertToDouble(); + llvm::errs() << "Original double constant: " << constValue << "\n"; + double decimalValue = checkDecimal(constValue); + llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; + if (decimalValue == constValue) + { + // 如果小数部分已经是整数,则直接量化 + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); + llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; + inInstruction->setOperand(idx, quantizedConst); + } + else + { + compensateFP(inInstruction, decimalValue, constValue, quantizedType); + } + } + else + { + assert(false && "unknown floating type"); + } + } + llvm::errs() << "Exiting handleConstant\n"; +} + +void +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) { llvm::errs() << "Handling FMul\n"; - IRBuilder<> Builder(inInstruction); + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + // Ensure operands are correctly converted to fixed-point integers + Value * lhs = llvmIrInstruction->getOperand(0); + Value * rhs = llvmIrInstruction->getOperand(1); - Value * lhs = inInstruction->getOperand(0); - Value * rhs = inInstruction->getOperand(1); + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + // If either operand is a float, convert both to fixed-point + if (lhsIsFloat) + { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + if (rhsIsFloat) + { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } + + // Ensure both operands are now integers bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); + if (lhsIsInteger && rhsIsInteger) { - // Handle constant simplification if one of the operands is a constant if (isa(lhs) || isa(rhs)) { llvm::errs() << "One of the operands is a constant, simplifying...\n"; - simplifyConstant(inInstruction, quantizedType, floatIntMul); + handleConstant(llvmIrInstruction, quantizedType); } else { llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - inInstruction->replaceAllUsesWith(callInst); - inInstruction->eraseFromParent(); + { + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); + } } } - else - { - llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; - } - - // Value * newInst = Builder.CreateMul(lhs, rhs); - Value * newInst = Builder.CreateCall(fixmul, {lhs, rhs}); - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FMul\n"; } void -handleFDiv(Instruction * inInstruction, Type * quantizedType, Function * fixdiv) +handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixdiv) { - llvm::errs() << "Handling FMul\n"; - IRBuilder<> Builder(inInstruction); + llvm::errs() << "Handling FDiv \n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); - Value * lhs = inInstruction->getOperand(0); - Value * rhs = inInstruction->getOperand(1); + // Ensure operands are correctly converted to fixed-point integers + Value * lhs = llvmIrInstruction->getOperand(0); + Value * rhs = llvmIrInstruction->getOperand(1); - // bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); - // bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - // - // - // bool lhsIsInteger = lhs->getType()->isIntegerTy(); - // bool rhsIsInteger = rhs->getType()->isIntegerTy(); - // if (lhsIsInteger && rhsIsInteger) - // { - // // Handle constant simplification if one of the operands is a constant - // if (isa(lhs) || isa(rhs)) - // { - // llvm::errs() << "One of the operands is a constant, simplifying...\n"; - // //simplifyConstant(inInstruction, quantizedType, floatIntMul); - // } - // else - // { - // // Call fixdiv with the converted fixed-point integers - // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - // llvm::CallInst * callInst = Builder.CreateCall(fixdiv, {lhs, rhs}); - // inInstruction->replaceAllUsesWith(callInst); - // inInstruction->eraseFromParent(); - // } - // } - // else - // { - // llvm::errs() << "Operands are not both integers, cannot handle FMul\n"; - // } + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; - // Value * newInst = Builder.CreateMul(lhs, rhs); - Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FMul\n"; + bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + // If either operand is a float, convert both to fixed-point + if (lhsIsFloat) + { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + if (rhsIsFloat) + { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } + + // Ensure both operands are now integers + bool lhsIsInteger = lhs->getType()->isIntegerTy(); + bool rhsIsInteger = rhs->getType()->isIntegerTy(); + + if (lhsIsInteger && rhsIsInteger) + { + if (isa(lhs) || isa(rhs)) + { + llvm::errs() << "One of the operands is a constant, simplifying...\n"; + handleConstant(llvmIrInstruction, quantizedType); + } + else + { + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + { + // Value * newInst = Builder.CreateMul(lhs, rhs); + Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FDiv\n"; + } + } + } } void @@ -1336,6 +1556,55 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) } } +void handleCall(CallInst *llvmIrCallInstruction, Type *quantizedType) { + + Function *calledFunction = llvmIrCallInstruction->getCalledFunction(); + if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + return; + + if (!calledFunction->getName().startswith("llvm.dbg.value") && + !calledFunction->getName().startswith("llvm.dbg.declare") && + !calledFunction->getName().startswith("llvm.dbg.label")) { + + if (calledFunction->isDeclaration()) { + // For library functions + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction *insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value *newInst = nullptr; + + if (calledFunction->getName().str() == "sqrt") { + // For sqrt + handleSqrtCall(llvmIrCallInstruction, quantizedType); + } else if (calledFunction->getName().str() == "sin") { + //For sin + handleSinCall(llvmIrCallInstruction, quantizedType); + } + else { + /* + * for other lib functions, de-quantize the arguments and quantize the return value + */ + dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); + } + } else { + /* + * for user-defined function, quantize the arguments + */ + // Uncomment and adapt the following lines if needed + for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) { + setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); + } + quantizeConstant(llvmIrCallInstruction, quantizedType); + /* + * then quantize the return type + */ + setQuantizedType(llvmIrCallInstruction, quantizedType); + } + } +} + + + // void handleStore(Instruction *llvmIrInstruction, Type *quantizedType) { // if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) { // IRBuilder<> Builder(llvmIrStoreInstruction); @@ -1361,6 +1630,37 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) // } // } +// void +// handleStore(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) +// { +// IRBuilder<> Builder(llvmIrStoreInstruction); +// +// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); +// if (valueType->isFloatTy() || valueType->isDoubleTy()) +// { +// llvm::errs() << "Original store value type: " << *valueType << "\n"; +// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; +// +// // 乘以 FRAC_BASE 然后转换为整数类型 +// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); +// quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); +// llvmIrStoreInstruction->setOperand(0, quantizedValue); +// } +// +// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); +// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) +// { +// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; +// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; +// +// // 设置新的量化类型 +// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); +// } +// } +// } + void handleStore(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1374,7 +1674,7 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) llvm::errs() << "Original store value type: " << *valueType << "\n"; llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; - // 乘以 FRAC_BASE 然后转换为整数类型 + // 转换存储的值为量化后的类型 auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); llvmIrStoreInstruction->setOperand(0, quantizedValue); @@ -1392,57 +1692,143 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } -// void -// handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) + + + +void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { + if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) { + IRBuilder<> Builder(llvmIrLoadInstruction); + + auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); + + // 打印指针类型以调试 + llvm::errs() << "Pointer type of load instruction: " << *pointerType << "\n"; + + //if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { + llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; + llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; + + // 更新指针操作数的类型为量化类型 + llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + + // 以整数形式加载值 + auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); + + // 检查后续使用并确保类型匹配 + for (auto &use : llvmIrLoadInstruction->uses()) { + auto user = use.getUser(); + if (auto storeInst = dyn_cast(user)) { + // 在存储指令中,如果值被存储为浮点型,则进行适当转换 + if (storeInst->getValueOperand() == llvmIrLoadInstruction) { + auto floatValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); + storeInst->setOperand(0, floatValue); + } + } + } + + // 替换原始load指令的所有使用 + llvmIrLoadInstruction->replaceAllUsesWith(loadedValue); + + // 移除原始load指令 + llvmIrLoadInstruction->eraseFromParent(); + // } else { + // llvm::errs() << "Pointer type is not float or double, skipping load handling.\n"; + // } + } +} + + + + + + + + + + + +//指针待处理 +//void +//handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) //{ // if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) // { // IRBuilder<> Builder(llvmIrLoadInstruction); // // auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); +// +// llvm::errs() << "Pointer type of load instruction: " << *pointerType << "\n"; +// // if (pointerType->isFloatTy() || pointerType->isDoubleTy()) // { // llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; // llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; // -// // 加载整数值,并转换为浮点数进行后续计算 +// // 更新指针操作数的类型为量化类型 // llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); +// +// // 以整数形式加载值 // auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); -// auto fpValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); // -// // 除以 FRAC_BASE 还原浮点数 -// fpValue = Builder.CreateFDiv(fpValue, ConstantFP::get(Type::getFloatTy(llvmIrInstruction->getContext()), FRAC_BASE)); +// llvm::errs() << "Loaded value type: " << *loadedValue->getType() << "\n"; +// +// +// // 确保类型转换为浮点数并恢复值 +// auto fpValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); +// fpValue = Builder.CreateFDiv(fpValue, ConstantFP::get(Type::getFloatTy(llvmIrInstruction->getContext()), FRAC_BASE)); +// +// llvm::errs() << "Loaded value type: " << *loadedValue->getType() << "\n"; // llvmIrLoadInstruction->replaceAllUsesWith(fpValue); // llvmIrLoadInstruction->eraseFromParent(); // } +// else +// { +// llvm::errs() << "Pointer type is not float or double, skipping load handling.\n"; +// } // } -// } +// llvm::errs() << "Exiting handleLoad\n"; +//} void -handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) { - if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) - { - IRBuilder<> Builder(llvmIrLoadInstruction); - - auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); - if (pointerType->isFloatTy() || pointerType->isDoubleTy()) - { - llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; - llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; - - // Update the type of the pointer operand to the quantized type - llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + llvm::errs() << "Handling FPExt: " << *llvmIrInstruction << "\n"; + auto * fpextInst = cast(llvmIrInstruction); + auto srcType = fpextInst->getSrcTy(); + auto destType = fpextInst->getDestTy(); - // Load the value as an integer - auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); + // 如果src为整型,dest为浮点或double型,则删除不必要的fpext指令 + if (srcType->isIntegerTy() && (destType->isFloatTy() || destType->isDoubleTy())) + { + // 删除不必要的 fpext 指令 + llvmIrInstruction->replaceAllUsesWith(fpextInst->getOperand(0)); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Removed unnecessary FPExt\n"; + } + else + { + llvm::errs() << "Unhandled FPExt conversion from " << *srcType << " to " << *destType << "\n"; + } +} - // Replace all uses of the original load instruction with the new loaded integer value - llvmIrLoadInstruction->replaceAllUsesWith(loadedValue); +void +handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; + auto * fptruncInst = cast(llvmIrInstruction); + auto srcType = fptruncInst->getSrcTy(); + auto destType = fptruncInst->getDestTy(); - // Remove the old load instruction - llvmIrLoadInstruction->eraseFromParent(); - } + if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) + { + // 删除不必要的 fptrunc 指令 + llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Removed unnecessary FPTrunc\n"; + } + else + { + llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; } } @@ -1482,22 +1868,14 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } break; case Instruction::FPExt: + { + handleFPExt(llvmIrInstruction, quantizedType); + break; + } + case Instruction::FPTrunc: { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) - { - newInst = Builder.CreateSIToFP(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } - else - { - newInst = Builder.CreateFPCast(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); + handleFPTrunc(llvmIrInstruction, quantizedType); break; } case Instruction::BitCast: @@ -1525,7 +1903,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" || functionName == "fixdiv") + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" + || functionName == "fixdiv" || functionName == "constantMulDiv"||functionName=="sin" ) // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) { llvm::errs() << "Skipping function: " << functionName << "\n"; @@ -1566,12 +1945,11 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve Type * intType = Type::getInt32Ty(llvmIrFunction.getContext()); // Deal with function signature - llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; + handleFunctionSignature(llvmIrFunction, quantizedType); - llvm::errs() << "Finished calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Update global variable type to integer type - llvm::errs() << "Calling handleGlobalVariable for function: " << llvmIrFunction.getName() << "\n"; + // Update global variables updateGlobalVariables(module, quantizedType); @@ -1579,16 +1957,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * generate hardcode function - fixmul and fixdiv * */ - llvm::errs() << "Calling createFixMul for function: " << llvmIrFunction.getName() << "\n"; - llvm::errs() << "inFunction address: " << &llvmIrFunction << "\n"; - // 先创建 fixmul 和 fixdiv 函数 llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); // generate hardcode function - floatIntMul function - llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); + // llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); /* * quantize the arguments type @@ -1646,52 +2021,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) - { - Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) - break; - if (!calledFunction->getName().startswith("llvm.dbg.value") && - !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) - { - if (calledFunction->isDeclaration()) - { - // For library functions - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (calledFunction->getName().str() == "sqrt") - { - // For sqrt - handleSqrtCall(llvmIrCallInstruction, quantizedType); - } - else - { - /* - * for other lib functions, de-quantize the arguments and quantize the return value - * */ - dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); - } - } - else - { - /* - * for user-defined function, quantize the arguments - * */ - // for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - // { - // setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - // } - // quantizeConstant(llvmIrCallInstruction, quantizedType); - // /* - // * then quantize the return type - // * */ - // setQuantizedType(llvmIrCallInstruction, quantizedType); - } - } - } + handleCall(cast(llvmIrInstruction), quantizedType); break; case Instruction::GetElementPtr: if (auto gepInst = dyn_cast(llvmIrInstruction)) @@ -1720,6 +2050,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } break; case Instruction::Load: + llvm::errs() << "Handling Load instruction: " << *llvmIrInstruction << "\n"; handleLoad(llvmIrInstruction, quantizedType); break; case Instruction::PHI: @@ -1747,7 +2078,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * */ case Instruction::FMul: llvm::errs() << "Found FMul instruction.\n"; - handleFMul(llvmIrInstruction, quantizedType, fixmul, nullptr); + handleFMul(llvmIrInstruction, quantizedType, fixmul); break; case Instruction::FDiv: @@ -1885,4 +2216,3 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } -} \ No newline at end of file From 8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 3 Aug 2024 12:21:20 +0100 Subject: [PATCH 049/213] made some changes * dev2. --- ...8315709192bf3eda7c9bc2be1346910b1b517e.txt | 48 +++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 24 +++- .../newton-irPass-LLVMIR-quantization.cpp | 135 +++--------------- 3 files changed, 86 insertions(+), 121 deletions(-) create mode 100644 analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt diff --git a/analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt b/analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt new file mode 100644 index 000000000..2ce031b7b --- /dev/null +++ b/analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt @@ -0,0 +1,48 @@ + +changeset: 1642:8e8315709192bf3eda7c9bc2be1346910b1b517e +char kNewtonVersion[] = "0.3-alpha-1642 (8e8315709192bf3eda7c9bc2be1346910b1b517e) (build 08-01-2024-14:25-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 65a632f3e..c9b115bfa 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -44,6 +44,10 @@ #include "newton-irPass-LLVMIR-optimizeByRange.h" #include "newton-irPass-LLVMIR-memoryAlignment.h" #include "newton-irPass-LLVMIR-emitAssume.h" + + +//added code +#include "myRangeAnalysis.h" #endif /* __cplusplus */ #include @@ -110,6 +114,7 @@ void finalCorrectionPass(Module &M, Type *quantizedType) { if (auto *loadInst = dyn_cast(&I)) { if (loadInst->getType() != quantizedType) { llvm::errs() << "Correcting load instruction: " << *loadInst << "\n"; + loadInst->mutateType(quantizedType); } } @@ -396,6 +401,21 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } + + + //Range Analysis + +// for (auto & mi : *Mod) +// { +// rangeAnalysis(mi); +// +// } + + + + + + // /* // * analyze the range of all local variables in each function // * */ @@ -546,8 +566,8 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); //finalCorrectionPass(*Mod, quantizedType); - finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization - saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + //finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization + //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 9d3d40311..a8cd6bcbe 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -307,27 +307,27 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) processedFunctions.insert(functionName); - // 调试信息:输出原始函数参数类型 + llvm::errs() << "Original function name: " << llvmIrFunction.getName() << "\n"; for (auto & arg : llvmIrFunction.args()) { llvm::errs() << "Original argument type: " << *arg.getType() << "\n"; } - // 保留原参数类型 + // Keep the original function parameters std::vector params; for (auto & arg : llvmIrFunction.args()) { params.push_back(arg.getType()); } - // 修改返回类型为量化后的类型 + // Create a new function with the quantized return type Type * returnType = quantizedType; FunctionType * newFuncType = FunctionType::get(returnType, params, false); Function * newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); - // 调试信息:输出新函数参数类型 + llvm::errs() << "New function name: " << newFunc->getName() << "\n"; for (auto & arg : newFunc->args()) { @@ -1336,7 +1336,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; if (decimalValue == constValue) { - // 如果小数部分已经是整数,则直接量化 + // If the decimal part is already an integer, quantize directly auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; @@ -1355,7 +1355,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; if (decimalValue == constValue) { - // 如果小数部分已经是整数,则直接量化 + // If the decimal part is already an integer, quantize directly int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; @@ -1544,13 +1544,13 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - // 检查是否需要转换类型 + // Check if the alloca instruction is of type float or double if (allocaType->isFloatTy() || allocaType->isDoubleTy()) { llvm::errs() << "Original alloca type: " << *allocaType << "\n"; llvm::errs() << "New quantized alloca type: " << *quantizedType << "\n"; - // 设置新的量化类型 + // Set the new quantized type for the alloca instruction llvmIrAllocaInstruction->setAllocatedType(quantizedType); } } @@ -1604,63 +1604,6 @@ void handleCall(CallInst *llvmIrCallInstruction, Type *quantizedType) { } - -// void handleStore(Instruction *llvmIrInstruction, Type *quantizedType) { -// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) { -// IRBuilder<> Builder(llvmIrStoreInstruction); -// -// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); -// if (valueType->isFloatTy() || valueType->isDoubleTy()) { -// llvm::errs() << "Original store value type: " << *valueType << "\n"; -// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; -// -// // 转换存储的值为量化后的类型 -// auto quantizedValue = Builder.CreateFPToSI(llvmIrStoreInstruction->getValueOperand(), quantizedType); -// llvmIrStoreInstruction->setOperand(0, quantizedValue); -// } -// -// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); -// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { -// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; -// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; -// -// // 设置新的量化类型 -// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); -// } -// } -// } - -// void -// handleStore(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) -// { -// IRBuilder<> Builder(llvmIrStoreInstruction); -// -// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); -// if (valueType->isFloatTy() || valueType->isDoubleTy()) -// { -// llvm::errs() << "Original store value type: " << *valueType << "\n"; -// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; -// -// // 乘以 FRAC_BASE 然后转换为整数类型 -// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); -// quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); -// llvmIrStoreInstruction->setOperand(0, quantizedValue); -// } -// -// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); -// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) -// { -// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; -// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; -// -// // 设置新的量化类型 -// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); -// } -// } -// } - void handleStore(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1686,7 +1629,7 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; - // 设置新的量化类型 + // Set the new quantized type for the pointer operand llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); } } @@ -1701,7 +1644,7 @@ void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); - // 打印指针类型以调试 + // Print the pointer type of the load instruction llvm::errs() << "Pointer type of load instruction: " << *pointerType << "\n"; //if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { @@ -1726,10 +1669,10 @@ void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { } } - // 替换原始load指令的所有使用 + //replace all uses of the original load instruction with the loaded value llvmIrLoadInstruction->replaceAllUsesWith(loadedValue); - // 移除原始load指令 + // erase the original load instruction llvmIrLoadInstruction->eraseFromParent(); // } else { // llvm::errs() << "Pointer type is not float or double, skipping load handling.\n"; @@ -1743,52 +1686,6 @@ void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { - - - - -//指针待处理 -//void -//handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) -// { -// IRBuilder<> Builder(llvmIrLoadInstruction); -// -// auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); -// -// llvm::errs() << "Pointer type of load instruction: " << *pointerType << "\n"; -// -// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) -// { -// llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; -// llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; -// -// // 更新指针操作数的类型为量化类型 -// llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); -// -// // 以整数形式加载值 -// auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); -// -// llvm::errs() << "Loaded value type: " << *loadedValue->getType() << "\n"; -// -// -// // 确保类型转换为浮点数并恢复值 -// auto fpValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); -// fpValue = Builder.CreateFDiv(fpValue, ConstantFP::get(Type::getFloatTy(llvmIrInstruction->getContext()), FRAC_BASE)); -// -// llvm::errs() << "Loaded value type: " << *loadedValue->getType() << "\n"; -// llvmIrLoadInstruction->replaceAllUsesWith(fpValue); -// llvmIrLoadInstruction->eraseFromParent(); -// } -// else -// { -// llvm::errs() << "Pointer type is not float or double, skipping load handling.\n"; -// } -// } -// llvm::errs() << "Exiting handleLoad\n"; -//} - void handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1797,10 +1694,10 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) auto srcType = fpextInst->getSrcTy(); auto destType = fpextInst->getDestTy(); - // 如果src为整型,dest为浮点或double型,则删除不必要的fpext指令 + // If the source type is an integer and the destination type is a floating-point type if (srcType->isIntegerTy() && (destType->isFloatTy() || destType->isDoubleTy())) { - // 删除不必要的 fpext 指令 + // Erase the unnecessary FPExt instruction llvmIrInstruction->replaceAllUsesWith(fpextInst->getOperand(0)); llvmIrInstruction->eraseFromParent(); llvm::errs() << "Removed unnecessary FPExt\n"; @@ -1821,7 +1718,7 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) { - // 删除不必要的 fptrunc 指令 + // Erase the unnecessary FPTrunc instruction llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); llvmIrInstruction->eraseFromParent(); llvm::errs() << "Removed unnecessary FPTrunc\n"; @@ -2212,7 +2109,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } // handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); - // fixLoadStoreTypes(llvmIrFunction, quantizedType); + return; } From 1e43b79ea3957b991e9baf2ad059f6488e687ea1 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 4 Aug 2024 23:15:59 +0100 Subject: [PATCH 050/213] fix sqrt issue * dev2. --- ...f720e35e3cfdae69167d152d6069da2199b66c.txt | 48 ++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 49 +++++++++++++------ 2 files changed, 82 insertions(+), 15 deletions(-) create mode 100644 analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt diff --git a/analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt b/analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt new file mode 100644 index 000000000..c418c1322 --- /dev/null +++ b/analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt @@ -0,0 +1,48 @@ + +changeset: 1643:37f720e35e3cfdae69167d152d6069da2199b66c +char kNewtonVersion[] = "0.3-alpha-1643 (37f720e35e3cfdae69167d152d6069da2199b66c) (build 08-03-2024-12:21-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a8cd6bcbe..1ab1915ff 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -70,7 +70,7 @@ setQuantizedType(Value * inValue, Type * quantizedType) valueType = valueType->getPointerElementType(); } - // 跳过整数类型和结构体类型 + //Skip integer and struct types if (valueType->isIntegerTy() || valueType->isStructTy()) { llvm::errs() << "Skipping quantization for type: " << *valueType << "\n"; @@ -286,7 +286,7 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" ||functionName == "printf") - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt"||functionName =="sin") + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt"||functionName =="sinf" ||functionName == "sqrtf" ) { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; @@ -365,23 +365,37 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); - llvm::errs() << "Operand type: " << *operand->getType() << "\n"; + // Cast the instruction to CallInst to access getCalledFunction method + CallInst *callInst = dyn_cast(llvmIrCallInstruction); + if (!callInst) { + llvm::errs() << "Error: Instruction is not a CallInst.\n"; + return; + } + + if (operand->getType()->isIntegerTy()) { Value * newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); llvmIrCallInstruction->setOperand(0, newOperand); } - auto cloneInst = llvmIrCallInstruction->clone(); - llvm::errs() << "Cloned instruction: " << *cloneInst << "\n"; - Value * fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); + // The sequence should be: + // 1. sitofp: Convert int to float + // 2. call sqrtf: Compute sqrt in float + // 3. fptosi: Convert float result to int + // 4. shl: Left shift the integer result for scaling + // Create call to sqrt function + Value *sqrtResult = Builder.CreateCall(callInst->getCalledFunction(), {callInst->getOperand(0)}); + // Convert the result back to integer + Value *fptosiInst = Builder.CreateFPToSI(sqrtResult, quantizedType); + // Perform left shift for scaling Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); Value * resInst = nullptr; /* * if (FRAC_Q%2) then multiply with 1.414213562; * */ - + // If FRAC_Q is odd, apply compensation if (FRAC_Q % 2) { Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); @@ -396,8 +410,8 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) } llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); - llvm::errs() << "Exiting handleSinCall\n"; + //ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); + llvmIrCallInstruction->eraseFromParent(); } } @@ -1576,15 +1590,20 @@ void handleCall(CallInst *llvmIrCallInstruction, Type *quantizedType) { if (calledFunction->getName().str() == "sqrt") { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType); - } else if (calledFunction->getName().str() == "sin") { - //For sin + + + } else if (calledFunction->getName().str() == "sqrtf") { + // For sqrtf + handleSqrtCall(llvmIrCallInstruction, quantizedType); + } else if (calledFunction->getName().str() == "sin") { + // For sin handleSinCall(llvmIrCallInstruction, quantizedType); - } - else { + } else { /* - * for other lib functions, de-quantize the arguments and quantize the return value + * for other lib functions, de-quantize the arguments and quantize the return value */ dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); + } } else { /* @@ -1801,7 +1820,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" - || functionName == "fixdiv" || functionName == "constantMulDiv"||functionName=="sin" ) + || functionName == "fixdiv" || functionName == "constantMulDiv"||functionName=="sinf" ) // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) { llvm::errs() << "Skipping function: " << functionName << "\n"; From 728ee98b027ece52ff06fb53330989a230eb16d2 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 5 Aug 2024 15:52:53 +0100 Subject: [PATCH 051/213] fix more constant issue * dev2. --- ...3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 769 +++++++++++++----- 2 files changed, 609 insertions(+), 208 deletions(-) create mode 100644 analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt diff --git a/analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt b/analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt new file mode 100644 index 000000000..bb5bd57e2 --- /dev/null +++ b/analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt @@ -0,0 +1,48 @@ + +changeset: 1644:8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5 +char kNewtonVersion[] = "0.3-alpha-1644 (8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5) (build 08-04-2024-23:16-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 1ab1915ff..417324466 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -46,6 +46,28 @@ isValidFloatingType(Type * type) return type->isFloatTy() || type->isDoubleTy(); } +bool +isMatrixOperation(Instruction * instr) +{ + // 检查指令的操作数是否为多维数组或矩阵类型 + // 这里假设多维数组使用特定的类型或有特定的标记 + for (unsigned i = 0; i < instr->getNumOperands(); ++i) + { + Value * operand = instr->getOperand(i); + if (operand->getType()->isPointerTy()) + { + // 检查指针指向的类型是否为多维数组类型 + Type * elementType = operand->getType()->getPointerElementType(); + if (elementType->isArrayTy()) + { + // 进一步检查数组的维度或特定特征 + return true; // 假设这是一个矩阵操作 + } + } + } + return false; // 如果没有匹配到矩阵操作的特征 +} + // Set the quantized type for a given value void setQuantizedType(Value * inValue, Type * quantizedType) @@ -70,7 +92,7 @@ setQuantizedType(Value * inValue, Type * quantizedType) valueType = valueType->getPointerElementType(); } - //Skip integer and struct types + // Skip integer and struct types if (valueType->isIntegerTy() || valueType->isStructTy()) { llvm::errs() << "Skipping quantization for type: " << *valueType << "\n"; @@ -286,7 +308,7 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" ||functionName == "printf") - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt"||functionName =="sinf" ||functionName == "sqrtf" ) + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "llvm.memcpy.p0i8.p0i8.i64" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" || functionName == "sinf" || functionName == "sqrtf") { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; @@ -307,7 +329,6 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) processedFunctions.insert(functionName); - llvm::errs() << "Original function name: " << llvmIrFunction.getName() << "\n"; for (auto & arg : llvmIrFunction.args()) { @@ -327,7 +348,6 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) Function * newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); - llvm::errs() << "New function name: " << newFunc->getName() << "\n"; for (auto & arg : newFunc->args()) { @@ -343,58 +363,58 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; } +// Helper function to recursively transform types +Type * +transformToQuantizedType(Type * originalType, Type * quantizedType) +{ + if (originalType->isArrayTy()) + { + auto elementType = originalType->getArrayElementType(); + auto newElementType = transformToQuantizedType(elementType, quantizedType); + return ArrayType::get(newElementType, originalType->getArrayNumElements()); + } + else if (originalType->isFloatTy() || originalType->isDoubleTy()) + { + return quantizedType; + } + // Return original type if no conversion is necessary + return originalType; +} + void handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { - /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sqrt(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ - IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); // Cast the instruction to CallInst to access getCalledFunction method - CallInst *callInst = dyn_cast(llvmIrCallInstruction); - if (!callInst) { + CallInst * callInst = dyn_cast(llvmIrCallInstruction); + if (!callInst) + { llvm::errs() << "Error: Instruction is not a CallInst.\n"; return; } + // Convert integer operand to float for sqrt computation + Value * newOperand = Builder.CreateSIToFP(operand, Type::getDoubleTy(llvmIrCallInstruction->getContext())); - if (operand->getType()->isIntegerTy()) + // Get the correct sqrt function with double return and argument types + Function * sqrtFunc = llvmIrCallInstruction->getModule()->getFunction("sqrt"); + if (!sqrtFunc) { - Value * newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); + FunctionType * sqrtType = FunctionType::get(Type::getDoubleTy(llvmIrCallInstruction->getContext()), {Type::getDoubleTy(llvmIrCallInstruction->getContext())}, false); + sqrtFunc = Function::Create(sqrtType, Function::ExternalLinkage, "sqrt", llvmIrCallInstruction->getModule()); } - // The sequence should be: - // 1. sitofp: Convert int to float - // 2. call sqrtf: Compute sqrt in float - // 3. fptosi: Convert float result to int - // 4. shl: Left shift the integer result for scaling - // Create call to sqrt function - Value *sqrtResult = Builder.CreateCall(callInst->getCalledFunction(), {callInst->getOperand(0)}); + // Create call to sqrt function with the double operand + Value * sqrtResult = Builder.CreateCall(sqrtFunc, {newOperand}); + // Convert the result back to integer - Value *fptosiInst = Builder.CreateFPToSI(sqrtResult, quantizedType); + Value * fptosiInst = Builder.CreateFPToSI(sqrtResult, quantizedType); // Perform left shift for scaling - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value * resInst = nullptr; + Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value * resInst = nullptr; - /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ // If FRAC_Q is odd, apply compensation if (FRAC_Q % 2) { @@ -410,59 +430,57 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) } llvmIrCallInstruction->replaceAllUsesWith(resInst); - //ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); llvmIrCallInstruction->eraseFromParent(); } } - -void handleSinCall(CallInst *llvmIrCallInstruction, Type *quantizedType) +void +handleSinCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { - // Debugging output llvm::errs() << "Entering handleSinCall\n"; /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sin(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sin(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ + * if the arg's type is int, convert to fp, + * after the call node, convert to int and shl FRAC_Q/2 + * + * int32_t res = (int32_t)sin(x)<<(FRAC_Q/2); + * if (FRAC_Q%2) + * return res*1.414213562; + * else + * return res; + * + * %25 = sitofp i32 %0 to double + * %26 = call double @sin(double %25) #3 + * %27 = fptosi double %26 to i32 + * %28 = shl i32 %27, 4 + * */ IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); + auto operand = llvmIrCallInstruction->getOperand(0); llvm::errs() << "Operand type: " << *operand->getType() << "\n"; if (operand->getType()->isIntegerTy()) { - Value *newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); + Value * newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); llvmIrCallInstruction->setOperand(0, newOperand); } auto cloneInst = llvmIrCallInstruction->clone(); llvm::errs() << "Cloned instruction: " << *cloneInst << "\n"; - Value *fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); - Value *shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value *resInst = nullptr; + Value * fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); + Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); + Value * resInst = nullptr; /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ + * if (FRAC_Q%2) then multiply with 1.414213562; + * */ if (FRAC_Q % 2) { - Value *lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); - Value *mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); + Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); + auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); + Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); + resInst = Builder.CreateFPToSI(mulInst, quantizedType); llvm::errs() << "Compensation applied\n"; } else @@ -475,6 +493,41 @@ void handleSinCall(CallInst *llvmIrCallInstruction, Type *quantizedType) llvm::errs() << "Exiting handleSinCall\n"; } +void +handleMemCpyCall(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto callInst = dyn_cast(llvmIrInstruction)) + { + Function * calledFunction = callInst->getCalledFunction(); + if (calledFunction && calledFunction->getName().startswith("llvm.memcpy")) + { + IRBuilder<> Builder(callInst); + + // 获取原始操作数和它们的类型 + Value * dest = callInst->getArgOperand(0); + Value * src = callInst->getArgOperand(1); + Value * size = callInst->getArgOperand(2); + + // 更新源和目标指针的类型 + Type * newDestType = transformToQuantizedType(dest->getType()->getPointerElementType(), quantizedType)->getPointerTo(); + Type * newSrcType = transformToQuantizedType(src->getType()->getPointerElementType(), quantizedType)->getPointerTo(); + + Value * newDest = Builder.CreateBitCast(dest, newDestType); + Value * newSrc = Builder.CreateBitCast(src, newSrcType); + + // 创建新的量化后的 memcpy 调用 + Function * memcpyFunc = Intrinsic::getDeclaration( + callInst->getModule(), + Intrinsic::memcpy, + {newDestType, newSrcType, Builder.getInt64Ty()}); + Builder.CreateCall(memcpyFunc, {newDest, newSrc, size, callInst->getArgOperand(3)}); + + // 删除旧的调用 + callInst->eraseFromParent(); + } + } +} + // A list of global variables to erase after processing std::vector globalsToErase; @@ -1195,43 +1248,21 @@ dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * qu llvmIrCallInstruction->eraseFromParent(); } -void -handleFAdd(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FSub\n"; - IRBuilder<> Builder(inInstruction); - - // quantizeConstant(inInstruction, quantizedType); - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - - Value * newInst = Builder.CreateAdd(op0, op1); - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FSub\n"; -} - -void -handleFSub(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FSub\n"; - IRBuilder<> Builder(inInstruction); - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - - // if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - // { - // op0 = Builder.CreateFPToSI(op0, quantizedType); - // } - // if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - // { - // op1 = Builder.CreateFPToSI(op1, quantizedType); - // } - Value * newInst = Builder.CreateSub(op0, op1); - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FSub\n"; -} +//void +//handleFAdd(Instruction * inInstruction, Type * quantizedType) +//{ +// llvm::errs() << "Handling FSub\n"; +// IRBuilder<> Builder(inInstruction); +// +// // quantizeConstant(inInstruction, quantizedType); +// Value * op0 = inInstruction->getOperand(0); +// Value * op1 = inInstruction->getOperand(1); +// +// Value * newInst = Builder.CreateAdd(op0, op1); +// inInstruction->replaceAllUsesWith(newInst); +// inInstruction->eraseFromParent(); +// llvm::errs() << "Finished handling FSub\n"; +//} float checkDecimal(float decimalNum) @@ -1269,7 +1300,7 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, nonConstOperand = inInstruction->getOperand(0); } - //uto quantizeNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + // uto quantizeNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); llvm::errs() << "Compensate Num: " << compensateNum << "\n"; @@ -1325,8 +1356,6 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, } } - - void handleConstant(Instruction * inInstruction, Type * quantizedType) { @@ -1352,7 +1381,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) { // If the decimal part is already an integer, quantize directly auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); - auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); + auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; inInstruction->setOperand(idx, quantizedConst); } @@ -1387,6 +1416,92 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) } llvm::errs() << "Exiting handleConstant\n"; } +void handleFAdd(Instruction *inInstruction, Type *quantizedType) { + llvm::errs() << "Handling FAdd\n"; + IRBuilder<> Builder(inInstruction); + + Value *op0 = inInstruction->getOperand(0); + Value *op1 = inInstruction->getOperand(1); + + // Dealing with floating point constants + handleConstant(inInstruction, quantizedType); + + + // Create integer addition + Value *newInst = Builder.CreateAdd(op0, op1); + + // Replace the original FAdd instruction + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FAdd\n"; +} + + +void handleFSub(Instruction *inInstruction, Type *quantizedType) { + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + Value *op0 = inInstruction->getOperand(0); + Value *op1 = inInstruction->getOperand(1); + + // Dealing with floating point constants + handleConstant(inInstruction, quantizedType); + + // Create integer subtraction + Value *newInst = Builder.CreateSub(op0, op1); + + // Replace the original FSub instruction + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FSub\n"; +} + + + + +void +handleMatrixOperations(Instruction * instr, Type * quantizedType) +{ + if (instr->getOpcode() == Instruction::FAdd || instr->getOpcode() == Instruction::FMul) + { + llvm::errs() << "Handling matrix operation: " << *instr << "\n"; + IRBuilder<> Builder(instr); + + Value * lhs = instr->getOperand(0); + Value * rhs = instr->getOperand(1); + + if (lhs->getType()->isPointerTy() && rhs->getType()->isPointerTy()) + { + auto * lhsElemType = lhs->getType()->getPointerElementType(); + auto * rhsElemType = rhs->getType()->getPointerElementType(); + + if ((lhsElemType->isArrayTy() && lhsElemType->getArrayElementType()->isFloatTy()) || + (rhsElemType->isArrayTy() && rhsElemType->getArrayElementType()->isFloatTy())) + { + llvm::errs() << "Quantizing matrix elements\n"; + + // 替换矩阵元素操作为整数操作 + for (unsigned i = 0; i < lhsElemType->getArrayNumElements(); ++i) + { + Value * lhsElem = Builder.CreateLoad(lhsElemType->getArrayElementType(), Builder.CreateGEP(lhsElemType, lhs, {Builder.getInt32(0), Builder.getInt32(i)})); + Value * rhsElem = Builder.CreateLoad(rhsElemType->getArrayElementType(), Builder.CreateGEP(rhsElemType, rhs, {Builder.getInt32(0), Builder.getInt32(i)})); + + if (instr->getOpcode() == Instruction::FAdd) + { + Value * resultElem = Builder.CreateAdd(lhsElem, rhsElem); + Builder.CreateStore(resultElem, Builder.CreateGEP(quantizedType->getArrayElementType(), lhs, {Builder.getInt32(0), Builder.getInt32(i)})); + } + else if (instr->getOpcode() == Instruction::FMul) + { + Value * resultElem = Builder.CreateMul(lhsElem, rhsElem); + Builder.CreateStore(resultElem, Builder.CreateGEP(quantizedType->getArrayElementType(), lhs, {Builder.getInt32(0), Builder.getInt32(i)})); + } + } + + instr->eraseFromParent(); + } + } + } +} void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) @@ -1551,6 +1666,7 @@ handleFCmp(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FCmp\n"; } } + void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1558,71 +1674,125 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - // Check if the alloca instruction is of type float or double - if (allocaType->isFloatTy() || allocaType->isDoubleTy()) - { - llvm::errs() << "Original alloca type: " << *allocaType << "\n"; - llvm::errs() << "New quantized alloca type: " << *quantizedType << "\n"; + // 递归转换所有层级的类型为量化类型 + Type * newAllocaType = transformToQuantizedType(allocaType, quantizedType); - // Set the new quantized type for the alloca instruction - llvmIrAllocaInstruction->setAllocatedType(quantizedType); - } + llvm::errs() << "Original alloca type: " << *allocaType << "\n"; + llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; + + // Set the new quantized type for the alloca instruction + llvmIrAllocaInstruction->setAllocatedType(newAllocaType); } } -void handleCall(CallInst *llvmIrCallInstruction, Type *quantizedType) { +// void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { +// if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) { +// // 获取原始的分配类型 +// Type * allocaType = llvmIrAllocaInstruction->getAllocatedType(); +// +// // 输出原始类型和新量化类型的调试信息 +// llvm::errs() << "Original alloca type: " << *allocaType << "\n"; +// llvm::errs() << "New quantized alloca type: " << *quantizedType << "\n"; +// +// Type * newAllocaType; +// +// // 递归转换所有层级的类型为量化类型 +// if (allocaType->isFloatTy()) { +// // 如果分配类型是浮点类型,转换为量化的整数类型 +// newAllocaType = Type::getInt32Ty(llvmIrInstruction->getContext()); +// } else if (allocaType->isPointerTy()) { +// // 如果分配类型是指针类型,保持其原有分配 +// newAllocaType = allocaType; +// } else { +// // 其他类型,保持不变 +// newAllocaType = allocaType; +// } +// +// // 设置新的量化类型 +// llvmIrAllocaInstruction->setAllocatedType(newAllocaType); +// } +// } - Function *calledFunction = llvmIrCallInstruction->getCalledFunction(); +void +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +{ + Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) return; if (!calledFunction->getName().startswith("llvm.dbg.value") && !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) { - - if (calledFunction->isDeclaration()) { + !calledFunction->getName().startswith("llvm.dbg.label")) + { + if (calledFunction->isDeclaration()) + { // For library functions - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction *insertPoint = llvmIrCallInstruction->getNextNode(); + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value *newInst = nullptr; + Value * newInst = nullptr; + + std::string funcName = calledFunction->getName().str(); - if (calledFunction->getName().str() == "sqrt") { + if (funcName == "sqrt" || funcName == "sqrtf") + { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType); + } - - } else if (calledFunction->getName().str() == "sqrtf") { - // For sqrtf - handleSqrtCall(llvmIrCallInstruction, quantizedType); - } else if (calledFunction->getName().str() == "sin") { + else if (funcName == "sin") + { // For sin handleSinCall(llvmIrCallInstruction, quantizedType); - } else { + } + + else if (funcName == "llvm.memcpy.p0i8.p0i8.i64" || funcName == "llvm.memcpy.p0i8.p0i8.i32") + { + // For llvm.memcpy + handleMemCpyCall(llvmIrCallInstruction, quantizedType); + } + else + { /* - * for other lib functions, de-quantize the arguments and quantize the return value + * for other lib functions, de-quantize the arguments and quantize the return value */ dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); + } + } + else + { + // For user-defined functions, only handle the arguments + for (size_t idx = 0; idx < llvmIrCallInstruction->getNumArgOperands(); ++idx) + { + Value * arg = llvmIrCallInstruction->getArgOperand(idx); + if (arg->getType()->isPointerTy()) + { + Type * elementType = arg->getType()->getPointerElementType(); + if (elementType->isFloatTy() || elementType->isDoubleTy()) + { + // Do not change pointer type, handle it when dereferenced + continue; + } + } + // Set quantized type for the argument if it is not a pointer + setQuantizedType(arg, quantizedType); + } + // Quantize the return type if necessary + if (llvmIrCallInstruction->getType()->isPointerTy()) + { + Type * returnElementType = llvmIrCallInstruction->getType()->getPointerElementType(); + if (returnElementType->isFloatTy() || returnElementType->isDoubleTy()) + { + // Keep the pointer type, but track the need for de-quantization + return; + } } - } else { - /* - * for user-defined function, quantize the arguments - */ - // Uncomment and adapt the following lines if needed - for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); - /* - * then quantize the return type - */ - setQuantizedType(llvmIrCallInstruction, quantizedType); + setQuantizedType(llvmIrCallInstruction, quantizedType); } } } - void handleStore(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1636,7 +1806,7 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) llvm::errs() << "Original store value type: " << *valueType << "\n"; llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; - // 转换存储的值为量化后的类型 + // Quantize the value operand auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); llvmIrStoreInstruction->setOperand(0, quantizedValue); @@ -1654,56 +1824,179 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } +// Function to handle loads involving pointer types +void +handlePointerLoad(LoadInst * llvmIrLoadInstruction, IRBuilder<> & Builder, Type * quantizedType) +{ + auto pointerOperand = llvmIrLoadInstruction->getPointerOperand(); + auto pointerType = pointerOperand->getType()->getPointerElementType(); + if (pointerType->isPointerTy()) + { + auto pointedType = pointerType->getPointerElementType(); + if (pointedType->isPointerTy()) + { + auto innerPointedType = pointedType->getPointerElementType(); + if (innerPointedType->isFloatTy() || innerPointedType->isDoubleTy()) + { + // Update pointer type for double** or float** to quantizedType** (e.g., int32_t**) + auto quantizedInnerPointerType = quantizedType->getPointerTo(); + auto quantizedPointerType = PointerType::get(quantizedInnerPointerType, pointerType->getPointerAddressSpace()); + pointerOperand->mutateType(quantizedPointerType); -void handleLoad(Instruction *llvmIrInstruction, Type *quantizedType) { - if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) { - IRBuilder<> Builder(llvmIrLoadInstruction); + // Load the inner pointer value + auto loadedPointerValue = Builder.CreateLoad(quantizedPointerType, pointerOperand); - auto pointerType = llvmIrLoadInstruction->getPointerOperand()->getType()->getPointerElementType(); + // Handle uses of the loaded pointer value + for (auto & use : llvmIrLoadInstruction->uses()) + { + auto user = use.getUser(); + if (auto storeInst = dyn_cast(user)) + { + if (storeInst->getValueOperand() == llvmIrLoadInstruction) + { + // If storing to a float or double, convert the loaded pointer value + auto storeType = storeInst->getPointerOperand()->getType()->getPointerElementType(); + if (storeType->isFloatTy()) + { + auto floatValue = Builder.CreateSIToFP(loadedPointerValue, Type::getFloatTy(llvmIrLoadInstruction->getContext())); + storeInst->setOperand(0, floatValue); + } + else if (storeType->isDoubleTy()) + { + auto doubleValue = Builder.CreateSIToFP(loadedPointerValue, Type::getDoubleTy(llvmIrLoadInstruction->getContext())); + storeInst->setOperand(0, doubleValue); + } + else + { + storeInst->setOperand(0, loadedPointerValue); + } + } + } + } - // Print the pointer type of the load instruction - llvm::errs() << "Pointer type of load instruction: " << *pointerType << "\n"; + // Replace all uses of the original load instruction with the new loaded pointer value + llvmIrLoadInstruction->replaceAllUsesWith(loadedPointerValue); + llvmIrLoadInstruction->eraseFromParent(); + return; + } + } + } +} + +void +handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) + { + IRBuilder<> Builder(llvmIrLoadInstruction); + auto pointerOperand = llvmIrLoadInstruction->getPointerOperand(); + auto pointerType = pointerOperand->getType()->getPointerElementType(); - //if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { + // Print debug information llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; - // 更新指针操作数的类型为量化类型 - llvmIrLoadInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + // Handle pointer to pointer (e.g., double**) + if (pointerType->isPointerTy()) + { + handlePointerLoad(llvmIrLoadInstruction, Builder, quantizedType); + return; + } - // 以整数形式加载值 + // Handle i64 type separately + if (pointerType->isIntegerTy(64)) + { + // For i64* types, do not modify or handle this load instruction + llvm::errs() << "Skipping i64 load instruction: " << *llvmIrLoadInstruction << "\n"; + return; // Skip further processing for i64 types + } + + // Update pointer operand's type if necessary + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + { + pointerOperand->mutateType(quantizedType->getPointerTo()); + } + + // Load the value using the quantized type auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); - // 检查后续使用并确保类型匹配 - for (auto &use : llvmIrLoadInstruction->uses()) { + // Handle uses of the loaded value + for (auto & use : llvmIrLoadInstruction->uses()) + { auto user = use.getUser(); - if (auto storeInst = dyn_cast(user)) { - // 在存储指令中,如果值被存储为浮点型,则进行适当转换 - if (storeInst->getValueOperand() == llvmIrLoadInstruction) { - auto floatValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); - storeInst->setOperand(0, floatValue); + if (auto storeInst = dyn_cast(user)) + { + if (storeInst->getValueOperand() == llvmIrLoadInstruction) + { + // If storing to a float or double, convert the loaded i64 to float/double + auto storeType = storeInst->getPointerOperand()->getType()->getPointerElementType(); + if (storeType->isFloatTy()) + { + auto floatValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); + storeInst->setOperand(0, floatValue); + } + else if (storeType->isDoubleTy()) + { + auto doubleValue = Builder.CreateSIToFP(loadedValue, Type::getDoubleTy(llvmIrInstruction->getContext())); + storeInst->setOperand(0, doubleValue); + } + else + { + storeInst->setOperand(0, loadedValue); + } } } + else + { + llvm::errs() << "Pointer type " << *pointerType << " is not supported for quantization.\n"; + } } - //replace all uses of the original load instruction with the loaded value + // Replace all uses of the original load instruction with the new loaded value llvmIrLoadInstruction->replaceAllUsesWith(loadedValue); - - // erase the original load instruction llvmIrLoadInstruction->eraseFromParent(); - // } else { - // llvm::errs() << "Pointer type is not float or double, skipping load handling.\n"; - // } } } +void +handleGetElementPtr(GetElementPtrInst * gepInst, Type * quantizedType) +{ + llvm::errs() << "Handling GetElementPtr instruction: " << *gepInst << "\n"; + IRBuilder<> Builder(gepInst); + auto sourceElementType = gepInst->getSourceElementType(); + auto resultType = gepInst->getResultElementType(); + if (sourceElementType->isArrayTy()) + { + Type * elementType = sourceElementType->getArrayElementType(); + if (elementType->isFloatTy() || elementType->isDoubleTy()) + { + llvm::errs() << "Quantizing element type: " << *elementType << "\n"; + // 量化后的元素类型 + Type * newElementType = quantizedType; + // 更新GEP指令的源和结果类型 + Type * newSourceType = ArrayType::get(newElementType, sourceElementType->getArrayNumElements()); + gepInst->setSourceElementType(newSourceType); + gepInst->setResultElementType(newElementType); + + // 更新索引操作数类型 + for (unsigned i = 0; i < gepInst->getNumIndices(); ++i) + { + Value * index = gepInst->getOperand(i + 1); + if (index->getType()->isIntegerTy() && index->getType()->getIntegerBitWidth() != quantizedType->getIntegerBitWidth()) + { + gepInst->setOperand(i + 1, Builder.CreateIntCast(index, quantizedType, true)); + } + } + } + } +} void handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) @@ -1748,6 +2041,47 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) } } +void +handleBitCast(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto bitcastInst = dyn_cast(llvmIrInstruction)) + { + IRBuilder<> Builder(bitcastInst); + + // 获取源类型和目标类型 + auto srcType = bitcastInst->getSrcTy(); + auto destType = bitcastInst->getDestTy(); + + // 转换源类型 + if (srcType->isPointerTy()) + { + auto originalElementType = srcType->getPointerElementType(); + auto newElementType = transformToQuantizedType(originalElementType, quantizedType); + srcType = newElementType->getPointerTo(); + } + + // 如果目标类型是浮点类型的指针,则转换为量化类型 + if (destType->isPointerTy() && destType->getPointerElementType()->isFloatTy()) + { + destType = quantizedType->getPointerTo(); + } + + // 输出日志信息 + llvm::errs() << "Original srcType: " << *bitcastInst->getSrcTy() << "\n"; + llvm::errs() << "Original destType: " << *bitcastInst->getDestTy() << "\n"; + llvm::errs() << "Transformed srcType: " << *srcType << "\n"; + llvm::errs() << "Transformed destType: " << *destType << "\n"; + + // 创建新的BitCast指令 + Value * newInst = Builder.CreateBitCast(bitcastInst->getOperand(0), destType); + bitcastInst->replaceAllUsesWith(newInst); + bitcastInst->eraseFromParent(); + + llvm::errs() << "Created new BitCast: " << *newInst << "\n"; + llvm::errs() << "Erased original BitCast\n"; + } +} + void adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { @@ -1796,12 +2130,14 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } case Instruction::BitCast: { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = Builder.CreateBitCast(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); + // IRBuilder<> Builder(llvmIrInstruction); + // Instruction * insertPoint = llvmIrInstruction->getNextNode(); + // Builder.SetInsertPoint(insertPoint); + // Value * newInst = Builder.CreateBitCast(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // llvmIrInstruction->replaceAllUsesWith(newInst); + // llvmIrInstruction->removeFromParent(); + llvm::errs() << "handle bitcast\n"; + handleBitCast(llvmIrInstruction, quantizedType); break; } } @@ -1819,8 +2155,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" - || functionName == "fixdiv" || functionName == "constantMulDiv"||functionName=="sinf" ) + if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "fixdiv" || functionName == "constantMulDiv" || functionName == "sinf") // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) { llvm::errs() << "Skipping function: " << functionName << "\n"; @@ -1873,8 +2208,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * generate hardcode function - fixmul and fixdiv * */ - // 先创建 fixmul 和 fixdiv 函数 - llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); @@ -1940,30 +2273,31 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleCall(cast(llvmIrInstruction), quantizedType); break; case Instruction::GetElementPtr: - if (auto gepInst = dyn_cast(llvmIrInstruction)) - { - auto gepType = gepInst->getType(); - auto sourceType = quantizedType; - // bool isPointer = false; - // unsigned pointerAddr = 0; - // if (gepType->isPointerTy()) { - // isPointer = true; - // pointerAddr = gepType->getPointerAddressSpace(); - // valueType = gepType->getPointerElementType(); - // } - if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) - { - sourceType = ArrayType::get(quantizedType, - gepInst->getSourceElementType()->getArrayNumElements()); - } - // if (isPointer) { - // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - // } - // if (gepType->isDoubleTy() || gepType->isFloatTy()) { - gepInst->setSourceElementType(sourceType); - gepInst->setResultElementType(quantizedType); - // } - } + // if (auto gepInst = dyn_cast(llvmIrInstruction)) + // { + // auto gepType = gepInst->getType(); + // auto sourceType = quantizedType; + // // bool isPointer = false; + // // unsigned pointerAddr = 0; + // // if (gepType->isPointerTy()) { + // // isPointer = true; + // // pointerAddr = gepType->getPointerAddressSpace(); + // // valueType = gepType->getPointerElementType(); + // // } + // if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) + // { + // sourceType = ArrayType::get(quantizedType, + // gepInst->getSourceElementType()->getArrayNumElements()); + // } + // // if (isPointer) { + // // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + // // } + // // if (gepType->isDoubleTy() || gepType->isFloatTy()) { + // gepInst->setSourceElementType(sourceType); + // gepInst->setResultElementType(quantizedType); + // // } + // } + handleGetElementPtr(cast(llvmIrInstruction), quantizedType); break; case Instruction::Load: llvm::errs() << "Handling Load instruction: " << *llvmIrInstruction << "\n"; @@ -1993,8 +2327,16 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * else substitute this instruction to a pre-implemented function: mulfix/fixdiv. * */ case Instruction::FMul: - llvm::errs() << "Found FMul instruction.\n"; - handleFMul(llvmIrInstruction, quantizedType, fixmul); + if (isMatrixOperation(llvmIrInstruction)) + { + llvm::errs() << "Found matrix multiplication operation.\n"; + handleMatrixOperations(llvmIrInstruction, quantizedType); + } + else + { + llvm::errs() << "Found FMul instruction.\n"; + handleFMul(llvmIrInstruction, quantizedType, fixmul); + } break; case Instruction::FDiv: @@ -2012,7 +2354,16 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleFCmp(llvmIrInstruction, quantizedType); break; case Instruction::FAdd: - handleFAdd(llvmIrInstruction, quantizedType); + if (isMatrixOperation(llvmIrInstruction)) + { + llvm::errs() << "Found matrix addition operation.\n"; + handleMatrixOperations(llvmIrInstruction, quantizedType); + } + else + { + llvm::errs() << "Found FAdd instruction.\n"; + handleFAdd(llvmIrInstruction, quantizedType); + } break; case Instruction::FSub: handleFSub(llvmIrInstruction, quantizedType); @@ -2088,6 +2439,9 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FPExt: case Instruction::FPTrunc: case Instruction::BitCast: + // log + llvm::errs() << "handle bitcast\n"; + handleBitCast(llvmIrInstruction, quantizedType); break; case Instruction::Ret: @@ -2129,6 +2483,5 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); - return; } From 03212c7b5b21a987cda580414026d64e97a3ebf5 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 5 Aug 2024 21:15:44 +0100 Subject: [PATCH 052/213] fix void signature issue * dev2. --- ...43b79ea3957b991e9baf2ad059f6488e687ea1.txt | 48 +++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 102 +++++------------- 2 files changed, 74 insertions(+), 76 deletions(-) create mode 100644 analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt diff --git a/analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt b/analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt new file mode 100644 index 000000000..6078e1019 --- /dev/null +++ b/analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt @@ -0,0 +1,48 @@ + +changeset: 1645:1e43b79ea3957b991e9baf2ad059f6488e687ea1 +char kNewtonVersion[] = "0.3-alpha-1645 (1e43b79ea3957b991e9baf2ad059f6488e687ea1) (build 08-05-2024-15:52-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 417324466..a963daeff 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -68,6 +68,10 @@ isMatrixOperation(Instruction * instr) return false; // 如果没有匹配到矩阵操作的特征 } +bool isMathFunction(const std::string &funcName) { + return funcName == "sqrt" || funcName == "sqrtf" || funcName == "sin"; +} + // Set the quantized type for a given value void setQuantizedType(Value * inValue, Type * quantizedType) @@ -128,67 +132,7 @@ setQuantizedType(Value * inValue, Type * quantizedType) // Quantize constants within an instruction std::unordered_map quantizedValueCache; -// Ensure load and store instructions have matching types -// void -// handleLoadStoreInstructions(Function & llvmIrFunction, Type * quantizedType) -//{ -// llvm::errs() << "Entering handleLoadStoreInstructions\n"; -// for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) -// { -// for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) -// { -// Instruction * llvmIrInstruction = &*itBB++; -// if (llvmIrInstruction->getOpcode() == Instruction::Load) -// { -// if (auto loadInst = dyn_cast(llvmIrInstruction)) -// { -// auto ptr = loadInst->getPointerOperand(); -// if (ptr->getType()->getPointerElementType()->isFloatTy() || -// ptr->getType()->getPointerElementType()->isDoubleTy()) -// { -// // 如果是全局变量,确保它的类型已经被转换 -// if (auto globalVar = dyn_cast(ptr)) -// { -// if (globalVar->getType()->getElementType()->isFloatTy() || -// globalVar->getType()->getElementType()->isDoubleTy()) -// { -// globalVar->mutateType(quantizedType->getPointerTo()); -// } -// } -// else -// { -// ptr->mutateType(quantizedType->getPointerTo()); -// } -// } -// } -// } -// else if (llvmIrInstruction->getOpcode() == Instruction::Store) -// { -// if (auto storeInst = dyn_cast(llvmIrInstruction)) -// { -// auto ptr = storeInst->getPointerOperand(); -// if (ptr->getType()->getPointerElementType()->isFloatTy() || -// ptr->getType()->getPointerElementType()->isDoubleTy()) -// { -// // 如果是全局变量,确保它的类型已经被转换 -// if (auto globalVar = dyn_cast(ptr)) -// { -// if (globalVar->getType()->getElementType()->isFloatTy() || -// globalVar->getType()->getElementType()->isDoubleTy()) -// { -// globalVar->mutateType(quantizedType->getPointerTo()); -// } -// } -// else -// { -// ptr->mutateType(quantizedType->getPointerTo()); -// } -// } -// } -// } -// } -// } -//} + void fixLoadStoreTypes(Function & F, Type * quantizedType) @@ -327,6 +271,12 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) return; } + // Skip if the function returns void + if (llvmIrFunction.getReturnType()->isVoidTy()) { + llvm::errs() << "Skipping function with void return type: " << functionName << "\n"; + return; + } + processedFunctions.insert(functionName); llvm::errs() << "Original function name: " << llvmIrFunction.getName() << "\n"; @@ -1248,21 +1198,6 @@ dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * qu llvmIrCallInstruction->eraseFromParent(); } -//void -//handleFAdd(Instruction * inInstruction, Type * quantizedType) -//{ -// llvm::errs() << "Handling FSub\n"; -// IRBuilder<> Builder(inInstruction); -// -// // quantizeConstant(inInstruction, quantizedType); -// Value * op0 = inInstruction->getOperand(0); -// Value * op1 = inInstruction->getOperand(1); -// -// Value * newInst = Builder.CreateAdd(op0, op1); -// inInstruction->replaceAllUsesWith(newInst); -// inInstruction->eraseFromParent(); -// llvm::errs() << "Finished handling FSub\n"; -//} float checkDecimal(float decimalNum) @@ -1503,6 +1438,21 @@ handleMatrixOperations(Instruction * instr, Type * quantizedType) } } +Value* convertToQuantizedType(Value *value, Type *quantizedType, IRBuilder<> &Builder) { + if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) { + // 如果是浮点类型,首先乘以 FRAC_BASE,然后转换为量化后的整数类型 + Value *scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), FRAC_BASE)); + return Builder.CreateFPToSI(scaledValue, quantizedType); + } else if (value->getType()->isIntegerTy()) { + // 如果已经是整数类型,则直接返回 + return value; + } else { + llvm::errs() << "Unsupported type for quantization: " << *value->getType() << "\n"; + return nullptr; + } +} + + void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) { From 27d764c13c3991109adcd534a7a8a5fe25e8154d Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 9 Aug 2024 22:22:40 +0100 Subject: [PATCH 053/213] fix some add and sub constant issue * dev2. --- ...8ee98b027ece52ff06fb53330989a230eb16d2.txt | 48 +++ .../newton/llvm-ir/c-files/MadgwickAHRS.cpp | 312 ----------------- .../newton-irPass-LLVMIR-quantization.cpp | 320 +++++++++++++----- 3 files changed, 291 insertions(+), 389 deletions(-) create mode 100644 analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt delete mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp diff --git a/analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt b/analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt new file mode 100644 index 000000000..b37c076b7 --- /dev/null +++ b/analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt @@ -0,0 +1,48 @@ + +changeset: 1646:728ee98b027ece52ff06fb53330989a230eb16d2 +char kNewtonVersion[] = "0.3-alpha-1646 (728ee98b027ece52ff06fb53330989a230eb16d2) (build 08-05-2024-21:15-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp b/applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp deleted file mode 100644 index 9fd6924bc..000000000 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.cpp +++ /dev/null @@ -1,312 +0,0 @@ -//===================================================================================================== -// MadgwickAHRS.c -//===================================================================================================== -// -// Implementation of Madgwick's IMU and AHRS algorithms. -// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms -// -// Date Author Notes -// 29/09/2011 SOH Madgwick Initial release -// 02/10/2011 SOH Madgwick Optimised for reduced CPU load -// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised -// -//===================================================================================================== - -//--------------------------------------------------------------------------------------------------- -// Header files - -#include "MadgwickAHRS.h" -#include // for printf - -//--------------------------------------------------------------------------------------------------- -// Definitions - -// #define sampleFreq 512.0f // sample frequency in Hz -// #define sampleFreq 100.0f // sample frequency in Hz -#define sampleFreq 28.0f // sample frequency in Hz -#define betaDef 0.1f // 2 * proportional gain - -#ifndef lowerBound -#define lowerBound -16 -#endif -#ifndef upperBound -#define upperBound 16 -#endif - -//--------------------------------------------------------------------------------------------------- -// Variable definitions - -volatile float beta = betaDef; // 2 * proportional gain (Kp) // 2 * proportional gain (Kp) -//volatile float q0 = 0.64306622f, q1 = 0.02828862f, q2 = -0.00567953f, q3 = -0.76526684f; // quaternion of sensor frame relative to auxiliary frame - -//--------------------------------------------------------------------------------------------------- -// Function declarations - -//--------------------------------------------------------------------------------------------------- -// Fast inverse square-root -// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root - -// 1/sqrtf(); -float invSqrt(float x) { - float halfx = 0.5f * x; - float y = x; -//#pragma unsupported - long i = *(long*)&y; - i = 0x5f3759df - (i>>1); - y = *(float*)&i; -//#end - y = y * (1.5f - (halfx * y * y)); - return y; -} - - - - - - -//==================================================================================================== -// Functions - -//--------------------------------------------------------------------------------------------------- -// AHRS algorithm update - -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { - float q0 = *q0_ptr; - float q1 = *q1_ptr; - float q2 = *q2_ptr; - float q3 = *q3_ptr; - - - -#ifdef ASSUME - __builtin_assume(ax > lowerBound && ax < upperBound); - __builtin_assume(ay > lowerBound && ay < upperBound); - __builtin_assume(az > lowerBound && az < upperBound); -#endif - float recipNorm; - float s0, s1, s2, s3; - float qDot1, qDot2, qDot3, qDot4; - float hx, hy; - float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; - - // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) - if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); - return; - } - - // Rate of change of quaternion from gyroscope - qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); - qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); - qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); - qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); - - - - - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); -// printf("1: %f\n", recipNorm); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; - - - // Normalise magnetometer measurement - recipNorm = invSqrt(mx * mx + my * my + mz * mz); - mx *= recipNorm; - my *= recipNorm; - mz *= recipNorm; - - - //printf("Normalized magnetometer: mx=%f, my=%f, mz=%f\n", mx, my, mz); - - - - // Auxiliary variables to avoid repeated arithmetic - _2q0mx = 2.0f * q0 * mx; - _2q0my = 2.0f * q0 * my; - _2q0mz = 2.0f * q0 * mz; - _2q1mx = 2.0f * q1 * mx; - _2q0 = 2.0f * q0; - _2q1 = 2.0f * q1; - _2q2 = 2.0f * q2; - _2q3 = 2.0f * q3; - _2q0q2 = 2.0f * q0 * q2; - _2q2q3 = 2.0f * q2 * q3; - q0q0 = q0 * q0; - q0q1 = q0 * q1; - q0q2 = q0 * q2; - q0q3 = q0 * q3; - q1q1 = q1 * q1; - q1q2 = q1 * q2; - q1q3 = q1 * q3; - q2q2 = q2 * q2; - q2q3 = q2 * q3; - q3q3 = q3 * q3; - - // Reference direction of Earth's magnetic field - hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; - hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; - _2bx = sqrt(hx * hx + hy * hy); - _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; - _4bx = 2.0f * _2bx; - _4bz = 2.0f * _2bz; - - //printf("hx=%f, hy=%f, _2bx=%f, _2bz=%f, _4bx=%f, _4bz=%f\n", hx, hy, _2bx, _2bz, _4bx, _4bz); - - // Gradient decent algorithm corrective step - s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude - s0 *= recipNorm; - s1 *= recipNorm; - s2 *= recipNorm; - s3 *= recipNorm; - - //printf("s values: s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); - - // Apply feedback step - qDot1 -= beta * s0; - qDot2 -= beta * s1; - qDot3 -= beta * s2; - qDot4 -= beta * s3; - } - - // Integrate rate of change of quaternion to yield quaternion - q0 += qDot1 * (1.0f / sampleFreq); - q1 += qDot2 * (1.0f / sampleFreq); - q2 += qDot3 * (1.0f / sampleFreq); - q3 += qDot4 * (1.0f / sampleFreq); - - //printf("Updated quaternion (before normalization): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); - - recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - - // Normalise quaternion - recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); -// printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", -// q0, q1, q2, q3, recipNorm); - - - q0 *= recipNorm; - q1 *= recipNorm; - q2 *= recipNorm; - q3 *= recipNorm; - - //printf("Normalized quaternion: q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); - - - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; - -// printf("Original: q0 = %f\n", q0); -} - -//--------------------------------------------------------------------------------------------------- -// IMU algorithm update - -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { - float q0 = *q0_ptr; - float q1 = *q1_ptr; - float q2 = *q2_ptr; - float q3 = *q3_ptr; - - //printf("Initial quaternion (IMU): q0=%f, q1=%f, q2=%f, q3=%f\n", q0, q1, q2, q3); - - - float recipNorm; - float s0, s1, s2, s3; - float qDot1, qDot2, qDot3, qDot4; - float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; - - // Rate of change of quaternion from gyroscope - qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); - qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); - qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); - qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); - - //printf("qDot values (IMU): qDot1=%f, qDot2=%f, qDot3=%f, qDot4=%f\n", qDot1, qDot2, qDot3, qDot4); - - - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { - - // Normalise accelerometer measurement - recipNorm = invSqrt(ax * ax + ay * ay + az * az); - ax *= recipNorm; - ay *= recipNorm; - az *= recipNorm; - - //printf("Normalized accelerometer (IMU): ax=%f, ay=%f, az=%f\n", ax, ay, az); - - - // Auxiliary variables to avoid repeated arithmetic - _2q0 = 2.0f * q0; - _2q1 = 2.0f * q1; - _2q2 = 2.0f * q2; - _2q3 = 2.0f * q3; - _4q0 = 4.0f * q0; - _4q1 = 4.0f * q1; - _4q2 = 4.0f * q2; - _8q1 = 8.0f * q1; - _8q2 = 8.0f * q2; - q0q0 = q0 * q0; - q1q1 = q1 * q1; - q2q2 = q2 * q2; - q3q3 = q3 * q3; - - // Gradient decent algorithm corrective step - s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; - s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; - s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; - s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; - recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude - s0 *= recipNorm; - s1 *= recipNorm; - s2 *= recipNorm; - s3 *= recipNorm; - - //printf("s values (IMU): s0=%f, s1=%f, s2=%f, s3=%f\n", s0, s1, s2, s3); - - - // Apply feedback step - qDot1 -= beta * s0; - qDot2 -= beta * s1; - qDot3 -= beta * s2; - qDot4 -= beta * s3; - } - - // Integrate rate of change of quaternion to yield quaternion - q0 += qDot1 * (1.0f / sampleFreq); - q1 += qDot2 * (1.0f / sampleFreq); - q2 += qDot3 * (1.0f / sampleFreq); - q3 += qDot4 * (1.0f / sampleFreq); - - - // Normalise quaternion - recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - q0 *= recipNorm; - q1 *= recipNorm; - q2 *= recipNorm; - q3 *= recipNorm; - - - - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; -} - -//==================================================================================================== -// END OF CODE -//==================================================================================================== diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a963daeff..268f1e74b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -68,9 +68,7 @@ isMatrixOperation(Instruction * instr) return false; // 如果没有匹配到矩阵操作的特征 } -bool isMathFunction(const std::string &funcName) { - return funcName == "sqrt" || funcName == "sqrtf" || funcName == "sin"; -} + // Set the quantized type for a given value void @@ -331,6 +329,38 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) return originalType; } +//llvm::Function *createFixPow(Module *irModule, Type *quantizedType, std::vector &functionsToInsert); + + + +//void handlePowCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto base = llvmIrCallInstruction->getArgOperand(0); +// auto exponent = llvmIrCallInstruction->getArgOperand(1); +// +// // Convert the operands to the appropriate type for fixpow (which expects quantizedType) +// Value * quantizedBase = Builder.CreateFPToSI(base, quantizedType); +// Value * quantizedExponent = Builder.CreateFPToSI(exponent, quantizedType); +// +// // Get or create the fixpow function +// Function * fixpowFunc = llvmIrCallInstruction->getModule()->getFunction("fixpow"); +// if (!fixpowFunc) +// { +// // If fixpow function doesn't exist, create it +// fixpowFunc = createFixPow(llvmIrCallInstruction->getModule(), quantizedType, functionsToInsert); +// } +// +// // Create call to fixpow with the quantized operands +// Value * fixpowResult = Builder.CreateCall(fixpowFunc, {quantizedBase, quantizedExponent}); +// +// // Replace the original pow call with the fixpow result +// llvmIrCallInstruction->replaceAllUsesWith(fixpowResult); +// llvmIrCallInstruction->eraseFromParent(); +//} + + + void handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { @@ -478,6 +508,27 @@ handleMemCpyCall(Instruction * llvmIrInstruction, Type * quantizedType) } } +void handleRsqrtCall(CallInst* llvmIrCallInstruction, Type* quantizedType, Function* fixrsqrt) { + IRBuilder<> builder(llvmIrCallInstruction); + + Value* operand = llvmIrCallInstruction->getOperand(0); + + if (operand->getType()->isIntegerTy()) { + operand = builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); + llvmIrCallInstruction->setOperand(0, operand); + } + + // Replace the rsqrt call with the custom fixrsqrt function + Value* rsqrtResult = builder.CreateCall(fixrsqrt, {llvmIrCallInstruction->getOperand(0)}); + + // Replace all uses of the original rsqrt call with the fixed-point result + llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); + + // Remove the original rsqrt call + llvmIrCallInstruction->eraseFromParent(); +} + + // A list of global variables to erase after processing std::vector globalsToErase; @@ -753,47 +804,7 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } -void -handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) -{ - llvm::errs() << "Handling FloatIntMul\n"; - llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; - IRBuilder<> Builder(llvmIrInstruction); - - // 获取操作数 - Value * lhs = llvmIrInstruction->getOperand(0); - Value * rhs = llvmIrInstruction->getOperand(1); - llvm::errs() << "LHS: " << *lhs << "\n"; - llvm::errs() << "RHS: " << *rhs << "\n"; - - // 确保左操作数是浮点数,右操作数是整数 - if (!lhs->getType()->isFloatTy() && !lhs->getType()->isDoubleTy()) - { - std::swap(lhs, rhs); - } - - if (lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy()) - { - if (rhs->getType()->isIntegerTy()) - { - llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Replaced with call to floatIntMul\n"; - } - else - { - llvm::errs() << "RHS is not an integer\n"; - } - } - else - { - llvm::errs() << "LHS is not a float\n"; - } - - llvm::errs() << "Finished handling FloatIntMul\n"; -} void simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) @@ -990,6 +1001,90 @@ createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vect return func; } +//llvm::Function* createFixPow(Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { +// llvm::errs() << "Entering createFixPow\n"; +// +// if (!irModule) { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixPowFuncName = "fixpow"; +// for (auto& function : *irModule) { +// if (function.getName() == fixPowFuncName) { +// llvm::errs() << "fixpow already exists\n"; +// return &function; +// } +// } +// +// llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); +// llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixPowFuncName, irModule); +// +// llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// builder.SetInsertPoint(entryBB); +// +// llvm::Function::arg_iterator args = func->arg_begin(); +// llvm::Value* base = &*args++; +// llvm::Value* exponent = &*args; +// +// // Initialize result as FRAC_BASE +// llvm::Value* result = builder.CreateAlloca(quantizedType, nullptr, "result"); +// builder.CreateStore(ConstantInt::get(quantizedType, FRAC_BASE), result); +// +// llvm::BasicBlock* loopBB = llvm::BasicBlock::Create(irModule->getContext(), "loop", func); +// llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(irModule->getContext(), "after", func); +// +// builder.CreateBr(loopBB); +// +// builder.SetInsertPoint(loopBB); +// llvm::PHINode* currentBase = builder.CreatePHI(quantizedType, 2, "currentBase"); +// llvm::PHINode* currentExponent = builder.CreatePHI(quantizedType, 2, "currentExponent"); +// +// currentBase->addIncoming(base, entryBB); +// currentExponent->addIncoming(exponent, entryBB); +// +// // Check if the current exponent is odd +// llvm::Value* isOdd = builder.CreateAnd(currentExponent, ConstantInt::get(quantizedType, 1)); +// llvm::Value* isOddCond = builder.CreateICmpEQ(isOdd, ConstantInt::get(quantizedType, 1)); +// +// // Create block for when exponent is odd +// llvm::BasicBlock* oddBB = llvm::BasicBlock::Create(irModule->getContext(), "odd", func); +// llvm::BasicBlock* continueBB = llvm::BasicBlock::Create(irModule->getContext(), "continue", func); +// +// builder.CreateCondBr(isOddCond, oddBB, continueBB); +// +// builder.SetInsertPoint(oddBB); +// llvm::Value* currentResult = builder.CreateLoad(quantizedType, result); +// llvm::Value* updatedResult = builder.CreateMul(currentResult, currentBase); +// updatedResult = builder.CreateAShr(updatedResult, FRAC_Q); +// builder.CreateStore(updatedResult, result); +// builder.CreateBr(continueBB); +// +// builder.SetInsertPoint(continueBB); +// llvm::Value* squaredBase = builder.CreateMul(currentBase, currentBase); +// squaredBase = builder.CreateAShr(squaredBase, FRAC_Q); +// +// llvm::Value* newExponent = builder.CreateLShr(currentExponent, 1); +// +// currentBase->addIncoming(squaredBase, continueBB); +// currentExponent->addIncoming(newExponent, continueBB); +// +// llvm::Value* endCond = builder.CreateICmpEQ(newExponent, ConstantInt::get(quantizedType, 0)); +// builder.CreateCondBr(endCond, afterBB, loopBB); +// +// builder.SetInsertPoint(afterBB); +// llvm::Value* finalResult = builder.CreateLoad(quantizedType, result); +// builder.CreateRet(finalResult); +// +// functionsToInsert.emplace_back(func); +// llvm::errs() << "Created fixpow function: " << func->getName() << "\n"; +// return func; +//} + + + + // Create a fixed-point multiplication function llvm::Function * createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1111,6 +1206,51 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vector& functionsToInsert) { +// +// // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) +// llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// // Create the function and insert it into the module +// llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); +// +// llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// +// // Get the function argument (x) +// llvm::Function::arg_iterator args = func->arg_begin(); +// llvm::Value* x = &*args++; +// +// // Create the fixed-point multiplication function +// llvm::Function* fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); +// +// // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); +// llvm::Value* halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); +// +// // Step 2: Convert x to floating-point and perform the initial approximation +// llvm::Value* fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); +// llvm::Value* i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); +// i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); +// fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); +// +// // Step 3: int_y = fp_y * FRAC_BASE; +// llvm::Value* int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); +// +// // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); +// llvm::Value* mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); +// llvm::Value* mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); +// llvm::Value* correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); +// llvm::Value* final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); +// +// +// // Return the final fixed-point result +// builder.CreateRet(final_y); +// functionsToInsert.emplace_back(func); +// +// return func; +//} + + // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -1355,44 +1495,78 @@ void handleFAdd(Instruction *inInstruction, Type *quantizedType) { llvm::errs() << "Handling FAdd\n"; IRBuilder<> Builder(inInstruction); + + Value *op0 = inInstruction->getOperand(0); Value *op1 = inInstruction->getOperand(1); - // Dealing with floating point constants - handleConstant(inInstruction, quantizedType); + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + if (ConstantFP *constFp = dyn_cast(op0)) { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + if (ConstantFP *constFp = dyn_cast(op1)) { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } - // Create integer addition + // Create fixed-point addition Value *newInst = Builder.CreateAdd(op0, op1); - // Replace the original FAdd instruction + // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FAdd\n"; } + void handleFSub(Instruction *inInstruction, Type *quantizedType) { llvm::errs() << "Handling FSub\n"; IRBuilder<> Builder(inInstruction); + + + Value *op0 = inInstruction->getOperand(0); Value *op1 = inInstruction->getOperand(1); - // Dealing with floating point constants - handleConstant(inInstruction, quantizedType); + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + if (ConstantFP *constFp = dyn_cast(op0)) { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + + if (ConstantFP *constFp = dyn_cast(op1)) { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } - // Create integer subtraction + // Create fixed-point subtraction Value *newInst = Builder.CreateSub(op0, op1); - // Replace the original FSub instruction + // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); inInstruction->eraseFromParent(); + llvm::errs() << "Finished handling FSub\n"; } + + + void handleMatrixOperations(Instruction * instr, Type * quantizedType) { @@ -1664,7 +1838,7 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) // } void -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert) { Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) @@ -1690,6 +1864,19 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType) handleSqrtCall(llvmIrCallInstruction, quantizedType); } +// else if (funcName == "pow" || funcName == "powf" || funcName == "powl") +// { +// // For pow +// handlePowCall(llvmIrCallInstruction, quantizedType, functionsToInsert); +// } + +// if (calledFunction && calledFunction->getName() == "rsqrt") { +// Function* fixrsqrt = createFixRsqrt(irModule, quantizedType); +// handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); +// } + + + else if (funcName == "sin") { // For sin @@ -2160,6 +2347,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + //fixpow + //llvm::Function * fixpow = createFixPow(module, quantizedType, functionsToInsert); // generate hardcode function - floatIntMul function // llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); @@ -2220,33 +2409,10 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - handleCall(cast(llvmIrInstruction), quantizedType); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert); break; case Instruction::GetElementPtr: - // if (auto gepInst = dyn_cast(llvmIrInstruction)) - // { - // auto gepType = gepInst->getType(); - // auto sourceType = quantizedType; - // // bool isPointer = false; - // // unsigned pointerAddr = 0; - // // if (gepType->isPointerTy()) { - // // isPointer = true; - // // pointerAddr = gepType->getPointerAddressSpace(); - // // valueType = gepType->getPointerElementType(); - // // } - // if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) - // { - // sourceType = ArrayType::get(quantizedType, - // gepInst->getSourceElementType()->getArrayNumElements()); - // } - // // if (isPointer) { - // // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - // // } - // // if (gepType->isDoubleTy() || gepType->isFloatTy()) { - // gepInst->setSourceElementType(sourceType); - // gepInst->setResultElementType(quantizedType); - // // } - // } + handleGetElementPtr(cast(llvmIrInstruction), quantizedType); break; case Instruction::Load: From fec5dace547d76d77a0b30e52a69668eec9a6055 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 10 Aug 2024 22:32:02 +0100 Subject: [PATCH 054/213] add fixsqrt * dev2. --- ...212c7b5b21a987cda580414026d64e97a3ebf5.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 514 +++++++++++++----- 2 files changed, 425 insertions(+), 137 deletions(-) create mode 100644 analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt diff --git a/analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt b/analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt new file mode 100644 index 000000000..5103a87b9 --- /dev/null +++ b/analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt @@ -0,0 +1,48 @@ + +changeset: 1647:03212c7b5b21a987cda580414026d64e97a3ebf5 +char kNewtonVersion[] = "0.3-alpha-1647 (03212c7b5b21a987cda580414026d64e97a3ebf5) (build 08-09-2024-22:22-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 268f1e74b..4013461d2 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -68,8 +68,6 @@ isMatrixOperation(Instruction * instr) return false; // 如果没有匹配到矩阵操作的特征 } - - // Set the quantized type for a given value void setQuantizedType(Value * inValue, Type * quantizedType) @@ -130,8 +128,6 @@ setQuantizedType(Value * inValue, Type * quantizedType) // Quantize constants within an instruction std::unordered_map quantizedValueCache; - - void fixLoadStoreTypes(Function & F, Type * quantizedType) { @@ -242,6 +238,25 @@ std::vector functionsToErase; // Track processed functions to avoid duplicate processing std::set processedFunctions; +bool shouldSkipFunction(const std::string &functionName) { + // List of function names to skip + static const std::unordered_set skipFunctions = { + "llvm.dbg.declare", + "llvm.dbg.value", + "llvm.dbg.label", + "fixmul", + "floatIntMul", + "fixdiv", + "fixsqrt", + "constantMulDiv", + "sinf", + "llvm.sqrt.f64", + "sqrt", + "sqrtf" + }; + + return skipFunctions.find(functionName) != skipFunctions.end(); +} // Handle the function signature change for quantization void handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) @@ -249,10 +264,7 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" ||functionName == "printf") - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "llvm.memcpy.p0i8.p0i8.i64" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt" || functionName == "sinf" || functionName == "sqrtf") - - { + if (shouldSkipFunction(functionName)) { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; return; } @@ -270,7 +282,8 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) } // Skip if the function returns void - if (llvmIrFunction.getReturnType()->isVoidTy()) { + if (llvmIrFunction.getReturnType()->isVoidTy()) + { llvm::errs() << "Skipping function with void return type: " << functionName << "\n"; return; } @@ -329,11 +342,9 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) return originalType; } -//llvm::Function *createFixPow(Module *irModule, Type *quantizedType, std::vector &functionsToInsert); +// llvm::Function *createFixPow(Module *irModule, Type *quantizedType, std::vector &functionsToInsert); - - -//void handlePowCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert) +// void handlePowCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert) //{ // IRBuilder<> Builder(llvmIrCallInstruction); // auto base = llvmIrCallInstruction->getArgOperand(0); @@ -357,12 +368,9 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) // // Replace the original pow call with the fixpow result // llvmIrCallInstruction->replaceAllUsesWith(fixpowResult); // llvmIrCallInstruction->eraseFromParent(); -//} - - - +// } void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) { IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); @@ -375,44 +383,73 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) return; } - // Convert integer operand to float for sqrt computation - Value * newOperand = Builder.CreateSIToFP(operand, Type::getDoubleTy(llvmIrCallInstruction->getContext())); - - // Get the correct sqrt function with double return and argument types - Function * sqrtFunc = llvmIrCallInstruction->getModule()->getFunction("sqrt"); - if (!sqrtFunc) + // Convert the operand to fixed-point format if necessary + if (operand->getType()->isFloatingPointTy()) { - FunctionType * sqrtType = FunctionType::get(Type::getDoubleTy(llvmIrCallInstruction->getContext()), {Type::getDoubleTy(llvmIrCallInstruction->getContext())}, false); - sqrtFunc = Function::Create(sqrtType, Function::ExternalLinkage, "sqrt", llvmIrCallInstruction->getModule()); + operand = Builder.CreateFPToSI(operand, quantizedType); } - // Create call to sqrt function with the double operand - Value * sqrtResult = Builder.CreateCall(sqrtFunc, {newOperand}); - - // Convert the result back to integer - Value * fptosiInst = Builder.CreateFPToSI(sqrtResult, quantizedType); - // Perform left shift for scaling - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value * resInst = nullptr; + // Create call to the fixed-point sqrt function + llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - // If FRAC_Q is odd, apply compensation - if (FRAC_Q % 2) - { - Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); - llvm::errs() << "Compensation applied\n"; - } - else - { - resInst = shlInst; - } - llvmIrCallInstruction->replaceAllUsesWith(resInst); + // No need to apply shl and compensation if it's already done in createFixSqrt + llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); } -} + +// void +// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto operand = llvmIrCallInstruction->getOperand(0); +// +// // Cast the instruction to CallInst to access getCalledFunction method +// CallInst * callInst = dyn_cast(llvmIrCallInstruction); +// if (!callInst) +// { +// llvm::errs() << "Error: Instruction is not a CallInst.\n"; +// return; +// } +// +// // Convert integer operand to float for sqrt computation +// Value * newOperand = Builder.CreateSIToFP(operand, Type::getDoubleTy(llvmIrCallInstruction->getContext())); +// +// // Get the correct sqrt function with double return and argument types +// Function * sqrtFunc = llvmIrCallInstruction->getModule()->getFunction("sqrt"); +// if (!sqrtFunc) +// { +// FunctionType * sqrtType = FunctionType::get(Type::getDoubleTy(llvmIrCallInstruction->getContext()), {Type::getDoubleTy(llvmIrCallInstruction->getContext())}, false); +// sqrtFunc = Function::Create(sqrtType, Function::ExternalLinkage, "sqrt", llvmIrCallInstruction->getModule()); +// } +// +// // Create call to sqrt function with the double operand +// Value * sqrtResult = Builder.CreateCall(sqrtFunc, {newOperand}); +// +// // Convert the result back to integer +// Value * fptosiInst = Builder.CreateFPToSI(sqrtResult, quantizedType); +// // Perform left shift for scaling +// Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); +// Value * resInst = nullptr; +// +// // If FRAC_Q is odd, apply compensation +// if (FRAC_Q % 2) +// { +// Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); +// auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); +// Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); +// resInst = Builder.CreateFPToSI(mulInst, quantizedType); +// llvm::errs() << "Compensation applied\n"; +// } +// else +// { +// resInst = shlInst; +// } +// +// llvmIrCallInstruction->replaceAllUsesWith(resInst); +// llvmIrCallInstruction->eraseFromParent(); +// } +// } void handleSinCall(CallInst * llvmIrCallInstruction, Type * quantizedType) @@ -508,18 +545,21 @@ handleMemCpyCall(Instruction * llvmIrInstruction, Type * quantizedType) } } -void handleRsqrtCall(CallInst* llvmIrCallInstruction, Type* quantizedType, Function* fixrsqrt) { +void +handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) +{ IRBuilder<> builder(llvmIrCallInstruction); - Value* operand = llvmIrCallInstruction->getOperand(0); + Value * operand = llvmIrCallInstruction->getOperand(0); - if (operand->getType()->isIntegerTy()) { + if (operand->getType()->isIntegerTy()) + { operand = builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); llvmIrCallInstruction->setOperand(0, operand); } // Replace the rsqrt call with the custom fixrsqrt function - Value* rsqrtResult = builder.CreateCall(fixrsqrt, {llvmIrCallInstruction->getOperand(0)}); + Value * rsqrtResult = builder.CreateCall(fixrsqrt, {llvmIrCallInstruction->getOperand(0)}); // Replace all uses of the original rsqrt call with the fixed-point result llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); @@ -528,7 +568,6 @@ void handleRsqrtCall(CallInst* llvmIrCallInstruction, Type* quantizedType, Funct llvmIrCallInstruction->eraseFromParent(); } - // A list of global variables to erase after processing std::vector globalsToErase; @@ -804,8 +843,6 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } - - void simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) { @@ -953,6 +990,121 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->removeFromParent(); } +//llvm::Function * +//createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//{ +// // check if irModule is valid +// if (!irModule) +// { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixSqrtFuncName = "fixsqrt"; +// for (auto & function : *irModule) +// { +// if (function.getName() == fixSqrtFuncName) +// { +// llvm::errs() << "fixsqrt already exists\n"; +// return &function; +// } +// } +// +// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixsqrt", irModule); +// +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// +// llvm::Function::arg_iterator args = func->arg_begin(); +// llvm::Value * x = &*args++; +// +// // Initial approximation: x / 2 +// llvm::Value * approx = builder.CreateLShr(x, 1); +// llvm::Value * halfBase = builder.CreateLShr(ConstantInt::get(quantizedType, FRAC_BASE), 1); +// +// for (int i = 0; i < 5; ++i) +// { // Run the approximation a few times +// llvm::Value * div = builder.CreateSDiv(x, approx); +// llvm::Value * avg = builder.CreateAdd(approx, div); +// approx = builder.CreateLShr(avg, 1); // approx = (approx + x / approx) / 2 +// } +// +// llvm::Value * result = approx; // Final square root approximation +// +// // Apply scaling: multiply by FRAC_BASE to maintain fixed-point representation +// result = builder.CreateMul(result, halfBase); +// +// builder.CreateRet(result); +// +// // Add the created function to the vector of functions to be inserted +// functionsToInsert.push_back(func); +// +// return func; +//} + +llvm::Function* createFixSqrt(llvm::Module* irModule, Type* quantizedType, std::vector &functionsToInsert) { + // Check if irModule is valid + if (!irModule) { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixSqrtFuncName = "fixsqrt"; + for (auto & function : *irModule) { + if (function.getName() == fixSqrtFuncName) { + llvm::errs() << "fixsqrt already exists\n"; + return &function; + } + } + + llvm::LLVMContext& context = irModule->getContext(); + + // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType + llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); + + llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(context, "entry", func); + llvm::IRBuilder<> builder(entryBB); + + llvm::Function::arg_iterator args = func->arg_begin(); + llvm::Value* x = &*args++; + + // Convert the fixed-point integer to a floating-point number for sqrt computation + llvm::Value* fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); + + // Call sqrt on the floating-point value + llvm::Function* sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); + llvm::Value* sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + + // Convert the result back to a fixed-point integer + llvm::Value* res = builder.CreateFPToSI(sqrtResult, quantizedType); + + // Perform a left shift to scale the result + llvm::Value* shlRes = builder.CreateShl(res, FRAC_Q / 2); + + // Apply compensation if FRAC_Q is odd + llvm::Value* finalRes = shlRes; + if (FRAC_Q % 2 != 0) { + llvm::Value* compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); + llvm::Value* fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); + llvm::Value* compensated = builder.CreateFMul(fpShlRes, compensationFactor); + finalRes = builder.CreateFPToSI(compensated, quantizedType); + } + + builder.CreateRet(finalRes); + + // Insert the newly created function into the list + functionsToInsert.push_back(func); + + llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; + + return func; +} + + + + llvm::Function * createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vector & functionsToInsert) { @@ -1001,7 +1153,7 @@ createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vect return func; } -//llvm::Function* createFixPow(Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { +// llvm::Function* createFixPow(Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { // llvm::errs() << "Entering createFixPow\n"; // // if (!irModule) { @@ -1080,10 +1232,7 @@ createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vect // functionsToInsert.emplace_back(func); // llvm::errs() << "Created fixpow function: " << func->getName() << "\n"; // return func; -//} - - - +// } // Create a fixed-point multiplication function llvm::Function * @@ -1207,7 +1356,7 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vector& functionsToInsert) { +// llvm::Function* createFixRsqrt(llvm::Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { // // // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) // llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); @@ -1250,7 +1399,6 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vectoreraseFromParent(); } - float checkDecimal(float decimalNum) { @@ -1491,32 +1638,34 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) } llvm::errs() << "Exiting handleConstant\n"; } -void handleFAdd(Instruction *inInstruction, Type *quantizedType) { +void +handleFAdd(Instruction * inInstruction, Type * quantizedType) +{ llvm::errs() << "Handling FAdd\n"; IRBuilder<> Builder(inInstruction); - - - Value *op0 = inInstruction->getOperand(0); - Value *op1 = inInstruction->getOperand(1); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE - if (ConstantFP *constFp = dyn_cast(op0)) { + if (ConstantFP * constFp = dyn_cast(op0)) + { // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); + op0 = ConstantInt::get(quantizedType, quantizedValue); } - if (ConstantFP *constFp = dyn_cast(op1)) { + if (ConstantFP * constFp = dyn_cast(op1)) + { // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); + op1 = ConstantInt::get(quantizedType, quantizedValue); } // Create fixed-point addition - Value *newInst = Builder.CreateAdd(op0, op1); + Value * newInst = Builder.CreateAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -1525,34 +1674,34 @@ void handleFAdd(Instruction *inInstruction, Type *quantizedType) { llvm::errs() << "Finished handling FAdd\n"; } - - -void handleFSub(Instruction *inInstruction, Type *quantizedType) { +void +handleFSub(Instruction * inInstruction, Type * quantizedType) +{ llvm::errs() << "Handling FSub\n"; IRBuilder<> Builder(inInstruction); - - - Value *op0 = inInstruction->getOperand(0); - Value *op1 = inInstruction->getOperand(1); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE - if (ConstantFP *constFp = dyn_cast(op0)) { + if (ConstantFP * constFp = dyn_cast(op0)) + { // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); + op0 = ConstantInt::get(quantizedType, quantizedValue); } - if (ConstantFP *constFp = dyn_cast(op1)) { + if (ConstantFP * constFp = dyn_cast(op1)) + { // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); + op1 = ConstantInt::get(quantizedType, quantizedValue); } // Create fixed-point subtraction - Value *newInst = Builder.CreateSub(op0, op1); + Value * newInst = Builder.CreateSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -1561,12 +1710,6 @@ void handleFSub(Instruction *inInstruction, Type *quantizedType) { llvm::errs() << "Finished handling FSub\n"; } - - - - - - void handleMatrixOperations(Instruction * instr, Type * quantizedType) { @@ -1612,21 +1755,27 @@ handleMatrixOperations(Instruction * instr, Type * quantizedType) } } -Value* convertToQuantizedType(Value *value, Type *quantizedType, IRBuilder<> &Builder) { - if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) { +Value * +convertToQuantizedType(Value * value, Type * quantizedType, IRBuilder<> & Builder) +{ + if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) + { // 如果是浮点类型,首先乘以 FRAC_BASE,然后转换为量化后的整数类型 - Value *scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), FRAC_BASE)); + Value * scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), FRAC_BASE)); return Builder.CreateFPToSI(scaledValue, quantizedType); - } else if (value->getType()->isIntegerTy()) { + } + else if (value->getType()->isIntegerTy()) + { // 如果已经是整数类型,则直接返回 return value; - } else { + } + else + { llvm::errs() << "Unsupported type for quantization: " << *value->getType() << "\n"; return nullptr; } } - void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) { @@ -1662,10 +1811,20 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix if (lhsIsInteger && rhsIsInteger) { - if (isa(lhs) || isa(rhs)) + // if (isa(lhs) || isa(rhs)) + // { + // llvm::errs() << "One of the operands is a constant, simplifying...\n"; + // handleConstant(llvmIrInstruction, quantizedType); + // } + + // Check if one of the operands is a constant integer + if (isa(lhs) || isa(rhs)) { - llvm::errs() << "One of the operands is a constant, simplifying...\n"; - handleConstant(llvmIrInstruction, quantizedType); + llvm::errs() << "One of the operands is a constant integer, simplifying...\n"; + // If either operand is an integer constant, no need to multiply by FRAC_BASE + Value * newInst = Builder.CreateMul(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); } else { @@ -1756,6 +1915,36 @@ handleFRem(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FRem\n"; } +void +handleFNeg(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FNeg\n"; + IRBuilder<> Builder(inInstruction); + + // Get the operand for the FNeg operation + Value * operand = inInstruction->getOperand(0); + + // Process the operand and quantize constants + quantizeConstant(inInstruction, quantizedType); + + // If the operand is a floating-point type, quantize it + if (operand->getType()->isFloatingPointTy()) + { + operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); + operand = Builder.CreateFPToSI(operand, quantizedType); + } + + // Perform the negation in fixed-point arithmetic + // Fixed-point negation is equivalent to integer negation + Value * newInst = Builder.CreateNeg(operand); + + // Replace the original FNeg instruction with the new fixed-point negation + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FNeg\n"; +} + void handleFCmp(Instruction * inInstruction, Type * quantizedType) { @@ -1838,7 +2027,7 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) // } void -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt) { Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) @@ -1861,21 +2050,19 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName() == "rsqrt") { -// Function* fixrsqrt = createFixRsqrt(irModule, quantizedType); -// handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); -// } - + // else if (funcName == "pow" || funcName == "powf" || funcName == "powl") + // { + // // For pow + // handlePowCall(llvmIrCallInstruction, quantizedType, functionsToInsert); + // } + // if (calledFunction && calledFunction->getName() == "rsqrt") { + // Function* fixrsqrt = createFixRsqrt(irModule, quantizedType); + // handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); + // } else if (funcName == "sin") { @@ -2157,25 +2344,67 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) } } +// void +// handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; +// auto * fptruncInst = cast(llvmIrInstruction); +// auto srcType = fptruncInst->getSrcTy(); +// auto destType = fptruncInst->getDestTy(); +// +// Value *operand = llvmIrInstruction->getOperand(0); +// +// if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) +// { +// // Erase the unnecessary FPTrunc instruction +// llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); +// llvmIrInstruction->eraseFromParent(); +// llvm::errs() << "Removed unnecessary FPTrunc\n"; +// } +// +// if (operand->getType()->isIntegerTy()) +// { +// llvmIrInstruction->replaceAllUsesWith(operand); +// llvmIrInstruction->eraseFromParent(); +// llvm::errs() << "Removed unnecessary FPTrunc for integer operand\n"; +// }else{ +// llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; +// } +// } + void handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; - auto * fptruncInst = cast(llvmIrInstruction); - auto srcType = fptruncInst->getSrcTy(); - auto destType = fptruncInst->getDestTy(); + IRBuilder<> Builder(llvmIrInstruction); + + // Insert point is after the current instruction + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); - if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) + // Get the operand of the FPTrunc instruction + Value * operand = llvmIrInstruction->getOperand(0); + Value * newInst = nullptr; + + // Check if the operand is an integer or a floating-point type + if (operand->getType()->isIntegerTy()) { - // Erase the unnecessary FPTrunc instruction - llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Removed unnecessary FPTrunc\n"; + // If it's an integer, convert it to a floating-point value + newInst = Builder.CreateSIToFP(operand, llvmIrInstruction->getType()); } else { - llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; + // Otherwise, perform the FPTrunc as a floating-point cast + newInst = Builder.CreateFPCast(operand, llvmIrInstruction->getType()); } + + // Replace all uses of the original FPTrunc with the new instruction + llvmIrInstruction->replaceAllUsesWith(newInst); + + // Remove the original FPTrunc instruction from the parent + llvmIrInstruction->removeFromParent(); + + llvm::errs() << "Finished handling FPTrunc\n"; } void @@ -2283,6 +2512,12 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Exiting adaptTypeCast\n"; } + + + + + + // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) @@ -2290,15 +2525,16 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - // Skip certain functions + + + // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "fixdiv" || functionName == "constantMulDiv" || functionName == "sinf") - // if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" ) - { - llvm::errs() << "Skipping function: " << functionName << "\n"; + if (shouldSkipFunction(functionName)) { + llvm::errs() << "Should Skipping function: " << functionName << "\n"; return; } + Type * quantizedType; switch (BIT_WIDTH) { @@ -2345,10 +2581,11 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * generate hardcode function - fixmul and fixdiv * */ - llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); - llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - //fixpow - //llvm::Function * fixpow = createFixPow(module, quantizedType, functionsToInsert); + llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); + llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + // fixpow + // llvm::Function * fixpow = createFixPow(module, quantizedType, functionsToInsert); // generate hardcode function - floatIntMul function // llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); @@ -2409,7 +2646,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt); break; case Instruction::GetElementPtr: @@ -2524,6 +2761,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve { // quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); + handleFNeg(llvmIrInstruction, quantizedType); + break; } @@ -2601,3 +2840,4 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } +} \ No newline at end of file From 1588a178980fdedaaec03ec6d9876db3a96aed46 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 11 Aug 2024 12:56:22 +0100 Subject: [PATCH 055/213] comtinue fix some constant issue * dev2. --- ...d764c13c3991109adcd534a7a8a5fe25e8154d.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 553 ++++++++++-------- 2 files changed, 344 insertions(+), 257 deletions(-) create mode 100644 analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt diff --git a/analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt b/analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt new file mode 100644 index 000000000..360cf534b --- /dev/null +++ b/analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt @@ -0,0 +1,48 @@ + +changeset: 1648:27d764c13c3991109adcd534a7a8a5fe25e8154d +char kNewtonVersion[] = "0.3-alpha-1648 (27d764c13c3991109adcd534a7a8a5fe25e8154d) (build 08-10-2024-22:32-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 4013461d2..e0b4f40eb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -843,136 +843,136 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } -void -simplifyConstant(Instruction * inInstruction, Type * quantizedType, Function * floatIntMul) -{ - llvm::errs() << "Entering simplifyConstant\n"; - - auto checkDecimal = [](float decimalNum) { - int digits = 0; - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) - { - decimalNum *= 10; // Scale decimal to avoid precision issues - digits++; - } - return decimalNum; - }; - - auto compensateFP = [inInstruction, quantizedType, floatIntMul](float quantizedNum, float decimalNum) { - float compensateNum = quantizedNum / decimalNum; - Value * constOperand = nullptr; - Value * nonConstOperand = nullptr; - unsigned constIdx = 0; - unsigned nonConstIdx = 0; - - if (isa(inInstruction->getOperand(0))) - { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); - nonConstOperand = inInstruction->getOperand(1); - } - else - { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); - nonConstOperand = inInstruction->getOperand(0); - } - - auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - - if (compensateNum == 1) - { - llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; - llvm::errs() << "Original Instruction: " << *inInstruction << "\n"; - llvm::errs() << "Quantized value: " << *quantizeNumValue << "\n"; - inInstruction->setOperand(constIdx, quantizeNumValue); - - IRBuilder<> Builder(inInstruction); - - if (nonConstOperand->getType()->isFloatTy() || nonConstOperand->getType()->isDoubleTy()) - { - llvm::errs() << "Calling handleFloatIntMul\n"; - llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {nonConstOperand, quantizeNumValue}); - inInstruction->replaceAllUsesWith(callInst); - } - else - { - llvm::errs() << "Replacing original fmul instruction with integer mul\n"; - Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); - inInstruction->replaceAllUsesWith(newMulValue); - } - inInstruction->eraseFromParent(); - // inInstruction->setOperand(constIdx, quantizeNumValue); - } - else - { - llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; - auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - - Value * newFirstInst = nullptr; - Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - - if (instOpCode == Instruction::FMul) - { - newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - llvm::errs() << "Created Mul instruction: " << *newFirstInst << "\n"; - newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) - { - newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) - { - newFirstInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); - newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); - } - - if (newSecondInst) - { - inInstruction->replaceAllUsesWith(newSecondInst); - inInstruction->eraseFromParent(); - } - else - { - llvm::errs() << "Failed to create new compensated instruction\n"; - } - } - }; - - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) - { - Value * inValue = inInstruction->getOperand(idx); - if (!isa(inValue)) - { - continue; - } - - ConstantFP * constFp = dyn_cast(inValue); - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - compensateFP(checkDecimal(constValue), constValue); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - compensateFP(checkDecimal(constValue), constValue); - } - else - { - assert(false && "unknown floating type"); - } - } - llvm::errs() << "Exiting simplifyConstant\n"; -} +//void +//simplifyConstant(Instruction * inInstruction, Type * quantizedType) +//{ +// llvm::errs() << "Entering simplifyConstant\n"; +// +// auto checkDecimal = [](float decimalNum) { +// int digits = 0; +// while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) +// { +// decimalNum *= 10; // Scale decimal to avoid precision issues +// digits++; +// } +// return decimalNum; +// }; +// +// auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { +// float compensateNum = quantizedNum / decimalNum; +// Value * constOperand = nullptr; +// Value * nonConstOperand = nullptr; +// unsigned constIdx = 0; +// unsigned nonConstIdx = 0; +// +// if (isa(inInstruction->getOperand(0))) +// { +// constIdx = 0; +// nonConstIdx = 1; +// constOperand = inInstruction->getOperand(0); +// nonConstOperand = inInstruction->getOperand(1); +// } +// else +// { +// constIdx = 1; +// nonConstIdx = 0; +// constOperand = inInstruction->getOperand(1); +// nonConstOperand = inInstruction->getOperand(0); +// } +// +// auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); +// +// if (compensateNum == 1) +// { +// llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; +// llvm::errs() << "Original Instruction: " << *inInstruction << "\n"; +// llvm::errs() << "Quantized value: " << *quantizeNumValue << "\n"; +// inInstruction->setOperand(constIdx, quantizeNumValue); +// +// IRBuilder<> Builder(inInstruction); +// +// if (nonConstOperand->getType()->isFloatTy() || nonConstOperand->getType()->isDoubleTy()) +// { +// llvm::errs() << "Calling handleFloatIntMul\n"; +// llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {nonConstOperand, quantizeNumValue}); +// inInstruction->replaceAllUsesWith(callInst); +// } +// else +// { +// llvm::errs() << "Replacing original fmul instruction with integer mul\n"; +// Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); +// inInstruction->replaceAllUsesWith(newMulValue); +// } +// inInstruction->eraseFromParent(); +// // inInstruction->setOperand(constIdx, quantizeNumValue); +// } +// else +// { +// llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; +// auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); +// IRBuilder<> Builder(inInstruction); +// Instruction * insertPoint = inInstruction->getNextNode(); +// Builder.SetInsertPoint(insertPoint); +// +// Value * newFirstInst = nullptr; +// Value * newSecondInst = nullptr; +// auto instOpCode = inInstruction->getOpcode(); +// +// if (instOpCode == Instruction::FMul) +// { +// newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); +// llvm::errs() << "Created Mul instruction: " << *newFirstInst << "\n"; +// newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); +// } +// else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) +// { +// newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); +// newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); +// } +// else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) +// { +// newFirstInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); +// newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); +// } +// +// if (newSecondInst) +// { +// inInstruction->replaceAllUsesWith(newSecondInst); +// inInstruction->eraseFromParent(); +// } +// else +// { +// llvm::errs() << "Failed to create new compensated instruction\n"; +// } +// } +// }; +// +// for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) +// { +// Value * inValue = inInstruction->getOperand(idx); +// if (!isa(inValue)) +// { +// continue; +// } +// +// ConstantFP * constFp = dyn_cast(inValue); +// if (inValue->getType()->isFloatTy()) +// { +// float constValue = constFp->getValueAPF().convertToFloat(); +// compensateFP(checkDecimal(constValue), constValue); +// } +// else if (inValue->getType()->isDoubleTy()) +// { +// double constValue = constFp->getValueAPF().convertToDouble(); +// compensateFP(checkDecimal(constValue), constValue); +// } +// else +// { +// assert(false && "unknown floating type"); +// } +// } +// llvm::errs() << "Exiting simplifyConstant\n"; +//} void substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) @@ -1599,12 +1599,17 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Original float constant: " << constValue << "\n"; float decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; - if (decimalValue == constValue) + //if (decimalValue == constValue) + if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly - auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); - auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); - llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; +// auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); +// auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); +// llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; +// inInstruction->setOperand(idx, quantizedConst); + int64_t integerPart = static_cast(round(constValue)); + auto quantizedConst = ConstantInt::get(quantizedType, integerPart); + llvm::errs() << "Converted double value to integer: " << integerPart << "\n"; inInstruction->setOperand(idx, quantizedConst); } else @@ -1618,12 +1623,19 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Original double constant: " << constValue << "\n"; double decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; - if (decimalValue == constValue) + //if (decimalValue == constValue) + if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); - llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; +// int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); +// auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); +// llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; +// inInstruction->setOperand(idx, quantizedConst); + + // If the decimal part is already an integer, just convert it directly + int64_t integerPart = static_cast(round(constValue)); + auto quantizedConst = ConstantInt::get(quantizedType, integerPart); + llvm::errs() << "Converted double value to integer: " << integerPart << "\n"; inInstruction->setOperand(idx, quantizedConst); } else @@ -1805,40 +1817,97 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; } - // Ensure both operands are now integers + // If either operand is a float constant, convert it to fixed-point using handleConstant + if (isa(lhs)) + { + llvm::errs() << "LHS is a floating-point constant, handling it with handleConstant\n"; + handleConstant(llvmIrInstruction, quantizedType); + //lhs = llvmIrInstruction->getOperand(0); // Update lhs after conversion + } + else if (isa(rhs)) + { + llvm::errs() << "RHS is a floating-point constant, handling it with handleConstant\n"; + handleConstant(llvmIrInstruction, quantizedType); + //rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion + } + + // If either operand is an integer constant, directly use mul + if (isa(lhs) || isa(rhs)) + { + llvm::errs() << "One of the operands is an integer constant, using mul\n"; + Value * newInst = Builder.CreateMul(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + return; + } + + // If both operands are integers, but neither is a constant, use fixmul bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); if (lhsIsInteger && rhsIsInteger) { - // if (isa(lhs) || isa(rhs)) - // { - // llvm::errs() << "One of the operands is a constant, simplifying...\n"; - // handleConstant(llvmIrInstruction, quantizedType); - // } - - // Check if one of the operands is a constant integer - if (isa(lhs) || isa(rhs)) - { - llvm::errs() << "One of the operands is a constant integer, simplifying...\n"; - // If either operand is an integer constant, no need to multiply by FRAC_BASE - Value * newInst = Builder.CreateMul(lhs, rhs); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - } - else - { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - } - } + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); } } + +//void +//handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) +//{ +// llvm::errs() << "Handling FMul\n"; +// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; +// IRBuilder<> Builder(llvmIrInstruction); +// +// // Ensure operands are correctly converted to fixed-point integers +// Value * lhs = llvmIrInstruction->getOperand(0); +// Value * rhs = llvmIrInstruction->getOperand(1); +// +// llvm::errs() << "LHS: " << *lhs << "\n"; +// llvm::errs() << "RHS: " << *rhs << "\n"; +// +// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); +// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); +// +// // If either operand is a float, convert both to fixed-point +// if (lhsIsFloat) +// { +// lhs = Builder.CreateFPToSI(lhs, quantizedType); +// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; +// } +// if (rhsIsFloat) +// { +// rhs = Builder.CreateFPToSI(rhs, quantizedType); +// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; +// } +// +// // Ensure both operands are now integers +// bool lhsIsInteger = lhs->getType()->isIntegerTy(); +// bool rhsIsInteger = rhs->getType()->isIntegerTy(); +// +// if (lhsIsInteger && rhsIsInteger) +// { +// if (isa(lhs) || isa(rhs)) +// { +// llvm::errs() << "One of the operands is a constant, simplifying...\n"; +// handleConstant(llvmIrInstruction, quantizedType); +// } +// else +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); +// llvmIrInstruction->replaceAllUsesWith(callInst); +// llvmIrInstruction->eraseFromParent(); +// } +// } +// } +//} + void handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixdiv) { @@ -2344,68 +2413,68 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) } } -// void -// handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; -// auto * fptruncInst = cast(llvmIrInstruction); -// auto srcType = fptruncInst->getSrcTy(); -// auto destType = fptruncInst->getDestTy(); -// -// Value *operand = llvmIrInstruction->getOperand(0); -// -// if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) -// { -// // Erase the unnecessary FPTrunc instruction -// llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); -// llvmIrInstruction->eraseFromParent(); -// llvm::errs() << "Removed unnecessary FPTrunc\n"; -// } -// -// if (operand->getType()->isIntegerTy()) -// { -// llvmIrInstruction->replaceAllUsesWith(operand); -// llvmIrInstruction->eraseFromParent(); -// llvm::errs() << "Removed unnecessary FPTrunc for integer operand\n"; -// }else{ -// llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; -// } -// } - -void -handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) + void + handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; - IRBuilder<> Builder(llvmIrInstruction); + auto * fptruncInst = cast(llvmIrInstruction); + auto srcType = fptruncInst->getSrcTy(); + auto destType = fptruncInst->getDestTy(); - // Insert point is after the current instruction - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - - // Get the operand of the FPTrunc instruction - Value * operand = llvmIrInstruction->getOperand(0); - Value * newInst = nullptr; + Value *operand = llvmIrInstruction->getOperand(0); - // Check if the operand is an integer or a floating-point type - if (operand->getType()->isIntegerTy()) + if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) { - // If it's an integer, convert it to a floating-point value - newInst = Builder.CreateSIToFP(operand, llvmIrInstruction->getType()); + // Erase the unnecessary FPTrunc instruction + llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Removed unnecessary FPTrunc\n"; } - else + + if (operand->getType()->isIntegerTy()) { - // Otherwise, perform the FPTrunc as a floating-point cast - newInst = Builder.CreateFPCast(operand, llvmIrInstruction->getType()); + llvmIrInstruction->replaceAllUsesWith(operand); + llvmIrInstruction->eraseFromParent(); + llvm::errs() << "Removed unnecessary FPTrunc for integer operand\n"; + }else{ + llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; } + } - // Replace all uses of the original FPTrunc with the new instruction - llvmIrInstruction->replaceAllUsesWith(newInst); - - // Remove the original FPTrunc instruction from the parent - llvmIrInstruction->removeFromParent(); - - llvm::errs() << "Finished handling FPTrunc\n"; -} +//void +//handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; +// IRBuilder<> Builder(llvmIrInstruction); +// +// // Insert point is after the current instruction +// Instruction * insertPoint = llvmIrInstruction->getNextNode(); +// Builder.SetInsertPoint(insertPoint); +// +// // Get the operand of the FPTrunc instruction +// Value * operand = llvmIrInstruction->getOperand(0); +// Value * newInst = nullptr; +// +// // Check if the operand is an integer or a floating-point type +// if (operand->getType()->isIntegerTy()) +// { +// // If it's an integer, convert it to a floating-point value +// newInst = Builder.CreateSIToFP(operand, llvmIrInstruction->getType()); +// } +// else +// { +// // Otherwise, perform the FPTrunc as a floating-point cast +// newInst = Builder.CreateFPCast(operand, llvmIrInstruction->getType()); +// } +// +// // Replace all uses of the original FPTrunc with the new instruction +// llvmIrInstruction->replaceAllUsesWith(newInst); +// +// // Remove the original FPTrunc instruction from the parent +// llvmIrInstruction->removeFromParent(); +// +// llvm::errs() << "Finished handling FPTrunc\n"; +//} void handleBitCast(Instruction * llvmIrInstruction, Type * quantizedType) @@ -2513,6 +2582,12 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } +void quantizeFunctionArguments(Function &llvmIrFunction, Type *quantizedType) { + for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) { + auto paramOp = llvmIrFunction.getArg(idx); + setQuantizedType(paramOp, quantizedType); + } +} @@ -2584,52 +2659,16 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); - // fixpow - // llvm::Function * fixpow = createFixPow(module, quantizedType, functionsToInsert); - // generate hardcode function - floatIntMul function - // llvm::Function * floatIntMul = createFloatIntMul(module, quantizedType, Type::getFloatTy(llvmIrFunction.getContext()), functionsToInsert); /* * quantize the arguments type * */ - // llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; - // for (size_t idx = 0; idx < llvmIrFunction.arg_size(); idx++) - // { - // llvm::errs() << "Quantizing parameter at index " << idx << "\n"; - // llvm::Function::arg_iterator argIt = llvmIrFunction.arg_begin(); - // std::advance(argIt, idx); - // - // if (argIt == llvmIrFunction.arg_end()) - // { - // llvm::errs() << "Error: Reached end of arguments while trying to access index " << idx << "\n"; - // continue; - // } - // - // llvm::Argument * paramOp = &*argIt; - // llvm::errs() << "Processing parameter: " << paramOp->getName() << "\n"; - // if (!paramOp) - // { - // llvm::errs() << "Error: paramOp is nullptr at index " << idx << "\n"; - // continue; - // } - // - // if (paramOp->getType()->isFloatTy() || paramOp->getType()->isDoubleTy()) - // { - // llvm::errs() << "Quantizing parameter from " << *paramOp->getType() << " to " << *quantizedType << "\n"; - // setQuantizedType(paramOp, quantizedType); - // } - // else - // { - // llvm::errs() << "Parameter at index " << idx << " is not a floating-point type, skipping\n"; - // } - // } - // for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) - // { - // auto paramOp = llvmIrFunction.getArg(idx); - // setQuantizedType(paramOp, quantizedType); - // } + //quantizeFunctionArguments(llvmIrFunction, quantizedType); + + + // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) From cc1b27c2e423a4822996d07a827190a205ac647a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 13 Aug 2024 23:18:46 +0800 Subject: [PATCH 056/213] update makefile to original one * dev2. --- src/newton/Makefile | 27 +++++++++------------------ 1 file changed, 9 insertions(+), 18 deletions(-) diff --git a/src/newton/Makefile b/src/newton/Makefile index 42c9a7d43..e31a11bb2 100644 --- a/src/newton/Makefile +++ b/src/newton/Makefile @@ -2,7 +2,7 @@ TREEROOT = ../.. include $(TREEROOT)/config.local -PRECOMMITHOOK = precommitStatisticsHook-$(OSTYPE).sh +PRECOMMITHOOK = precommitStatisticsHook-$(OSTYPE).sh COMMONPATH = ../common NOISYPATH = ../noisy @@ -100,8 +100,6 @@ SOURCES =\ newton-irPass-LLVMIR-constantSubstitution.cpp\ newton-irPass-LLVMIR-shrinkTypeByRange.cpp\ newton-irPass-LLVMIR-quantization.cpp\ - - newton-irPass-LLVMIR-dequantization.cpp\ newton-irPass-LLVMIR-memoryAlignment.cpp\ newton-irPass-LLVMIR-emitAssume.cpp\ @@ -139,7 +137,6 @@ OBJS =\ newton-irPass-LLVMIR-constantSubstitution.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION)\ - newton-irPass-LLVMIR-dequantization.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION)\ newton-irPass-invariantSignalAnnotation.$(OBJECTEXTENSION)\ newton-irPass-piGroupsSignalAnnotation.$(OBJECTEXTENSION)\ @@ -189,7 +186,6 @@ CGIOBJS =\ newton-irPass-LLVMIR-constantSubstitution.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION)\ - newton-irPass-LLVMIR-dequantization.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION)\ newton-irPass-invariantSignalAnnotation.$(OBJECTEXTENSION)\ newton-irPass-piGroupsSignalAnnotation.$(OBJECTEXTENSION)\ @@ -238,7 +234,6 @@ LIBNEWTONOBJS =\ newton-irPass-LLVMIR-constantSubstitution.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION)\ - newton-irPass-LLVMIR-dequantization.$(OBJECTEXTENSION)\ newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION)\ newton-irPass-invariantSignalAnnotation.$(OBJECTEXTENSION)\ newton-irPass-piGroupsSignalAnnotation.$(OBJECTEXTENSION)\ @@ -318,24 +313,24 @@ installhooks: # Libraries # -lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a: $(LIBNEWTONOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a: $(LIBNEWTONOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(AR) $(ARFLAGS) $@ $(LIBNEWTONOBJS) # # Executables # -target: $(OBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +target: $(OBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(LD) $(LINKDIRS) $(LDFLAGS) $(OBJS) $(LLVMLIBS) $(SYSTEMLIBS) -lflex-$(OSTYPE) -lm $(LINKDIRS) $(LDFLAGS) -o $(TARGET) -lstdc++ -cgi:lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a $(CGIOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +cgi:lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a $(CGIOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(LD) $(LINKDIRS) $(LDFLAGS) $(CGIOBJS) $(LLVMLIBS) $(SYSTEMLIBS) -lflex-$(OSTYPE) $(LINKDIRS) $(LDFLAGS) -o $(CGI_TARGET) -lstdc++ # # Objects # -%.$(OBJECTEXTENSION): %.c $(HEADERS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +%.$(OBJECTEXTENSION): %.c $(HEADERS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(CC) $(FLEXFLAGS) $(INCDIRS) $(CCFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CC) $(FLEXFLAGS) $(INCDIRS) $(CCFLAGS) $(WFLAGS) $(OPTFLAGS) $< @@ -375,10 +370,6 @@ newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-quant $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< -newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-dequantization.cpp - $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< - $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< - newton-irPass-LLVMIR-memoryAlignment.$(OBJECTEXTENSION): newton-irPass-LLVMIR-memoryAlignment.cpp $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< @@ -446,14 +437,14 @@ examples: $(EXAMPLES) target installcgi: cgi sudo cp $(CGI_TARGET) $(CGI_BIN) sudo mkdir -p $(HTMLBASEPATH) - sudo cp ../icons/* $(HTMLBASEPATH)/ - sudo chmod 777 $(HTMLBASEPATH) + sudo cp ../icons/* $(HTMLBASEPATH)/ + sudo chmod 777 $(HTMLBASEPATH) sudo chmod 755 $(HTMLBASEPATH)/*.png test: - ./$(TARGET) --dot 0 $(EXAMPLESPATH)/invariants.nt | dot -Tpdf -O ; open noname.gv.pdf + ./$(TARGET) --dot 0 $(EXAMPLESPATH)/invariants.nt | dot -Tpdf -O ; open noname.gv.pdf clean: rm -rf version.c $(OBJS) $(CGIOBJS) $(LIBNEWTONOBJS) $(CGI_TARGET) $(CGI_TARGET).dSYM $(TARGET) $(TARGET).dSYM $(CGI_TARGET) $(CGI_TARGET).dsym lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a *.o *.plist - cd ../common && make clean + cd ../common && make clean \ No newline at end of file From 484908d1897c17fa4e3a54e78371a6e5268c5d46 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 14 Aug 2024 22:52:55 +0800 Subject: [PATCH 057/213] save before change * dev2. --- ...88a178980fdedaaec03ec6d9876db3a96aed46.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 558 +++++++++++------- 2 files changed, 378 insertions(+), 228 deletions(-) create mode 100644 analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt diff --git a/analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt b/analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt new file mode 100644 index 000000000..863cdad12 --- /dev/null +++ b/analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt @@ -0,0 +1,48 @@ + +changeset: 1650:1588a178980fdedaaec03ec6d9876db3a96aed46 +char kNewtonVersion[] = "0.3-alpha-1650 (1588a178980fdedaaec03ec6d9876db3a96aed46) (build 08-13-2024-23:18-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e0b4f40eb..baf2cf1be 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -238,7 +238,9 @@ std::vector functionsToErase; // Track processed functions to avoid duplicate processing std::set processedFunctions; -bool shouldSkipFunction(const std::string &functionName) { +bool +shouldSkipFunction(const std::string & functionName) +{ // List of function names to skip static const std::unordered_set skipFunctions = { "llvm.dbg.declare", @@ -252,8 +254,7 @@ bool shouldSkipFunction(const std::string &functionName) { "sinf", "llvm.sqrt.f64", "sqrt", - "sqrtf" - }; + "sqrtf"}; return skipFunctions.find(functionName) != skipFunctions.end(); } @@ -264,7 +265,8 @@ handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; // Skip certain functions std::string functionName = llvmIrFunction.getName().str(); - if (shouldSkipFunction(functionName)) { + if (shouldSkipFunction(functionName)) + { llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; return; } @@ -392,10 +394,10 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function // Create call to the fixed-point sqrt function llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - // No need to apply shl and compensation if it's already done in createFixSqrt llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); + } // void @@ -607,6 +609,29 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } +std::vector instructionsToErase; +void +eraseOldInstructions() +{ + llvm::errs() << "Entering eraseOldInstructions\n"; + for (auto *instr : instructionsToErase) + { +// if (!instr->use_empty()) +// { +// llvm::errs() << "Erasing old instruction: " << *instr << "\n"; +// instr->eraseFromParent(); +// } +// else +// { +// llvm::errs() << "Cannot erase instruction still in use: " << *instr << "\n"; +// } + llvm::errs() << "Erasing old instrunctions " << *instr << "\n"; + instr->eraseFromParent(); + } + instructionsToErase.clear(); + llvm::errs() << "Exiting eraseOldInstructions\n"; +} + // Function to update global variables from floating-point to integer types // void updateGlobalVariables(llvm::Module *module, llvm::Type *quantizedType) { // for (llvm::GlobalVariable &globalVar : module->globals()) { @@ -843,136 +868,121 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } -//void -//simplifyConstant(Instruction * inInstruction, Type * quantizedType) -//{ -// llvm::errs() << "Entering simplifyConstant\n"; -// -// auto checkDecimal = [](float decimalNum) { -// int digits = 0; -// while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) -// { -// decimalNum *= 10; // Scale decimal to avoid precision issues -// digits++; -// } -// return decimalNum; -// }; -// -// auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { -// float compensateNum = quantizedNum / decimalNum; -// Value * constOperand = nullptr; -// Value * nonConstOperand = nullptr; -// unsigned constIdx = 0; -// unsigned nonConstIdx = 0; -// -// if (isa(inInstruction->getOperand(0))) -// { -// constIdx = 0; -// nonConstIdx = 1; -// constOperand = inInstruction->getOperand(0); -// nonConstOperand = inInstruction->getOperand(1); -// } -// else -// { -// constIdx = 1; -// nonConstIdx = 0; -// constOperand = inInstruction->getOperand(1); -// nonConstOperand = inInstruction->getOperand(0); -// } -// -// auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); -// -// if (compensateNum == 1) -// { -// llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; -// llvm::errs() << "Original Instruction: " << *inInstruction << "\n"; -// llvm::errs() << "Quantized value: " << *quantizeNumValue << "\n"; -// inInstruction->setOperand(constIdx, quantizeNumValue); -// -// IRBuilder<> Builder(inInstruction); -// -// if (nonConstOperand->getType()->isFloatTy() || nonConstOperand->getType()->isDoubleTy()) -// { -// llvm::errs() << "Calling handleFloatIntMul\n"; -// llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {nonConstOperand, quantizeNumValue}); -// inInstruction->replaceAllUsesWith(callInst); -// } -// else -// { -// llvm::errs() << "Replacing original fmul instruction with integer mul\n"; -// Value * newMulValue = Builder.CreateMul(nonConstOperand, quantizeNumValue); -// inInstruction->replaceAllUsesWith(newMulValue); -// } -// inInstruction->eraseFromParent(); -// // inInstruction->setOperand(constIdx, quantizeNumValue); -// } -// else -// { -// llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; -// auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); -// IRBuilder<> Builder(inInstruction); -// Instruction * insertPoint = inInstruction->getNextNode(); -// Builder.SetInsertPoint(insertPoint); -// -// Value * newFirstInst = nullptr; -// Value * newSecondInst = nullptr; -// auto instOpCode = inInstruction->getOpcode(); -// -// if (instOpCode == Instruction::FMul) -// { -// newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); -// llvm::errs() << "Created Mul instruction: " << *newFirstInst << "\n"; -// newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); -// } -// else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) -// { -// newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); -// newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); -// } -// else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) -// { -// newFirstInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); -// newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); -// } -// -// if (newSecondInst) -// { -// inInstruction->replaceAllUsesWith(newSecondInst); -// inInstruction->eraseFromParent(); -// } -// else -// { -// llvm::errs() << "Failed to create new compensated instruction\n"; -// } -// } -// }; -// -// for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) -// { -// Value * inValue = inInstruction->getOperand(idx); -// if (!isa(inValue)) -// { -// continue; -// } -// -// ConstantFP * constFp = dyn_cast(inValue); -// if (inValue->getType()->isFloatTy()) -// { -// float constValue = constFp->getValueAPF().convertToFloat(); -// compensateFP(checkDecimal(constValue), constValue); -// } -// else if (inValue->getType()->isDoubleTy()) -// { -// double constValue = constFp->getValueAPF().convertToDouble(); -// compensateFP(checkDecimal(constValue), constValue); -// } -// else -// { -// assert(false && "unknown floating type"); -// } -// } -// llvm::errs() << "Exiting simplifyConstant\n"; -//} +void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { + llvm::errs() << "Handling constant operand\n"; + auto checkDecimal = [](float decimalNum) { + int digits = 0; + /* + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { + decimalNum *= 10; + digits++; + } + return decimalNum; + }; + + auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { + /* + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ + float compensateNum = quantizedNum / decimalNum; + + Value *constOperand, *nonConstOperand; + unsigned constIdx, nonConstIdx; + if (isa(inInstruction->getOperand(0))) { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); + nonConstOperand = inInstruction->getOperand(1); + } else { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); + nonConstOperand = inInstruction->getOperand(0); + } + + auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); + + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newFisrtInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + + if (compensateNum == 1) { + llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; + //newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + inInstruction->setOperand(constIdx, quantizeNumValue); +// inInstruction->replaceAllUsesWith(newSecondInst); +// //inInstruction->removeFromParent(); +// instructionsToErase.push_back(inInstruction); + } else { + llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; + auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newFisrtInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) { + llvm::errs() << "Handling FMul instruction\n"; + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { + llvm::errs() << "Handling FDiv instruction with constant denominator\n"; + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); + } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { + llvm::errs() << "Handling FDiv instruction with constant numerator\n"; + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + } + + inInstruction->replaceAllUsesWith(newSecondInst); + inInstruction->removeFromParent(); + } + }; + + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { + Value * inValue = inInstruction->getOperand(idx); + + if (!isa(inValue)) { + continue; + } + + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; + if (inValue->getType()->isFloatTy()) + { + float constValue = constFp->getValueAPF().convertToFloat(); + compensateFP(checkDecimal(constValue), constValue); + } + else if (inValue->getType()->isDoubleTy()) + { + double constValue = constFp->getValueAPF().convertToDouble(); + compensateFP(checkDecimal(constValue), constValue); + } + else + { + assert(false && "unknown floating type"); + } + } + llvm::errs() << "Exiting Simplifying Constant\n"; +} void substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) @@ -990,8 +1000,8 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->removeFromParent(); } -//llvm::Function * -//createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +// llvm::Function * +// createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) //{ // // check if irModule is valid // if (!irModule) @@ -1041,55 +1051,61 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: // functionsToInsert.push_back(func); // // return func; -//} +// } -llvm::Function* createFixSqrt(llvm::Module* irModule, Type* quantizedType, std::vector &functionsToInsert) { +llvm::Function * +createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ // Check if irModule is valid - if (!irModule) { + if (!irModule) + { llvm::errs() << "Error: irModule is nullptr\n"; return nullptr; } std::string fixSqrtFuncName = "fixsqrt"; - for (auto & function : *irModule) { - if (function.getName() == fixSqrtFuncName) { + for (auto & function : *irModule) + { + if (function.getName() == fixSqrtFuncName) + { llvm::errs() << "fixsqrt already exists\n"; return &function; } } - llvm::LLVMContext& context = irModule->getContext(); + llvm::LLVMContext & context = irModule->getContext(); // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType - llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); - llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); - llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(context, "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); + llvm::IRBuilder<> builder(entryBB); llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value* x = &*args++; + llvm::Value * x = &*args++; // Convert the fixed-point integer to a floating-point number for sqrt computation - llvm::Value* fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); + llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); // Call sqrt on the floating-point value - llvm::Function* sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); - llvm::Value* sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); + llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); // Convert the result back to a fixed-point integer - llvm::Value* res = builder.CreateFPToSI(sqrtResult, quantizedType); + llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); // Perform a left shift to scale the result - llvm::Value* shlRes = builder.CreateShl(res, FRAC_Q / 2); + llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); // Apply compensation if FRAC_Q is odd - llvm::Value* finalRes = shlRes; - if (FRAC_Q % 2 != 0) { - llvm::Value* compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); - llvm::Value* fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); - llvm::Value* compensated = builder.CreateFMul(fpShlRes, compensationFactor); - finalRes = builder.CreateFPToSI(compensated, quantizedType); + llvm::Value * finalRes = shlRes; + if (FRAC_Q % 2 != 0) + { + llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); + llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); + llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); + finalRes = builder.CreateFPToSI(compensated, quantizedType); } builder.CreateRet(finalRes); @@ -1102,9 +1118,6 @@ llvm::Function* createFixSqrt(llvm::Module* irModule, Type* quantizedType, std:: return func; } - - - llvm::Function * createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vector & functionsToInsert) { @@ -1570,6 +1583,8 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, llvm::errs() << "Replacing old instruction with compensated instruction\n"; inInstruction->replaceAllUsesWith(newSecondInst); inInstruction->eraseFromParent(); + //instructionsToErase.push_back(inInstruction); + } else { @@ -1599,17 +1614,17 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Original float constant: " << constValue << "\n"; float decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; - //if (decimalValue == constValue) - if (fabs(decimalValue - constValue) < 0.001) + if (decimalValue == constValue) + //if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly -// auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); -// auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); -// llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; -// inInstruction->setOperand(idx, quantizedConst); - int64_t integerPart = static_cast(round(constValue)); - auto quantizedConst = ConstantInt::get(quantizedType, integerPart); - llvm::errs() << "Converted double value to integer: " << integerPart << "\n"; + // auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); + // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); + // llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; + // inInstruction->setOperand(idx, quantizedConst); + int64_t integerPart = static_cast(round(constValue)); + auto quantizedConst = ConstantInt::get(quantizedType, integerPart); + llvm::errs() << "Converted float value to integer: " << integerPart << "\n"; inInstruction->setOperand(idx, quantizedConst); } else @@ -1623,18 +1638,18 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Original double constant: " << constValue << "\n"; double decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; - //if (decimalValue == constValue) + // if (decimalValue == constValue) if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly -// int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); -// auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); -// llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; -// inInstruction->setOperand(idx, quantizedConst); + // int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); + // llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; + // inInstruction->setOperand(idx, quantizedConst); // If the decimal part is already an integer, just convert it directly - int64_t integerPart = static_cast(round(constValue)); - auto quantizedConst = ConstantInt::get(quantizedType, integerPart); + int64_t integerPart = static_cast(round(constValue)); + auto quantizedConst = ConstantInt::get(quantizedType, integerPart); llvm::errs() << "Converted double value to integer: " << integerPart << "\n"; inInstruction->setOperand(idx, quantizedConst); } @@ -1648,6 +1663,10 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) assert(false && "unknown floating type"); } } + + + + llvm::errs() << "Exiting handleConstant\n"; } void @@ -1659,6 +1678,37 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) Value * op0 = inInstruction->getOperand(0); Value * op1 = inInstruction->getOperand(1); + // Ensure both operands are integers + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op0)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } else { + op0 = Builder.CreateFPToSI(op0, quantizedType); + } + } + + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op1)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } else { + op1 = Builder.CreateFPToSI(op1, quantizedType); + } + } + + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op1)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } else { + op1 = Builder.CreateFPToSI(op1, quantizedType); + } + } + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE if (ConstantFP * constFp = dyn_cast(op0)) { @@ -1695,6 +1745,27 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) Value * op0 = inInstruction->getOperand(0); Value * op1 = inInstruction->getOperand(1); + // Ensure both operands are integers + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op0)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } else { + op0 = Builder.CreateFPToSI(op0, quantizedType); + } + } + + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op1)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } else { + op1 = Builder.CreateFPToSI(op1, quantizedType); + } + } + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE if (ConstantFP * constFp = dyn_cast(op0)) { @@ -1795,6 +1866,9 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; IRBuilder<> Builder(llvmIrInstruction); +// bool lhsConstantQuantized = false; +// bool rhsConstantQuantized = false; + // Ensure operands are correctly converted to fixed-point integers Value * lhs = llvmIrInstruction->getOperand(0); Value * rhs = llvmIrInstruction->getOperand(1); @@ -1805,32 +1879,32 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - // If either operand is a float, convert both to fixed-point - if (lhsIsFloat) - { - lhs = Builder.CreateFPToSI(lhs, quantizedType); - llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; - } - if (rhsIsFloat) - { - rhs = Builder.CreateFPToSI(rhs, quantizedType); - llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; - } - // If either operand is a float constant, convert it to fixed-point using handleConstant if (isa(lhs)) { llvm::errs() << "LHS is a floating-point constant, handling it with handleConstant\n"; - handleConstant(llvmIrInstruction, quantizedType); - //lhs = llvmIrInstruction->getOperand(0); // Update lhs after conversion + //handleConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); + lhs = llvmIrInstruction->getOperand(0); + lhsIsFloat = false; // Update float status as it's now a fixed-point } - else if (isa(rhs)) + + + if (isa(rhs)) { llvm::errs() << "RHS is a floating-point constant, handling it with handleConstant\n"; - handleConstant(llvmIrInstruction, quantizedType); - //rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion + //handleConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); + rhsIsFloat = false; + rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion + } +// if (isa(llvmIrInstruction->getOperand(0)) || +// isa(llvmIrInstruction->getOperand(1))) { +// simplifyConstant(llvmIrInstruction, quantizedType); +// } + // If either operand is an integer constant, directly use mul if (isa(lhs) || isa(rhs)) { @@ -1841,6 +1915,25 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix return; } + + + + // If either operand is a float, convert both to fixed-point +// if (lhsIsFloat) +// { +// lhs = Builder.CreateFPToSI(lhs, quantizedType); +// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; +// }else { +// allOperandsAreInt &= rhs->getType()->isIntegerTy(); +// } +// +// if (rhsIsFloat) +// { +// rhs = Builder.CreateFPToSI(rhs, quantizedType); +// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; +// } + + // If both operands are integers, but neither is a constant, use fixmul bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); @@ -1852,11 +1945,17 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvmIrInstruction->replaceAllUsesWith(callInst); llvmIrInstruction->eraseFromParent(); } -} +// if (allOperandsAreInt && llvmIrInstruction->getOpcode() == Instruction::FMul) { +// llvm::errs() << "Both operands are integers, converting FMul to Mul\n"; +// Value *mulInst = Builder.CreateMul(lhs, rhs); +// llvmIrInstruction->replaceAllUsesWith(mulInst); +// llvmIrInstruction->eraseFromParent(); +// } +} -//void -//handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) +// void +// handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) //{ // llvm::errs() << "Handling FMul\n"; // llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -1906,7 +2005,7 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix // } // } // } -//} +// } void handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixdiv) @@ -2155,21 +2254,32 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetNumArgOperands(); ++idx) +// for (size_t idx = 0; idx < llvmIrCallInstruction->getNumArgOperands(); ++idx) +// { +// Value * arg = llvmIrCallInstruction->getArgOperand(idx); +// if (arg->getType()->isPointerTy()) +// { +// Type * elementType = arg->getType()->getPointerElementType(); +// if (elementType->isFloatTy() || elementType->isDoubleTy()) +// { +// // Do not change pointer type, handle it when dereferenced +// continue; +// } +// } +// // Set quantized type for the argument if it is not a pointer +// setQuantizedType(arg, quantizedType); +// } + + /* + * for user-defined function, quantize the arguments + * */ + for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) { - Value * arg = llvmIrCallInstruction->getArgOperand(idx); - if (arg->getType()->isPointerTy()) - { - Type * elementType = arg->getType()->getPointerElementType(); - if (elementType->isFloatTy() || elementType->isDoubleTy()) - { - // Do not change pointer type, handle it when dereferenced - continue; - } - } - // Set quantized type for the argument if it is not a pointer - setQuantizedType(arg, quantizedType); + setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); } + quantizeConstant(llvmIrCallInstruction, quantizedType); + + // Quantize the return type if necessary if (llvmIrCallInstruction->getType()->isPointerTy()) @@ -2581,18 +2691,16 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Exiting adaptTypeCast\n"; } - -void quantizeFunctionArguments(Function &llvmIrFunction, Type *quantizedType) { - for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) { +void +quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) +{ + for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) + { auto paramOp = llvmIrFunction.getArg(idx); setQuantizedType(paramOp, quantizedType); } } - - - - // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) @@ -2600,16 +2708,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - - // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); - if (shouldSkipFunction(functionName)) { + if (shouldSkipFunction(functionName)) + { llvm::errs() << "Should Skipping function: " << functionName << "\n"; return; } - Type * quantizedType; switch (BIT_WIDTH) { @@ -2660,15 +2766,11 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); - /* * quantize the arguments type * */ - //quantizeFunctionArguments(llvmIrFunction, quantizedType); - - - + quantizeFunctionArguments(llvmIrFunction, quantizedType); // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) From acd45eb3919365d2234c69fe3c822ca0463bb16d Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 16 Aug 2024 15:56:58 +0800 Subject: [PATCH 058/213] save before handle Nan issue * dev2. --- ...1b27c2e423a4822996d07a827190a205ac647a.txt | 48 ++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 50 ++++++++++--------- 2 files changed, 75 insertions(+), 23 deletions(-) create mode 100644 analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt diff --git a/analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt b/analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt new file mode 100644 index 000000000..ec0881b8e --- /dev/null +++ b/analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt @@ -0,0 +1,48 @@ + +changeset: 1651:cc1b27c2e423a4822996d07a827190a205ac647a +char kNewtonVersion[] = "0.3-alpha-1651 (cc1b27c2e423a4822996d07a827190a205ac647a) (build 08-14-2024-22:52-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index baf2cf1be..0969c83ff 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1919,19 +1919,17 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix // If either operand is a float, convert both to fixed-point -// if (lhsIsFloat) -// { -// lhs = Builder.CreateFPToSI(lhs, quantizedType); -// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; -// }else { -// allOperandsAreInt &= rhs->getType()->isIntegerTy(); -// } -// -// if (rhsIsFloat) -// { -// rhs = Builder.CreateFPToSI(rhs, quantizedType); -// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; -// } + if (lhsIsFloat) + { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + + if (rhsIsFloat) + { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } // If both operands are integers, but neither is a constant, use fixmul @@ -2098,7 +2096,7 @@ handleFNeg(Instruction * inInstruction, Type * quantizedType) // If the operand is a floating-point type, quantize it if (operand->getType()->isFloatingPointTy()) { - operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); + //operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); operand = Builder.CreateFPToSI(operand, quantizedType); } @@ -2532,6 +2530,7 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) auto destType = fptruncInst->getDestTy(); Value *operand = llvmIrInstruction->getOperand(0); + llvm::errs() << "Operand type before conversion: " << *operand->getType() << "\n"; if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) { @@ -2569,14 +2568,19 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) // if (operand->getType()->isIntegerTy()) // { // // If it's an integer, convert it to a floating-point value +// llvm::errs() << "Operand is an integer, converting to floating-point type.\n"; // newInst = Builder.CreateSIToFP(operand, llvmIrInstruction->getType()); // } // else // { // // Otherwise, perform the FPTrunc as a floating-point cast +// llvm::errs() << "Operand is a floating-point, performing FPTrunc.\n"; // newInst = Builder.CreateFPCast(operand, llvmIrInstruction->getType()); // } // +// // Log the type of the new instruction created +// llvm::errs() << "New instruction type after conversion: " << *newInst->getType() << "\n"; +// // // Replace all uses of the original FPTrunc with the new instruction // llvmIrInstruction->replaceAllUsesWith(newInst); // @@ -2640,15 +2644,15 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) switch (llvmIrInstruction->getOpcode()) { - case Instruction::Alloca: - handleAlloca(llvmIrInstruction, quantizedType); - break; - case Instruction::Store: - handleStore(llvmIrInstruction, quantizedType); - break; - case Instruction::Load: - handleLoad(llvmIrInstruction, quantizedType); - break; +// case Instruction::Alloca: +// handleAlloca(llvmIrInstruction, quantizedType); +// break; +// case Instruction::Store: +// handleStore(llvmIrInstruction, quantizedType); +// break; +// case Instruction::Load: +// handleLoad(llvmIrInstruction, quantizedType); +// break; case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: From aa88e2d6c3c11fb59f0d4c70b57122722b202729 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 17 Aug 2024 13:33:49 +0800 Subject: [PATCH 059/213] add more test files * dev2. --- ...4908d1897c17fa4e3a54e78371a6e5268c5d46.txt | 48 ++++++ .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 162 ++++++++++++++++++ .../newton/llvm-ir/testMadgwickfix.sh | 24 +++ 3 files changed, 234 insertions(+) create mode 100644 analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt create mode 100644 applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c create mode 100755 applications/newton/llvm-ir/testMadgwickfix.sh diff --git a/analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt b/analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt new file mode 100644 index 000000000..f15251c9d --- /dev/null +++ b/analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt @@ -0,0 +1,48 @@ + +changeset: 1652:484908d1897c17fa4e3a54e78371a6e5268c5d46 +char kNewtonVersion[] = "0.3-alpha-1652 (484908d1897c17fa4e3a54e78371a6e5268c5d46) (build 08-16-2024-15:56-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c new file mode 100644 index 000000000..7aac89b2d --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -0,0 +1,162 @@ +#include +#include +#include +#include +#include +#include +#include +#define FRAC_Q 10 +#define BIT_WIDTH 32 +#define ITERATION 1 +#include "MadgwickAHRSfix.h" +extern volatile int32_t q0, q1, q2, q3; +extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); + +// #include "MadgwickAHRS.h" + +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +// Quantization function +int +quantize(float value, int frac_base) +{ + return round(value * frac_base); +} + +// Dequantization function +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); +} + +extern int perform_addition(float a, float b); + +timespec +tic() +{ + + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} +int +main() +{ + /* + * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 + */ + + float num1 = -7.249095917; + float num2 = 26.43893433; + float num3 = -37.16656494; + float num4 = -0.1184487343; + float num5 = 0.001258035656; + float num6 = 0.008988874033; + float num7 = -0.3570917249; + float num8 = 0.3941296637; + float num9 = 9.963726044; + + float num10 = 0.64306622; + float num11 = 0.02828862; + float num12 = -0.00567953; + float num13 = -0.76526684; + + int32_t mag_x = quantize(num1, FRAC_BASE); + int32_t mag_y = quantize(num2, FRAC_BASE); + int32_t mag_z = quantize(num3, FRAC_BASE); + int32_t gyr_x = quantize(num4, FRAC_BASE); + int32_t gyr_y = quantize(num5, FRAC_BASE); + int32_t gyr_z = quantize(num6, FRAC_BASE); + int32_t acc_x = quantize(num7, FRAC_BASE); + int32_t acc_y = quantize(num8, FRAC_BASE); + int32_t acc_z = quantize(num9, FRAC_BASE); + int32_t q0 = quantize(num10, FRAC_BASE); + int32_t q1 = quantize(num11, FRAC_BASE); + int32_t q2 = quantize(num12, FRAC_BASE); + int32_t q3 = quantize(num13, FRAC_BASE); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + timespec timer = tic(); + // for (size_t ts = 0; ts < DATA_SIZE; ts++) + //{ +// MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], +// acc_x[ts], acc_y[ts], acc_z[ts], +// mag_x[ts], mag_y[ts], mag_z[ts], +// &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + //} + + MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, + acc_x, acc_y, acc_z, + mag_x, mag_y, mag_z, + &q0, &q1, &q2, &q3); + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwickfix.sh b/applications/newton/llvm-ir/testMadgwickfix.sh new file mode 100755 index 000000000..90004a6d2 --- /dev/null +++ b/applications/newton/llvm-ir/testMadgwickfix.sh @@ -0,0 +1,24 @@ +# Step 1: Generate LLVM IR file +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + + +# Step 4: Optimize the generated LLVM IR file +opt /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll --simplifycfg --instsimplify -O3 -Os -S -o /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace -o /home/xyf/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc /home/xyf/CoSense/applications/newton/llvm-ir/out.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c /home/xyf/CoSense/applications/newton/llvm-ir/out.s -o /home/xyf/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/libout.a /home/xyf/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +clang /home/xyf/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L/home/xyf/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o /home/xyf/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +/home/xyf/CoSense/applications/newton/llvm-ir/main_out From 2f260ad3f710627bd8d432d40a897b7a5239516e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 18 Aug 2024 11:20:18 +0800 Subject: [PATCH 060/213] save before change * dev2. --- ...d45eb3919365d2234c69fe3c822ca0463bb16d.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 481 +++++++++++------- 2 files changed, 349 insertions(+), 180 deletions(-) create mode 100644 analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt diff --git a/analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt b/analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt new file mode 100644 index 000000000..e263f7b3c --- /dev/null +++ b/analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt @@ -0,0 +1,48 @@ + +changeset: 1653:acd45eb3919365d2234c69fe3c822ca0463bb16d +char kNewtonVersion[] = "0.3-alpha-1653 (acd45eb3919365d2234c69fe3c822ca0463bb16d) (build 08-17-2024-13:33-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 0969c83ff..480a38381 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -46,6 +46,27 @@ isValidFloatingType(Type * type) return type->isFloatTy() || type->isDoubleTy(); } +bool +isSpecialIEEE754(llvm::Value * value, llvm::IRBuilder<> & builder) +{ + // 创建NaN和Infinity的IEEE表示 + auto * nanValue = llvm::ConstantFP::getNaN(value->getType()); + auto * posInf = llvm::ConstantFP::getInfinity(value->getType(), false); + auto * negInf = llvm::ConstantFP::getInfinity(value->getType(), true); + + // 比较检测 + llvm::Value * isNan = builder.CreateFCmpUNO(value, value); // 检测NaN + llvm::Value * isPosInf = builder.CreateFCmpOEQ(value, posInf); // 检测正无穷 + llvm::Value * isNegInf = builder.CreateFCmpOEQ(value, negInf); // 检测负无穷 + + // 根据需要处理 + if (isNan || isPosInf || isNegInf) + { + return true; // 是特殊值 + } + return false; +} + bool isMatrixOperation(Instruction * instr) { @@ -84,6 +105,7 @@ setQuantizedType(Value * inValue, Type * quantizedType) if (valueType != nullptr) { + // Process pointer types if (valueType->isPointerTy()) { isPointer = true; @@ -332,17 +354,42 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) { if (originalType->isArrayTy()) { - auto elementType = originalType->getArrayElementType(); - auto newElementType = transformToQuantizedType(elementType, quantizedType); + // Handle arrays: quantize the element type + Type * elementType = originalType->getArrayElementType(); + Type * newElementType = transformToQuantizedType(elementType, quantizedType); return ArrayType::get(newElementType, originalType->getArrayNumElements()); } + else if (originalType->isPointerTy()) + { + // Handle pointers: quantize the pointee type + Type * pointeeType = originalType->getPointerElementType(); + Type * newPointeeType = transformToQuantizedType(pointeeType, quantizedType); + return PointerType::get(newPointeeType, originalType->getPointerAddressSpace()); + } else if (originalType->isFloatTy() || originalType->isDoubleTy()) { + // Handle floating-point types: replace with quantized type return quantizedType; } // Return original type if no conversion is necessary return originalType; } +// Type * +// transformToQuantizedType(Type * originalType, Type * quantizedType) +//{ +// if (originalType->isArrayTy()) +// { +// auto elementType = originalType->getArrayElementType(); +// auto newElementType = transformToQuantizedType(elementType, quantizedType); +// return ArrayType::get(newElementType, originalType->getArrayNumElements()); +// } +// else if (originalType->isFloatTy() || originalType->isDoubleTy()) +// { +// return quantizedType; +// } +// // Return original type if no conversion is necessary +// return originalType; +// } // llvm::Function *createFixPow(Module *irModule, Type *quantizedType, std::vector &functionsToInsert); @@ -397,7 +444,6 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function // No need to apply shl and compensation if it's already done in createFixSqrt llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); - } // void @@ -609,22 +655,22 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } -std::vector instructionsToErase; +std::vector instructionsToErase; void eraseOldInstructions() { llvm::errs() << "Entering eraseOldInstructions\n"; - for (auto *instr : instructionsToErase) - { -// if (!instr->use_empty()) -// { -// llvm::errs() << "Erasing old instruction: " << *instr << "\n"; -// instr->eraseFromParent(); -// } -// else -// { -// llvm::errs() << "Cannot erase instruction still in use: " << *instr << "\n"; -// } + for (auto * instr : instructionsToErase) + { + // if (!instr->use_empty()) + // { + // llvm::errs() << "Erasing old instruction: " << *instr << "\n"; + // instr->eraseFromParent(); + // } + // else + // { + // llvm::errs() << "Cannot erase instruction still in use: " << *instr << "\n"; + // } llvm::errs() << "Erasing old instrunctions " << *instr << "\n"; instr->eraseFromParent(); } @@ -868,15 +914,18 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } -void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { +void +simplifyConstant(Instruction * inInstruction, Type * quantizedType) +{ llvm::errs() << "Handling constant operand\n"; auto checkDecimal = [](float decimalNum) { int digits = 0; /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) + { decimalNum *= 10; digits++; } @@ -885,70 +934,81 @@ void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ float compensateNum = quantizedNum / decimalNum; - Value *constOperand, *nonConstOperand; + Value * constOperand, *nonConstOperand; unsigned constIdx, nonConstIdx; - if (isa(inInstruction->getOperand(0))) { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); + if (isa(inInstruction->getOperand(0))) + { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); nonConstOperand = inInstruction->getOperand(1); - } else { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); + } + else + { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); nonConstOperand = inInstruction->getOperand(0); } auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - IRBuilder<> Builder(inInstruction); + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); + auto instOpCode = inInstruction->getOpcode(); - if (compensateNum == 1) { + if (compensateNum == 1) + { llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; - //newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); inInstruction->setOperand(constIdx, quantizeNumValue); -// inInstruction->replaceAllUsesWith(newSecondInst); -// //inInstruction->removeFromParent(); -// instructionsToErase.push_back(inInstruction); - } else { + // inInstruction->replaceAllUsesWith(newSecondInst); + // //inInstruction->removeFromParent(); + // instructionsToErase.push_back(inInstruction); + } + else + { llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - IRBuilder<> Builder(inInstruction); + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) { + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) + { llvm::errs() << "Handling FMul instruction\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) + { llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) + { llvm::errs() << "Handling FDiv instruction with constant numerator\n"; - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } @@ -957,15 +1017,17 @@ void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { } }; - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { Value * inValue = inInstruction->getOperand(idx); - if (!isa(inValue)) { + if (!isa(inValue)) + { continue; } - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -1583,8 +1645,7 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, llvm::errs() << "Replacing old instruction with compensated instruction\n"; inInstruction->replaceAllUsesWith(newSecondInst); inInstruction->eraseFromParent(); - //instructionsToErase.push_back(inInstruction); - + // instructionsToErase.push_back(inInstruction); } else { @@ -1615,7 +1676,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) float decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; if (decimalValue == constValue) - //if (fabs(decimalValue - constValue) < 0.001) + // if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly // auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); @@ -1664,9 +1725,6 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) } } - - - llvm::errs() << "Exiting handleConstant\n"; } void @@ -1679,32 +1737,44 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) Value * op1 = inInstruction->getOperand(1); // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op0)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op0)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op0 = Builder.CreateFPToSI(op0, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op1)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op1)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op1 = Builder.CreateFPToSI(op1, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op1)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op1)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op1 = Builder.CreateFPToSI(op1, quantizedType); } } @@ -1746,22 +1816,30 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) Value * op1 = inInstruction->getOperand(1); // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op0)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op0)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op0 = Builder.CreateFPToSI(op0, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op1)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op1)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op1 = Builder.CreateFPToSI(op1, quantizedType); } } @@ -1866,8 +1944,8 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; IRBuilder<> Builder(llvmIrInstruction); -// bool lhsConstantQuantized = false; -// bool rhsConstantQuantized = false; + // bool lhsConstantQuantized = false; + // bool rhsConstantQuantized = false; // Ensure operands are correctly converted to fixed-point integers Value * lhs = llvmIrInstruction->getOperand(0); @@ -1883,27 +1961,25 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix if (isa(lhs)) { llvm::errs() << "LHS is a floating-point constant, handling it with handleConstant\n"; - //handleConstant(llvmIrInstruction, quantizedType); + // handleConstant(llvmIrInstruction, quantizedType); simplifyConstant(llvmIrInstruction, quantizedType); - lhs = llvmIrInstruction->getOperand(0); + lhs = llvmIrInstruction->getOperand(0); lhsIsFloat = false; // Update float status as it's now a fixed-point } - - if (isa(rhs)) + if (isa(rhs)) { llvm::errs() << "RHS is a floating-point constant, handling it with handleConstant\n"; - //handleConstant(llvmIrInstruction, quantizedType); + // handleConstant(llvmIrInstruction, quantizedType); simplifyConstant(llvmIrInstruction, quantizedType); rhsIsFloat = false; - rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion - + rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion } -// if (isa(llvmIrInstruction->getOperand(0)) || -// isa(llvmIrInstruction->getOperand(1))) { -// simplifyConstant(llvmIrInstruction, quantizedType); -// } + // if (isa(llvmIrInstruction->getOperand(0)) || + // isa(llvmIrInstruction->getOperand(1))) { + // simplifyConstant(llvmIrInstruction, quantizedType); + // } // If either operand is an integer constant, directly use mul if (isa(lhs) || isa(rhs)) @@ -1915,9 +1991,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix return; } - - - // If either operand is a float, convert both to fixed-point if (lhsIsFloat) { @@ -1931,7 +2004,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; } - // If both operands are integers, but neither is a constant, use fixmul bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); @@ -1944,12 +2016,12 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvmIrInstruction->eraseFromParent(); } -// if (allOperandsAreInt && llvmIrInstruction->getOpcode() == Instruction::FMul) { -// llvm::errs() << "Both operands are integers, converting FMul to Mul\n"; -// Value *mulInst = Builder.CreateMul(lhs, rhs); -// llvmIrInstruction->replaceAllUsesWith(mulInst); -// llvmIrInstruction->eraseFromParent(); -// } + // if (allOperandsAreInt && llvmIrInstruction->getOpcode() == Instruction::FMul) { + // llvm::errs() << "Both operands are integers, converting FMul to Mul\n"; + // Value *mulInst = Builder.CreateMul(lhs, rhs); + // llvmIrInstruction->replaceAllUsesWith(mulInst); + // llvmIrInstruction->eraseFromParent(); + // } } // void @@ -2096,7 +2168,7 @@ handleFNeg(Instruction * inInstruction, Type * quantizedType) // If the operand is a floating-point type, quantize it if (operand->getType()->isFloatingPointTy()) { - //operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); + // operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); operand = Builder.CreateFPToSI(operand, quantizedType); } @@ -2145,7 +2217,6 @@ handleFCmp(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FCmp\n"; } } - void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -2153,17 +2224,58 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - // 递归转换所有层级的类型为量化类型 - Type * newAllocaType = transformToQuantizedType(allocaType, quantizedType); - + // Print the original type for debugging llvm::errs() << "Original alloca type: " << *allocaType << "\n"; - llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; - // Set the new quantized type for the alloca instruction - llvmIrAllocaInstruction->setAllocatedType(newAllocaType); + // Transform to quantized type considering pointers + Type * newAllocaType = nullptr; + if (allocaType->isPointerTy()) + { + // Get the element type of the pointer + Type * elementType = allocaType->getPointerElementType(); + + // Quantize the element type + Type * quantizedElementType = transformToQuantizedType(elementType, quantizedType); + + // Reconstruct the pointer type with the new quantized element type + newAllocaType = PointerType::get(quantizedElementType, allocaType->getPointerAddressSpace()); + } + else + { + // Quantize non-pointer types directly + newAllocaType = transformToQuantizedType(allocaType, quantizedType); + } + + if (newAllocaType) + { + llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; + llvmIrAllocaInstruction->setAllocatedType(newAllocaType); + } + else + { + llvm::errs() << "Error: Failed to quantize the type.\n"; + } } } +// void +// handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) +// { +// auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); +// +// // 递归转换所有层级的类型为量化类型 +// Type * newAllocaType = transformToQuantizedType(allocaType, quantizedType); +// +// llvm::errs() << "Original alloca type: " << *allocaType << "\n"; +// llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; +// +// // Set the new quantized type for the alloca instruction +// llvmIrAllocaInstruction->setAllocatedType(newAllocaType); +// } +// } + // void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { // if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) { // // 获取原始的分配类型 @@ -2252,33 +2364,31 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetNumArgOperands(); ++idx) -// { -// Value * arg = llvmIrCallInstruction->getArgOperand(idx); -// if (arg->getType()->isPointerTy()) -// { -// Type * elementType = arg->getType()->getPointerElementType(); -// if (elementType->isFloatTy() || elementType->isDoubleTy()) -// { -// // Do not change pointer type, handle it when dereferenced -// continue; -// } -// } -// // Set quantized type for the argument if it is not a pointer -// setQuantizedType(arg, quantizedType); -// } + // for (size_t idx = 0; idx < llvmIrCallInstruction->getNumArgOperands(); ++idx) + // { + // Value * arg = llvmIrCallInstruction->getArgOperand(idx); + // if (arg->getType()->isPointerTy()) + // { + // Type * elementType = arg->getType()->getPointerElementType(); + // if (elementType->isFloatTy() || elementType->isDoubleTy()) + // { + // // Do not change pointer type, handle it when dereferenced + // continue; + // } + // } + // // Set quantized type for the argument if it is not a pointer + // setQuantizedType(arg, quantizedType); + // } /* - * for user-defined function, quantize the arguments - * */ + * for user-defined function, quantize the arguments + * */ for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) { setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); } quantizeConstant(llvmIrCallInstruction, quantizedType); - - // Quantize the return type if necessary if (llvmIrCallInstruction->getType()->isPointerTy()) { @@ -2294,36 +2404,45 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector(llvmIrInstruction)) - { - IRBuilder<> Builder(llvmIrStoreInstruction); - - auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); - if (valueType->isFloatTy() || valueType->isDoubleTy()) - { - llvm::errs() << "Original store value type: " << *valueType << "\n"; - llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; - - // Quantize the value operand - auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); - quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); - llvmIrStoreInstruction->setOperand(0, quantizedValue); - } +void handleStore(Instruction* llvmIrInstruction, Type* quantizedType) { + /* + * If either of the operands is constant, change it to a int value + * */ + setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + quantizeConstant(llvmIrInstruction, quantizedType); +} - auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); - if (pointerType->isFloatTy() || pointerType->isDoubleTy()) - { - llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; - llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; - // Set the new quantized type for the pointer operand - llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); - } - } -} +//void +//handleStore(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) +// { +// IRBuilder<> Builder(llvmIrStoreInstruction); +// +// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); +// if (valueType->isFloatTy() || valueType->isDoubleTy()) +// { +// llvm::errs() << "Original store value type: " << *valueType << "\n"; +// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; +// +// // Quantize the value operand +// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); +// quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); +// llvmIrStoreInstruction->setOperand(0, quantizedValue); +// } +// +// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); +// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) +// { +// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; +// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; +// +// // Set the new quantized type for the pointer operand +// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); +// } +// } +//} // Function to handle loads involving pointer types void @@ -2521,15 +2640,15 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) } } - void - handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +void +handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; auto * fptruncInst = cast(llvmIrInstruction); auto srcType = fptruncInst->getSrcTy(); auto destType = fptruncInst->getDestTy(); - Value *operand = llvmIrInstruction->getOperand(0); + Value * operand = llvmIrInstruction->getOperand(0); llvm::errs() << "Operand type before conversion: " << *operand->getType() << "\n"; if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) @@ -2545,13 +2664,15 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) llvmIrInstruction->replaceAllUsesWith(operand); llvmIrInstruction->eraseFromParent(); llvm::errs() << "Removed unnecessary FPTrunc for integer operand\n"; - }else{ + } + else + { llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; } - } +} -//void -//handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +// void +// handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) //{ // llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; // IRBuilder<> Builder(llvmIrInstruction); @@ -2588,7 +2709,7 @@ handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) // llvmIrInstruction->removeFromParent(); // // llvm::errs() << "Finished handling FPTrunc\n"; -//} +// } void handleBitCast(Instruction * llvmIrInstruction, Type * quantizedType) @@ -2653,6 +2774,7 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) // case Instruction::Load: // handleLoad(llvmIrInstruction, quantizedType); // break; + case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: @@ -2980,7 +3102,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } } } - // handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); return; From f1644445572b5322612e2f10dd972b71fa46505a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 21:59:48 +0800 Subject: [PATCH 061/213] update some test files * dev2. --- .../c-files/floating_point_operations.c | 418 ++++++++++++++++-- .../llvm-ir/c-files/test_MadgwickAHRS.c | 170 +++++++ .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 2 +- .../newton/llvm-ir/c-files/test_Original.c | 164 +++++++ .../c-files/test_floating_point_operations.c | 308 +++++++++---- applications/newton/llvm-ir/replace.sh | 26 ++ applications/newton/llvm-ir/run.sh | 30 ++ applications/newton/llvm-ir/testMadgwick.sh | 51 +++ applications/newton/llvm-ir/testOriginal.sh | 24 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 63 ++- .../newton-irPass-LLVMIR-quantization.cpp | 178 ++++---- .../newton-irPass-LLVMIR-quantization.h | 3 +- 12 files changed, 1215 insertions(+), 222 deletions(-) create mode 100644 applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c create mode 100644 applications/newton/llvm-ir/c-files/test_Original.c create mode 100755 applications/newton/llvm-ir/replace.sh create mode 100755 applications/newton/llvm-ir/run.sh create mode 100755 applications/newton/llvm-ir/testMadgwick.sh create mode 100755 applications/newton/llvm-ir/testOriginal.sh diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.c b/applications/newton/llvm-ir/c-files/floating_point_operations.c index 170a0c4bd..69369f278 100644 --- a/applications/newton/llvm-ir/c-files/floating_point_operations.c +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.c @@ -2,66 +2,179 @@ #include #include "floating_point_operations.h" #include +#include +#include +// float invSqrt(float x) { +// float halfx = 0.5f * x; +// float y = x; +// //#pragma unsupported +// long i = *(long*)&y; +// i = 0x5f3759df - (i>>1); +// y = *(float*)&i; +// //#end +// y = y * (1.5f - (halfx * y * y)); +// return y; +// } - //Function to perform basic floating point operations - //Function to perform addition - float perform_addition(float a, float b) { +// double +// my_quantize(double a) +//{ +// return a; +// } +// Function to perform basic floating point operations +// Function to perform addition + float perform_addition(float a, float b) { return a + b; -} - - - -//// Function to perform subtraction - float perform_subtraction(float a, float b) { - return a - b; } -//// -////// Function to perform multiplication +// +// +// +////// Function to perform subtraction +// float perform_subtraction(float a, float b) { +// return a - b; +// } +// +// float perform_constant_subtraction(float a) { +// return 1.5f - a; +// } +// +// float perform_constant_subtraction2(float a) { +// return 0.5 - a; +// } + +//////// +////////// Function to perform multiplication float perform_multiplication(float a, float b) { - //printf("Performing multiplication\ a: %f, b: %f\n", a, b); return a * b; } +//// +// float perform_division(float a, float b) { +// return a / b; +// } - float perform_division(float a, float b) { - return a / b; - } - -//float -//perform_constant_multiplication(float a) +// float +// perform_constant_multiplication(float a) //{ -// return a * 0.21; -//} +// return 0.5*a; +// } +//// +// float +// perform_constant_multiplication2(float a) +// { +// return 0.51*a; +// } // // float -// perform_constant_division(float a) +// perform_constant_multiplication3(float a) +// { +// return 2.0f*a; +// } + +// float +// perform_constant_multiplication4(float a) +// { +// return a*a; +// } +// +// +// +// float +// perform_constant_multiplication3(float a) +// { +// return 2*a; +// } + +// float +// perform_constant_multiplication(float a) +// { +// return 0.5 *a; +// } + +// 2x2矩阵乘法 +// void multiply_matrices(float A[2][2], float B[2][2], float C[2][2]) { +// // 简化的矩阵乘法实现,不使用循环 +// C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0]; +// C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1]; +// C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0]; +// C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1]; +// } + +// 2x2矩阵加法 +// void add_matrices() { +// // 直接对应元素相加 +// float A[2][2] = {{1, 2}, {3, 4}}; +// float B[2][2] = {{5, 6}, {7, 8}}; +// float C[2][2]; // 结果矩阵 +// +// //矩阵加法 +// C[0][0] = A[0][0] + B[0][0]; +// C[0][1] = A[0][1] + B[0][1]; +// C[1][0] = A[1][0] + B[1][0]; +// C[1][1] = A[1][1] + B[1][1]; +// } + +// float +// perform_constant_division(float a) //{ // return a / 0.21; +// } + +// float compute_sin(float angle) { +// return sinf(angle); +// } + +// float compute_cos(float angle) { +// return cosf(angle); +// } + +// float compute_sqrt(float number) { +// return sqrtf(number); +// } + +// double compute_pow_double( double base, double exponent) { +// return powf(base, exponent); +// } + +// float compute_pow(float base, float exponent) { +// return powf(base, exponent); +// } + +// double compute_sqrt_double(double number) { +// double number2 = number +2.14; +// return sqrt(number2); +// +// } + +// 交换两个指针指向的浮点数值 +// void swap_float_values(float *a, float *b) { +// float temp = *a; +// *a = *b; +// *b = temp; //} + // -//float -//perform_operations(float a, float b, float c, float d) +// float +// perform_operations(float a, float b, float c, float d) //{ // float result = a * b; // First multiplication // result = result + c; // Addition // result = result - d; // Subtraction // result = result * 1.5f; // Second multiplication with a constant // return result; -//} +// } - -//float computePolynomial(float x) { +// float computePolynomial(float x) { // float result = 0.0; // for (int i = 0; i < 1000; ++i) { // result += 0.3 * x * x * x + 0.2 * x * x + 0.5 * x + 0.7; // } // return result; -//} -#define PI 3.14159265358979323846 +// } +// #define PI 3.14159265358979323846 - -//void fft(float* real, float* imag, int n) { +// void fft(float* real, float* imag, int n) { // int i, j, k, m, m_max; // float t_real, t_imag, u_real, u_imag, w_real, w_imag, theta; // @@ -101,12 +214,243 @@ // w_imag = t_real * u_imag + w_imag * u_real; // } // } +// } + +// double polynomial(double x) { +// // Evaluate a polynomial: P(x) = 3x^4 - 5x^3 + 2x^2 - x + 7 +// double result = 5.51*x*x*x*x + 0.21*x*x*x + 0.3*x*x + x; +//// return result; +// //double result = x*x*x*x; +// return result; //} -double polynomial(double x) { - // Evaluate a polynomial: P(x) = 3x^4 - 5x^3 + 2x^2 - x + 7 -// double result = 5.51*x*x*x*x - 0.21*x*x*x + 0.3*x*x + x; +// double +// polynomial(double x) +//{ +// // Evaluate a more complex polynomial +// double result = 0.0; +// +// // High-order terms of the polynomial +// for (int i = 1; i <= 10; i++) +// { +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 7.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += pow(x, 16) * i; +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 7.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// } +// +// // Nested operations to increase complexity +// for (int i = 1; i <= 10; i++) +// { +// for (int j = 1; j <= 100; j++) +// { +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 7.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += pow(x, 4) * i * j; +// result += 8.51 * x * x * x * x * x * x ; +// } +// } +// return result; +// } +// + +// // return result; - double result = x*x*x*x; - return result; -} \ No newline at end of file +//} + +// 计算线性回归参数 a (斜率) 和 b (截距) +// void linear_regression(int n, double x[], double y[], double *a, double *b) { +// double sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0; +// int i; +// +// for (i = 0; i < n; i++) { +// sum_x += x[i]; +// sum_y += y[i]; +// sum_xy += x[i] * y[i]; +// sum_xx += x[i] * x[i]; +// } +// +// // 计算斜率 (a) 和截距 (b) +// *a = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x); +// *b = (sum_y - (*a) * sum_x) / n; +//} + +//// 对两个 double 进行加法 +// double add(double a, double b) { +// return a + b; +// } +// +//// 对两个 double 进行乘法 +// double multiply(double a, double b) { +// return a * b; +// } +// +//// 对 double 进行幂运算 +// double power(double base, double exponent) { +// return pow(base, exponent); +// } +// +//// double_add_test 函数的重构版本 +// void double_add_test(double* leftOp, double* rightOp, double* result, size_t iteration_num) { +// for (size_t idx = 0; idx < iteration_num; idx++) { +// double sum = add(leftOp[idx], rightOp[idx]); +// result[idx] = sum; +// +// double x = add(leftOp[idx], 12.789); +// double y = add(rightOp[idx], 15.653); +// double z = add(x, y); +// double pow_result = power(z, 2.5); +// result[idx] = multiply(result[idx], pow_result); +// } +// return; +// } + +// float updateQuaternion_w(float q0, float q1, float q2, float q3, float ax, float ay, float az, float gx, float gy, float gz, float beta, float deltaT) { +// float norm = sqrt(ax * ax + ay * ay + az * az); +// if (norm == 0.0f) return q0; +// norm = 1.0f / norm; +// ax *= norm; +// ay *= norm; +// az *= norm; +// +// float f1 = 2.0f * (q1 * q3 - q0 * q2) - ax; +// float f2 = 2.0f * (q0 * q1 + q2 * q3) - ay; +// float f3 = 1.0f - 2.0f * (q1 * q1 + q2 * q2) - az; +// float J_14or21 = 2.0f * q1; +// +// float step0 = J_14or21 * f2 - 2.0f * q2 * f1; +// norm = sqrt(step0 * step0); +// norm = 1.0f / norm; +// step0 *= norm; +// +// float qDot0 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz) - beta * step0; +// q0 += qDot0 * deltaT; +// +// norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); +// norm = 1.0f / norm; +// return q0 * norm; +// } +// +// float updateQuaternion_x(float q0, float q1, float q2, float q3, float ax, float ay, float az, float gx, float gy, float gz, float beta, float deltaT) { +// float norm = sqrt(ax * ax + ay * ay + az * az); +// if (norm == 0.0f) return q1; +// norm = 1.0f / norm; +// ax *= norm; +// ay *= norm; +// az *= norm; +// +// float f1 = 2.0f * (q1 * q3 - q0 * q2) - ax; +// float f2 = 2.0f * (q0 * q1 + q2 * q3) - ay; +// float f3 = 1.0f - 2.0f * (q1 * q1 + q2 * q2) - az; +// float J_14or21 = 2.0f * q1; +// +// float step1 = 2.0f * q3 * f1 + J_14or21 * f2 - 2.0f * (q0 * f2 + q2 * f1); +// norm = sqrt(step1 * step1); +// norm = 1.0f / norm; +// step1 *= norm; +// +// float qDot1 = 0.5f * (q0 * gx + q2 * gz - q3 * gy) - beta * step1; +// q1 += qDot1 * deltaT; +// +// norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); +// norm = 1.0f / norm; +// return q1 * norm; +// } +// +// float normalizeAccelerometer(float ax, float ay, float az) { +// float norm = sqrt(ax * ax + ay * ay + az * az); +// return norm == 0.0f ? 0.0f : 1.0f / norm; +// } +// +// float computeGradientStep0(float q0, float q1, float q2, float q3, float ax, float ay, float az) { +// float f1 = 2.0f * (q1 * q3 - q0 * q2) - ax; +// float f2 = 2.0f * (q0 * q1 + q2 * q3) - ay; +// float f3 = 1.0f - 2.0f * (q1 * q1 + q2 * q2) - az; +// float J_14or21 = 2.0f * q1; +// +// float step0 = J_14or21 * f2 - 2.0f * q2 * f1; +// float norm = sqrt(step0 * step0); +// norm = 1.0f / norm; +// return step0 * norm; +// } + +typedef uint64_t float64; + +// void float64_add(float64 a, float64 b, float64 *result) { +// // Extract components of first number +// int sign1 = (a >> 63) & 1; +// int exponent1 = (a >> 52) & 0x7FF; +// uint64_t mantissa1 = a & 0xFFFFFFFFFFFFF; +// +// // Normalize mantissa to have an implicit leading one if it's not zero +// if (exponent1 != 0) mantissa1 |= 0x10000000000000; +// +// // Extract components of second number +// int sign2 = (b >> 63) & 1; +// int exponent2 = (b >> 52) & 0x7FF; +// uint64_t mantissa2 = b & 0xFFFFFFFFFFFFF; +// +// if (exponent2 != 0) mantissa2 |= 0x10000000000000; +// +// // Align mantissas +// if (exponent1 > exponent2) { +// mantissa2 >>= (exponent1 - exponent2); +// exponent2 = exponent1; +// } else if (exponent1 < exponent2) { +// mantissa1 >>= (exponent2 - exponent1); +// exponent1 = exponent2; +// } +// +// // Add mantissas (ignore signs for simplicity in this example) +// uint64_t mantissa = mantissa1 + mantissa2; +// +// // Normalize result +// while (mantissa >= (1ULL << 53)) { +// mantissa >>= 1; +// exponent1++; +// } +// +// // Avoid double normalization +// if (exponent1 >= 0x7FF) { +// exponent1 = 0x7FF; +// mantissa = 0; // Produce infinity if overflow +// } else if (mantissa >= (1ULL << 52)) { +// mantissa &= ~(1ULL << 52); +// } +// +// // Combine components back into a double +// *result = ((uint64_t)sign1 << 63) | ((uint64_t)exponent1 << 52) | (mantissa & 0xFFFFFFFFFFFFF); +// } + +//float invSqrt(float x) { +// float halfx = 0.5f * x; +// float y = x; +// //#pragma unsupported +// long i = *(long*)&y; +// i = 0x5f3759df - (i>>1); +// y = *(float*)&i; +// //#end +// y = y * (1.5f - (halfx * y * y)); +// return y; +//} + + +//void +//MadgwickAHRSupdateIMU(float ax, +// float * q0_ptr) +//{ +// float q0 = *q0_ptr; +//} +// +//void +//MadgwickAHRSupdate(float gx, float ax, +// float * q0_ptr) +//{ +// float q0 = *q0_ptr; +// if((ax == 0.0f)) { +// MadgwickAHRSupdateIMU(ax, q0_ptr); +// return; +// } +//} diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c new file mode 100644 index 000000000..c601f8c15 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -0,0 +1,170 @@ +#include +#include +#include +#include +#include +#include +#include +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 +#define ITERATION 1 +/* + * void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + */ +//#include "MadgwickAHRS.h" +extern volatile int32_t q0, q1, q2, q3; +//extern volatile float q0, q1, q2, q3; +//extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +//extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); + +extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); + +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +// Quantization function +int +quantize(float value, int frac_base) +{ + return round(value * frac_base); +} + +// Dequantization function +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); +} + +extern int perform_addition(float a, float b); + +timespec +tic() +{ + + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} +int +main() +{ + /* + * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 + */ + + float num1 = -7.249095917; + float num2 = 26.43893433; + float num3 = -37.16656494; + float num4 = -0.1184487343; + float num5 = 0.001258035656; + float num6 = 0.008988874033; + float num7 = -0.3570917249; + float num8 = 0.3941296637; + float num9 = 9.963726044; + + float num10 = 0.64306622; + float num11 = 0.02828862; + float num12 = -0.00567953; + float num13 = -0.76526684; + + int32_t mag_x = quantize(num1, FRAC_BASE); + int32_t mag_y = quantize(num2, FRAC_BASE); + int32_t mag_z = quantize(num3, FRAC_BASE); + int32_t gyr_x = quantize(num4, FRAC_BASE); + int32_t gyr_y = quantize(num5, FRAC_BASE); + int32_t gyr_z = quantize(num6, FRAC_BASE); + int32_t acc_x = quantize(num7, FRAC_BASE); + int32_t acc_y = quantize(num8, FRAC_BASE); + int32_t acc_z = quantize(num9, FRAC_BASE); + int32_t q0 = quantize(num10, FRAC_BASE); + int32_t q1 = quantize(num11, FRAC_BASE); + int32_t q2 = quantize(num12, FRAC_BASE); + int32_t q3 = quantize(num13, FRAC_BASE); +// float q0 = num10; +// float q1 = num11; +// float q2 = num12; +// float q3 = num13; + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + timespec timer = tic(); + + + MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, + acc_x, acc_y, acc_z, + mag_x, mag_y, mag_z, + &q0, &q1, &q2, &q3); + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index 7aac89b2d..d989b4de4 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -12,7 +12,7 @@ extern volatile int32_t q0, q1, q2, q3; extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); - +extern int32_t sqrt_rsqrt(int32_t x, int recip); // #include "MadgwickAHRS.h" /*************************************** diff --git a/applications/newton/llvm-ir/c-files/test_Original.c b/applications/newton/llvm-ir/c-files/test_Original.c new file mode 100644 index 000000000..18217aa99 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_Original.c @@ -0,0 +1,164 @@ +#include +#include +#include +#include +#include +#include +#include +#define FRAC_Q 10 +#define BIT_WIDTH 32 +#define ITERATION 100000 + +extern volatile float q0, q1, q2, q3; +extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +// Quantization function +int +quantize(float value, int frac_base) +{ + return round(value * frac_base); +} + +// Dequantization function +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); +} + +extern int perform_addition(float a, float b); + +timespec +tic() +{ + + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} +int +main() +{ + /* + * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 + */ + +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; + + float mag_x = -7.249095917; + float mag_y = 26.43893433; + float mag_z = -37.16656494; + float gyr_x = -0.1184487343; + float gyr_y = 0.001258035656; + float gyr_z = 0.008988874033; + float acc_x = -0.3570917249; + float acc_y = 0.3941296637; + float acc_z = 9.963726044; + float q0 = 0.64306622; + float q1 = 0.02828862; + float q2 = -0.00567953; + float q3 = -0.76526684; + + + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + timespec timer = tic(); +// for (size_t ts = 0; ts < DATA_SIZE; ts++) +// { +// MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], +// acc_x[ts], acc_y[ts], acc_z[ts], +// mag_x[ts], mag_y[ts], mag_z[ts], +// &q0[ts], &q1[ts], &q2[ts], &q3[ts]); +// } + + MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, + acc_x, acc_y, acc_z, + mag_x, mag_y, mag_z, + &q0, &q1, &q2, &q3); + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_floating_point_operations.c b/applications/newton/llvm-ir/c-files/test_floating_point_operations.c index 1127e71ad..8db03e799 100644 --- a/applications/newton/llvm-ir/c-files/test_floating_point_operations.c +++ b/applications/newton/llvm-ir/c-files/test_floating_point_operations.c @@ -1,120 +1,264 @@ #include +#include #include - +#include +#include +#include +#include #define FRAC_Q 10 #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 +#define ITERATION 1 + +#include "MadgwickAHRS.h" +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} // Quantization function -int quantize(float value, int frac_base) { +int +quantize(float value, int frac_base) +{ return round(value * frac_base); } // Dequantization function -float dequantize(int quantized_value, int frac_base) { - return (float)quantized_value / frac_base; +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); } -extern int perform_addition(float a, float b); -extern int perform_subtraction(float a, float b); +//extern int perform_addition(float a, float b); +extern int perform_addition(int a ,int b); +// extern int perform_subtraction(float a, float b); //extern int perform_multiplication(int a, int b); +//extern int perform_multiplication(float a, float b); +//extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +//extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); -int main() { - float num1 = 8.10; - printf("num1: %f\n", num1); - float num2 = 2.25; - printf("num2: %f\n", num2); - printf("FRAC_BASE: %d\n", FRAC_BASE); - - int a = 10; - int b = 3; - - float num3 = 0; - float num4 = 0; - float num5 = 0; - float num6 = 0; +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} - int quantized_num1 = quantize(num1, FRAC_BASE); - int quantized_num2 = quantize(num2, FRAC_BASE); +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} - int addition_result = perform_addition(num1, num2); - float actual_addition_result = dequantize(addition_result, FRAC_BASE); +int +main() +{ + int num1 = 10; + int num2 = 3; + //extern int perform_multiplication(int a, int b); + //int num3 = perform_multiplication(num1, num2); + int num4 = perform_addition(num1, num2); + //printf("num3: %d\n", num3); + printf("num4: %d\n", num4); - printf("Addition Result after quantization: %d\n", addition_result); - printf("expected Addition Result after dequantization: %f\n",dequantize(quantized_num1 + quantized_num2, FRAC_BASE)); - printf("Actual Addition Result after dequantization: %f\n", actual_addition_result); - // Perform original operations to compare results later - float original_addition = num1 + num2; - float original_subtraction = num1 - num2; - float original_multiplication = num1 * num2; - float original_division = num1 / num2; - printf("Original Addition: %f\n", original_addition); - printf("Original Subtraction: %f\n", original_subtraction); - int subtraction_result = perform_subtraction(num1, num2); - float actual_subtraction_result = dequantize(subtraction_result, FRAC_BASE); - printf("Subtraction Result after quantization: %d\n", subtraction_result); - printf("Actual Subtraction Result after dequantization: %f\n", actual_subtraction_result); + // float num1 = 8.10; + // printf("num1: %f\n", num1); + // float num2 = 2.25; + // printf("num2: %f\n", num2); + // printf("FRAC_BASE: %d\n", FRAC_BASE); + // + // int a = 10; + // int b = 3; + // + // float num3 = 0; + // float num4 = 0; + // float num5 = 0; + // float num6 = 0; + // + // int quantized_num1 = quantize(num1, FRAC_BASE); + // int quantized_num2 = quantize(num2, FRAC_BASE); + // + // int addition_result = perform_addition(num1, num2); + // float actual_addition_result = dequantize(addition_result); + // + // printf("Addition Result after quantization: %d\n", addition_result); + // printf("expected Addition Result after dequantization: %f\n",dequantize(quantized_num1 + quantized_num2)); + // printf("Actual Addition Result after dequantization: %f\n", actual_addition_result); + // + // + // // Perform original operations to compare results later + // float original_addition = num1 + num2; + // float original_subtraction = num1 - num2; + // float original_multiplication = num1 * num2; + // float original_division = num1 / num2; + // printf("Original Addition: %f\n", original_addition); + // printf("Original Subtraction: %f\n", original_subtraction); + // + // int subtraction_result = perform_subtraction(num1, num2); + // float actual_subtraction_result = dequantize(subtraction_result); + // printf("Subtraction Result after quantization: %d\n", subtraction_result); + // printf("Actual Subtraction Result after dequantization: %f\n", actual_subtraction_result); // Quantization of input numbers + // int subtraction_result = perform_subtraction(a, b); + // printf("Subtraction Result: %d\n", subtraction_result); - //int subtraction_result = perform_subtraction(a, b); - //printf("Subtraction Result: %d\n", subtraction_result); + // // Perform operations on the quantized numbers + // float addition = perform_addition(num1, num2); + // + // float subtraction = perform_subtraction(num1, num2); + // + // float multiplication = perform_multiplication(num1, num2); + // + // float division = perform_division(num1, num2); + // + // + // // Dequantization of actual results + // float my_dequantized_addition = dequantize(addition, FRAC_BASE); + // float my_dequantized_subtraction = dequantize(subtraction, FRAC_BASE); + // float my_dequantized_multiplication = dequantize( multiplication, FRAC_BASE * FRAC_BASE); + // float my_dequantized_division = division; + // + // + // num3 = (my_dequantized_addition + my_dequantized_subtraction) / 2; + // num4 = (my_dequantized_addition -my_dequantized_subtraction) / 2; + // printf("num3: %f\n", num3); + // printf("num4: %f\n", num4); + // + // + // num5 = sqrt(my_dequantized_multiplication*my_dequantized_division); + // printf("xdeduced : %f\n", num5); + // num6 = sqrt(my_dequantized_multiplication/my_dequantized_division); + // printf("num6: %f\n", num6); + // + // + // + // + // printf("Expected after Dequantized Addition: %f\n", dequantize(quantized_num1 + quantized_num2, FRAC_BASE)); + // printf("Actual after Dequantized Addition: %f\n", my_dequantized_addition); + // + // printf("Original Subtraction: %f\n", original_subtraction); + // printf("Expected after Dequantized Subtraction: %f\n", dequantize(quantized_num1 - quantized_num2, FRAC_BASE)); + // printf("Actual after Dequantized Subtraction: %f\n", my_dequantized_subtraction); + // + // printf("Original Multiplication: %f\n", original_multiplication); + // + // printf("Expected after Dequantized Multiplication: %f\n", dequantize((int)((long long)quantized_num1 * quantized_num2), FRAC_BASE * FRAC_BASE)); + // //printf("Expected after Dequantized Multiplication: %f\n", dequantize((quantized_num1 * quantized_num2), (FRAC_BASE * FRAC_BASE))); + // printf("Actual after Dequantized Multiplication: %f\n", my_dequantized_multiplication); + // + // printf("Original Division: %f\n", original_division); + // printf("Expected after Dequantized Division: %f\n", (float)quantized_num1 / quantized_num2 ); + // printf("Actual after Dequantized Division: %f\n", my_dequantized_division); -// // Perform operations on the quantized numbers -// float addition = perform_addition(num1, num2); -// -// float subtraction = perform_subtraction(num1, num2); +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; // -// float multiplication = perform_multiplication(num1, num2); +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; // -// float division = perform_division(num1, num2); -// -// -// // Dequantization of actual results -// float my_dequantized_addition = dequantize(addition, FRAC_BASE); -// float my_dequantized_subtraction = dequantize(subtraction, FRAC_BASE); -// float my_dequantized_multiplication = dequantize( multiplication, FRAC_BASE * FRAC_BASE); -// float my_dequantized_division = division; -// -// -// num3 = (my_dequantized_addition + my_dequantized_subtraction) / 2; -// num4 = (my_dequantized_addition -my_dequantized_subtraction) / 2; -// printf("num3: %f\n", num3); -// printf("num4: %f\n", num4); -// -// -// num5 = sqrt(my_dequantized_multiplication*my_dequantized_division); -// printf("xdeduced : %f\n", num5); -// num6 = sqrt(my_dequantized_multiplication/my_dequantized_division); -// printf("num6: %f\n", num6); -// -// -// -// - -// printf("Expected after Dequantized Addition: %f\n", dequantize(quantized_num1 + quantized_num2, FRAC_BASE)); -// printf("Actual after Dequantized Addition: %f\n", my_dequantized_addition); +// int32_t mag_x = quantize(num1, FRAC_BASE); +// int32_t mag_y = quantize(num2, FRAC_BASE); +// int32_t mag_z = quantize(num3, FRAC_BASE); +// int32_t gyr_x = quantize(num4, FRAC_BASE); +// int32_t gyr_y = quantize(num5, FRAC_BASE); +// int32_t gyr_z = quantize(num6, FRAC_BASE); +// int32_t acc_x = quantize(num7, FRAC_BASE); +// int32_t acc_y = quantize(num8, FRAC_BASE); +// int32_t acc_z = quantize(num9, FRAC_BASE); +// int32_t q0 = quantize(num10, FRAC_BASE); +// int32_t q1 = quantize(num11, FRAC_BASE); +// int32_t q2 = quantize(num12, FRAC_BASE); +// int32_t q3 = quantize(num13, FRAC_BASE); // -// printf("Original Subtraction: %f\n", original_subtraction); -// printf("Expected after Dequantized Subtraction: %f\n", dequantize(quantized_num1 - quantized_num2, FRAC_BASE)); -// printf("Actual after Dequantized Subtraction: %f\n", my_dequantized_subtraction); +// u_int64_t time_slots[ITERATION]; // -// printf("Original Multiplication: %f\n", original_multiplication); +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// // for (size_t ts = 0; ts < DATA_SIZE; ts++) +// //{ +// // MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], +// // acc_x[ts], acc_y[ts], acc_z[ts], +// // mag_x[ts], mag_y[ts], mag_z[ts], +// // &q0[ts], &q1[ts], &q2[ts], &q3[ts]); +// //} // -// printf("Expected after Dequantized Multiplication: %f\n", dequantize((int)((long long)quantized_num1 * quantized_num2), FRAC_BASE * FRAC_BASE)); -// //printf("Expected after Dequantized Multiplication: %f\n", dequantize((quantized_num1 * quantized_num2), (FRAC_BASE * FRAC_BASE))); -// printf("Actual after Dequantized Multiplication: %f\n", my_dequantized_multiplication); +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// acc_x, acc_y, acc_z, +// mag_x, mag_y, mag_z, +// &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// } // -// printf("Original Division: %f\n", original_division); -// printf("Expected after Dequantized Division: %f\n", (float)quantized_num1 / quantized_num2 ); -// printf("Actual after Dequantized Division: %f\n", my_dequantized_division); +// u_int64_t average_time = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// average_time += time_slots[idx]; +// } +// average_time /= ITERATION; +// printf("average time = %lu nm\n", average_time); return 0; } \ No newline at end of file diff --git a/applications/newton/llvm-ir/replace.sh b/applications/newton/llvm-ir/replace.sh new file mode 100755 index 000000000..49ddee761 --- /dev/null +++ b/applications/newton/llvm-ir/replace.sh @@ -0,0 +1,26 @@ +#!/bin/bash + +# Define the file path +file="MadgwickAHRS_opt.ll" + +# Check if the file exists +if [ ! -f "$file" ]; then + echo "Error: File does not exist." + exit 1 +fi + +# Use sed to replace the text +sed -i 's/declare dso_local i32 @printf(i8\*)/declare dso_local i32 @printf(i8\*, i32)/g' "$file" + +# Replace float** with i32** first to avoid conflicting replacements +sed -i 's/float\*\*/i32**/g' "$file" + +# Replace float* with i32* +sed -i 's/float\*/i32*/g' "$file" + + + + + + +echo "Replacement complete." diff --git a/applications/newton/llvm-ir/run.sh b/applications/newton/llvm-ir/run.sh new file mode 100755 index 000000000..069d11f8e --- /dev/null +++ b/applications/newton/llvm-ir/run.sh @@ -0,0 +1,30 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +echo "Step 7: Compile the assembly file to object file" +clang -c $USER_HOME/CoSense/applications/newton/llvm-ir/out.s -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +/home/xyf/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh new file mode 100755 index 000000000..55974be83 --- /dev/null +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -0,0 +1,51 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +FILE_PATH="$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" + + #Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd $USER_HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $USER_HOME/CoSense/applications/newton/sensors/test.nt +# +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +#llvm-dis $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll +cd $USER_HOME/CoSense/applications/newton/llvm-ir/&& +./replace.sh $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll + +python3 replace.py + + +# Step 4: Optimize the generated LLVM IR file +echo "Step 4: Optimize the generated LLVM IR file" +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +echo "Step 7: Compile the assembly file to object file" +clang -c $USER_HOME/CoSense/applications/newton/llvm-ir/out.s -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +/home/xyf/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh new file mode 100755 index 000000000..6d49eae92 --- /dev/null +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -0,0 +1,24 @@ +# Step 1: Generate LLVM IR file +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + + +# Step 4: Optimize the generated LLVM IR file +opt /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --simplifycfg --instsimplify -O3 -Os -S -o /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace -o /home/xyf/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc /home/xyf/CoSense/applications/newton/llvm-ir/out.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c /home/xyf/CoSense/applications/newton/llvm-ir/out.s -o /home/xyf/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/libout.a /home/xyf/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +clang /home/xyf/CoSense/applications/newton/llvm-ir/c-files/test_Original.c -no-pie -L/home/xyf/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o /home/xyf/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +/home/xyf/CoSense/applications/newton/llvm-ir/main_out diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index c9b115bfa..76b93fd21 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -79,6 +79,49 @@ using namespace llvm; + +void handleSpecialNumber(llvm::Module &M, llvm::LLVMContext &Context) { + llvm::errs() << "Starting special number handling in the module.\n"; + + // 创建一个特定的 NaN 值(双精度浮点数的 IEEE NaN 表示) + uint64_t NaNBits = 0x7FF8000000000000; + llvm::APFloat SpecialNaN(llvm::APFloat::IEEEdouble(), llvm::APInt(64, NaNBits)); + + // 创建一个特殊的整数值作为替换,这里使用 INT_MAX + llvm::Constant *specialValue = llvm::ConstantInt::get(llvm::Type::getInt32Ty(Context), 0x7FFFFFFF); + + // 遍历模块中的所有函数和指令 + for (llvm::Function &F : M) { + for (llvm::BasicBlock &BB : F) { + for (llvm::Instruction &I : BB) { + if (auto *fpInst = llvm::dyn_cast(&I)) { + for (unsigned opIdx = 0; opIdx < fpInst->getNumOperands(); ++opIdx) { + if (auto *cst = llvm::dyn_cast(fpInst->getOperand(opIdx))) { + // 使用 bitcast 检查操作数是否是特定的 NaN 值 + if (cst->getValueAPF().bitcastToAPInt() == SpecialNaN.bitcastToAPInt()) { + // 日志输出 + llvm::errs() << "Replacing special NaN in function " << F.getName() << " at instruction " << *fpInst << "\n"; + + // 替换操作数 + fpInst->setOperand(opIdx, specialValue); + } + } + } + } + } + } + } + + llvm::errs() << "Completed special number handling in the module.\n"; +} + + + + + + + + // Function to save the IR of a module to a file void saveModuleIR(llvm::Module &M, const std::string &fileName) { std::error_code EC; @@ -559,15 +602,31 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl eraseOldGlobals(); + + // Perform text replacement to remove "_quantized" suffixes removeQuantizedSuffixInModule(*Mod); + //correct fmul + //correctAllFMulsInModule(Mod.get()); + + eraseOldInstructions(); + + + + + + + // 处理特殊的数值 + //handleSpecialNumber(*Mod, Mod->getContext()); + + // Save the optimized IR to a file - //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); //finalCorrectionPass(*Mod, quantizedType); //finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization - //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 480a38381..2a3535d66 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -272,6 +272,7 @@ shouldSkipFunction(const std::string & functionName) "floatIntMul", "fixdiv", "fixsqrt", + "fixrsqrt" "constantMulDiv", "sinf", "llvm.sqrt.f64", @@ -1062,58 +1063,7 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->removeFromParent(); } -// llvm::Function * -// createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -//{ -// // check if irModule is valid -// if (!irModule) -// { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixSqrtFuncName = "fixsqrt"; -// for (auto & function : *irModule) -// { -// if (function.getName() == fixSqrtFuncName) -// { -// llvm::errs() << "fixsqrt already exists\n"; -// return &function; -// } -// } -// -// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixsqrt", irModule); -// -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// -// llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value * x = &*args++; -// -// // Initial approximation: x / 2 -// llvm::Value * approx = builder.CreateLShr(x, 1); -// llvm::Value * halfBase = builder.CreateLShr(ConstantInt::get(quantizedType, FRAC_BASE), 1); -// -// for (int i = 0; i < 5; ++i) -// { // Run the approximation a few times -// llvm::Value * div = builder.CreateSDiv(x, approx); -// llvm::Value * avg = builder.CreateAdd(approx, div); -// approx = builder.CreateLShr(avg, 1); // approx = (approx + x / approx) / 2 -// } -// -// llvm::Value * result = approx; // Final square root approximation -// -// // Apply scaling: multiply by FRAC_BASE to maintain fixed-point representation -// result = builder.CreateMul(result, halfBase); -// -// builder.CreateRet(result); -// -// // Add the created function to the vector of functions to be inserted -// functionsToInsert.push_back(func); -// -// return func; -// } + llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1430,49 +1380,75 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vector& functionsToInsert) { -// -// // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) -// llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// // Create the function and insert it into the module -// llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); -// -// llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// -// // Get the function argument (x) -// llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value* x = &*args++; -// -// // Create the fixed-point multiplication function -// llvm::Function* fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); -// -// // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); -// llvm::Value* halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); -// -// // Step 2: Convert x to floating-point and perform the initial approximation -// llvm::Value* fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); -// llvm::Value* i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); -// i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); -// fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); -// -// // Step 3: int_y = fp_y * FRAC_BASE; -// llvm::Value* int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); -// -// // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); -// llvm::Value* mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); -// llvm::Value* mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); -// llvm::Value* correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); -// llvm::Value* final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); -// -// -// // Return the final fixed-point result -// builder.CreateRet(final_y); -// functionsToInsert.emplace_back(func); -// -// return func; -//} + + + //Create a fixed-point reversed square root function + llvm::Function* createFixRsqrt(llvm::Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { + + llvm::errs() << "Entering createFixRsqrt\n"; + + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixrsqrtFuncName = "fixrsqrt"; + for (auto & function : *irModule) + { + if (function.getName() == fixrsqrtFuncName) + { + llvm::errs() << "fixrsqrt already exists\n"; + return &function; + } + } + + // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) + llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + // Create the function and insert it into the module + llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); + + llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + + // Get the function argument (x) + llvm::Function::arg_iterator args = func->arg_begin(); + llvm::Value* x = &*args++; + + // Create the fixed-point multiplication function + llvm::Function* fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); + + // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); + llvm::Value* halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); + + // Step 2: Convert x to floating-point and perform the initial approximation + llvm::Value* fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); + //fdiv + + + + + llvm::Value* i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); + i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + + // Step 3: int_y = fp_y * FRAC_BASE; + llvm::Value* int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + + // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); + llvm::Value* mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + llvm::Value* mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + llvm::Value* correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value* final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + + + // Return the final fixed-point result + builder.CreateRet(final_y); + functionsToInsert.emplace_back(func); + + return func; +} // Quantize simple floating-point instructions CmpInst::Predicate @@ -2305,6 +2281,7 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) // } void +//handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt) { Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); @@ -2337,10 +2314,11 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName() == "rsqrt") { - // Function* fixrsqrt = createFixRsqrt(irModule, quantizedType); - // handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); - // } +// else if (funcName == "invSqrt") +// { +// //Function * fixrsqrt = createFixRsqrt(irModule, quantizedType); +// handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); +// } else if (funcName == "sin") { @@ -2891,6 +2869,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + //llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); /* * quantize the arguments type @@ -2913,6 +2892,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: + //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt,fixrsqrt); handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt); break; case Instruction::GetElementPtr: diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 62f5a6ab2..7826b6f4e 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -37,13 +37,14 @@ extern "C" #endif /* __cplusplus */ extern std::vector functionsToErase; extern std::vector globalsToErase; +extern std::vector instructionsToErase; void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); extern void eraseOldFunctions(); -extern +void eraseOldInstructions(); void eraseOldGlobals(); #ifdef __cplusplus } /* extern "C" */ From 96ca7a1ae1f0cf3c422c9fc8c560f39b24617284 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 22:30:36 +0800 Subject: [PATCH 062/213] update testfile * dev2. --- ...260ad3f710627bd8d432d40a897b7a5239516e.txt | 48 +++++++++++++++ ...88e2d6c3c11fb59f0d4c70b57122722b202729.txt | 48 +++++++++++++++ .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 61 +++++++++++++++++++ 3 files changed, 157 insertions(+) create mode 100644 analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt create mode 100644 analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt diff --git a/analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt b/analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt new file mode 100644 index 000000000..0f26162a7 --- /dev/null +++ b/analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt @@ -0,0 +1,48 @@ + +changeset: 1655:2f260ad3f710627bd8d432d40a897b7a5239516e +char kNewtonVersion[] = "0.3-alpha-1655 (2f260ad3f710627bd8d432d40a897b7a5239516e) (build 08-19-2024-21:59-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt b/analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt new file mode 100644 index 000000000..b7a9ab2f2 --- /dev/null +++ b/analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt @@ -0,0 +1,48 @@ + +changeset: 1654:aa88e2d6c3c11fb59f0d4c70b57122722b202729 +char kNewtonVersion[] = "0.3-alpha-1654 (aa88e2d6c3c11fb59f0d4c70b57122722b202729) (build 08-18-2024-11:20-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index d989b4de4..1e4dcef2a 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -19,6 +19,23 @@ extern int32_t sqrt_rsqrt(int32_t x, int recip); * Timer functions of the test framework ***************************************/ + +typedef struct { + int quantized1; + int quantized2; + int quantized3; + int quantized4; +} QuantizedValues; + +typedef struct{ + float dequantized1; + float dequantized2; + float dequantized3; + float dequantized4; +} DequantizedValues; + + + typedef struct timespec timespec; timespec diff(timespec start, timespec end) @@ -67,6 +84,24 @@ quantize(float value, int frac_base) return round(value * frac_base); } +QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { + QuantizedValues result; + result.quantized1 = round(value1 * frac_base); + result.quantized2 = round(value2 * frac_base); + result.quantized3 = round(value3 * frac_base); + result.quantized4 = round(value4 * frac_base); + return result; +} + +DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { + DequantizedValues result; + result.dequantized1 = (float)(quantized1 >> FRAC_Q); + result.dequantized2 = (float)(quantized2 >> FRAC_Q); + result.dequantized3 = (float)(quantized3 >> FRAC_Q); + result.dequantized4 = (float)(quantized4 >> FRAC_Q); + return result; +} + // Dequantization function float dequantize(int quantized_value) @@ -118,6 +153,12 @@ main() float num12 = -0.00567953; float num13 = -0.76526684; + //result + int num14 = 657; + int num15 = 28; + int num16 = -6; + int num17 = -785; + int32_t mag_x = quantize(num1, FRAC_BASE); int32_t mag_y = quantize(num2, FRAC_BASE); int32_t mag_z = quantize(num3, FRAC_BASE); @@ -152,6 +193,22 @@ main() time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } + //tic toc quantize4耗时 + timespec timer = tic(); + QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); + timespec durationQuantize = toc(&timer, "Quantize"); + printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); + + + //tic toc dequantize4耗时 + timer = tic(); + DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); + timespec durationDequantize = toc(&timer, "Dequantize"); + printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); + + + + u_int64_t average_time = 0; for (size_t idx = 0; idx < ITERATION; idx++) { @@ -159,4 +216,8 @@ main() } average_time /= ITERATION; printf("average time = %lu nm\n", average_time); + + //计算total time, quantize+dequantize+average + u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; + printf("total time = %lu nm\n", totalTime); } \ No newline at end of file From fe815c6275474688987f46fc84e177dfe1553a1b Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 22:43:36 +0800 Subject: [PATCH 063/213] continue update testfiles * dev2. --- ...644445572b5322612e2f10dd972b71fa46505a.txt | 48 +++++++++++++++ .../llvm-ir/c-files/test_MadgwickAHRS.c | 58 +++++++++++++++++++ .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 2 + .../newton/llvm-ir/c-files/test_Original.c | 44 +++++++++++++- 4 files changed, 151 insertions(+), 1 deletion(-) create mode 100644 analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt diff --git a/analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt b/analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt new file mode 100644 index 000000000..be86f9458 --- /dev/null +++ b/analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt @@ -0,0 +1,48 @@ + +changeset: 1656:f1644445572b5322612e2f10dd972b71fa46505a +char kNewtonVersion[] = "0.3-alpha-1656 (f1644445572b5322612e2f10dd972b71fa46505a) (build 08-19-2024-22:30-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c index c601f8c15..3f21ced34 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -28,6 +28,19 @@ extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax /*************************************** * Timer functions of the test framework ***************************************/ +typedef struct { + int quantized1; + int quantized2; + int quantized3; + int quantized4; +} QuantizedValues; + +typedef struct{ + float dequantized1; + float dequantized2; + float dequantized3; + float dequantized4; +} DequantizedValues; typedef struct timespec timespec; timespec @@ -84,6 +97,24 @@ dequantize(int quantized_value) return (float)(quantized_value >> FRAC_Q); } +QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { + QuantizedValues result; + result.quantized1 = round(value1 * frac_base); + result.quantized2 = round(value2 * frac_base); + result.quantized3 = round(value3 * frac_base); + result.quantized4 = round(value4 * frac_base); + return result; +} + +DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { + DequantizedValues result; + result.dequantized1 = (float)(quantized1 >> FRAC_Q); + result.dequantized2 = (float)(quantized2 >> FRAC_Q); + result.dequantized3 = (float)(quantized3 >> FRAC_Q); + result.dequantized4 = (float)(quantized4 >> FRAC_Q); + return result; +} + extern int perform_addition(float a, float b); timespec @@ -146,6 +177,13 @@ main() // float q2 = num12; // float q3 = num13; + + //result + int num14 = 657; + int num15 = 28; + int num16 = -6; + int num17 = -785; + u_int64_t time_slots[ITERATION]; for (size_t idx = 0; idx < ITERATION; idx++) @@ -160,6 +198,22 @@ main() time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } + + //tic toc quantize4耗时 + timespec timer = tic(); + QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); + timespec durationQuantize = toc(&timer, "Quantize"); + printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); + + + //tic toc dequantize4耗时 + timer = tic(); + DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); + timespec durationDequantize = toc(&timer, "Dequantize"); + printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); + + + u_int64_t average_time = 0; for (size_t idx = 0; idx < ITERATION; idx++) { @@ -167,4 +221,8 @@ main() } average_time /= ITERATION; printf("average time = %lu nm\n", average_time); + + //计算total time, quantize+dequantize+average + u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; + printf("total time = %lu nm\n", totalTime); } \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index 1e4dcef2a..533b8c3d0 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -209,6 +209,8 @@ main() + + u_int64_t average_time = 0; for (size_t idx = 0; idx < ITERATION; idx++) { diff --git a/applications/newton/llvm-ir/c-files/test_Original.c b/applications/newton/llvm-ir/c-files/test_Original.c index 18217aa99..03522beec 100644 --- a/applications/newton/llvm-ir/c-files/test_Original.c +++ b/applications/newton/llvm-ir/c-files/test_Original.c @@ -7,7 +7,7 @@ #include #define FRAC_Q 10 #define BIT_WIDTH 32 -#define ITERATION 100000 +#define ITERATION 1 extern volatile float q0, q1, q2, q3; extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, @@ -18,8 +18,23 @@ extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float /*************************************** * Timer functions of the test framework ***************************************/ +typedef struct { + int quantized1; + int quantized2; + int quantized3; + int quantized4; +} QuantizedValues; + +typedef struct{ + float dequantized1; + float dequantized2; + float dequantized3; + float dequantized4; +} DequantizedValues; typedef struct timespec timespec; + + timespec diff(timespec start, timespec end) { @@ -74,6 +89,24 @@ dequantize(int quantized_value) return (float)(quantized_value >> FRAC_Q); } +QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { + QuantizedValues result; + result.quantized1 = round(value1 * frac_base); + result.quantized2 = round(value2 * frac_base); + result.quantized3 = round(value3 * frac_base); + result.quantized4 = round(value4 * frac_base); + return result; +} + +DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { + DequantizedValues result; + result.dequantized1 = (float)(quantized1 >> FRAC_Q); + result.dequantized2 = (float)(quantized2 >> FRAC_Q); + result.dequantized3 = (float)(quantized3 >> FRAC_Q); + result.dequantized4 = (float)(quantized4 >> FRAC_Q); + return result; +} + extern int perform_addition(float a, float b); timespec @@ -133,6 +166,13 @@ main() float q3 = -0.76526684; + //result + int num14 = 657; + int num15 = 28; + int num16 = -6; + int num17 = -785; + + u_int64_t time_slots[ITERATION]; @@ -154,6 +194,8 @@ main() time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } + + u_int64_t average_time = 0; for (size_t idx = 0; idx < ITERATION; idx++) { From 49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 22:46:06 +0800 Subject: [PATCH 064/213] update Original c file * dev2. --- ...ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt | 48 ++++ .../newton/llvm-ir/c-files/MadgwickAHRS.c | 270 ++++++++++++++++++ 2 files changed, 318 insertions(+) create mode 100644 analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRS.c diff --git a/analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt b/analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt new file mode 100644 index 000000000..8c6d092e3 --- /dev/null +++ b/analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt @@ -0,0 +1,48 @@ + +changeset: 1657:96ca7a1ae1f0cf3c422c9fc8c560f39b24617284 +char kNewtonVersion[] = "0.3-alpha-1657 (96ca7a1ae1f0cf3c422c9fc8c560f39b24617284) (build 08-19-2024-22:43-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c new file mode 100644 index 000000000..e4fd42fc0 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -0,0 +1,270 @@ +//===================================================================================================== +// MadgwickAHRS.c +//===================================================================================================== +// +// Implementation of Madgwick's IMU and AHRS algorithms. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised +// +//===================================================================================================== + +//--------------------------------------------------------------------------------------------------- +// Header files + +#include "MadgwickAHRS.h" + +//--------------------------------------------------------------------------------------------------- +// Definitions + +// #define sampleFreq 512.0f // sample frequency in Hz +// #define sampleFreq 100.0f // sample frequency in Hz +#define sampleFreq 28.0f // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain + +#ifndef lowerBound +#define lowerBound -16 +#endif +#ifndef upperBound +#define upperBound 16 +#endif + +//--------------------------------------------------------------------------------------------------- +// Variable definitions + +volatile float beta = betaDef; // 2 * proportional gain (Kp) +//volatile float q0 = 0.64306622f, q1 = 0.02828862f, q2 = -0.00567953f, q3 = -0.76526684f; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +// 1/sqrtf(); +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + //#pragma unsupported + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + //#end + y = y * (1.5f - (halfx * y * y)); + return y; +} + +//==================================================================================================== +// Functions + +//--------------------------------------------------------------------------------------------------- +// AHRS algorithm update + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; +#ifdef ASSUME + __builtin_assume(ax > lowerBound && ax < upperBound); + __builtin_assume(ay > lowerBound && ay < upperBound); + __builtin_assume(az > lowerBound && az < upperBound); +#endif + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float hx, hy; + float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + // printf("1: %f\n", recipNorm); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2.0f * q0 * mx; + _2q0my = 2.0f * q0 * my; + _2q0mz = 2.0f * q0 * mz; + _2q1mx = 2.0f * q1 * mx; + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _2q0q2 = 2.0f * q0 * q2; + _2q2q3 = 2.0f * q2 * q3; + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; + hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; + _2bx = sqrt(hx * hx + hy * hy); + _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", + // q0, q1, q2, q3, recipNorm); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + + //q0,q1,q2,q3 + printf("Final quaternion value: q0 = %d\n", q0); + printf("Final quaternion value: q1 = %d\n", q1); + printf("Final quaternion value: q2 = %d\n", q2); + printf("Final quaternion value: q3 = %d\n", q3); + + // printf("Original: q0 = %f\n", q0); +} + +//--------------------------------------------------------------------------------------------------- +// IMU algorithm update + +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _4q0 = 4.0f * q0; + _4q1 = 4.0f * q1; + _4q2 = 4.0f * q2; + _8q1 = 8.0f * q1; + _8q2 = 8.0f * q2; + q0q0 = q0 * q0; + q1q1 = q1 * q1; + q2q2 = q2 * q2; + q3q3 = q3 * q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; + s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; + s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; + s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} + +//==================================================================================================== +// END OF CODE +//==================================================================================================== \ No newline at end of file From c4b170bb8c985e6abee8cdadc4591c25d59bffe8 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 22:47:55 +0800 Subject: [PATCH 065/213] update Original c file * dev2. --- ...815c6275474688987f46fc84e177dfe1553a1b.txt | 48 +++++++++++++++++++ .../newton/llvm-ir/c-files/MadgwickAHRS.c | 8 ++-- 2 files changed, 52 insertions(+), 4 deletions(-) create mode 100644 analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt diff --git a/analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt b/analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt new file mode 100644 index 000000000..9b28df523 --- /dev/null +++ b/analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt @@ -0,0 +1,48 @@ + +changeset: 1658:fe815c6275474688987f46fc84e177dfe1553a1b +char kNewtonVersion[] = "0.3-alpha-1658 (fe815c6275474688987f46fc84e177dfe1553a1b) (build 08-19-2024-22:46-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index e4fd42fc0..40d6dc1b3 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -177,10 +177,10 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float *q3_ptr = q3; //q0,q1,q2,q3 - printf("Final quaternion value: q0 = %d\n", q0); - printf("Final quaternion value: q1 = %d\n", q1); - printf("Final quaternion value: q2 = %d\n", q2); - printf("Final quaternion value: q3 = %d\n", q3); +// printf("Final quaternion value: q0 = %d\n", q0); +// printf("Final quaternion value: q1 = %d\n", q1); +// printf("Final quaternion value: q2 = %d\n", q2); +// printf("Final quaternion value: q3 = %d\n", q3); // printf("Original: q0 = %f\n", q0); } From d75c826fb59c52307c5b9b3dec1480b538830566 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 22:54:33 +0800 Subject: [PATCH 066/213] update test script * dev2. --- ...a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt | 48 +++++++++++++++++++ .../newton/llvm-ir/testMadgwickfix.sh | 18 +++---- applications/newton/llvm-ir/testOriginal.sh | 19 ++++---- 3 files changed, 69 insertions(+), 16 deletions(-) create mode 100644 analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt diff --git a/analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt b/analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt new file mode 100644 index 000000000..63546089a --- /dev/null +++ b/analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt @@ -0,0 +1,48 @@ + +changeset: 1659:49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0 +char kNewtonVersion[] = "0.3-alpha-1659 (49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0) (build 08-19-2024-22:47-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/testMadgwickfix.sh b/applications/newton/llvm-ir/testMadgwickfix.sh index 90004a6d2..2ee1f17e2 100755 --- a/applications/newton/llvm-ir/testMadgwickfix.sh +++ b/applications/newton/llvm-ir/testMadgwickfix.sh @@ -1,24 +1,26 @@ +USER_HOME=$HOME + # Step 1: Generate LLVM IR file -clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c # Step 4: Optimize the generated LLVM IR file -opt /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll --simplifycfg --instsimplify -O3 -Os -S -o /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll --simplifycfg --instsimplify -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.llperformace # Step 5: Compile the optimized LLVM IR file to bitcode -llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace -o /home/xyf/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly -llc /home/xyf/CoSense/applications/newton/llvm-ir/out.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/out.s +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s # Step 7: Compile the assembly file to object file -clang -c /home/xyf/CoSense/applications/newton/llvm-ir/out.s -o /home/xyf/CoSense/applications/newton/llvm-ir/out.o +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 8: Package the object file into a static library -ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/libout.a /home/xyf/CoSense/applications/newton/llvm-ir/out.o +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library -clang /home/xyf/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L/home/xyf/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o /home/xyf/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable -/home/xyf/CoSense/applications/newton/llvm-ir/main_out +$HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index 6d49eae92..fd4822805 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -1,24 +1,27 @@ +USER_HOME=$HOME + + # Step 1: Generate LLVM IR file -clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 4: Optimize the generated LLVM IR file -opt /home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --simplifycfg --instsimplify -O3 -Os -S -o /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --simplifycfg --instsimplify -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.llperformace # Step 5: Compile the optimized LLVM IR file to bitcode -llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/out.llperformace -o /home/xyf/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly -llc /home/xyf/CoSense/applications/newton/llvm-ir/out.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/out.s +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s # Step 7: Compile the assembly file to object file -clang -c /home/xyf/CoSense/applications/newton/llvm-ir/out.s -o /home/xyf/CoSense/applications/newton/llvm-ir/out.o +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 8: Package the object file into a static library -ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/libout.a /home/xyf/CoSense/applications/newton/llvm-ir/out.o +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library -clang /home/xyf/CoSense/applications/newton/llvm-ir/c-files/test_Original.c -no-pie -L/home/xyf/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o /home/xyf/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_Original.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable -/home/xyf/CoSense/applications/newton/llvm-ir/main_out +$HOME/CoSense/applications/newton/llvm-ir/main_out From a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 22:58:17 +0800 Subject: [PATCH 067/213] update replace script * dev2. --- ...b170bb8c985e6abee8cdadc4591c25d59bffe8.txt | 48 ++++++++++++ applications/newton/llvm-ir/replace.py | 77 +++++++++++++++++++ 2 files changed, 125 insertions(+) create mode 100644 analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt create mode 100644 applications/newton/llvm-ir/replace.py diff --git a/analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt b/analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt new file mode 100644 index 000000000..217a24e27 --- /dev/null +++ b/analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt @@ -0,0 +1,48 @@ + +changeset: 1660:c4b170bb8c985e6abee8cdadc4591c25d59bffe8 +char kNewtonVersion[] = "0.3-alpha-1660 (c4b170bb8c985e6abee8cdadc4591c25d59bffe8) (build 08-19-2024-22:54-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/replace.py b/applications/newton/llvm-ir/replace.py new file mode 100644 index 000000000..d0128c20e --- /dev/null +++ b/applications/newton/llvm-ir/replace.py @@ -0,0 +1,77 @@ +import re + + +def replace_function_content(filepath, old_content, new_content): + # 读取文件内容 + with open(filepath, 'r', encoding='utf-8') as file: + content = file.read() + + # 替换函数定义 + #updated_content = re.sub(re.escape(old_content), new_content, content, flags=re.DOTALL) + + # 定义要匹配的模式 + pattern = re.compile(r'(define dso_local i32 @invSqrt\(i32 %0\) #1 .*?\{.*?^\})', re.DOTALL | re.MULTILINE) + + # 替换函数定义 + updated_content = pattern.sub(new_content, content) + + # 将更新后的内容写回文件 + with open(filepath, 'w', encoding='utf-8') as file: + file.write(updated_content) + + + + +# 定义新函数内容 +new_content = """define dso_local i32 @invSqrt(i32 %0) #0 !dbg !61 { + %2 = alloca i32, align 4 + %3 = alloca i32, align 4 + %4 = alloca float, align 4 + %5 = alloca i64, align 8 + %6 = alloca i32, align 4 + store i32 %0, i32* %2, align 4 + call void @llvm.dbg.declare(metadata i32* %2, metadata !62, metadata !DIExpression()), !dbg !63 + call void @llvm.dbg.declare(metadata i32* %3, metadata !64, metadata !DIExpression()), !dbg !65 + %7 = load i32, i32* %2, align 4, !dbg !66 + %8 = call i32 @fixmul(i32 512, i32 %7), !dbg !67 + store i32 %8, i32* %3, align 4, !dbg !65 + call void @llvm.dbg.declare(metadata float* %4, metadata !68, metadata !DIExpression()), !dbg !69 + %9 = load i32, i32* %2, align 4, !dbg !70 + %10 = sitofp i32 %9 to float, !dbg !71 + %11 = fdiv float %10, 1.024000e+03, !dbg !72 + store float %11, float* %4, align 4, !dbg !69 + call void @llvm.dbg.declare(metadata i64* %5, metadata !73, metadata !DIExpression()), !dbg !74 + %12 = bitcast float* %4 to i64*, !dbg !75 + %13 = load i64, i64* %12, align 4, !dbg !75 + store i64 %13, i64* %5, align 8, !dbg !74 + %14 = load i64, i64* %5, align 8, !dbg !76 + %15 = ashr i64 %14, 1, !dbg !77 + %16 = sub nsw i64 1597463007, %15, !dbg !78 + store i64 %16, i64* %5, align 8, !dbg !79 + %17 = bitcast i64* %5 to float*, !dbg !80 + %18 = load float, float* %17, align 8, !dbg !80 + store float %18, float* %4, align 4, !dbg !81 + call void @llvm.dbg.declare(metadata i32* %6, metadata !82, metadata !DIExpression()), !dbg !83 + %19 = load float, float* %4, align 4, !dbg !84 + %20 = fmul float %19, 1.024000e+03, !dbg !85 + %21 = fptosi float %20 to i32, !dbg !84 + store i32 %21, i32* %6, align 4, !dbg !83 + %22 = load i32, i32* %6, align 4, !dbg !86 + %23 = load i32, i32* %3, align 4, !dbg !87 + %24 = load i32, i32* %6, align 4, !dbg !88 + %25 = call i32 @fixmul(i32 %23, i32 %24), !dbg !89 + %26 = load i32, i32* %6, align 4, !dbg !90 + %27 = call i32 @fixmul(i32 %25, i32 %26), !dbg !91 + %28 = sub nsw i32 1536, %27, !dbg !92 + %29 = call i32 @fixmul(i32 %22, i32 %28), !dbg !93 + store i32 %29, i32* %6, align 4, !dbg !94 + %30 = load i32, i32* %6, align 4, !dbg !95 + ret i32 %30, !dbg !96 +}""" + +# 文件路径 +filepath = 'MadgwickAHRS_opt.ll' + +replace_function_content(filepath, 'define dso_local i32 @invSqrt\(i32 %0\) #1', new_content) + + From 375a659df702ad3153897f1b8036b4fbbeccc7de Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 19 Aug 2024 23:05:44 +0800 Subject: [PATCH 068/213] update optimizeByRange.cpp * dev2. --- ...5c826fb59c52307c5b9b3dec1480b538830566.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt diff --git a/analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt b/analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt new file mode 100644 index 000000000..83b1c055b --- /dev/null +++ b/analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt @@ -0,0 +1,48 @@ + +changeset: 1661:d75c826fb59c52307c5b9b3dec1480b538830566 +char kNewtonVersion[] = "0.3-alpha-1661 (d75c826fb59c52307c5b9b3dec1480b538830566) (build 08-19-2024-22:58-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 59838c712a9bd377eb39365bb131d8ec2fd7baed Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 26 Aug 2024 13:24:31 +0800 Subject: [PATCH 069/213] update some test files * dev2. --- .../newton/llvm-ir/c-files/MadgwickAHRSfix.c | 504 ++++++++---------- .../llvm-ir/c-files/test_MadgwickAHRS.c | 341 +++++++++--- .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 326 ++++++++--- .../newton/llvm-ir/c-files/test_Original.c | 307 +++++++++-- applications/newton/llvm-ir/replace.py | 71 ++- applications/newton/llvm-ir/testMadgwick.sh | 9 +- .../newton/llvm-ir/testMadgwickfix.sh | 10 +- applications/newton/llvm-ir/testOriginal.sh | 4 +- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 18 +- .../newton-irPass-LLVMIR-quantization.cpp | 2 +- 10 files changed, 1044 insertions(+), 548 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c index 060d5a38a..d13a4f45b 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c @@ -1,7 +1,7 @@ #include "MadgwickAHRSfix.h" -#define sampleFreq 28 // sample frequency in Hz -#define betaDef 0.1f // 2 * proportional gain +#define sampleFreq 28 // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain typedef int32_t bmx055xAcceleration; typedef int32_t bmx055yAcceleration; @@ -13,9 +13,9 @@ typedef int32_t bmx055xMagneto; typedef int32_t bmx055yMagneto; typedef int32_t bmx055zMagneto; -volatile bmx055xAcceleration beta = (uint8_t)(betaDef*FRAC_BASE); //0.1f // 2 * proportional gain (Kp) -//volatile bmx055xAcceleration q0 = 0.64306622f*FRAC_BASE, q1 = 0.02828862f*FRAC_BASE, -// q2 = -0.00567953f*FRAC_BASE, q3 = -0.76526684f*FRAC_BASE; // quaternion of sensor frame relative to auxiliary frame +volatile bmx055xAcceleration beta = (uint8_t)(betaDef * FRAC_BASE); // 0.1f // 2 * proportional gain (Kp) +// volatile bmx055xAcceleration q0 = 0.64306622f*FRAC_BASE, q1 = 0.02828862f*FRAC_BASE, +// q2 = -0.00567953f*FRAC_BASE, q3 = -0.76526684f*FRAC_BASE; // quaternion of sensor frame relative to auxiliary frame // m=-7 1/Yest=0.0858 Yest=11.3120 Yest_hex=B50 // m=-6 1/Yest=0.1248 Yest=8.0000 Yest_hex=800 @@ -48,42 +48,68 @@ volatile bmx055xAcceleration beta = (uint8_t)(betaDef*FRAC_BASE); //0.1f // 2 // m=21 1/Yest=1448.0000 Yest=0.0000 Yest_hex=0 // m=22 1/Yest=2048.0000 Yest=0.0000 Yest_hex=0 -int32_t -mulfix(int32_t x, int32_t y) +static inline int32_t +fixmul(int32_t x, int32_t y) { -// int32_t result; -// int64_t temp; -// temp = (int64_t)x * (int64_t)y; -// temp += K; -// result = round(temp/FRAC_BASE); -// return result; -// return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; - return ((int64_t)x*y)>>FRAC_Q; + // int32_t result; + // int64_t temp; + // temp = (int64_t)x * (int64_t)y; + // temp += K; + // result = round(temp/FRAC_BASE); + // return result; + // return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; + return ((int64_t)x * y) >> FRAC_Q; + //return (x * y) >> FRAC_Q; } /* * Compute square root of x and reciprocal with Goldschmidt's method */ +//int32_t +//sqrt_rsqrt(int32_t x, int recip) +//{ +// if (recip) +// { +// int32_t int_halfx = fixmul(0.5 * FRAC_BASE, x); +// float fp_y = (float)x / FRAC_BASE; +// long i = *(long *)&fp_y; +// i = 0x5f3759df - (i >> 1); +// fp_y = *(float *)&i; +// int32_t int_y = fp_y * FRAC_BASE; +// int_y = fixmul(int_y, ((int32_t)(1.5f * FRAC_BASE) - (fixmul(fixmul(int_halfx, int_y), int_y)))); +// return int_y; +// } +// else +// { +// int32_t res = (int32_t)sqrt((double)x) << (FRAC_Q / 2); +// if (FRAC_Q % 2) +// return res * 1.414213562; +// else +// return res; +// } +//} + int32_t -sqrt_rsqrt(int32_t x, int recip) { - if (recip) { - int32_t int_halfx = mulfix(0.5*FRAC_BASE, x); - float fp_y = (float)x/FRAC_BASE; - long i = *(long*)&fp_y; - i = 0x5f3759df - (i>>1); - fp_y = *(float*)&i; - int32_t int_y = fp_y*FRAC_BASE; - int_y = mulfix(int_y, ((int32_t)(1.5f*FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - return int_y; -// fp_y = fp_y * (1.5f - (halfx * fp_y * fp_y)); -// return fp_y*FRAC_BASE; - } else { - int32_t res = (int32_t)sqrt((double)x)<<(FRAC_Q/2); - if (FRAC_Q%2) - return res*1.414213562; - else - return res; - } +fixsqrt(int32_t x) +{ + int32_t res = (int32_t)sqrt((double)x) << (FRAC_Q / 2); + if (FRAC_Q % 2) + return res * 1.414213562; + else + return res; +} + +int32_t +invSqrt(int32_t x) +{ + int32_t int_halfx = fixmul(0.5 * FRAC_BASE, x); + float fp_y = (float)x / FRAC_BASE; + long i = *(long *)&fp_y; + i = 0x5f3759df - (i >> 1); + fp_y = *(float *)&i; + int32_t int_y = fp_y * FRAC_BASE; + int_y = fixmul(int_y, ((int32_t)(1.5f * FRAC_BASE) - (fixmul(fixmul(int_halfx, int_y), int_y)))); + return int_y; } //==================================================================================================== @@ -92,179 +118,113 @@ sqrt_rsqrt(int32_t x, int recip) { //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, - bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, - bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { - - int32_t q0 = *q0_ptr; - int32_t q1 = *q1_ptr; - int32_t q2 = *q2_ptr; - int32_t q3 = *q3_ptr; + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr) +{ + int32_t q0 = *q0_ptr; + int32_t q1 = *q1_ptr; + int32_t q2 = *q2_ptr; + int32_t q3 = *q3_ptr; - int32_t recipNorm; - int32_t s0, s1, s2, s3; - int32_t qDot1, qDot2, qDot3, qDot4; - int32_t hx, hy; - int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + int32_t recipNorm; + int32_t s0, s1, s2, s3; + int32_t qDot1, qDot2, qDot3, qDot4; + int32_t hx, hy; + int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) - if((mx == 0x0) && (my == 0x0) && (mz == 0x0)) { + if ((mx == 0x0) && (my == 0x0) && (mz == 0x0)) + { MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); return; } // Rate of change of quaternion from gyroscope - qDot1 = (-mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; - qDot2 = (mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; - qDot3 = (mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; - qDot4 = (mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; + qDot1 = (-fixmul(q1, gx) - fixmul(q2, gy) - fixmul(q3, gz)) / 2; + qDot2 = (fixmul(q0, gx) + fixmul(q2, gz) - fixmul(q3, gy)) / 2; + qDot3 = (fixmul(q0, gy) - fixmul(q1, gz) + fixmul(q3, gx)) / 2; + qDot4 = (fixmul(q0, gz) + fixmul(q1, gy) - fixmul(q2, gx)) / 2; + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { + if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) + { + // Normalise accelerometer measurement - recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); -// printf("1: %f\n", (double)recipNorm/FRAC_BASE); - ax = mulfix(ax, recipNorm); - ay = mulfix(ay, recipNorm); - az = mulfix(az, recipNorm); + recipNorm = invSqrt(fixmul(ax, ax) + fixmul(ay, ay) + fixmul(az, az)); + // printf("1: %f\n", (double)recipNorm/FRAC_BASE); + ax = fixmul(ax, recipNorm); + ay = fixmul(ay, recipNorm); + az = fixmul(az, recipNorm); + // Normalise magnetometer measurement - recipNorm = sqrt_rsqrt(mulfix(mx, mx) + mulfix(my, my) + mulfix(mz, mz), true); - mx = mulfix(mx, recipNorm); - my = mulfix(my, recipNorm); - mz = mulfix(mz, recipNorm); + recipNorm = invSqrt(fixmul(mx, mx) + fixmul(my, my) + fixmul(mz, mz)); + mx = fixmul(mx, recipNorm); + my = fixmul(my, recipNorm); + mz = fixmul(mz, recipNorm); + // Auxiliary variables to avoid repeated arithmetic - _2q0mx = 2 * mulfix(q0, mx); - _2q0my = 2 * mulfix(q0, my); - _2q0mz = 2 * mulfix(q0, mz); - _2q1mx = 2 * mulfix(q1, mx); - _2q0 = 2*q0; - _2q1 = 2*q1; - _2q2 = 2*q2; - _2q3 = 2*q3; - _2q0q2 = 2 * mulfix(q0, q2); - _2q2q3 = 2 * mulfix(q2, q3); - q0q0 = mulfix(q0, q0); - q0q1 = mulfix(q0, q1); - q0q2 = mulfix(q0, q2); - q0q3 = mulfix(q0, q3); - q1q1 = mulfix(q1, q1); - q1q2 = mulfix(q1, q2); - q1q3 = mulfix(q1, q3); - q2q2 = mulfix(q2, q2); - q2q3 = mulfix(q2, q3); - q3q3 = mulfix(q3, q3); + _2q0mx = 2 * fixmul(q0, mx); + _2q0my = 2 * fixmul(q0, my); + _2q0mz = 2 * fixmul(q0, mz); + _2q1mx = 2 * fixmul(q1, mx); + _2q0 = 2 * q0; + _2q1 = 2 * q1; + _2q2 = 2 * q2; + _2q3 = 2 * q3; + _2q0q2 = 2 * fixmul(q0, q2); + _2q2q3 = 2 * fixmul(q2, q3); + q0q0 = fixmul(q0, q0); + q0q1 = fixmul(q0, q1); + q0q2 = fixmul(q0, q2); + q0q3 = fixmul(q0, q3); + q1q1 = fixmul(q1, q1); + q1q2 = fixmul(q1, q2); + q1q3 = fixmul(q1, q3); + q2q2 = fixmul(q2, q2); + q2q3 = fixmul(q2, q3); + q3q3 = fixmul(q3, q3); + // Reference direction of Earth's magnetic field - hx = mulfix(mx, q0q0) - - mulfix(_2q0my, q3) - + mulfix(_2q0mz, q2) - + mulfix(mx, q1q1) - + mulfix(mulfix(_2q1, my), q2) - + mulfix(mulfix(_2q1, mz), q3) - - mulfix(mx, q2q2) - - mulfix(mx, q3q3); - hy = mulfix(_2q0mx, q3) - + mulfix(my, q0q0) - - mulfix(_2q0mz, q1) - + mulfix(_2q1mx, q2) - - mulfix(my, q1q1) - + mulfix(my, q2q2) - + mulfix(mulfix(_2q2, mz), q3) - - mulfix(my, q3q3); - _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); - _2bz = -mulfix(_2q0mx, q2) - + mulfix(_2q0my, q1) - + mulfix(mz, q0q0) - + mulfix(_2q1mx, q3) - - mulfix(mz, q1q1) - + mulfix(mulfix(_2q2, my), q3) - - mulfix(mz, q2q2) - + mulfix(mz, q3q3); - _4bx = 2*_2bx; - _4bz = 2*_2bz; + hx = fixmul(mx, q0q0) - fixmul(_2q0my, q3) + fixmul(_2q0mz, q2) + fixmul(mx, q1q1) + fixmul(fixmul(_2q1, my), q2) + fixmul(fixmul(_2q1, mz), q3) - fixmul(mx, q2q2) - fixmul(mx, q3q3); + hy = fixmul(_2q0mx, q3) + fixmul(my, q0q0) - fixmul(_2q0mz, q1) + fixmul(_2q1mx, q2) - fixmul(my, q1q1) + fixmul(my, q2q2) + fixmul(fixmul(_2q2, mz), q3) - fixmul(my, q3q3); + _2bx = fixsqrt(fixmul(hx, hx) + fixmul(hy, hy)); + _2bz = -fixmul(_2q0mx, q2) + fixmul(_2q0my, q1) + fixmul(mz, q0q0) + fixmul(_2q1mx, q3) - fixmul(mz, q1q1) + fixmul(fixmul(_2q2, my), q3) - fixmul(mz, q2q2) + fixmul(mz, q3q3); + _4bx = 2 * _2bx; + _4bz = 2 * _2bz; + // Gradient decent algorithm corrective step - s0 = - mulfix(_2q2, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q1, (2*q0q1 + _2q2q3 - ay)) - - mulfix(mulfix(_2bz, q2), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix(mulfix(_2bx, q2), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); - s1 = mulfix(_2q3, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q0, (2*q0q1 + _2q2q3 - ay)) - - 4 * mulfix(q1, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) - + mulfix(mulfix(_2bz, q3), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); - s2 = - mulfix(_2q0, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q3, (2*q0q1 + _2q2q3 - ay)) - - 4 * mulfix(q2, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) - + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); - s3 = mulfix(_2q1, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q2, (2*q0q1 + _2q2q3 - ay)) - + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix(mulfix(_2bx, q1), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); - recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude - s0 = mulfix(s0, recipNorm); - s1 = mulfix(s1, recipNorm); - s2 = mulfix(s2, recipNorm); - s3 = mulfix(s3, recipNorm); - + s0 = -fixmul(_2q2, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q1, (2 * q0q1 + _2q2q3 - ay)) - fixmul(fixmul(_2bz, q2), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul((-fixmul(_2bx, q3) + fixmul(_2bz, q1)), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul(fixmul(_2bx, q2), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); + + s1 = fixmul(_2q3, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q0, (2 * q0q1 + _2q2q3 - ay)) - 4 * fixmul(q1, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + fixmul(fixmul(_2bz, q3), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul(fixmul(_2bx, q2) + fixmul(_2bz, q0), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul(fixmul(_2bx, q3) - fixmul(_4bz, q1), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); + + + + s2 = -fixmul(_2q0, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q3, (2 * q0q1 + _2q2q3 - ay)) - 4 * fixmul(q2, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + fixmul((-fixmul(_4bx, q2) - fixmul(_2bz, q0)), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul((fixmul(_2bx, q1) + fixmul(_2bz, q3)), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul((fixmul(_2bx, q0) - fixmul(_4bz, q2)), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); + + s3 = fixmul(_2q1, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q2, (2 * q0q1 + _2q2q3 - ay)) + fixmul((-fixmul(_4bx, q3) + fixmul(_2bz, q1)), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul((-fixmul(_2bx, q0) + fixmul(_2bz, q2)), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul(fixmul(_2bx, q1), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); + /* 2nd iter normalizaton */ - // recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude - // s0 = mulfix(s0, recipNorm); - // s1 = mulfix(s1, recipNorm); - // s2 = mulfix(s2, recipNorm); - // s3 = mulfix(s3, recipNorm); +// recipNorm = invSqrt(fixmul(s0, s0) + fixmul(s1, s1) + fixmul(s2, s2) + fixmul(s3, s3)); // normalise step magnitude +// s0 = fixmul(s0, recipNorm); +// s1 = fixmul(s1, recipNorm); +// s2 = fixmul(s2, recipNorm); +// s3 = fixmul(s3, recipNorm); // Apply feedback step - qDot1 -= mulfix(beta, s0); - qDot2 -= mulfix(beta, s1); - qDot3 -= mulfix(beta, s2); - qDot4 -= mulfix(beta, s3); + qDot1 -= fixmul(beta, s0); + qDot2 -= fixmul(beta, s1); + qDot3 -= fixmul(beta, s2); + qDot4 -= fixmul(beta, s3); } // Integrate rate of change of quaternion to yield quaternion @@ -274,28 +234,29 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR q3 += qDot4 / sampleFreq; // Normalise quaternion - recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); -// printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", -// (double)q0/FRAC_BASE, -// (double)q1/FRAC_BASE, -// (double)q2/FRAC_BASE, -// (double)q3/FRAC_BASE, -// (double)recipNorm/FRAC_BASE); - q0 = mulfix(q0, recipNorm); - q1 = mulfix(q1, recipNorm); - q2 = mulfix(q2, recipNorm); - q3 = mulfix(q3, recipNorm); - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; - -// printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", -// DISPLAY_INT(q0), DISPLAY_FRAC(q0), -// DISPLAY_INT(q1), DISPLAY_FRAC(q1), -// DISPLAY_INT(q2), DISPLAY_FRAC(q2), -// DISPLAY_INT(q3), DISPLAY_FRAC(q3)); - + recipNorm = invSqrt(fixmul(q0, q0) + fixmul(q1, q1) + fixmul(q2, q2) + fixmul(q3, q3)); + // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", + // (double)q0/FRAC_BASE, + // (double)q1/FRAC_BASE, + // (double)q2/FRAC_BASE, + // (double)q3/FRAC_BASE, + // (double)recipNorm/FRAC_BASE); + + q0 = fixmul(q0, recipNorm); + q1 = fixmul(q1, recipNorm); + q2 = fixmul(q2, recipNorm); + q3 = fixmul(q3, recipNorm); + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + + // printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", + // DISPLAY_INT(q0), DISPLAY_FRAC(q0), + // DISPLAY_INT(q1), DISPLAY_FRAC(q1), + // DISPLAY_INT(q2), DISPLAY_FRAC(q2), + // DISPLAY_INT(q3), DISPLAY_FRAC(q3)); + // /* 2nd iter normalization */ // recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); // q0 = mulfix(q0, recipNorm); @@ -307,85 +268,66 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, - bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { - int32_t q0 = *q0_ptr; - int32_t q1 = *q1_ptr; - int32_t q2 = *q2_ptr; - int32_t q3 = *q3_ptr; - int32_t recipNorm; - int32_t s0, s1, s2, s3; - int32_t qDot1, qDot2, qDot3, qDot4; - int32_t _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr) +{ + int32_t q0 = *q0_ptr; + int32_t q1 = *q1_ptr; + int32_t q2 = *q2_ptr; + int32_t q3 = *q3_ptr; + int32_t recipNorm; + int32_t s0, s1, s2, s3; + int32_t qDot1, qDot2, qDot3, qDot4; + int32_t _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3; // Rate of change of quaternion from gyroscope - qDot1 = (- mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; - qDot2 = ( mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; - qDot3 = ( mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; - qDot4 = ( mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; + qDot1 = (-fixmul(q1, gx) - fixmul(q2, gy) - fixmul(q3, gz)) / 2; + qDot2 = (fixmul(q0, gx) + fixmul(q2, gz) - fixmul(q3, gy)) / 2; + qDot3 = (fixmul(q0, gy) - fixmul(q1, gz) + fixmul(q3, gx)) / 2; + qDot4 = (fixmul(q0, gz) + fixmul(q1, gy) - fixmul(q2, gx)) / 2; // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { - + if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) + { // Normalise accelerometer measurement - recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); - ax = mulfix(ax, recipNorm); - ay = mulfix(ay, recipNorm); - az = mulfix(az, recipNorm); + recipNorm = invSqrt(fixmul(ax, ax) + fixmul(ay, ay) + fixmul(az, az)); + ax = fixmul(ax, recipNorm); + ay = fixmul(ay, recipNorm); + az = fixmul(az, recipNorm); // Auxiliary variables to avoid repeated arithmetic - _2q0 = 2*q0; - _2q1 = 2*q1; - _2q2 = 2*q2; - _2q3 = 2*q3; - _4q0 = 4*q0; - _4q1 = 4*q1; - _4q2 = 4*q2; - _8q1 = 8*q1; - _8q2 = 8*q2; - q0q0 = mulfix(q0, q0); - q1q1 = mulfix(q1, q1); - q2q2 = mulfix(q2, q2); - q3q3 = mulfix(q3, q3); + _2q0 = 2 * q0; + _2q1 = 2 * q1; + _2q2 = 2 * q2; + _2q3 = 2 * q3; + _4q0 = 4 * q0; + _4q1 = 4 * q1; + _4q2 = 4 * q2; + _8q1 = 8 * q1; + _8q2 = 8 * q2; + q0q0 = fixmul(q0, q0); + q1q1 = fixmul(q1, q1); + q2q2 = fixmul(q2, q2); + q3q3 = fixmul(q3, q3); // Gradient decent algorithm corrective step - s0 = mulfix(_4q0, q2q2) - + mulfix(_2q2, ax) - + mulfix(_4q0, q1q1) - - mulfix(_2q1, ay); - s1 = mulfix(_4q1, q3q3) - - mulfix(_2q3, ax) - + 4 * mulfix(q0q0, q1) - - mulfix(_2q0, ay) - - _4q1 - + mulfix(_8q1, q1q1) - + mulfix(_8q1, q2q2) - + mulfix(_4q1, az); - s2 = 4 * mulfix(q0q0, q2) - + mulfix(_2q0, ax) - + mulfix(_4q2, q3q3) - - mulfix(_2q3, ay) - - _4q2 - + mulfix(_8q2, q1q1) - + mulfix(_8q2, q2q2) - + mulfix(_4q2, az); - s3 = 4 * mulfix(q1q1, q3) - - mulfix(_2q1, ax) - + 4 * mulfix(q2q2, q3) - - mulfix(_2q2, ay); - recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude - s0 = mulfix(s0, recipNorm); - s1 = mulfix(s1, recipNorm); - s2 = mulfix(s2, recipNorm); - s3 = mulfix(s3, recipNorm); + s0 = fixmul(_4q0, q2q2) + fixmul(_2q2, ax) + fixmul(_4q0, q1q1) - fixmul(_2q1, ay); + s1 = fixmul(_4q1, q3q3) - fixmul(_2q3, ax) + 4 * fixmul(q0q0, q1) - fixmul(_2q0, ay) - _4q1 + fixmul(_8q1, q1q1) + fixmul(_8q1, q2q2) + fixmul(_4q1, az); + s2 = 4 * fixmul(q0q0, q2) + fixmul(_2q0, ax) + fixmul(_4q2, q3q3) - fixmul(_2q3, ay) - _4q2 + fixmul(_8q2, q1q1) + fixmul(_8q2, q2q2) + fixmul(_4q2, az); + s3 = 4 * fixmul(q1q1, q3) - fixmul(_2q1, ax) + 4 * fixmul(q2q2, q3) - fixmul(_2q2, ay); + recipNorm = invSqrt(fixmul(s0, s0) + fixmul(s1, s1) + fixmul(s2, s2) + fixmul(s3, s3)); // normalise step magnitude + s0 = fixmul(s0, recipNorm); + s1 = fixmul(s1, recipNorm); + s2 = fixmul(s2, recipNorm); + s3 = fixmul(s3, recipNorm); // Apply feedback step - qDot1 -= mulfix(beta, s0); - qDot2 -= mulfix(beta, s1); - qDot3 -= mulfix(beta, s2); - qDot4 -= mulfix(beta, s3); + qDot1 -= fixmul(beta, s0); + qDot2 -= fixmul(beta, s1); + qDot3 -= fixmul(beta, s2); + qDot4 -= fixmul(beta, s3); } // Integrate rate of change of quaternion to yield quaternion @@ -395,13 +337,13 @@ MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngul q3 += qDot4 / sampleFreq; // Normalise quaternion - recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); - q0 = mulfix(q0, recipNorm); - q1 = mulfix(q1, recipNorm); - q2 = mulfix(q2, recipNorm); - q3 = mulfix(q3, recipNorm); - q0_ptr = &q0; - q1_ptr = &q1; - q2_ptr = &q2; - q3_ptr = &q3; + recipNorm = invSqrt(fixmul(q0, q0) + fixmul(q1, q1) + fixmul(q2, q2) + fixmul(q3, q3)); + q0 = fixmul(q0, recipNorm); + q1 = fixmul(q1, recipNorm); + q2 = fixmul(q2, recipNorm); + q3 = fixmul(q3, recipNorm); + q0_ptr = &q0; + q1_ptr = &q1; + q2_ptr = &q2; + q3_ptr = &q3; } diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c index 3f21ced34..d443bbc5a 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -2,25 +2,30 @@ #include #include #include +#include #include #include #include #define FRAC_Q 10 + #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 -#define ITERATION 1 + + +#define ITERATION 10 +#define DATA_SIZE 10000 /* * void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); */ -//#include "MadgwickAHRS.h" +// #include "MadgwickAHRS.h" extern volatile int32_t q0, q1, q2, q3; -//extern volatile float q0, q1, q2, q3; -//extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// extern volatile float q0, q1, q2, q3; +// extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, // float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); -//extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); +// extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az,float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); @@ -35,13 +40,46 @@ typedef struct { int quantized4; } QuantizedValues; -typedef struct{ +typedef struct { float dequantized1; float dequantized2; float dequantized3; float dequantized4; } DequantizedValues; +typedef struct { + uint64_t start; + uint64_t current; + uint64_t min; + uint64_t max; +} ELAPSED_TIME; + +ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 + +static inline uint64_t rdtsc() { + uint32_t lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return ((uint64_t)hi << 32) | lo; +} + + + +void elapsed_time_start(uint32_t i) { + elapsed_time_tbl[i].start = rdtsc(); +} + +void elapsed_time_stop(uint32_t i) { + uint64_t stop = rdtsc(); + ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; + p_tbl->current = stop - p_tbl->start; + if (p_tbl->max < p_tbl->current) { + p_tbl->max = p_tbl->current; + } + if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { + p_tbl->min = p_tbl->current; + } +} + typedef struct timespec timespec; timespec diff(timespec start, timespec end) @@ -97,7 +135,9 @@ dequantize(int quantized_value) return (float)(quantized_value >> FRAC_Q); } -QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { +QuantizedValues +quantize4(float value1, float value2, float value3, float value4, int frac_base) +{ QuantizedValues result; result.quantized1 = round(value1 * frac_base); result.quantized2 = round(value2 * frac_base); @@ -106,7 +146,9 @@ QuantizedValues quantize4(float value1, float value2, float value3, float value4 return result; } -DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { +DequantizedValues +dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) +{ DequantizedValues result; result.dequantized1 = (float)(quantized1 >> FRAC_Q); result.dequantized2 = (float)(quantized2 >> FRAC_Q); @@ -120,7 +162,6 @@ extern int perform_addition(float a, float b); timespec tic() { - timespec start_time; clock_gettime(CLOCK_REALTIME, &start_time); return start_time; @@ -136,93 +177,235 @@ toc(timespec * start_time, const char * prefix) *start_time = current_time; return time_consump; } -int -main() + + int + main() { - /* - * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) -0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 - */ - - float num1 = -7.249095917; - float num2 = 26.43893433; - float num3 = -37.16656494; - float num4 = -0.1184487343; - float num5 = 0.001258035656; - float num6 = 0.008988874033; - float num7 = -0.3570917249; - float num8 = 0.3941296637; - float num9 = 9.963726044; - - float num10 = 0.64306622; - float num11 = 0.02828862; - float num12 = -0.00567953; - float num13 = -0.76526684; - - int32_t mag_x = quantize(num1, FRAC_BASE); - int32_t mag_y = quantize(num2, FRAC_BASE); - int32_t mag_z = quantize(num3, FRAC_BASE); - int32_t gyr_x = quantize(num4, FRAC_BASE); - int32_t gyr_y = quantize(num5, FRAC_BASE); - int32_t gyr_z = quantize(num6, FRAC_BASE); - int32_t acc_x = quantize(num7, FRAC_BASE); - int32_t acc_y = quantize(num8, FRAC_BASE); - int32_t acc_z = quantize(num9, FRAC_BASE); - int32_t q0 = quantize(num10, FRAC_BASE); - int32_t q1 = quantize(num11, FRAC_BASE); - int32_t q2 = quantize(num12, FRAC_BASE); - int32_t q3 = quantize(num13, FRAC_BASE); -// float q0 = num10; -// float q1 = num11; -// float q2 = num12; -// float q3 = num13; - - - //result - int num14 = 657; - int num15 = 28; - int num16 = -6; - int num17 = -785; + FILE * fp = fopen("input.csv", "r"); - u_int64_t time_slots[ITERATION]; + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + double time[DATA_SIZE]; - for (size_t idx = 0; idx < ITERATION; idx++) + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) { - timespec timer = tic(); + q0[i] = (0.64306622f * FRAC_BASE); + q1[i] = (0.02828862f * FRAC_BASE); + q2[i] = (-0.00567953f * FRAC_BASE); + q3[i] = (-0.76526684f * FRAC_BASE); + } + int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; - MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, - acc_x, acc_y, acc_z, - mag_x, mag_y, mag_z, - &q0, &q1, &q2, &q3); - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: + mag_x[row - 2] = round(atof(value) * FRAC_BASE); + break; + case 2: + mag_y[row - 2] = round(atof(value) * FRAC_BASE); + break; + case 3: + mag_z[row - 2] = round(atof(value) * FRAC_BASE); + break; + case 4: + gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + break; + case 5: + gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + break; + case 6: + gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + break; + case 7: + acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + break; + case 8: + acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + break; + case 9: + acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } } + fclose(fp); - //tic toc quantize4耗时 - timespec timer = tic(); - QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); - timespec durationQuantize = toc(&timer, "Quantize"); - printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); + u_int64_t time_slots[ITERATION]; + for (size_t idx = 0; idx < ITERATION; idx++) { + elapsed_time_start(idx); // 开始计时 + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - //tic toc dequantize4耗时 - timer = tic(); - DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); - timespec durationDequantize = toc(&timer, "Dequantize"); - printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); + } + elapsed_time_stop(idx); // 结束计时 + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) - { + for (size_t idx = 0; idx < ITERATION; idx++) { average_time += time_slots[idx]; } average_time /= ITERATION; printf("average time = %lu nm\n", average_time); - //计算total time, quantize+dequantize+average - u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; - printf("total time = %lu nm\n", totalTime); -} \ No newline at end of file + // 打印出每次迭代的最大、最小和当前时间 + for (size_t idx = 0; idx < ITERATION; idx++) { + printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", + idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); + } + + FILE *fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, (double)q0[ts]/FRAC_BASE, + ts, (double)q1[ts]/FRAC_BASE, + ts, (double)q2[ts]/FRAC_BASE, + ts, (double)q3[ts]/FRAC_BASE); + } + fclose(fptr); + return 0; + } + + +//int +//main() +//{ +// +// +// { +// /* +// * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +// */ +// +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; +// +// int32_t mag_x = quantize(num1, FRAC_BASE); +// int32_t mag_y = quantize(num2, FRAC_BASE); +// int32_t mag_z = quantize(num3, FRAC_BASE); +// int32_t gyr_x = quantize(num4, FRAC_BASE); +// int32_t gyr_y = quantize(num5, FRAC_BASE); +// int32_t gyr_z = quantize(num6, FRAC_BASE); +// int32_t acc_x = quantize(num7, FRAC_BASE); +// int32_t acc_y = quantize(num8, FRAC_BASE); +// int32_t acc_z = quantize(num9, FRAC_BASE); +// int32_t q0 = quantize(num10, FRAC_BASE); +// int32_t q1 = quantize(num11, FRAC_BASE); +// int32_t q2 = quantize(num12, FRAC_BASE); +// int32_t q3 = quantize(num13, FRAC_BASE); +// // float q0 = num10; +// // float q1 = num11; +// // float q2 = num12; +// // float q3 = num13; +// +// // result +// int num14 = 657; +// int num15 = 28; +// int num16 = -6; +// int num17 = -785; +// +// u_int64_t time_slots[ITERATION]; +// +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// +// for (size_t ts = 0; ts < DATA_SIZE; ts++) +// { +// MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], +// acc_x[ts], acc_y[ts], acc_z[ts], +// mag_x[ts], mag_y[ts], mag_z[ts], +// &q0[ts], &q1[ts], &q2[ts], &q3[ts]); +// } +// } +// +// // MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// // acc_x, acc_y, acc_z, +// // mag_x, mag_y, mag_z, +// // &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// +// +// +// // tic toc quantize4耗时 +// timespec timer = tic(); +// QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); +// timespec durationQuantize = toc(&timer, "Quantize"); +// printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); +// +// // tic toc dequantize4耗时 +// timer = tic(); +// DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); +// timespec durationDequantize = toc(&timer, "Dequantize"); +// printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); +// +// u_int64_t average_time = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// average_time += time_slots[idx]; +// } +// average_time /= ITERATION; +// printf("average time = %lu nm\n", average_time); +// +// // 计算total time, quantize+dequantize+average +// u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; +// printf("total time = %lu nm\n", totalTime); +// } +// +//} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index 533b8c3d0..1ccfd4ebf 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -7,19 +7,19 @@ #include #define FRAC_Q 10 #define BIT_WIDTH 32 -#define ITERATION 1 +#define ITERATION 10 +#define DATA_SIZE 10000 #include "MadgwickAHRSfix.h" extern volatile int32_t q0, q1, q2, q3; -extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); -extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); -extern int32_t sqrt_rsqrt(int32_t x, int recip); +extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +extern int32_t sqrt_rsqrt(int32_t x, int recip); // #include "MadgwickAHRS.h" /*************************************** * Timer functions of the test framework ***************************************/ - typedef struct { int quantized1; int quantized2; @@ -27,7 +27,7 @@ typedef struct { int quantized4; } QuantizedValues; -typedef struct{ +typedef struct { float dequantized1; float dequantized2; float dequantized3; @@ -36,6 +36,38 @@ typedef struct{ +typedef struct { + uint64_t start; + uint64_t current; + uint64_t min; + uint64_t max; +} ELAPSED_TIME; + +ELAPSED_TIME elapsed_time_tbl[10]; + +static inline uint64_t rdtsc() { + uint32_t lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return ((uint64_t)hi << 32) | lo; +} + +void elapsed_time_start(uint32_t i) { + elapsed_time_tbl[i].start = rdtsc(); +} + +void elapsed_time_stop(uint32_t i) { + uint64_t stop = rdtsc(); + ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; + p_tbl->current = stop - p_tbl->start; + if (p_tbl->max < p_tbl->current) { + p_tbl->max = p_tbl->current; + } + if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { + p_tbl->min = p_tbl->current; + } +} + + typedef struct timespec timespec; timespec diff(timespec start, timespec end) @@ -84,7 +116,9 @@ quantize(float value, int frac_base) return round(value * frac_base); } -QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { +QuantizedValues +quantize4(float value1, float value2, float value3, float value4, int frac_base) +{ QuantizedValues result; result.quantized1 = round(value1 * frac_base); result.quantized2 = round(value2 * frac_base); @@ -93,7 +127,9 @@ QuantizedValues quantize4(float value1, float value2, float value3, float value4 return result; } -DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { +DequantizedValues +dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) +{ DequantizedValues result; result.dequantized1 = (float)(quantized1 >> FRAC_Q); result.dequantized2 = (float)(quantized2 >> FRAC_Q); @@ -114,7 +150,6 @@ extern int perform_addition(float a, float b); timespec tic() { - timespec start_time; clock_gettime(CLOCK_REALTIME, &start_time); return start_time; @@ -130,96 +165,223 @@ toc(timespec * start_time, const char * prefix) *start_time = current_time; return time_consump; } -int -main() + int + main() { - /* - * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) -0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 - */ - - float num1 = -7.249095917; - float num2 = 26.43893433; - float num3 = -37.16656494; - float num4 = -0.1184487343; - float num5 = 0.001258035656; - float num6 = 0.008988874033; - float num7 = -0.3570917249; - float num8 = 0.3941296637; - float num9 = 9.963726044; - - float num10 = 0.64306622; - float num11 = 0.02828862; - float num12 = -0.00567953; - float num13 = -0.76526684; - - //result - int num14 = 657; - int num15 = 28; - int num16 = -6; - int num17 = -785; - - int32_t mag_x = quantize(num1, FRAC_BASE); - int32_t mag_y = quantize(num2, FRAC_BASE); - int32_t mag_z = quantize(num3, FRAC_BASE); - int32_t gyr_x = quantize(num4, FRAC_BASE); - int32_t gyr_y = quantize(num5, FRAC_BASE); - int32_t gyr_z = quantize(num6, FRAC_BASE); - int32_t acc_x = quantize(num7, FRAC_BASE); - int32_t acc_y = quantize(num8, FRAC_BASE); - int32_t acc_z = quantize(num9, FRAC_BASE); - int32_t q0 = quantize(num10, FRAC_BASE); - int32_t q1 = quantize(num11, FRAC_BASE); - int32_t q2 = quantize(num12, FRAC_BASE); - int32_t q3 = quantize(num13, FRAC_BASE); + FILE * fp = fopen("input.csv", "r"); - u_int64_t time_slots[ITERATION]; + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + double time[DATA_SIZE]; - for (size_t idx = 0; idx < ITERATION; idx++) + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) { - timespec timer = tic(); - // for (size_t ts = 0; ts < DATA_SIZE; ts++) - //{ -// MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], -// acc_x[ts], acc_y[ts], acc_z[ts], -// mag_x[ts], mag_y[ts], mag_z[ts], -// &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - //} - - MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, - acc_x, acc_y, acc_z, - mag_x, mag_y, mag_z, - &q0, &q1, &q2, &q3); - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + q0[i] = (0.64306622f * FRAC_BASE); + q1[i] = (0.02828862f * FRAC_BASE); + q2[i] = (-0.00567953f * FRAC_BASE); + q3[i] = (-0.76526684f * FRAC_BASE); } - //tic toc quantize4耗时 - timespec timer = tic(); - QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); - timespec durationQuantize = toc(&timer, "Quantize"); - printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); + int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: + //mag_x[row - 2] = round(atof(value) * FRAC_BASE); + mag_x[row - 2] = quantize(atof(value), FRAC_BASE); + break; + case 2: + //mag_y[row - 2] = round(atof(value) * FRAC_BASE); + mag_y[row - 2] = quantize(atof(value), FRAC_BASE); + break; + case 3: + mag_z[row - 2] = round(atof(value) * FRAC_BASE); + break; + case 4: + gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + break; + case 5: + gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + break; + case 6: + gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + break; + case 7: + acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + break; + case 8: + acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + break; + case 9: + acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } - //tic toc dequantize4耗时 - timer = tic(); - DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); - timespec durationDequantize = toc(&timer, "Dequantize"); - printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); + fclose(fp); + u_int64_t time_slots[ITERATION]; + for (size_t idx = 0; idx < ITERATION; idx++) { + elapsed_time_start(idx); // 开始计时 + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + elapsed_time_stop(idx); // 结束计时 + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) - { + for (size_t idx = 0; idx < ITERATION; idx++) { average_time += time_slots[idx]; } average_time /= ITERATION; printf("average time = %lu nm\n", average_time); - //计算total time, quantize+dequantize+average - u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; - printf("total time = %lu nm\n", totalTime); -} \ No newline at end of file + // 打印出每次迭代的最大、最小和当前时间 + for (size_t idx = 0; idx < ITERATION; idx++) { + printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", + idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); + } + + FILE *fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, (double)q0[ts]/FRAC_BASE, + ts, (double)q1[ts]/FRAC_BASE, + ts, (double)q2[ts]/FRAC_BASE, + ts, (double)q3[ts]/FRAC_BASE); + } + fclose(fptr); + return 0; + } + +//int main () +//{ +// /* +// * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +// */ +// +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; +// +// // result +// int num14 = 657; +// int num15 = 28; +// int num16 = -6; +// int num17 = -785; +// +// int32_t mag_x = quantize(num1, FRAC_BASE); +// int32_t mag_y = quantize(num2, FRAC_BASE); +// int32_t mag_z = quantize(num3, FRAC_BASE); +// int32_t gyr_x = quantize(num4, FRAC_BASE); +// int32_t gyr_y = quantize(num5, FRAC_BASE); +// int32_t gyr_z = quantize(num6, FRAC_BASE); +// int32_t acc_x = quantize(num7, FRAC_BASE); +// int32_t acc_y = quantize(num8, FRAC_BASE); +// int32_t acc_z = quantize(num9, FRAC_BASE); +// int32_t q0 = quantize(num10, FRAC_BASE); +// int32_t q1 = quantize(num11, FRAC_BASE); +// int32_t q2 = quantize(num12, FRAC_BASE); +// int32_t q3 = quantize(num13, FRAC_BASE); +// +// u_int64_t time_slots[ITERATION]; +// +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// +// +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// acc_x, acc_y, acc_z, +// mag_x, mag_y, mag_z, +// &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// } +// +// // tic toc quantize4耗时 +// timespec timer = tic(); +// QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); +// timespec durationQuantize = toc(&timer, "Quantize"); +// printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); +// +// // tic toc dequantize4耗时 +// timer = tic(); +// DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); +// timespec durationDequantize = toc(&timer, "Dequantize"); +// printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); +// +//// u_int64_t average_time = 0; +//// for (size_t idx = 0; idx < ITERATION; idx++) +//// { +//// average_time += time_slots[idx]; +//// } +//// average_time /= ITERATION; +//// printf("average time = %lu nm\n", average_time); +// +// u_int64_t totalTime = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// totalTime += time_slots[idx]; +// } +// printf("total time = %lu nm\n", totalTime); +// +// // 计算total time, quantize+dequantize+average +// //u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; +// //printf("total time = %lu nm\n", totalTime); +//} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_Original.c b/applications/newton/llvm-ir/c-files/test_Original.c index 03522beec..a2247605e 100644 --- a/applications/newton/llvm-ir/c-files/test_Original.c +++ b/applications/newton/llvm-ir/c-files/test_Original.c @@ -5,15 +5,19 @@ #include #include #include +#include + #define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 -#define ITERATION 1 +#define ITERATION 10 +#define DATA_SIZE 10000 extern volatile float q0, q1, q2, q3; -extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); -extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); +extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); /*************************************** * Timer functions of the test framework @@ -25,7 +29,7 @@ typedef struct { int quantized4; } QuantizedValues; -typedef struct{ +typedef struct { float dequantized1; float dequantized2; float dequantized3; @@ -34,6 +38,45 @@ typedef struct{ typedef struct timespec timespec; +typedef struct { + uint64_t start; + uint64_t current; + uint64_t min; + uint64_t max; +} ELAPSED_TIME; + +ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 + +static inline uint64_t rdtsc() { + uint32_t lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return ((uint64_t)hi << 32) | lo; +} + + + +void elapsed_time_start(uint32_t i) { + elapsed_time_tbl[i].start = rdtsc(); +} + +void elapsed_time_stop(uint32_t i) { + uint64_t stop = rdtsc(); + ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; + p_tbl->current = stop - p_tbl->start; + if (p_tbl->max < p_tbl->current) { + p_tbl->max = p_tbl->current; + } + if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { + p_tbl->min = p_tbl->current; + } +} + + + + + + + timespec diff(timespec start, timespec end) @@ -89,7 +132,9 @@ dequantize(int quantized_value) return (float)(quantized_value >> FRAC_Q); } -QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { +QuantizedValues +quantize4(float value1, float value2, float value3, float value4, int frac_base) +{ QuantizedValues result; result.quantized1 = round(value1 * frac_base); result.quantized2 = round(value2 * frac_base); @@ -98,7 +143,9 @@ QuantizedValues quantize4(float value1, float value2, float value3, float value4 return result; } -DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { +DequantizedValues +dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) +{ DequantizedValues result; result.dequantized1 = (float)(quantized1 >> FRAC_Q); result.dequantized2 = (float)(quantized2 >> FRAC_Q); @@ -112,7 +159,6 @@ extern int perform_addition(float a, float b); timespec tic() { - timespec start_time; clock_gettime(CLOCK_REALTIME, &start_time); return start_time; @@ -131,66 +177,107 @@ toc(timespec * start_time, const char * prefix) int main() { - /* - * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) -0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 - */ + FILE * fp = fopen("input.csv", "r"); -// float num1 = -7.249095917; -// float num2 = 26.43893433; -// float num3 = -37.16656494; -// float num4 = -0.1184487343; -// float num5 = 0.001258035656; -// float num6 = 0.008988874033; -// float num7 = -0.3570917249; -// float num8 = 0.3941296637; -// float num9 = 9.963726044; -// -// float num10 = 0.64306622; -// float num11 = 0.02828862; -// float num12 = -0.00567953; -// float num13 = -0.76526684; + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + double time[DATA_SIZE]; + + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; - float mag_x = -7.249095917; - float mag_y = 26.43893433; - float mag_z = -37.16656494; - float gyr_x = -0.1184487343; - float gyr_y = 0.001258035656; - float gyr_z = 0.008988874033; - float acc_x = -0.3570917249; - float acc_y = 0.3941296637; - float acc_z = 9.963726044; - float q0 = 0.64306622; - float q1 = 0.02828862; - float q2 = -0.00567953; - float q3 = -0.76526684; + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; - //result - int num14 = 657; - int num15 = 28; - int num16 = -6; - int num17 = -785; + char * value = strtok(buffer, ", "); + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: + mag_x[row - 2] = atof(value); + break; + case 2: + mag_y[row - 2] = atof(value); + break; + case 3: + mag_z[row - 2] = atof(value); + break; + case 4: + gyr_x[row - 2] = atof(value) * 61; + break; + case 5: + gyr_y[row - 2] = atof(value) * 61; + break; + case 6: + gyr_z[row - 2] = atof(value) * 61; + break; + case 7: + acc_x[row - 2] = atof(value) * 2; + break; + case 8: + acc_y[row - 2] = atof(value) * 2; + break; + case 9: + acc_z[row - 2] = atof(value) * 2; + break; + default: + break; + } + value = strtok(NULL, ", "); + column++; + } + } + fclose(fp); u_int64_t time_slots[ITERATION]; for (size_t idx = 0; idx < ITERATION; idx++) { + elapsed_time_start(idx); // 开始计时 timespec timer = tic(); -// for (size_t ts = 0; ts < DATA_SIZE; ts++) -// { -// MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], -// acc_x[ts], acc_y[ts], acc_z[ts], -// mag_x[ts], mag_y[ts], mag_z[ts], -// &q0[ts], &q1[ts], &q2[ts], &q3[ts]); -// } - - MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, - acc_x, acc_y, acc_z, - mag_x, mag_y, mag_z, - &q0, &q1, &q2, &q3); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + + + } + + // MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, + // acc_x, acc_y, acc_z, + // mag_x, mag_y, mag_z, + // &q0, &q1, &q2, &q3); + elapsed_time_stop(idx); // 结束计时 time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } @@ -203,4 +290,108 @@ main() } average_time /= ITERATION; printf("average time = %lu nm\n", average_time); -} \ No newline at end of file + + // 打印出每次迭代的最大、最小和当前时间 + for (size_t idx = 0; idx < ITERATION; idx++) { + printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", + idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); + } + + FILE * fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + return 0; +} + + +//int main () +//{ +// /* +// * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +// */ +// +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; +// +// // result +// int num14 = 657; +// int num15 = 28; +// int num16 = -6; +// int num17 = -785; +// +//// int32_t mag_x = quantize(num1, FRAC_BASE); +//// int32_t mag_y = quantize(num2, FRAC_BASE); +//// int32_t mag_z = quantize(num3, FRAC_BASE); +//// int32_t gyr_x = quantize(num4, FRAC_BASE); +//// int32_t gyr_y = quantize(num5, FRAC_BASE); +//// int32_t gyr_z = quantize(num6, FRAC_BASE); +//// int32_t acc_x = quantize(num7, FRAC_BASE); +//// int32_t acc_y = quantize(num8, FRAC_BASE); +//// int32_t acc_z = quantize(num9, FRAC_BASE); +//// int32_t q0 = quantize(num10, FRAC_BASE); +//// int32_t q1 = quantize(num11, FRAC_BASE); +//// int32_t q2 = quantize(num12, FRAC_BASE); +//// int32_t q3 = quantize(num13, FRAC_BASE); +// +// float mag_x = num1; +// float mag_y = num2; +// float mag_z = num3; +// float gyr_x = num4; +// float gyr_y = num5; +// float gyr_z = num6; +// float acc_x = num7; +// float acc_y = num8; +// float acc_z = num9; +// float q0 = num10; +// float q1 = num11; +// float q2 = num12; +// float q3 = num13; +// +// +// u_int64_t time_slots[ITERATION]; +// +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// +// +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// acc_x, acc_y, acc_z, +// mag_x, mag_y, mag_z, +// &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// } +// +// +// +// +// +// u_int64_t average_time = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// average_time += time_slots[idx]; +// } +// average_time /= ITERATION; +// printf("average time = %lu nm\n", average_time); +// +// +//} \ No newline at end of file diff --git a/applications/newton/llvm-ir/replace.py b/applications/newton/llvm-ir/replace.py index d0128c20e..a52314e21 100644 --- a/applications/newton/llvm-ir/replace.py +++ b/applications/newton/llvm-ir/replace.py @@ -23,50 +23,45 @@ def replace_function_content(filepath, old_content, new_content): # 定义新函数内容 -new_content = """define dso_local i32 @invSqrt(i32 %0) #0 !dbg !61 { +new_content = """define dso_local i32 @invSqrt(i32 %0) #0 { %2 = alloca i32, align 4 %3 = alloca i32, align 4 %4 = alloca float, align 4 %5 = alloca i64, align 8 %6 = alloca i32, align 4 store i32 %0, i32* %2, align 4 - call void @llvm.dbg.declare(metadata i32* %2, metadata !62, metadata !DIExpression()), !dbg !63 - call void @llvm.dbg.declare(metadata i32* %3, metadata !64, metadata !DIExpression()), !dbg !65 - %7 = load i32, i32* %2, align 4, !dbg !66 - %8 = call i32 @fixmul(i32 512, i32 %7), !dbg !67 - store i32 %8, i32* %3, align 4, !dbg !65 - call void @llvm.dbg.declare(metadata float* %4, metadata !68, metadata !DIExpression()), !dbg !69 - %9 = load i32, i32* %2, align 4, !dbg !70 - %10 = sitofp i32 %9 to float, !dbg !71 - %11 = fdiv float %10, 1.024000e+03, !dbg !72 - store float %11, float* %4, align 4, !dbg !69 - call void @llvm.dbg.declare(metadata i64* %5, metadata !73, metadata !DIExpression()), !dbg !74 - %12 = bitcast float* %4 to i64*, !dbg !75 - %13 = load i64, i64* %12, align 4, !dbg !75 - store i64 %13, i64* %5, align 8, !dbg !74 - %14 = load i64, i64* %5, align 8, !dbg !76 - %15 = ashr i64 %14, 1, !dbg !77 - %16 = sub nsw i64 1597463007, %15, !dbg !78 - store i64 %16, i64* %5, align 8, !dbg !79 - %17 = bitcast i64* %5 to float*, !dbg !80 - %18 = load float, float* %17, align 8, !dbg !80 - store float %18, float* %4, align 4, !dbg !81 - call void @llvm.dbg.declare(metadata i32* %6, metadata !82, metadata !DIExpression()), !dbg !83 - %19 = load float, float* %4, align 4, !dbg !84 - %20 = fmul float %19, 1.024000e+03, !dbg !85 - %21 = fptosi float %20 to i32, !dbg !84 - store i32 %21, i32* %6, align 4, !dbg !83 - %22 = load i32, i32* %6, align 4, !dbg !86 - %23 = load i32, i32* %3, align 4, !dbg !87 - %24 = load i32, i32* %6, align 4, !dbg !88 - %25 = call i32 @fixmul(i32 %23, i32 %24), !dbg !89 - %26 = load i32, i32* %6, align 4, !dbg !90 - %27 = call i32 @fixmul(i32 %25, i32 %26), !dbg !91 - %28 = sub nsw i32 1536, %27, !dbg !92 - %29 = call i32 @fixmul(i32 %22, i32 %28), !dbg !93 - store i32 %29, i32* %6, align 4, !dbg !94 - %30 = load i32, i32* %6, align 4, !dbg !95 - ret i32 %30, !dbg !96 + %7 = load i32, i32* %2, align 4 + %8 = call i32 @fixmul(i32 512, i32 %7) + store i32 %8, i32* %3, align 4 + %9 = load i32, i32* %2, align 4 + %10 = sitofp i32 %9 to float + %11 = fdiv float %10, 1.024000e+03 + store float %11, float* %4, align 4 + %12 = bitcast float* %4 to i64* + %13 = load i64, i64* %12, align 4 + store i64 %13, i64* %5, align 8 + %14 = load i64, i64* %5, align 8 + %15 = ashr i64 %14, 1 + %16 = sub nsw i64 1597463007, %15 + store i64 %16, i64* %5, align 8 + %17 = bitcast i64* %5 to float* + %18 = load float, float* %17, align 8 + store float %18, float* %4, align 4 + %19 = load float, float* %4, align 4 + %20 = fmul float %19, 1.024000e+03 + %21 = fptosi float %20 to i32 + store i32 %21, i32* %6, align 4 + %22 = load i32, i32* %6, align 4 + %23 = load i32, i32* %3, align 4 + %24 = load i32, i32* %6, align 4 + %25 = call i32 @fixmul(i32 %23, i32 %24) + %26 = load i32, i32* %6, align 4 + %27 = call i32 @fixmul(i32 %25, i32 %26) + %28 = sub nsw i32 1536, %27 + %29 = call i32 @fixmul(i32 %22, i32 %28) + store i32 %29, i32* %6, align 4 + %30 = load i32, i32* %6, align 4 + ret i32 %30 }""" # 文件路径 diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 55974be83..27dddd3bc 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -7,7 +7,12 @@ FILE_PATH="$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" #Step 1: Generate LLVM IR file echo "Step 1: Generate LLVM IR file" -clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + +#clang -g -O0 -Xclang -disable-O0-optnone -fno-math-errno -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + +#clang -O3 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll +#$HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 2: Use newton for optimization and quantization echo "Step 2: Use newton for optimization and quantization" @@ -24,7 +29,7 @@ python3 replace.py # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" diff --git a/applications/newton/llvm-ir/testMadgwickfix.sh b/applications/newton/llvm-ir/testMadgwickfix.sh index 2ee1f17e2..227a52fab 100755 --- a/applications/newton/llvm-ir/testMadgwickfix.sh +++ b/applications/newton/llvm-ir/testMadgwickfix.sh @@ -1,7 +1,15 @@ USER_HOME=$HOME # Step 1: Generate LLVM IR file -clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +#clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + +#clang -g -O0 -Xclang -disable-O0-optnone -fno-math-errno -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + + +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + + +#clang -O0 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c # Step 4: Optimize the generated LLVM IR file diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index fd4822805..0b5ae535d 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -2,8 +2,8 @@ USER_HOME=$HOME # Step 1: Generate LLVM IR file -clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c - +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +#clang -O0 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 4: Optimize the generated LLVM IR file opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --simplifycfg --instsimplify -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.llperformace diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 76b93fd21..d1eca4acc 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -47,7 +47,7 @@ //added code -#include "myRangeAnalysis.h" +//#include "myRangeAnalysis.h" #endif /* __cplusplus */ #include @@ -620,13 +620,23 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // 处理特殊的数值 //handleSpecialNumber(*Mod, Mod->getContext()); - + const char* homeDir = getenv("HOME"); + if (!homeDir) { + llvm::errs() << "Error: HOME environment variable not set.\n"; + return; + } // Save the optimized IR to a file - saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"; + saveModuleIR(*Mod, fileName); + // Save the optimized IR to a file + //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); //finalCorrectionPass(*Mod, quantizedType); //finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization - saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + + //替换为$HOMR + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 2a3535d66..4301377a2 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -2302,7 +2302,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "sqrt" || funcName == "sqrtf") + if (funcName == "sqrt" || funcName == "sqrtf" ||funcName =="llvm.sqrt.f64" ) { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); From c4c45e21839a99f707dcde8a2fae9fd2cc5254f2 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 26 Aug 2024 13:45:18 +0800 Subject: [PATCH 070/213] update some test files * dev2. --- .../llvm-ir/c-files/test_MadgwickAHRS.c | 78 +++++++++--------- .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 74 ++++++++--------- .../newton/llvm-ir/c-files/test_Original.c | 82 +++++++++---------- 3 files changed, 115 insertions(+), 119 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c index d443bbc5a..ab5b0fcc8 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -47,38 +47,38 @@ typedef struct { float dequantized4; } DequantizedValues; -typedef struct { - uint64_t start; - uint64_t current; - uint64_t min; - uint64_t max; -} ELAPSED_TIME; - -ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 - -static inline uint64_t rdtsc() { - uint32_t lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return ((uint64_t)hi << 32) | lo; -} - - - -void elapsed_time_start(uint32_t i) { - elapsed_time_tbl[i].start = rdtsc(); -} - -void elapsed_time_stop(uint32_t i) { - uint64_t stop = rdtsc(); - ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; - p_tbl->current = stop - p_tbl->start; - if (p_tbl->max < p_tbl->current) { - p_tbl->max = p_tbl->current; - } - if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { - p_tbl->min = p_tbl->current; - } -} +//typedef struct { +// uint64_t start; +// uint64_t current; +// uint64_t min; +// uint64_t max; +//} ELAPSED_TIME; +// +//ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 +// +//static inline uint64_t rdtsc() { +// uint32_t lo, hi; +// __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +// return ((uint64_t)hi << 32) | lo; +//} +// +// +// +//void elapsed_time_start(uint32_t i) { +// elapsed_time_tbl[i].start = rdtsc(); +//} +// +//void elapsed_time_stop(uint32_t i) { +// uint64_t stop = rdtsc(); +// ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; +// p_tbl->current = stop - p_tbl->start; +// if (p_tbl->max < p_tbl->current) { +// p_tbl->max = p_tbl->current; +// } +// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { +// p_tbl->min = p_tbl->current; +// } +//} typedef struct timespec timespec; timespec @@ -264,7 +264,7 @@ toc(timespec * start_time, const char * prefix) u_int64_t time_slots[ITERATION]; for (size_t idx = 0; idx < ITERATION; idx++) { - elapsed_time_start(idx); // 开始计时 + //elapsed_time_start(idx); // 开始计时 timespec timer = tic(); for (size_t ts = 0; ts < DATA_SIZE; ts++) { MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], @@ -275,7 +275,7 @@ toc(timespec * start_time, const char * prefix) } - elapsed_time_stop(idx); // 结束计时 + //elapsed_time_stop(idx); // 结束计时 time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } @@ -286,11 +286,11 @@ toc(timespec * start_time, const char * prefix) average_time /= ITERATION; printf("average time = %lu nm\n", average_time); - // 打印出每次迭代的最大、最小和当前时间 - for (size_t idx = 0; idx < ITERATION; idx++) { - printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", - idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); - } +// // 打印出每次迭代的最大、最小和当前时间 +// for (size_t idx = 0; idx < ITERATION; idx++) { +// printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", +// idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); +// } FILE *fptr = fopen("int_result.txt", "w"); for (size_t ts = 0; ts < DATA_SIZE; ts++) { diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index 1ccfd4ebf..3c2e36147 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -36,36 +36,36 @@ typedef struct { -typedef struct { - uint64_t start; - uint64_t current; - uint64_t min; - uint64_t max; -} ELAPSED_TIME; - -ELAPSED_TIME elapsed_time_tbl[10]; - -static inline uint64_t rdtsc() { - uint32_t lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return ((uint64_t)hi << 32) | lo; -} - -void elapsed_time_start(uint32_t i) { - elapsed_time_tbl[i].start = rdtsc(); -} - -void elapsed_time_stop(uint32_t i) { - uint64_t stop = rdtsc(); - ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; - p_tbl->current = stop - p_tbl->start; - if (p_tbl->max < p_tbl->current) { - p_tbl->max = p_tbl->current; - } - if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { - p_tbl->min = p_tbl->current; - } -} +//typedef struct { +// uint64_t start; +// uint64_t current; +// uint64_t min; +// uint64_t max; +//} ELAPSED_TIME; +// +//ELAPSED_TIME elapsed_time_tbl[10]; +// +//static inline uint64_t rdtsc() { +// uint32_t lo, hi; +// __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +// return ((uint64_t)hi << 32) | lo; +//} +// +//void elapsed_time_start(uint32_t i) { +// elapsed_time_tbl[i].start = rdtsc(); +//} +// +//void elapsed_time_stop(uint32_t i) { +// uint64_t stop = rdtsc(); +// ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; +// p_tbl->current = stop - p_tbl->start; +// if (p_tbl->max < p_tbl->current) { +// p_tbl->max = p_tbl->current; +// } +// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { +// p_tbl->min = p_tbl->current; +// } +//} typedef struct timespec timespec; @@ -253,7 +253,7 @@ toc(timespec * start_time, const char * prefix) u_int64_t time_slots[ITERATION]; for (size_t idx = 0; idx < ITERATION; idx++) { - elapsed_time_start(idx); // 开始计时 + //elapsed_time_start(idx); // 开始计时 timespec timer = tic(); for (size_t ts = 0; ts < DATA_SIZE; ts++) { MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], @@ -264,7 +264,7 @@ toc(timespec * start_time, const char * prefix) } - elapsed_time_stop(idx); // 结束计时 + //elapsed_time_stop(idx); // 结束计时 time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } @@ -275,11 +275,11 @@ toc(timespec * start_time, const char * prefix) average_time /= ITERATION; printf("average time = %lu nm\n", average_time); - // 打印出每次迭代的最大、最小和当前时间 - for (size_t idx = 0; idx < ITERATION; idx++) { - printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", - idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); - } +// // 打印出每次迭代的最大、最小和当前时间 +// for (size_t idx = 0; idx < ITERATION; idx++) { +// printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", +// idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); +// } FILE *fptr = fopen("int_result.txt", "w"); for (size_t ts = 0; ts < DATA_SIZE; ts++) { diff --git a/applications/newton/llvm-ir/c-files/test_Original.c b/applications/newton/llvm-ir/c-files/test_Original.c index a2247605e..ed245adf7 100644 --- a/applications/newton/llvm-ir/c-files/test_Original.c +++ b/applications/newton/llvm-ir/c-files/test_Original.c @@ -38,38 +38,38 @@ typedef struct { typedef struct timespec timespec; -typedef struct { - uint64_t start; - uint64_t current; - uint64_t min; - uint64_t max; -} ELAPSED_TIME; - -ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 - -static inline uint64_t rdtsc() { - uint32_t lo, hi; - __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); - return ((uint64_t)hi << 32) | lo; -} - - - -void elapsed_time_start(uint32_t i) { - elapsed_time_tbl[i].start = rdtsc(); -} - -void elapsed_time_stop(uint32_t i) { - uint64_t stop = rdtsc(); - ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; - p_tbl->current = stop - p_tbl->start; - if (p_tbl->max < p_tbl->current) { - p_tbl->max = p_tbl->current; - } - if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { - p_tbl->min = p_tbl->current; - } -} +//typedef struct { +// uint64_t start; +// uint64_t current; +// uint64_t min; +// uint64_t max; +//} ELAPSED_TIME; +// +//ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 +// +//static inline uint64_t rdtsc() { +// uint32_t lo, hi; +// __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +// return ((uint64_t)hi << 32) | lo; +//} +// +// +// +//void elapsed_time_start(uint32_t i) { +// elapsed_time_tbl[i].start = rdtsc(); +//} +// +//void elapsed_time_stop(uint32_t i) { +// uint64_t stop = rdtsc(); +// ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; +// p_tbl->current = stop - p_tbl->start; +// if (p_tbl->max < p_tbl->current) { +// p_tbl->max = p_tbl->current; +// } +// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { +// p_tbl->min = p_tbl->current; +// } +//} @@ -261,7 +261,7 @@ main() for (size_t idx = 0; idx < ITERATION; idx++) { - elapsed_time_start(idx); // 开始计时 + //elapsed_time_start(idx); // 开始计时 timespec timer = tic(); for (size_t ts = 0; ts < DATA_SIZE; ts++) { @@ -273,11 +273,7 @@ main() } - // MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, - // acc_x, acc_y, acc_z, - // mag_x, mag_y, mag_z, - // &q0, &q1, &q2, &q3); - elapsed_time_stop(idx); // 结束计时 + //elapsed_time_stop(idx); // 结束计时 time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } @@ -291,11 +287,11 @@ main() average_time /= ITERATION; printf("average time = %lu nm\n", average_time); - // 打印出每次迭代的最大、最小和当前时间 - for (size_t idx = 0; idx < ITERATION; idx++) { - printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", - idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); - } +// // 打印出每次迭代的最大、最小和当前时间 +// for (size_t idx = 0; idx < ITERATION; idx++) { +// printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", +// idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); +// } FILE * fptr = fopen("fp_result.txt", "w"); for (size_t ts = 0; ts < DATA_SIZE; ts++) From 6ca839299d6958488ea11f28a6d4b76d3d5496b0 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 26 Aug 2024 13:48:59 +0800 Subject: [PATCH 071/213] add input.csv * dev2. --- ...5a659df702ad3153897f1b8036b4fbbeccc7de.txt | 48 + ...838c712a9bd377eb39365bb131d8ec2fd7baed.txt | 48 + ...a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt | 48 + applications/newton/llvm-ir/input.csv | 1001 +++++++++++++++++ 4 files changed, 1145 insertions(+) create mode 100644 analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt create mode 100644 analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt create mode 100644 analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt create mode 100644 applications/newton/llvm-ir/input.csv diff --git a/analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt b/analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt new file mode 100644 index 000000000..f12df194f --- /dev/null +++ b/analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt @@ -0,0 +1,48 @@ + +changeset: 1663:375a659df702ad3153897f1b8036b4fbbeccc7de +char kNewtonVersion[] = "0.3-alpha-1663 (375a659df702ad3153897f1b8036b4fbbeccc7de) (build 08-26-2024-13:24-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt b/analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt new file mode 100644 index 000000000..a588874a8 --- /dev/null +++ b/analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt @@ -0,0 +1,48 @@ + +changeset: 1664:59838c712a9bd377eb39365bb131d8ec2fd7baed +char kNewtonVersion[] = "0.3-alpha-1664 (59838c712a9bd377eb39365bb131d8ec2fd7baed) (build 08-26-2024-13:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt b/analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt new file mode 100644 index 000000000..42df43cdb --- /dev/null +++ b/analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt @@ -0,0 +1,48 @@ + +changeset: 1662:a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9 +char kNewtonVersion[] = "0.3-alpha-1662 (a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9) (build 08-19-2024-23:05-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/input.csv b/applications/newton/llvm-ir/input.csv new file mode 100644 index 000000000..ce7c7c2b3 --- /dev/null +++ b/applications/newton/llvm-ir/input.csv @@ -0,0 +1,1001 @@ +Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.08969094,-6.654678345,26.09465027,-37.29600525,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.09969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.10969094,-7.548351288,24.87819672,-39.49668884,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.11969094,-7.311084747,23.94067001,-40.19360352,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.12969094,-5.958099365,24.8057251,-37.20587158,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.13969094,-8.100597382,25.37778282,-37.85224915,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.14969094,-6.394374847,25.43608284,-37.70759583,-0.1151283234,-0.00251355581,0.01010152325,-0.2793728709,0.4129949808,10.27848434 +0.15969094,-7.610450745,26.11058998,-38.66853333,-0.08246348053,0.00447106408,0.004323757719,-0.2367789149,0.4112356901,10.43229961 +0.16969094,-6.729293823,25.38845825,-38.77174377,-0.03473320603,0.04428362101,0.0009580431506,-0.2995947599,0.416141957,10.24356842 +0.17969094,-6.832756042,26.19889259,-38.67654419,0.002444048412,0.07756645232,-0.003223320469,-0.2556326687,0.3563099205,9.911549568 +0.18969094,-7.576393127,26.12816811,-37.14372253,0.007744635455,0.09112588316,-0.008515236899,-0.1705993563,0.3450968266,9.673620224 +0.19969094,-5.582935333,26.2094841,-38.06962585,-0.007077907212,0.1003924161,-0.01438220032,-0.2904628515,0.4137096405,9.693481445 +0.20969094,-7.26531601,26.16348839,-37.14692688,-0.0214833729,0.07035970688,-0.01663675718,-0.4015436172,0.4240173995,9.765337944 +0.21969094,-7.610450745,26.11058998,-38.66853333,-0.02840357646,0.0412395969,-0.01503071003,-0.4420829713,0.4474136829,9.710318565 +0.22969094,-5.917541504,25.49433899,-37.25494385,-0.04069363698,0.0300014019,-0.01120906137,-0.5065568686,0.4349407554,9.780290604 +0.23969094,-5.917541504,25.49433899,-37.25494385,-0.04557721689,0.01997687295,-0.003380747745,-0.4558943808,0.4060772061,9.982199669 +0.24969094,-7.248287201,26.17227745,-36.38452148,-0.03875789419,-0.002376349643,-0.001149083488,-0.4019374549,0.4131945372,10.07145596 +0.25969094,-5.484790802,27.59550095,-37.40536499,-0.02963040024,-0.007826571353,-0.0005718094762,-0.3436793387,0.4178147614,10.06400871 +0.26969094,-7.59992218,25.44834328,-36.33224487,-0.01951537654,-0.001477465499,-0.004841145594,-0.2960692346,0.3756780624,9.936094284 +0.27969094,-7.536643982,27.08343887,-37.97483826,-0.01999245957,-0.006206189282,-0.007511992939,-0.3121205568,0.3395512402,9.855197906 +0.28969094,-5.894504547,25.77330017,-36.96961975,-0.02960216627,-0.0179349836,-0.009124945849,-0.3137557507,0.3006750047,9.795846939 +0.29969094,-5.791351318,25.63038635,-38.94366455,-0.04506304488,-0.02701679431,-0.006459381431,-0.3230510056,0.3795295656,9.721902847 +0.30969094,-6.642169952,27.03585815,-39.3469696,-0.06745177507,-0.03394386917,-0.005393324886,-0.3160211146,0.3835323155,9.728869438 +0.31969094,-8.179725647,26.60138512,-37.78651428,-0.07811312377,-0.03392977268,-0.003795302706,-0.3117079139,0.3867061734,9.83324337 +0.32969094,-6.671707153,26.08586121,-38.05841064,-0.07162205875,-0.02754923515,-0.002687801374,-0.3693574667,0.4109267592,9.983849525 +0.33969094,-7.483146667,25.31245995,-37.69638062,-0.05240149051,-0.02055237256,0.006894228514,-0.4506206214,0.425347209,10.05073643 +0.34969094,-7.178882599,25.34426498,-38.00453186,-0.01982024126,-0.008340923116,0.01644631103,-0.424624294,0.4272369146,10.15809345 +0.35969094,-5.974441528,27.26355934,-37.68589783,0.01485640649,-0.002841048408,0.0174930077,-0.322455287,0.4224477112,10.03915787 +0.36969094,-5.750793457,26.31900024,-38.99273682,0.03709132969,0.01580360159,0.01747041196,-0.2883752882,0.3374709487,9.90159893 +0.37969094,-5.870170593,26.18646812,-36.99906921,0.03729007766,0.02409679629,0.01276430115,-0.2753283978,0.2819014192,9.6376791 +0.38969094,-7.402835846,26.9563446,-38.57655334,0.01491038781,0.01591248065,0.01207613945,-0.2228651792,0.2963768244,9.694525719 +0.39969094,-8.981754303,26.09991455,-37.74905396,-0.008029373363,-0.004363908432,0.009553089738,-0.3137707412,0.3058240712,9.756849289 +0.40969094,-7.179374695,24.94340134,-36.9077301,-0.0325601548,-0.01107135322,0.008988874033,-0.347237438,0.2963377833,9.893450737 +0.41969094,-6.377658844,26.11239243,-38.82402039,-0.0477651991,-0.02031742781,0.01180266216,-0.2945395708,0.2534381747,10.08242321 +0.42969094,-7.576393127,26.12816811,-37.14372253,-0.03290509433,-0.03066522814,0.01591363735,-0.3064144552,0.2915987968,10.09379768 +0.43969094,-6.654678345,26.09465027,-37.29600525,-0.007921610959,-0.01810567081,0.01840451546,-0.220240429,0.3167188168,10.02454567 +0.44969094,-7.910011292,26.21474838,-38.52267456,0.01434707176,0.005370598286,0.01262276806,-0.2308089137,0.3418518007,9.914393425 +0.45969094,-7.616950989,25.43955421,-37.09465027,0.02976943925,0.02135305665,0.01048317552,-0.2660395503,0.3493344486,9.844152451 +0.46969094,-6.706256866,25.66741943,-38.48641968,0.03813938051,0.02182977088,0.005633147899,-0.216332078,0.3326821923,9.803571701 +0.47969094,-6.727798462,27.58842468,-37.70732117,0.0336618945,0.00669084629,-0.002252099337,-0.3407348692,0.3455455303,9.784972191 +0.48969094,-6.272701263,27.50192642,-37.85481262,0.02714404091,-0.02482098155,-7.733469829e-06,-0.4290958643,0.3308064342,9.846453667 +0.49969094,-7.497264862,24.9045639,-37.20947266,0.02085882053,-0.04777231812,0.005195887294,-0.4415936172,0.3619870543,9.872394562 +0.50969094,-6.638454437,26.37009621,-37.31562805,0.01971525326,-0.06770654023,0.007923526689,-0.3541632593,0.3599542379,9.863398552 +0.51969094,-7.459617615,25.99228477,-38.50785828,0.02144834772,-0.05833188444,0.005967103411,-0.3571015596,0.2838820815,9.942513466 +0.52969094,-6.602794647,24.85698509,-38.581604,0.03338546306,-0.04018680751,0.002523483243,-0.3252090514,0.3449226618,9.981822014 +0.53969094,-6.37815094,25.71152878,-37.72721863,0.0395555906,-0.03105190583,0.002672136296,-0.3542988896,0.3391857445,9.916908264 +0.54969094,-7.507480621,24.89929199,-37.66693115,0.04480022192,-0.009855329059,0.002203115728,-0.2928415239,0.3543523848,9.862686157 +0.55969094,-7.940353394,25.53140831,-38.0161438,0.04905298352,0.03105917946,-0.004327977076,-0.2486828268,0.414686501,9.692631721 +0.56969094,-6.474308014,26.92634201,-38.42385864,0.04452182725,0.05528639257,-0.009296388365,-0.2784487903,0.3446758687,9.670860291 +0.57969094,-6.825942993,26.20240784,-38.37158203,0.0319024995,0.02479611523,-0.006458672695,-0.3158861697,0.380629003,9.788747787 +0.58969094,-6.840065002,25.7945137,-37.88470459,0.02158891037,-0.03473115712,-0.001664606389,-0.3231438696,0.411952734,9.852827072 +0.59969094,-7.162658691,25.61971092,-38.02415466,0.01061961707,-0.06082195044,-0.003890550463,-0.2853017449,0.4193702936,9.816333771 +0.60969094,-6.856781006,25.11820412,-36.76826477,0.007375336252,-0.03639098257,-0.006122777238,-0.2694948912,0.4136307836,9.676003456 +0.61969094,-6.896842957,24.83045387,-37.81599426,0.002672176808,-0.001061701681,-0.01018739119,-0.3234909773,0.3764295876,9.812074661 +0.62969094,-6.792694092,26.48664284,-37.6288147,0.005003871396,0.01006212085,-0.008162576705,-0.4266119301,0.3720042706,10.06733036 +0.63969094,-6.353816986,26.12469673,-37.75666809,0.02046790347,-0.01681301743,0.0007936262991,-0.3262727559,0.3997677267,10.05027294 +0.64969094,-7.610450745,26.11058998,-38.66853333,0.03206149116,-0.02037302963,0.0003540727776,-0.2678869963,0.4512276053,9.878255844 +0.65969094,-6.743099213,24.31304169,-36.40600586,0.0311081633,0.002321796725,-0.003910094965,-0.3650930524,0.3835353553,9.755164146 +0.66969094,-7.426364899,26.27651978,-37.76507568,0.01962944865,0.01086440869,0.0001691966318,-0.2833980918,0.3181568384,9.629114151 +0.67969094,-8.06734848,25.66201782,-37.10948181,-0.003784938715,-0.009464945644,0.004316138569,-0.2693619728,0.3249111176,9.749714851 +0.68969094,-8.06734848,25.66201782,-37.10948181,-0.01996190473,-0.01890161261,0.004569322802,-0.2541345954,0.3612316251,9.785536766 +0.69969094,-6.71957016,24.99286652,-37.21748352,-0.03055388108,-0.002701988444,0.001403293107,-0.2876216173,0.42038095,9.840220451 +0.70969094,-6.71957016,24.99286652,-37.21748352,-0.02469141968,0.02064591087,0.0002037035301,-0.3182426989,0.4196154475,9.82704258 +0.71969094,-6.593383789,25.12891579,-38.90620422,-0.01536074467,0.02345331945,-0.0008636235725,-0.3334631324,0.4254368544,9.891711235 +0.72969094,-6.681922913,26.08058739,-38.51586914,0.0003667054698,0.02293391153,0.00113602588,-0.3242745101,0.4562799931,9.878070831 +0.73969094,-6.498645782,26.51317406,-38.39442444,0.01448426675,0.03026067652,0.000355088152,-0.3454433978,0.4558205009,9.850791931 +0.74969094,-6.310348511,26.41420555,-36.556427,0.02660195902,0.02360223606,0.002740718424,-0.3830767274,0.4391977191,9.813574791 +0.75969094,-7.249095917,26.43893433,-37.16656494,0.03125537932,-0.009130412713,0.006767154206,-0.3287489414,0.4146726429,9.829097748 +0.76969094,-8.217372894,25.51366615,-36.48812866,0.02221588045,-0.04891215265,0.01043653488,-0.2150225341,0.3947498798,9.832125664 +0.77969094,-8.854763031,25.96930504,-38.65571594,0.01269038301,-0.05699107051,0.007309804205,-0.2531299889,0.4025529325,9.883623123 +0.78969094,-7.472930908,25.31773376,-37.23892212,0.009331553243,-0.0362610966,0.007923526689,-0.2762732506,0.3899558783,9.84967804 +0.79969094,-5.784229279,24.96638107,-36.75984192,0.001331721433,-0.02163651586,0.007390852552,-0.234718129,0.4027428627,9.921022415 +0.80969094,-7.957382202,25.52261925,-38.77854919,-0.001178442501,-0.02419010922,0.007390852552,-0.1688663065,0.3537279963,9.899970055 +0.81969094,-7.96679306,25.25068855,-38.45397949,-0.0002859253436,-0.02208984457,0.003662134986,-0.1612079442,0.3765885234,9.860362053 +0.82969094,-6.607620239,27.45429993,-38.91896057,-0.005500952248,-0.00137518486,0.002451392356,-0.2259380519,0.4388239086,9.777145386 +0.83969094,-6.706256866,25.66741943,-38.48641968,-0.008655028418,0.03205142915,0.001516730525,-0.2650729716,0.4043938816,9.713522911 +0.84969094,-6.545207977,25.55438805,-37.86827087,-0.01504812483,0.05091909319,0.004732759669,-0.2684932947,0.3750241399,9.674304008 +0.85969094,-7.96679306,25.25068855,-38.45397949,-0.02855567634,0.04647941142,0.006329850759,-0.2255412191,0.4341191947,9.723275185 +0.86969094,-7.983821869,25.24189949,-39.21638489,-0.03407438844,0.03285332024,0.009157052264,-0.2946357727,0.4097974002,9.758426666 +0.87969094,-6.569046021,25.54208374,-38.93565369,-0.04476465285,0.03424095362,0.007229163777,-0.3785450459,0.4446446896,9.875383377 +0.88969094,-7.569580078,26.13168335,-36.83876038,-0.04716154933,0.02489669435,0.01244034618,-0.3616573513,0.4302352071,10.03662205 +0.89969094,-7.520793915,24.22473907,-36.397995,-0.03513527289,0.009162386879,0.01834226586,-0.3369231224,0.354245156,9.933807373 +0.90969094,-6.706256866,25.66741943,-38.48641968,-0.03387806937,5.956785753e-05,0.01586125046,-0.2603001595,0.413443327,9.827401161 +0.91969094,-8.26845932,25.48729897,-38.77534485,-0.0300873816,0.01511435304,0.008654415607,-0.2779739201,0.3738090396,9.869602203 +0.92969094,-5.997970581,26.58373451,-36.87442017,-0.02735616267,0.04325161129,0.004194807727,-0.3075370789,0.3841251135,9.859921455 +0.93969094,-6.103237152,25.86172104,-39.7224884,-0.02637853473,0.05735059083,0.003129459452,-0.320104301,0.373578757,9.733283043 +0.94969094,-7.90920639,25.94809151,-37.74064636,-0.03651535884,0.0361250788,0.005883715581,-0.3417606056,0.3402200043,9.643527985 +0.95969094,-7.259311676,26.43366051,-37.62400818,-0.05156690255,0.009104821831,0.006214913446,-0.3017233014,0.362526238,9.718540192 +0.96969094,-6.360630035,26.12118149,-38.06161499,-0.05347704142,0.01731107384,0.002591064665,-0.2577753067,0.3784553707,9.827540398 +0.97969094,-6.320072174,26.80979538,-38.11068726,-0.03843300045,0.03169715032,-0.0004796904977,-0.3363775015,0.3911237717,9.874797821 +0.98969094,-8.060348511,26.73391724,-39.78018188,-0.02594162524,0.03712494671,0.0003746603616,-0.4213152528,0.352683723,9.925812721 +0.99969094,-6.769851685,24.69984436,-38.72267151,-0.01860057376,0.03343068063,0.002202911768,-0.4492088258,0.3678621352,10.10691547 +1.00969094,-5.576122284,26.21299934,-37.76467896,-0.001166775823,0.01629613154,0.006246880163,-0.4433344305,0.3575173914,10.19894409 +1.01969094,-7.339931488,25.45729637,-38.62266541,0.02069848031,0.005792571232,0.009291321039,-0.3940068483,0.3785350621,9.979031563 +1.02969094,-4.853103638,25.20479393,-37.23670959,0.02874404564,0.01429859176,0.008222779259,-0.3645473719,0.3816692829,9.829014778 +1.03969094,-6.648670197,26.36482239,-37.77308655,0.02584087476,0.01577451825,0.008752264082,-0.3319935501,0.391680181,9.704668045 +1.04969094,-7.364269257,25.04412842,-38.5932312,0.01320957113,0.02140369639,0.004434604198,-0.2616887093,0.3081431389,9.435370445 +1.05969094,-5.186531067,27.35713577,-37.23646545,-0.01679551601,0.01554101706,0.00070909434,-0.3204491436,0.3072315753,9.469326973 +1.06969094,-6.095619202,25.59858131,-38.63548279,-0.04527019337,-0.02895073779,0.003868098371,-0.3079995215,0.3426241875,9.777582169 +1.07969094,-8.571918488,25.18883705,-37.68516541,-0.05302047729,-0.07532060146,0.007323132362,-0.3083209097,0.3275064826,9.900536537 +1.08969094,-6.433750153,27.6149559,-38.47293091,-0.04696979001,-0.09256293625,0.009241476655,-0.3125772476,0.3005108833,9.937273979 +1.09969094,-7.12210083,26.30832481,-38.07322693,-0.03368562087,-0.08957409114,0.008200418204,-0.2579268813,0.3488419354,9.958529472 +1.10969094,-7.961097717,26.1883812,-40.80989075,-0.01956947893,-0.11456047,0.01081642881,-0.3258158267,0.3308687806,10.03633881 +1.11969094,-6.585765839,24.86577415,-37.81919861,0.004703424871,-0.1317160726,0.01432392001,-0.2255369574,0.315831691,10.02883244 +1.12969094,-6.65839386,26.76041222,-39.3273468,0.03163328022,-0.09282163531,0.007392741274,-0.2747437358,0.4104028642,9.873939514 +1.13969094,-8.267654419,25.22064209,-37.99331665,0.03962652758,-0.04242435843,0.003125044052,-0.2230227441,0.3918129504,9.700381279 +1.14969094,-8.267654419,25.22064209,-37.99331665,0.0334565714,-0.01739601418,-0.0003491293173,-0.2507834136,0.3877517879,9.647592545 +1.15969094,-6.522174835,25.83334923,-37.58294678,0.01399359573,-0.01048641745,-0.0005820093211,-0.2868594825,0.3809897602,9.612992287 +1.16969094,-6.522174835,25.83334923,-37.58294678,-0.009211212397,0.002303284593,-0.002197280526,-0.2456134111,0.3624066412,9.478822708 +1.17969094,-7.220245361,24.92230797,-38.73748779,-0.03906954825,-0.005264308304,-0.002449053805,-0.223413676,0.3052503765,9.675016403 +1.18969094,-8.320838928,26.32410049,-36.39294434,-0.04616601393,-0.01693219133,0.0009987638332,-0.2595686018,0.3547726274,9.831622124 +1.19969094,-6.297035217,27.08875656,-37.82536316,-0.04363083839,-0.01177432761,0.006406869274,-0.3021802008,0.4283012152,9.923987389 +1.20969094,-5.802066803,25.22424889,-38.30429077,-0.03484881297,-0.002998926677,0.005745526869,-0.3050480485,0.3657292128,10.12125111 +1.21969094,-8.227901459,26.17591286,-38.82441711,-0.01179379877,-0.006921730004,0.009910203516,-0.3035609126,0.3256982863,10.10582829 +1.22969094,-6.744827271,27.57963562,-38.46972656,-0.0006591975689,-0.01147360541,0.0132014323,-0.2544778585,0.2881220877,9.779053688 +1.23969094,-7.299373627,26.14591026,-38.67173767,-0.0105999019,-0.00578986574,0.01048056968,-0.1460589617,0.283655107,9.60635376 +1.24969094,-7.473735809,25.58439064,-38.02095032,-0.03279230744,0.02829709277,-0.0002586811315,-0.1317581385,0.2602887452,9.617964745 +1.25969094,-6.430034637,26.94919395,-36.44160461,-0.04902238026,0.0402559936,-0.007753454149,-0.2250817716,0.3019514382,9.776272774 +1.26969094,-6.785385132,26.89102173,-38.4206543,-0.04932562262,0.03157758713,-0.007301884238,-0.2387727499,0.3511135578,9.836483002 +1.27969094,-7.3736763,24.77219772,-38.26863098,-0.04486268014,0.04647804797,-0.007624792866,-0.2069167346,0.3134610355,9.70259285 +1.28969094,-6.866500854,25.51379395,-38.32250977,-0.04943052307,0.0468153283,-0.008807333186,-0.2923846543,0.272872448,9.821870804 +1.29969094,-6.055557251,25.88633156,-37.5877533,-0.04847415164,0.01401153393,3.832438961e-05,-0.2815147936,0.2507653534,9.938878059 +1.30969094,-8.090877533,24.98219299,-36.29800415,-0.0390965566,-0.007415878586,0.002172368579,-0.3992255032,0.3018830121,10.052248 +1.31969094,-6.433750153,27.6149559,-38.47293091,-0.01272503659,0.01556072105,0.006858177949,-0.3525747061,0.3768803477,10.04895401 +1.32969094,-5.842926025,26.20053101,-35.77920532,0.009118192829,0.05159369111,0.003864748403,-0.3418606818,0.3400193453,9.91121769 +1.33969094,-6.688735962,26.07707214,-38.82081604,0.02072456852,0.05234477669,0.00173030328,-0.3416000307,0.3463162482,9.807590485 +1.34969094,-7.080738068,26.73028183,-37.340271,0.02005904168,0.03351046145,0.001531437505,-0.2878206968,0.3056016266,9.60395813 +1.35969094,-8.100597382,25.37778282,-37.85224915,0.005226348527,0.03811069578,-0.003207825124,-0.2599823773,0.2751262188,9.588534355 +1.36969094,-6.607307434,26.7867794,-37.04013062,-0.01296652295,0.01011377014,-0.003455437953,-0.2150486112,0.2730696797,9.68800354 +1.37969094,-7.91601944,25.94457626,-38.04559326,-0.02920342982,-0.03429959714,-0.003262628801,-0.1792165935,0.256131053,9.581644058 +1.38969094,-6.480808258,26.25530624,-36.84997559,-0.05070129409,-0.048120372,-0.005925999023,-0.1703465581,0.202443108,9.596740723 +1.39969094,-5.802066803,25.22424889,-38.30429077,-0.06068585813,-0.06144545227,-0.00397459697,-0.2702684999,0.2518742383,9.811048508 +1.40969094,-6.799507141,26.48312759,-37.93377686,-0.05394846946,-0.08207222819,0.003652619664,-0.3534995615,0.319263339,9.85598278 +1.41969094,-5.760700226,25.6462059,-37.57131958,-0.03585629165,-0.04909897596,0.01270254329,-0.3109368086,0.2954011559,9.816308975 +1.42969094,-7.402030945,26.68968773,-37.79452515,-0.02535336465,-0.01209592633,0.01307334937,-0.3471710384,0.3598883748,9.775583267 +1.43969094,-7.899482727,25.55250168,-36.18638611,-0.01951367408,-0.001619831193,0.0187486317,-0.3248218298,0.3403574526,9.822806358 +1.44969094,-7.186187744,24.93988609,-37.212677,-0.01519426517,-0.008781705052,0.02070770226,-0.3171883821,0.308766067,9.850348473 +1.45969094,-6.514865875,26.23772812,-38.37478638,-0.02264731191,-0.0292735938,0.01713754982,-0.2401963323,0.2161890417,9.744996071 +1.46969094,-7.224758148,26.85210228,-37.19599915,-0.03933265805,-0.05343721062,0.01005422324,-0.0907349363,0.2035675794,9.581429482 +1.47969094,-6.823036194,25.80330276,-37.12229919,-0.06534571946,-0.08001222461,0.001245157328,-0.05353847891,0.21554254,9.619607925 +1.48969094,-5.802066803,25.22424889,-38.30429077,-0.08012887836,-0.1155415028,-0.008874082938,-0.07394784689,0.2216080427,9.712301254 +1.49969094,-7.926235199,25.93930244,-38.50305176,-0.08614850789,-0.1406992674,-0.01109797321,-0.1761151403,0.2405264229,9.942374229 +1.50969094,-8.060848236,26.33305359,-38.68336487,-0.06363983452,-0.131477356,-0.008736706339,-0.1644631177,0.2284149528,10.12247753 +1.51969094,-5.934570312,25.48554993,-38.01734924,-0.03686951846,-0.1067624614,-0.009261385538,-0.2282977104,0.1928204149,10.15298653 +1.52969094,-6.481929779,27.18948364,-39.51086426,-0.01791328192,-0.08309015632,-0.00313524832,-0.2743725479,0.21198605,10.15187454 +1.53969094,-7.875953674,26.23232651,-36.99786377,-0.008983654901,-0.07694666088,0.002590309829,-0.2886238396,0.1790893525,10.07465935 +1.54969094,-7.529022217,26.82029724,-36.88783264,-0.009366864339,-0.07829668373,0.01138180681,-0.1872850209,0.1983563304,9.777256966 +1.55969094,-7.105072021,26.31711388,-37.31082153,-0.03254691884,-0.04712443799,0.01258446462,-0.1739229262,0.2279125303,9.533390045 +1.56969094,-7.194416046,27.53544235,-37.70251465,-0.05746591091,-0.001762664411,0.009799225256,-0.1670654416,0.1957996339,9.661688805 +1.57969094,-7.11448288,26.04518318,-36.98625183,-0.06772604585,0.003953762818,0.0142403841,-0.2083566934,0.2039169073,9.748625755 +1.58969094,-7.978923798,27.4436245,-37.99946594,-0.07317771018,-0.02151427604,0.02289812267,-0.2312756628,0.2643887997,9.760647774 +1.59969094,-5.639026642,27.71204758,-37.71853638,-0.06665766984,-0.04223006219,0.02425481752,-0.1424741298,0.2968740463,9.914733887 +1.60969094,-5.639026642,27.71204758,-37.71853638,-0.04367075861,-0.04056430608,0.0234865211,-0.1608794183,0.3256749511,10.09056568 +1.61969094,-7.868648529,26.6367054,-37.78971863,-0.008530608378,-0.04159038514,0.02200041711,-0.189670369,0.2899492383,10.21168613 +1.62969094,-7.868648529,26.6367054,-37.78971863,0.02628235519,-0.05120419711,0.02283840254,-0.2218288481,0.3283816576,10.20733452 +1.63969094,-6.303535461,26.41772079,-36.2514801,0.05489103496,-0.03783921897,0.02347395197,-0.07787474245,0.2735392451,9.849228859 +1.64969094,-8.169509888,26.60665894,-37.32905579,0.04711384699,-0.003802698106,0.01128105074,-0.0208355207,0.2059793174,9.5058918 +1.65969094,-6.614925385,27.04992104,-38.12710571,0.01760414243,0.01516226307,0.0005188670475,-0.01495215017,0.2007467449,9.477257729 +1.66969094,-7.610450745,26.11058998,-38.66853333,-0.0118032163,0.003432350932,-0.004136003554,-0.1004913151,0.2752893269,9.617282867 +1.67969094,-5.836917877,26.47070312,-36.25628662,-0.03128357977,-0.01314315572,-0.003888061969,-0.1696598381,0.2897028029,9.776549339 +1.68969094,-8.07756424,25.656744,-37.56692505,-0.03902820498,-0.031104194,0.0003777854145,-0.1696388274,0.3016649187,9.855332375 +1.69969094,-8.01266861,26.75852585,-37.64543152,-0.0361819528,-0.04390206188,0.004108215682,-0.08676551282,0.2340180129,9.961453438 +1.70969094,-6.815727234,26.20768166,-37.91413879,-0.02684909478,-0.06165890396,0.004194807727,-0.08061584085,0.2390247434,10.00907612 +1.71969094,-8.774143219,26.94566917,-37.65705872,-0.006290666759,-0.06052558869,0.005260156002,-0.09469732642,0.2780058086,9.99105072 +1.72969094,-6.751327515,26.90859985,-36.89584351,0.009505183436,-0.03102665953,0.002138116863,-0.1390231103,0.2646279931,9.884099007 +1.73969094,-7.415344238,26.01513672,-36.52558899,0.005679448135,0.004524962511,0.001461617649,-0.008892144077,0.1571245342,9.586317062 +1.74969094,-6.648670197,26.36482239,-37.77308655,-0.01966399699,0.02597477287,-0.003588251071,-0.02148039639,0.06291545182,9.521533966 +1.75969094,-7.916511536,25.54371262,-36.9487915,-0.04305823147,0.0223974213,-0.003466613824,-0.0427948162,0.1390912533,9.611943245 +1.76969094,-8.504920959,26.15816879,-37.29640198,-0.05958180875,0.0147998305,-0.003130199853,-0.0402473025,0.1930487752,9.696281433 +1.77969094,-7.408531189,26.01865196,-36.22064209,-0.06862412393,0.02192832902,-0.002607707866,-0.1067857593,0.1676802784,9.801758766 +1.78969094,-7.651504517,25.02111244,-37.5226593,-0.06073463336,0.009087075479,-0.00118764746,-0.1356327534,0.1706100255,9.846673965 +1.79969094,-7.519615173,27.09222794,-37.21243286,-0.05577040464,-0.02627890185,0.006700108293,-0.1201330051,0.2078993767,9.865870476 +1.80969094,-7.292560577,26.14942551,-38.36677551,-0.045699507,-0.05088428408,0.009521549568,-0.1055382341,0.194249332,9.946352005 +1.81969094,-8.941196442,26.78852844,-37.79812622,-0.02589777857,-0.03927788138,0.007972843945,-0.1263555437,0.2195701152,9.982694626 +1.82969094,-8.136257172,26.89089394,-36.58627319,-0.0022355197,-0.007897994481,0.00841078721,-0.13340047,0.2100664824,9.810973167 +1.83969094,-7.91601944,25.94457626,-38.04559326,0.006072483025,0.02714213356,0.008006671444,-0.08557648957,0.1570303142,9.632401466 +1.84969094,-6.8097229,26.47785378,-38.39122009,-0.003367578145,0.0620284602,0.001605473459,-0.1225117221,0.1410100162,9.67570591 +1.85969094,-7.31608963,25.46960068,-37.55529785,-0.0129718259,0.06267061085,0.002064112108,-0.1651064754,0.1498811394,9.766771317 +1.86969094,-8.440029144,27.25995255,-37.37492371,-0.01939735189,0.02667776495,0.001498597208,-0.07774727792,0.1837403625,9.869438171 +1.87969094,-6.269790649,27.10281944,-36.60549927,-0.01915593445,-0.02103452012,-0.003069892991,-0.06515015662,0.1889760047,9.92991066 +1.88969094,-6.245456696,27.5159874,-36.63494873,-0.006940102205,-0.03827885538,-0.005393324886,-0.1165964454,0.1897324771,9.984901428 +1.89969094,-8.234401703,25.50487709,-37.25053406,0.01909381524,-0.03864673525,-0.004890202545,-0.1061906591,0.1626597643,10.02290344 +1.90969094,-6.614612579,26.38240051,-36.24827576,0.04109552875,-0.06041933596,-0.003773062024,-0.1020373926,0.171957165,9.990610123 +1.91969094,-7.17206955,25.34778023,-37.69958496,0.05656939,-0.05699628592,-0.003241203493,-0.09723923355,0.2134001702,9.856469154 +1.92969094,-8.266967773,27.6872654,-37.7109375,0.05981186032,-0.0220338013,-0.0090006385,-0.09895791858,0.1922320724,9.676743507 +1.93969094,-7.080738068,26.73028183,-37.340271,0.04462248087,0.01481336262,-0.01446673647,-0.07513602078,0.1447238624,9.591789246 +1.94969094,-5.692897797,26.34888268,-36.40054321,0.01682722196,0.01619235054,-0.01550634205,-0.04328014329,0.08844234794,9.693587303 +1.95969094,-8.033290863,25.67959595,-35.58467102,-0.004610041622,-0.01159483008,-0.01657947898,-0.06921155751,0.1747955829,9.817505836 +1.96969094,-6.930095673,24.54621887,-38.55877686,-0.01155486237,-0.02273061872,-0.01285894588,-0.1380582452,0.2311763912,9.925704956 +1.97969094,-7.033367157,27.42241287,-37.08439636,-0.007763602771,-0.007799980231,-0.0128642004,-0.08171288669,0.2393242121,9.866889954 +1.98969094,-8.105110168,27.30757713,-36.31077576,0.000226826407,0.007088335231,-0.01391610876,-0.07211033255,0.1989753842,9.786602974 +1.99969094,-8.221088409,26.1794281,-38.51947021,0.007151844911,0.01721406542,-0.0149721168,-0.1541464329,0.2528064251,9.78149128 +2.00969094,-6.014190674,26.30828857,-36.8547821,0.01787129417,0.0273047667,-0.01124131307,-0.2166621983,0.3024373949,9.920562744 +2.01969094,-6.497837067,26.24651718,-37.61238098,0.02909519151,0.02771151997,-0.009713578969,-0.1204545349,0.2341998667,10.05337238 +2.02969094,-7.641288757,25.02638626,-37.06521606,0.03541889042,0.009614607319,-0.01449812017,-0.0005609099171,0.1977932751,9.951425552 +2.03969094,-6.648670197,26.36482239,-37.77308655,0.03854234144,-0.002443780191,-0.02357402071,0.02601853013,0.1741227955,9.71210289 +2.04969094,-6.823036194,25.80330276,-37.12229919,0.03266156837,0.002902421635,-0.03311468288,-0.06614656001,0.2000939846,9.64405632 +2.05969094,-8.481391907,26.83799362,-38.10787964,0.03341981024,0.004892181139,-0.03618502244,-0.1074707657,0.162389487,9.802295685 +2.06969094,-8.481391907,26.83799362,-38.10787964,0.04172888771,-0.004742521793,-0.03478867188,-0.08618438244,0.1765760481,9.902980804 +2.07969094,-9.125282288,26.62259865,-38.70158386,0.04610145465,-0.01111476123,-0.03636684269,-0.1150004193,0.2032267451,9.933595657 +2.08969094,-7.097454071,26.05397224,-36.22384644,0.04376486316,-0.005667108111,-0.03837662563,-0.1557287276,0.1533870399,9.807321548 +2.09969094,-8.169509888,26.60665894,-37.32905579,0.03125278652,-0.001980882138,-0.03598660603,-0.1652607918,0.2026120722,9.855483055 +2.10969094,-7.881961823,25.96215439,-36.52078247,0.0225853771,-0.01276575774,-0.03193943202,-0.1723911315,0.2648062408,9.907776833 +2.11969094,-8.129444122,26.89440918,-36.28132629,0.0193426609,-0.01047625113,-0.02931698225,-0.1844214499,0.2498556525,9.777618408 +2.12969094,-8.090877533,24.98219299,-36.29800415,0.01299724448,0.03212872893,-0.02499904484,-0.1594483554,0.185912326,9.599106789 +2.13969094,-6.583778381,27.46660423,-37.85160828,0.003714879043,0.0774788782,-0.02019474469,-0.1376511902,0.1288460642,9.618491173 +2.14969094,-8.394954681,26.01877022,-38.96549988,-0.004882628098,0.07974929363,-0.01858952455,-0.1264213771,0.2041591108,9.738189697 +2.15969094,-7.352062225,27.65023232,-38.16816711,-0.01172669791,0.06241033226,-0.01871017553,-0.1282311976,0.2556934357,9.854340553 +2.16969094,-6.314064026,27.0799675,-38.58776855,-0.007096226327,0.06087465584,-0.02018002048,-0.140379414,0.2431407869,9.895014763 +2.17969094,-7.603637695,26.11410522,-38.36357117,-0.0009169559926,0.05228283256,-0.02110611834,-0.2296079248,0.2668908536,9.884092331 +2.18969094,-7.875953674,26.23232651,-36.99786377,0.01122065168,0.03513076901,-0.0161176268,-0.2607826889,0.2880160809,9.85874939 +2.19969094,-7.865737915,26.23760033,-36.54040527,0.01991503313,0.01621211134,-0.01369080879,-0.2125987709,0.2349860221,9.828289986 +2.20969094,-7.449401855,25.99755859,-38.05039978,0.02593856305,0.001058021793,-0.01391610876,-0.1139675379,0.1974376589,9.821710587 +2.21969094,-8.971538544,26.10518837,-37.29159546,0.03127063811,-0.00808400847,-0.01858368888,-0.07830224186,0.1740580052,9.894181252 +2.22969094,-8.855258942,25.56844139,-37.55891418,0.04016362131,-0.01468131132,-0.02297156677,-0.08382807672,0.1723902375,9.791069031 +2.23969094,-6.840065002,25.7945137,-37.88470459,0.03510941565,-0.005808715709,-0.02527568862,-0.09655064344,0.2753678262,9.775838852 +2.24969094,-6.344406128,26.39662743,-38.08123779,0.03261594847,-0.001005770639,-0.0267905239,-0.1564678401,0.3032001257,9.954015732 +2.25969094,-6.631149292,26.7744751,-38.10748291,0.03750025481,-0.0004138355143,-0.02738986723,-0.2111902684,0.2499201596,9.906594276 +2.26969094,-5.894012451,26.17416382,-38.06642151,0.03352537006,-0.006534338929,-0.02287421189,-0.185904786,0.256785661,9.880761147 +2.27969094,-7.282344818,26.15469933,-37.90933228,0.02651475742,-0.01083567925,-0.01977552287,-0.1777724028,0.3054589629,9.881853104 +2.28969094,-7.128601074,25.63728905,-36.49934387,0.02215582877,-0.01636308059,-0.01840080321,-0.200542137,0.2789077759,9.947379112 +2.29969094,-8.145980835,27.28648376,-38.14053345,0.0253528133,-0.01774885505,-0.01157522202,-0.2354736328,0.327449441,10.0650425 +2.30969094,-7.559364319,26.13695717,-36.38131714,0.03911292553,-0.01410826668,-0.0002708618995,-0.2110538632,0.3184655607,10.03411579 +2.31969094,-8.043819427,26.34184265,-37.92095947,0.05202747509,0.004895074293,0.007309973706,-0.1512760371,0.3286831975,9.895553589 +2.32969094,-8.00245285,26.76379967,-37.18798828,0.04810490087,0.02173821256,0.008107297122,-0.08198977262,0.3075869679,9.735380173 +2.33969094,-7.408843994,26.68617249,-38.09947205,0.03104375303,0.02506607771,0.008456200361,-0.1293141246,0.3014782965,9.657446861 +2.34969094,-7.115287781,26.31184006,-37.76828003,0.008865046315,0.01515081618,0.01111957058,-0.1489645094,0.3126883209,9.760924339 +2.35969094,-8.885910034,25.55262184,-38.9312439,-0.01044041198,0.005883761216,0.01086863503,-0.1335579604,0.3199432492,9.854743004 +2.36969094,-8.196754456,26.59259605,-38.54891968,-0.01020090934,0.02274373919,0.009521549568,-0.1970662028,0.3293652236,9.818229675 +2.37969094,-8.354896545,26.30652237,-37.91775513,-0.004453454632,0.0365036875,0.01297717914,-0.2366603762,0.3252747357,9.787967682 +2.38969094,-8.15328598,26.88210487,-37.34867859,-0.002847630531,0.03556828201,0.01924502663,-0.2097179592,0.2747732401,9.877931595 +2.39969094,-7.432373047,26.00634766,-37.28799438,0.001555474475,0.03062932193,0.02191158198,-0.1824806482,0.2579643428,9.950833321 +2.40969094,-7.249095917,26.43893433,-37.16656494,0.006780474447,0.03764603287,0.02351267263,-0.1464302391,0.268522054,9.896903038 +2.41969094,-7.10426712,26.050457,-36.52879333,0.01062327344,0.04378296435,0.01857700758,-0.1064542532,0.2074498385,9.702510834 +2.42969094,-7.85161972,26.64549637,-37.02731323,0.002669626847,0.02873747237,0.0129544735,-0.04887576029,0.2210505307,9.567022324 +2.43969094,-7.363460541,24.77747154,-37.81118774,-0.0103612449,0.005181383342,0.009140042588,-0.1335268766,0.2172933221,9.608876228 +2.44969094,-6.644954681,25.69906044,-35.741745,-0.01882448047,-0.004420454614,0.008147323504,-0.09323616326,0.2520536184,9.751316071 +2.45969094,-7.416149139,26.28179359,-37.30761719,-0.02168631367,-0.001290564891,0.004412197508,-0.103556484,0.2825931907,9.850436211 +2.46969094,-8.162696838,26.61017418,-37.02410889,-0.01086263545,0.006069350056,-0.0008704911452,-0.1312154382,0.2413202971,9.851606369 +2.47969094,-7.145629883,25.62849998,-37.26174927,-0.003843489569,0.01436809357,-0.001623692689,-0.1820041239,0.2070912719,9.713765144 +2.48969094,-7.275531769,26.15821457,-37.60437012,-0.009312344715,0.01892914996,-0.001100572641,-0.1220566928,0.1783026159,9.680235863 +2.49969094,-7.507480621,24.89929199,-37.66693115,-0.01717316359,0.02942520566,-0.003816983197,-0.06498123705,0.1999483556,9.725384712 +2.50969094,-7.90920639,25.94809151,-37.74064636,-0.02303904109,0.02805838361,-0.005925999023,-0.1256715059,0.2209081352,9.832509995 +2.51969094,-7.90920639,25.94809151,-37.74064636,-0.02268951386,0.01364582684,-0.003972060047,-0.2051095217,0.225720942,9.763577461 +2.52969094,-6.614120483,26.78326416,-37.34507751,-0.02658636495,0.01437338069,0.002244035713,-0.1648696512,0.2123297453,9.703748703 +2.53969094,-6.614120483,26.78326416,-37.34507751,-0.02037812956,0.02540178038,0.003479044419,-0.1159800962,0.2158698142,9.719527245 +2.54969094,-7.148540497,26.02760506,-38.51106262,-0.0118260067,0.03690432012,0.003315735608,-0.1407853812,0.261910826,9.750429153 +2.55969094,-8.043819427,26.34184265,-37.92095947,-0.006829578429,0.02317268588,0.004384300672,-0.1386529505,0.3286429048,9.795340538 +2.56969094,-8.094593048,25.64795494,-38.32933044,0.003840013407,0.007176350802,0.001100537833,-0.06916922331,0.3458430767,9.878037453 +2.57969094,-6.825942993,26.20240784,-38.37158203,0.008876708336,0.0003457232378,-0.006905544549,-0.08735194057,0.3348065913,9.828860283 +2.58969094,-8.531360626,25.87745094,-37.73423767,0.005340719596,-0.00217997143,-0.01371701993,-0.126806885,0.3439081907,9.871929169 +2.59969094,-6.774856567,26.22877502,-36.08436584,0.0004153251648,-0.01131269149,-0.01645131782,-0.1413100064,0.3308411539,9.938947678 +2.60969094,-7.85161972,26.64549637,-37.02731323,-0.004786434583,-0.003533433191,-0.02084087208,-0.1905422211,0.3367064893,9.995604515 +2.61969094,-6.839752197,25.12699318,-36.00585938,-0.003631051164,0.02111057006,-0.02646948397,-0.1596919447,0.3178349733,9.962801933 +2.62969094,-6.727798462,27.58842468,-37.70732117,-0.002874007449,0.04105066508,-0.02904283628,-0.1408151686,0.3369486332,9.898715019 +2.63969094,-7.459617615,25.99228477,-38.50785828,-0.008197206073,0.04637994617,-0.03437278792,-0.1868165284,0.3002360463,9.948604584 +2.64969094,-7.563079834,26.80271912,-38.41264343,-0.009471255355,0.04070337862,-0.03384329379,-0.1625285,0.3253901303,10.03444958 +2.65969094,-6.765449524,26.50070572,-36.40896606,0.001410008408,0.02944688685,-0.03491182253,-0.1994553506,0.3648633659,9.999612808 +2.66969094,-6.336788177,26.13348579,-36.9942627,0.009185058065,0.03163524717,-0.03544767573,-0.2142654508,0.3202452958,9.846342087 +2.67969094,-7.402030945,26.68968773,-37.79452515,0.01375771593,0.03424095362,-0.03149435297,-0.1759790331,0.2779944837,9.759856224 +2.68969094,-6.60439682,26.38767433,-35.79081726,0.0170276463,0.04406008124,-0.02959464677,-0.1392083019,0.281003803,9.720905304 +2.69969094,-8.084377289,25.65322876,-37.87188721,0.01672596484,0.04845323414,-0.03155862913,-0.1621757448,0.2420970947,9.75135231 +2.70969094,-6.504650116,26.24300194,-37.91734314,0.009563808329,0.04708301276,-0.02664239891,-0.1740728915,0.2218662202,9.769033432 +2.71969094,-7.81186676,27.60076523,-37.8584137,-7.901713252e-05,0.03207566217,-0.02283397503,-0.26397264,0.2458071113,9.813800812 +2.72969094,-7.875461578,26.63319016,-38.09466553,-0.007686767727,0.004682029597,-0.0156044066,-0.2840597332,0.180323258,9.903401375 +2.73969094,-6.792198181,26.88750458,-38.72561646,-0.008024005219,-0.02341856062,-0.004444222432,-0.2712144554,0.2306922525,9.956364632 +2.74969094,-7.576889038,25.72730446,-36.04692078,0.001746371388,-0.02758159675,0.001749235205,-0.2706443071,0.2705234885,9.986332893 +2.75969094,-7.259311676,26.43366051,-37.62400818,0.01849942282,-0.009009528905,0.01289303042,-0.2955932617,0.2673327327,9.963009834 +2.76969094,-7.399120331,26.29058266,-36.54521179,0.03639570996,0.008357273415,0.01962275989,-0.2361007482,0.2875300348,9.913796425 +2.77969094,-7.133922577,27.83374023,-35.73989868,0.04758138955,0.0147917401,0.02016180754,-0.1789459288,0.2186049521,9.904644012 +2.78969094,-7.132316589,26.30305099,-38.53068542,0.05693943799,0.003899033181,0.02071453631,-0.09112010896,0.210464716,9.896371841 +2.79969094,-7.495277405,27.50539398,-37.24186707,0.0550789088,-0.01156693138,0.01165349409,-0.1010814682,0.2988709211,9.751430511 +2.80969094,-7.276340485,26.42487144,-38.38641357,0.04573579505,-0.02328784391,0.003650189843,-0.1644459963,0.2800166309,9.548920631 +2.81969094,-7.063709259,26.73907089,-36.5778656,0.01850998029,-0.01182917133,-0.004340337589,-0.1469102055,0.2510396242,9.466347694 +2.82969094,-6.741111755,26.91387177,-36.43840027,-0.004324021749,-0.01375587657,-0.008865067735,-0.1834320128,0.2701443136,9.711741447 +2.83969094,-6.631641388,26.37361145,-37.01068115,-0.001867462881,-0.02513966337,-0.0110240262,-0.1669787318,0.3215048611,9.896787643 +2.84969094,-7.425559998,26.0098629,-36.98304749,0.01628770307,-0.02488586679,-0.01466791332,-0.2579631507,0.3567141593,9.860242844 +2.85969094,-8.243812561,25.2329464,-36.92596436,0.02688464522,-0.004169604741,-0.01387823559,-0.2582223117,0.2771977484,9.736753464 +2.86969094,-8.193843842,26.19349098,-37.29960632,0.02200842649,0.03450661153,-0.01309495978,-0.272795558,0.2449849695,9.676970482 +2.87969094,-6.823036194,25.80330276,-37.12229919,0.006453162991,0.05085517466,-0.006890017539,-0.3061911166,0.2199545354,9.631800652 +2.88969094,-7.40933609,26.28530884,-37.00267029,-0.01606200263,0.04909879714,0.003005686682,-0.2995644808,0.201525405,9.73643589 +2.89969094,-7.641288757,25.02638626,-37.06521606,-0.02808204293,0.04346767068,0.006027473602,-0.1448681653,0.272339046,9.628669739 +2.90969094,-7.46692276,25.58790588,-37.71600342,-0.04177628458,0.04816025496,0.002225785982,-0.116304405,0.2411215901,9.453680038 +2.91969094,-6.624336243,26.77799034,-37.80253601,-0.05434793979,0.06665814668,-0.006078326143,-0.2214545161,0.2327720374,9.527388573 +2.92969094,-5.72095108,26.60147667,-38.4024353,-0.06135207415,0.06435563415,-0.004860650748,-0.2514836788,0.2456260026,9.681171417 +2.93969094,-7.189098358,25.33899117,-38.46199036,-0.05704882741,0.0275369063,0.0003332057968,-0.2850147784,0.2860251069,9.833475113 +2.94969094,-7.529335022,27.48781586,-38.76667786,-0.04562484473,0.002622469561,0.00259678578,-0.2471337616,0.2965653837,9.949759483 +2.95969094,-8.473773956,26.5748539,-37.02090454,-0.02654643729,0.0005687286612,0.001860674936,-0.2698851526,0.3216832578,10.0168438 +2.96969094,-7.249095917,26.43893433,-37.16656494,-0.006175159011,-6.634369493e-05,0.001427219715,-0.2811920643,0.2962718606,10.01611328 +2.97969094,-7.450206757,26.26421547,-38.83242798,0.006415885873,-0.007441923022,0.002711253706,-0.2567391992,0.20896779,9.862981796 +2.98969094,-7.450206757,26.26421547,-38.83242798,0.0108665498,-0.009281657636,0.00197905954,-0.1455981433,0.2174957097,9.758725166 +2.99969094,-6.447872162,27.20706177,-37.98605347,0.004828017205,0.01278313715,-0.00765129365,-0.09340839088,0.2501848638,9.700984001 +3.00969094,-6.447872162,27.20706177,-37.98605347,-0.003581494559,0.02978005633,-0.01544828713,-0.1543234587,0.2795874476,9.821495056 +3.01969094,-8.49810791,26.16168594,-36.99145508,-0.009767705575,0.02843782678,-0.01857014373,-0.2008584142,0.3034571409,9.921107292 +3.02969094,-8.06734848,25.66201782,-37.10948181,-0.01092633884,0.01583724841,-0.01798436232,-0.2255139798,0.2753494382,9.935453415 +3.03969094,-6.695236206,25.40603638,-37.24693298,-0.01146858186,0.002507640515,-0.01536135934,-0.2470640689,0.2939111292,9.952013969 +3.04969094,-7.316898346,25.73625755,-38.33734131,-0.01291702595,-0.005841163918,-0.01194457337,-0.2390746474,0.2554622889,9.902705193 +3.05969094,-7.374786377,26.70375061,-36.57466125,-0.01152374595,-0.02042293362,-0.009471164085,-0.2179525644,0.2555968761,9.93608284 +3.06969094,-7.333118439,25.46081161,-38.31770325,-0.008825613186,-0.03340864927,-0.007532409392,-0.2380346656,0.3015532494,9.908273697 +3.07969094,-8.468456268,24.37840271,-37.78034973,-0.005647540092,-0.02399409376,-0.006282707676,-0.1862966716,0.2948144078,9.761417389 +3.08969094,-6.474308014,26.92634201,-38.42385864,-0.008505098522,0.01370482426,-0.008589369245,-0.2072754353,0.290933311,9.65144825 +3.09969094,-7.194416046,27.53544235,-37.70251465,-0.01222948264,0.04708583653,-0.009292855859,-0.1612217426,0.2714120448,9.722549438 +3.10969094,-7.488464355,27.50891113,-36.93690491,-0.01520463172,0.05751100183,-0.009822325781,-0.2179809064,0.2705034912,9.771174431 +3.11969094,-7.842208862,26.91742516,-37.35188293,-0.01752957702,0.04657791555,-0.01018739119,-0.20446226,0.3115225434,9.746862411 +3.12969094,-6.607307434,26.7867794,-37.04013062,-0.02082055621,0.03212425858,-0.01252835803,-0.284610033,0.3318831623,9.818239212 +3.13969094,-8.050319672,25.67080688,-36.34707642,-0.02162656561,0.006840092596,-0.01146937907,-0.2710173726,0.335179776,9.85300827 +3.14969094,-6.631641388,26.37361145,-37.01068115,-0.01494333707,-0.003114380408,-0.01216324605,-0.293641299,0.3532663584,9.856822014 +3.15969094,-8.227588654,25.50839233,-36.94558716,-0.00701501593,0.003271889873,-0.01338343509,-0.3079952896,0.2997109592,9.897614479 +3.16969094,-7.115287781,26.31184006,-37.76828003,0.0001519322395,0.005005017854,-0.01125273854,-0.2283413708,0.3019617796,9.842337608 +3.17969094,-7.956577301,25.25596237,-37.996521,0.008300120942,-0.003143170848,-0.01512670144,-0.1571785808,0.2411979139,9.994022369 +3.18969094,-6.794185638,24.2866745,-38.69322205,0.01882113516,-0.01369971037,-0.02005962282,-0.1790753752,0.2787266076,10.03287792 +3.19969094,-7.600730896,25.71500015,-37.11428833,0.0226082243,-0.01807713695,-0.01910396852,-0.2966942191,0.2534326017,9.856265068 +3.20969094,-7.275531769,26.15821457,-37.60437012,0.01845968887,-0.02622262016,-0.01432321966,-0.2260305434,0.2392146587,9.849489212 +3.21969094,-8.507831573,26.55727577,-38.54571533,0.01486025844,-0.02195211686,-0.01205309667,-0.1015152261,0.2238649428,9.757564545 +3.22969094,-8.193843842,26.19349098,-37.29960632,0.007541217841,-0.009552078322,-0.01419021189,-0.1901240647,0.2519818842,9.725886345 +3.23969094,-8.170314789,26.87331581,-38.11108398,-0.002043720335,-0.002106974367,-0.01097225025,-0.3190883398,0.2920241356,9.870922089 +3.24969094,-7.138324738,26.03287888,-38.05360413,-0.002122517675,0.001215161756,-0.002811893821,-0.3439500332,0.3194630742,9.970935822 +3.25969094,-5.583427429,25.80862045,-36.9728241,0.004528208636,0.01517218351,0.0009987638332,-0.246371001,0.294268012,9.926848412 +3.26969094,-6.74079895,26.24635315,-34.55955505,0.01069999952,0.0292138122,0.003129459452,-0.2038469911,0.2858913839,9.858620644 +3.27969094,-7.333118439,25.46081161,-38.31770325,0.01235124376,0.03107888252,0.0003867314663,-0.1960114241,0.3041076958,9.8361063 +3.28969094,-6.695236206,25.40603638,-37.24693298,0.01681809872,0.02263225801,4.35502734e-05,-0.2625229657,0.3344432414,9.877408981 +3.29969094,-8.365112305,26.30124855,-38.37519836,0.01864273101,0.02571816929,-0.001450675773,-0.3248307705,0.3089620173,9.92305851 +3.30969094,-5.703922272,26.61026573,-37.64002991,0.0246123001,0.02688729018,-0.004978229757,-0.1456692666,0.2753237486,9.839014053 +3.31969094,-6.840065002,25.7945137,-37.88470459,0.01809304953,0.03596933186,-0.01544332877,-0.1127016768,0.1825166345,9.618488312 +3.32969094,-7.235782623,27.11348534,-38.43548584,0.0004593646154,0.0279977899,-0.0206976831,-0.2280323654,0.2231822312,9.626499176 +3.33969094,-8.49810791,26.16168594,-36.99145508,-0.01300951093,-0.008212786168,-0.01676802337,-0.2872509062,0.2894925773,9.755972862 +3.34969094,-8.084686279,26.32074928,-39.75073242,-0.01604039222,-0.02611956932,-0.01259132661,-0.3058491349,0.2855690122,9.846001625 +3.35969094,-7.926235199,25.93930244,-38.50305176,-0.01576715335,-0.02967993356,-0.007701023482,-0.2758152187,0.2458155155,9.883844376 +3.36969094,-7.593421936,26.11937904,-37.90612793,-0.0165593382,-0.02559154853,-0.004947154783,-0.2677181363,0.2292198688,9.824416161 +3.37969094,-7.408531189,26.01865196,-36.22064209,-0.01147498749,-0.008676798083,-0.000233884668,-0.288110882,0.2577656209,9.797032356 +3.38969094,-7.148540497,26.02760506,-38.51106262,-0.006633196957,0.01344986074,-0.0008964412846,-0.3024396598,0.3181247711,9.880328178 +3.39969094,-6.470592499,26.26058006,-36.39253235,0.00531923119,0.03014460765,0.0009987638332,-0.2945103049,0.3188195527,9.94841671 +3.40969094,-8.007350922,24.56207466,-38.40490723,0.01756862178,0.03873810172,0.00289370399,-0.2079023421,0.2359249741,9.747755051 +3.41969094,-10.57120132,25.9057312,-39.25784302,0.01010619756,0.05189544708,-0.001664606389,-0.1738738865,0.1913950294,9.680673599 +3.42969094,-7.552864075,26.80799103,-37.9552002,-0.0005048532039,0.03880349547,-0.008658854291,-0.194183588,0.2359473258,9.722563744 +3.43969094,-8.193843842,26.19349098,-37.29960632,-0.01316022221,0.007196725346,-0.008648898453,-0.3240816891,0.297080785,9.751264572 +3.44969094,-8.193843842,26.19349098,-37.29960632,-0.01896319911,0.002648235299,-0.00752402097,-0.348402977,0.307802856,9.702807426 +3.45969094,-7.282344818,26.15469933,-37.90933228,-0.02673238888,0.003566776402,-0.004272747319,-0.3012737632,0.2431114316,9.759227753 +3.46969094,-7.282344818,26.15469933,-37.90933228,-0.02919545397,-0.002234932035,-6.658444181e-05,-0.3082113266,0.2360342592,9.839511871 +3.47969094,-7.601039886,26.38251877,-38.99313354,-0.02647368982,-0.0003828592598,0.004247863777,-0.3241005838,0.2539158165,9.896888733 +3.48969094,-7.70747757,23.79039574,-36.67192078,-0.01391642354,0.005569877103,0.005652846303,-0.3335336745,0.2924467623,9.974705696 +3.49969094,-7.466117859,25.32124901,-36.93397522,-0.002039574087,0.006009228528,0.008374901488,-0.3174402714,0.2626022398,9.936785698 +3.50969094,-6.774856567,26.22877502,-36.08436584,0.005044433288,-0.002327063121,0.009446952492,-0.3287468851,0.2626532614,9.900091171 +3.51969094,-6.638454437,26.37009621,-37.31562805,0.009231860749,-0.0183471106,0.01005422324,-0.2419033647,0.2037884295,9.751329422 +3.52969094,-8.237312317,25.90398216,-38.49984741,-0.0002129487693,-0.0351838246,0.006396608893,-0.2397907376,0.2444081306,9.79660511 +3.53969094,-7.794345856,28.01041794,-38.19281006,-0.001384953968,-0.05765299499,0.004162475467,-0.2784003317,0.2528136969,9.876300812 +3.54969094,-6.01499939,26.57494545,-37.63682556,0.01022412907,-0.04971811175,0.004277116619,-0.2988288105,0.2813760936,9.848402023 +3.55969094,-7.399120331,26.29058266,-36.54521179,0.02244941518,-0.01563763618,0.004172147717,-0.3169019222,0.3055851161,9.799267769 +3.56969094,-7.289653778,25.75032043,-37.11749268,0.0274567008,0.02185425535,0.001589378342,-0.233324185,0.2500683963,9.69851017 +3.57969094,-7.165569305,26.01881599,-39.27346802,0.02480689436,0.02791698091,-0.003749892116,-0.3112936914,0.2143467516,9.725136757 +3.58969094,-6.753314972,24.30776978,-36.86346436,0.01731849089,0.004670565017,-0.003262628801,-0.3077295125,0.2493745536,9.750574112 +3.59969094,-6.712265015,25.39724731,-38.00933838,0.004657322541,-0.01840234734,0.002560213674,-0.3492403328,0.2538228631,9.572223663 +3.60969094,-8.330558777,26.71968842,-37.94718933,-0.01725715771,-0.02274436876,0.00684737647,-0.3248106241,0.2503971457,9.603363991 +3.61969094,-6.785385132,26.89102173,-38.4206543,-0.02854960784,-0.01157763973,0.008985424414,-0.313901335,0.2334174663,9.79224205 +3.62969094,-6.785385132,26.89102173,-38.4206543,-0.03440004215,0.01084966771,0.01005065627,-0.2761927247,0.2305997759,9.864484787 +3.63969094,-7.546051025,26.81150818,-37.65023804,-0.02321261168,0.03533025831,0.0105829034,-0.2884257436,0.258186996,9.792014122 +3.64969094,-7.489959717,25.3089447,-38.00132751,-0.01417162456,0.04542586207,0.01005422324,-0.3501100838,0.2739596069,9.894083023 +3.65969094,-7.131511688,26.03639412,-37.74865723,-0.0024527614,0.04169920832,0.01005422324,-0.3122108281,0.283588022,9.821797371 +3.66969094,-8.243812561,25.2329464,-36.92596436,0.001286050305,0.04755017161,0.006820009556,-0.3645493984,0.2892889977,9.780883789 +3.67969094,-8.11762619,25.36899376,-38.61465454,0.002876439132,0.03516138345,0.006307389122,-0.3239471614,0.2848519981,9.735665321 +3.68969094,-6.440250397,26.94392014,-36.89904785,-0.0002477457747,0.0002992372029,0.007414806169,-0.2544038296,0.3007819951,9.809139252 +3.69969094,-7.536643982,27.08343887,-37.97483826,0.003989266232,-0.05071535707,0.00947406888,-0.2319714129,0.3080909848,9.878863335 +3.70969094,-6.672199249,25.68499756,-36.96160889,0.008822058327,-0.06696711481,0.008434649557,-0.2179051787,0.2917186916,9.934447289 +3.71969094,-7.923324585,25.54019737,-37.2537384,0.0168600902,-0.05562448502,0.00521544693,-0.1962778419,0.2554365098,9.943925858 +3.72969094,-7.080738068,26.73028183,-37.340271,0.02695737779,-0.04445654154,0.00677522039,-0.2058451921,0.2460807711,9.855082512 +3.73969094,-7.258815765,26.83452415,-38.72080994,0.0257118158,-0.03880500048,0.009023677558,-0.2229058892,0.2702342272,9.8239851 +3.74969094,-8.029697418,26.74973679,-38.40783691,0.01767116413,-0.03354083747,0.01173234172,-0.1730276793,0.293876797,9.859820366 +3.75969094,-8.277870178,25.21536827,-38.45077515,0.00750257913,-0.0134898033,0.01595561951,-0.2170909047,0.2744894028,9.763518333 +3.76969094,-7.784622192,27.61482811,-36.6385498,-0.008115497418,0.004707356449,0.01857700758,-0.1322013289,0.2862198949,9.706252098 +3.77969094,-7.408531189,26.01865196,-36.22064209,-0.01740895212,0.02163187601,0.02066392824,-0.200186044,0.2886405289,9.775326729 +3.78969094,-7.276340485,26.42487144,-38.38641357,-0.01349348295,0.02156052925,0.0227907449,-0.05037189275,0.2798060477,10.02560616 +3.79969094,-8.060535431,25.66553307,-36.80451965,-0.006179019809,0.01282461267,0.01740583777,-0.1050582752,0.304669559,9.953327179 +3.80969094,-6.792694092,26.48664284,-37.6288147,-0.008539781906,0.04109317809,0.01307771914,-0.1367159933,0.2985886037,9.855304718 +3.81969094,-6.528675079,25.16231346,-36.00906372,-0.01666204073,0.06206073612,0.009280072525,-0.1876604408,0.2774381042,9.739678383 +3.82969094,-6.840065002,25.7945137,-37.88470459,-0.03033562005,0.04966237396,0.009583637118,-0.1894158125,0.2180558145,9.681078911 +3.83969094,-8.82151413,26.25354004,-37.91294861,-0.0442554988,0.02925441973,0.007262540516,-0.1306591481,0.2705550194,9.748282433 +3.84969094,-7.875461578,26.63319016,-38.09466553,-0.04479436576,0.01652778126,0.002259462141,-0.1970492601,0.2646980286,9.908373833 +3.85969094,-8.313529968,26.72847748,-37.18478394,-0.03184996918,0.02280884422,-0.00311926892,-0.2717116773,0.2997603416,9.914752007 +3.86969094,-6.546016693,25.82104492,-38.65031433,-0.02147505246,0.03477363288,-0.0003719876986,-0.347087115,0.2509671748,9.840457916 +3.87969094,-5.744480133,25.92165184,-37.59095764,-0.01409151033,0.03734870255,0.003584513441,-0.2748141289,0.2386694402,9.767773628 +3.88969094,-7.956577301,25.25596237,-37.996521,-0.005113671999,0.02281517908,0.005340028089,-0.303516686,0.2496834397,9.829120636 +3.89969094,-7.333118439,25.46081161,-38.31770325,0.003993850201,-0.002983287908,0.00775645813,-0.1925490499,0.3337683082,9.893627167 +3.90969094,-7.333118439,25.46081161,-38.31770325,0.01015300211,-0.02382051758,0.0009108139202,-0.2402744293,0.2796029747,9.859054565 +3.91969094,-8.14566803,26.61896324,-36.26170349,0.01105037984,-0.01546274498,-0.003354544286,-0.2564902306,0.254522115,9.739465714 +3.92969094,-8.180530548,26.86804199,-38.56854248,0.005029072054,-0.003389192745,-0.003890158376,-0.2920319736,0.2168327123,9.626190186 +3.93969094,-8.244125366,25.90046692,-38.80479431,-0.0132009536,-0.01026250795,-0.003068285529,-0.2643445432,0.226190418,9.728908539 +3.94969094,-7.898990631,25.95336533,-37.28318787,-0.01736517623,-0.01826028526,0.0005658168811,-0.2710779905,0.2723842859,9.839493752 +3.95969094,-7.899795532,26.2200222,-38.06521606,-0.01492538489,-0.01595095545,0.0008957325481,-0.2685719728,0.2795063257,9.843836784 +3.96969094,-7.566673279,25.73257828,-35.58947754,-0.01235895604,0.003454227,0.001958799548,-0.3106808662,0.2576106787,9.792434692 +3.97969094,-6.982593536,28.11629868,-36.67601013,-0.01192790549,0.006320843473,0.00643603364,-0.2947559953,0.2017954588,9.794699669 +3.98969094,-7.617759705,25.70621109,-37.87669373,-0.01215213537,-0.005099052563,0.00940784812,-0.251095444,0.2411850542,9.778581619 +3.99969094,-6.648178101,26.76568604,-38.86988831,-0.01203843486,-0.008605884388,0.007923526689,-0.2290615439,0.2813150585,9.819487572 +4.00969094,-7.11448288,26.04518318,-36.98625183,-0.006584511138,-0.001146110706,0.008336946368,-0.2567182481,0.3083212674,9.838685036 +4.01969094,-7.385807037,26.96513367,-37.81414795,0.004388485104,0.01485629193,0.002841629088,-0.2343084961,0.2451559454,9.847247124 +4.02969094,-7.097766876,26.72149277,-38.10267639,0.008203179576,0.02133086324,0.00259678578,-0.2072534859,0.2421934903,9.698073387 +4.03969094,-8.01266861,26.75852585,-37.64543152,-0.0003045490012,0.01985875331,-0.003262628801,-0.1555505842,0.1782817245,9.711576462 +4.04969094,-6.823036194,25.80330276,-37.12229919,-0.01336695347,0.005080743693,-0.006058639847,-0.1943382621,0.2094749659,9.714632034 +4.05969094,-8.01997757,26.35414696,-36.85359192,-0.03109871596,-0.01130758598,-0.008986420929,-0.2392482162,0.2315007001,9.763028145 +4.06969094,-7.651504517,25.02111244,-37.5226593,-0.0400146544,-0.004739507101,-0.01046456024,-0.2673299909,0.2497864366,9.792757988 +4.07969094,-7.242595673,27.10997009,-38.740448,-0.03906309232,0.006150948815,-0.01100350544,-0.2492612153,0.2543903887,9.845363617 +4.08969094,-7.074729919,27.00045395,-37.81735229,-0.03663897514,0.004508738406,-0.01430373639,-0.2727160156,0.224393189,9.859981537 +4.09969094,-6.320072174,26.80979538,-38.11068726,-0.02902301773,-0.007368606515,-0.01376769505,-0.2551684082,0.249186188,9.86097908 +4.10969094,-7.587417603,26.38955116,-38.38320923,-0.02162656561,-0.005713349208,-0.0148298014,-0.2201159149,0.2390990704,9.782792091 +4.11969094,-7.859237671,26.90863609,-38.11428833,-0.02336135507,0.003191094613,-0.01513622142,-0.2662590444,0.2411587536,9.972793579 +4.12969094,-7.305873871,25.4748745,-37.09785461,-0.009118622169,-0.002612174489,-0.01077926904,-0.282861501,0.2332253903,9.922781944 +4.13969094,-6.574367523,27.73853493,-38.17617798,-0.005163447931,-0.009760250337,-0.007363054901,-0.211932838,0.212636292,9.728738785 +4.14969094,-8.435512543,25.33015633,-38.91642761,-0.007328464184,0.003674271284,-0.008220901713,-0.20717749,0.1956729591,9.695152283 +4.15969094,-6.055557251,25.88633156,-37.5877533,-0.01590443403,0.01273629628,-0.009684925899,-0.2054186314,0.205473423,9.685699463 +4.16969094,-7.834903717,27.32180405,-38.14373779,-0.02339531854,-0.001536996569,-0.005243080668,-0.2339800149,0.2187104374,9.73903656 +4.17969094,-8.187030792,26.19700623,-36.99465942,-0.02800761536,-0.02326576225,0.001716356725,-0.2859913707,0.2686445117,9.796771049 +4.18969094,-7.242282867,26.44244957,-36.86160278,-0.02908233926,-0.03056327812,0.003839137033,-0.2978691161,0.2407368124,9.87355423 +4.19969094,-7.145629883,25.62849998,-37.26174927,-0.02445448563,-0.04781426489,0.01024218835,-0.2982214987,0.2228002399,9.95925045 +4.20969094,-8.521144867,25.88272476,-37.27677917,-0.01644831337,-0.06270572543,0.01859445311,-0.3007469773,0.2295433432,9.89234066 +4.21969094,-7.385002136,26.69847679,-37.03211975,-0.01185182575,-0.04533778131,0.02409035899,-0.2922000289,0.2343971729,9.830682755 +4.22969094,-7.899482727,25.55250168,-36.18638611,-0.009681108408,-0.02127336524,0.02458944358,-0.1705660373,0.2193717957,9.817026138 +4.23969094,-6.296230316,26.82210159,-37.04333496,-0.008695719764,-0.004451250657,0.01799800247,-0.1621389687,0.2197939306,9.757343292 +4.24969094,-6.509967804,28.43945312,-37.15789795,-0.01495374739,0.002140143886,0.01305411384,-0.2275846601,0.2546940744,9.810265541 +4.25969094,-7.916511536,25.54371262,-36.9487915,-0.02169186994,8.451147005e-05,0.008854851127,-0.2209734768,0.2321222872,9.868536949 +4.26969094,-7.392620087,26.96161842,-38.11909485,-0.02849860862,-0.001703454647,0.006528029684,-0.1220632419,0.1541925073,9.71141243 +4.27969094,-6.614120483,26.78326416,-37.34507751,-0.04428917542,0.01810290664,-0.002197280526,-0.2801389396,0.1237404421,9.710455894 +4.28969094,-7.392307281,26.2940979,-36.24026489,-0.04955516383,0.02923793532,0.0008839904331,-0.2690411806,0.03041800112,9.910918236 +4.29969094,-7.131511688,26.03639412,-37.74865723,-0.03079982474,0.03252982348,-0.002942085499,-0.226893425,0.06968209893,10.19825363 +4.30969094,-8.054035187,26.33656883,-38.37840271,0.008452025242,0.07177698612,0.002925334033,-0.2675082386,0.2150973082,10.1730938 +4.31969094,-7.242595673,27.10997009,-38.740448,0.04162428528,0.1026687622,0.005137396511,-0.3166666329,0.2443601489,10.15234661 +4.32969094,-7.234973907,26.84682846,-37.65344238,0.07101240754,0.09248439223,0.005703500006,-0.2230912894,0.2666797042,9.999241829 +4.33969094,-9.125778198,26.221735,-37.6047821,0.0769181326,0.04123746604,0.003129459452,-0.1260693818,0.2381351441,9.691967964 +4.34969094,-6.775665283,26.4954319,-36.8664093,0.05713566393,-0.01057719067,-0.007675570901,-0.1283408254,0.208796829,9.449485779 +4.35969094,-7.322902679,25.46608543,-37.86026001,0.021940846,-0.03525879979,-0.0164392069,-0.2080651373,0.2594524622,9.481989861 +4.36969094,-7.322902679,25.46608543,-37.86026001,-0.012359038,-0.04465859383,-0.02090466022,-0.33076033,0.2426670343,9.544941902 +4.37969094,-6.873313904,25.5102787,-38.62747192,-0.03849985823,-0.04358688742,-0.01385076717,-0.4688956439,0.2643616199,9.787895203 +4.38969094,-6.873313904,25.5102787,-38.62747192,-0.04186818004,-0.04309883714,-0.0009931600653,-0.4567618966,0.2665080726,9.873948097 +4.39969094,-6.815727234,26.20768166,-37.91413879,-0.03789544106,-0.02908178978,0.01127431542,-0.4167534411,0.3044225276,9.863181114 +4.40969094,-6.782478333,26.49191666,-37.17137146,-0.03473445773,-0.001430485398,0.01775887236,-0.3834793866,0.3153073788,9.803701401 +4.41969094,-8.043819427,26.34184265,-37.92095947,-0.03422413021,0.01866585761,0.01907779463,-0.2802246213,0.277962029,9.730218887 +4.42969094,-7.369091034,27.64144325,-38.93057251,-0.03385256976,0.02039143257,0.01885609329,-0.2379066348,0.2587588131,9.743079185 +4.43969094,-8.297309875,27.00392532,-37.204422,-0.04078369215,0.01425610762,0.01538096368,-0.2097538263,0.237317726,9.7883358 +4.44969094,-6.798698425,26.21647072,-37.1517334,-0.04215364158,0.007632749155,0.0129903052,-0.2162558734,0.2124440819,9.842716217 +4.45969094,-6.679012299,25.68148232,-37.26655579,-0.03894165531,0.002023714595,0.01219128072,-0.21327281,0.2051690519,9.822431564 +4.46969094,-7.881961823,25.96215439,-36.52078247,-0.03920482472,-0.004644265398,0.01271759532,-0.1758301556,0.1789844632,9.79445076 +4.47969094,-9.782485962,25.73265076,-38.02636719,-0.04025738314,-0.007038090378,0.006575869862,-0.1418743432,0.1429290026,9.867178917 +4.48969094,-8.773334503,26.6790123,-36.87501526,-0.03090364486,-0.01158179156,0.001804163679,-0.1442653984,0.1189981252,9.951881409 +4.49969094,-6.671707153,26.08586121,-38.05841064,-0.01513878815,-0.02521918528,-6.658444181e-05,-0.1738433093,0.1544630975,9.960613251 +4.50969094,-6.0623703,25.88281631,-37.89271545,-0.0008522793651,-0.02589283884,0.0004660896957,-0.1996041238,0.1565450281,9.95832634 +4.51969094,-7.868648529,26.6367054,-37.78971863,0.01431652624,-0.004012350924,-0.002729954664,-0.2186203599,0.1420396417,9.912900925 +4.52969094,-7.892982483,26.22353745,-37.76026917,0.018323984,0.02168132737,-0.0004264845047,-0.2232411653,0.1151062325,9.794009209 +4.53969094,-8.274959564,24.81626129,-37.20146179,0.008296172135,0.03482315689,0.0001749894582,-0.1810871363,0.09140643477,9.71138382 +4.54969094,-6.775169373,26.89629364,-37.96321106,-0.0138578536,0.0290421769,0.0007601014804,-0.1903544068,0.133223936,9.736455917 +4.55969094,-6.423221588,26.9527092,-36.13664246,-0.03204216808,0.02157061175,0.001588354819,-0.215617761,0.2027130872,9.723331451 +4.56969094,-8.013477325,27.02518272,-38.42747498,-0.04305369407,0.02441652864,0.00265686959,-0.21561867,0.1740983278,9.768782616 +4.57969094,-8.473773956,26.5748539,-37.02090454,-0.04656537622,0.01722446829,0.005860616919,-0.2287943214,0.1416486502,9.826238632 +4.58969094,-7.249095917,26.43893433,-37.16656494,-0.0395822376,0.01232369244,0.008911250159,-0.2248601615,0.1444966495,9.854561806 +4.59969094,-7.443393707,26.26773071,-38.52748108,-0.03166176751,0.00800199993,0.008988874033,-0.2275328785,0.1459973603,9.916668892 +4.60969094,-8.831729889,26.24826622,-38.37039185,-0.0206013117,0.009386268444,0.008898375556,-0.2268878073,0.1908883601,9.917303085 +4.61969094,-5.772026062,27.57248497,-36.33480835,-0.01100313012,0.01832520589,0.006325504277,-0.2854395807,0.2194937319,9.911237717 +4.62969094,-7.928646088,27.73664856,-36.49429321,-0.003731844481,0.04050150514,0.003878329415,-0.3024586439,0.1961129755,9.810222626 +4.63969094,-6.263290405,27.77385521,-38.17938232,-0.0005319332704,0.05672128499,-0.001344260294,-0.22144261,0.1550287455,9.70290947 +4.64969094,-8.314025879,26.32761574,-36.08798218,-0.002774584573,0.05401900411,-0.006458672695,-0.1730443388,0.1541241705,9.64126873 +4.65969094,-6.654678345,26.09465027,-37.29600525,-0.01445338782,0.03875076026,-0.01371094957,-0.2011986077,0.1363305449,9.544471741 +4.66969094,-7.570388794,26.39834023,-37.62080383,-0.02568543702,0.01708991826,-0.01378848217,-0.29481107,0.1495176256,9.718362808 +4.67969094,-6.825942993,26.20240784,-38.37158203,-0.02655274048,-0.0216173958,-0.01138909534,-0.296559602,0.1946366876,9.950929642 +4.68969094,-7.906295776,25.54898643,-36.49133301,-0.01781709492,-0.04153759778,-0.005925999023,-0.3151355982,0.1484638304,10.02096558 +4.69969094,-7.559364319,26.13695717,-36.38131714,-0.002749661915,-0.03404572606,-0.003847503802,-0.3055528998,0.1347839087,10.00041008 +4.70969094,-8.094593048,25.64795494,-38.32933044,0.00945944991,-0.008453158662,-0.0002174130641,-0.3440961838,0.1186381653,9.896161079 +4.71969094,-6.487621307,26.251791,-37.15493774,0.01406259369,-0.001046618447,0.005578093696,-0.3545964956,0.1277017146,9.871976852 +4.72969094,-7.586608887,26.12289429,-37.60116577,0.007003881969,-0.01737609878,0.01359666139,-0.3624377251,0.1338391006,9.84253788 +4.73969094,-6.487621307,26.251791,-37.15493774,-0.007547943387,-0.01550474204,0.01591363735,-0.2639065683,0.1462768465,9.776966095 +4.74969094,-6.751327515,26.90859985,-36.89584351,-0.02195086703,-0.001110700425,0.01218491793,-0.2222563624,0.1621421576,9.738617897 +4.75969094,-7.249095917,26.43893433,-37.16656494,-0.03246286511,-0.008031003177,0.01146942563,-0.248240903,0.115946807,9.743156433 +4.76969094,-6.457279205,26.93513107,-37.66145325,-0.03635413572,-0.02225174196,0.01254490949,-0.2521977723,0.102270402,9.834392548 +4.77969094,-5.980941772,26.59252357,-36.11201477,-0.02922541276,-0.03147359192,0.01022274233,-0.190122053,0.1274154633,9.831977844 +4.78969094,-7.479057312,27.78084183,-37.26150513,-0.01552046463,-0.0149680879,0.005340931471,-0.2009503394,0.1568839103,9.832667351 +4.79969094,-7.26581192,25.76262474,-36.05012512,-0.0007052477449,0.01664851978,0.001047774218,-0.2484272271,0.1699940115,9.815591812 +4.80969094,-9.155311584,25.27173805,-36.31622314,0.006605156697,0.02571816929,9.230547585e-05,-0.2894585431,0.1414472759,9.803195 +4.81969094,-9.155311584,25.27173805,-36.31622314,0.003320282325,0.007851457223,0.0008433877956,-0.2730394006,0.1397527158,9.857546806 +4.82969094,-7.358562469,26.97919655,-36.59428406,-0.003061691765,-0.01043106336,0.0001573381014,-0.258321166,0.1311504543,9.84120369 +4.83969094,-7.358562469,26.97919655,-36.59428406,-0.004729580134,-0.01538466103,-0.0008347684052,-0.263558358,0.1183589473,9.855064392 +4.84969094,-8.036197662,26.07870102,-36.83395386,-0.004580997862,-0.01142347418,-0.002197280526,-0.2746455669,0.1134366319,9.814276695 +4.85969094,-6.672199249,25.68499756,-36.96160889,-0.007733293343,-0.0003262530081,-0.0008874703199,-0.1970769614,0.08623953164,9.628912926 +4.86969094,-8.81470108,26.25705528,-37.60798645,-0.01761743426,0.01848483458,-0.005533532239,-0.259354353,0.09551355988,9.633827209 +4.87969094,-7.85843277,26.64197922,-37.33226013,-0.02588795871,0.01906336285,-0.0006064993795,-0.2534437478,0.1723184586,9.753379822 +4.88969094,-7.25850296,26.16700363,-36.84196472,-0.03109328821,0.003598091658,0.001400619745,-0.2143057436,0.1092486531,9.908551216 +4.89969094,-7.898990631,25.95336533,-37.28318787,-0.02612610161,-0.01706181467,-0.001536136726,-0.2153220028,0.1003462896,10.02652073 +4.90969094,-6.45356369,26.26936913,-35.63012695,-0.01295110211,-0.02876726352,-0.002070615999,-0.2522668839,0.1105454043,10.04345798 +4.91969094,-9.015007019,25.81567955,-38.49183655,0.002009376884,-0.0287384484,-0.001379664754,-0.2668540478,0.1403126717,9.930277824 +4.92969094,-7.378501892,27.36951256,-38.60600281,0.01198495831,-0.00673567038,-0.003675399348,-0.2398301959,0.1118507236,9.92348671 +4.93969094,-7.471748352,28.18522072,-38.05334473,0.02573502436,0.02284619026,-0.004629021976,-0.224476397,0.1067397967,9.81465435 +4.94969094,-6.574054718,27.0710144,-36.29734802,0.02536131069,0.03054238297,-0.004635522142,-0.2303179353,0.0717818737,9.722005844 +4.95969094,-6.296230316,26.82210159,-37.04333496,0.01556797791,0.004868612625,-0.002214650391,-0.2246153355,0.1043654531,9.742161751 +4.96969094,-7.560173035,26.40361404,-37.1633606,2.473592758e-06,-0.0173169449,-0.0008139780257,-0.2097203881,0.1449587941,9.785895348 +4.97969094,-6.471401215,26.52723694,-37.17457581,-0.008842390031,-0.02489814535,-0.0002747424878,-0.3028791845,0.1854895651,9.777700424 +4.98969094,-7.569580078,26.13168335,-36.83876038,-0.01287231874,-0.02937872149,0.003762538079,-0.271209687,0.129128173,9.908959389 +4.99969094,-6.624336243,26.77799034,-37.80253601,-0.004194155335,-0.04173757136,0.004533623811,-0.1912932694,0.1316759884,10.00805187 +5.00969094,-6.457279205,26.93513107,-37.66145325,0.01475238521,-0.03149639815,0.003035570029,-0.2443001717,0.1331238598,9.971121788 +5.01969094,-8.210559845,25.5171814,-36.18318176,0.03028877825,-0.006620025262,-0.0008588049095,-0.2268170714,0.1420782506,9.910667419 +5.02969094,-7.385002136,26.69847679,-37.03211975,0.04052030668,0.01364735421,-0.004151983652,-0.237571016,0.158927694,9.847308159 +5.03969094,-7.442901611,26.66859436,-39.62428284,0.03705263138,0.0003825714812,-0.006458672695,-0.2139079124,0.1738214344,9.855632782 +5.04969094,-7.299869537,25.74504662,-37.57493591,0.03569614887,-0.0260367766,-0.007442372385,-0.2120704055,0.182754904,9.782229424 +5.05969094,-7.995639801,26.76731491,-36.88302612,0.03453819454,-0.03720206767,-0.00965471752,-0.2041972578,0.1758445352,9.763034821 +5.06969094,-8.423805237,27.53539658,-37.39454651,0.03948170319,-0.04894115031,-0.007673261221,-0.2060015351,0.1358015239,9.880899429 +5.07969094,-7.416149139,26.28179359,-37.30761719,0.04444842041,-0.05113239586,-0.00752402097,-0.2362748682,0.1675562263,9.965304375 +5.08969094,-8.899833679,27.21048546,-37.06517029,0.06157614291,-0.0353282243,-0.003937673289,-0.2722059488,0.193922624,9.950833321 +5.09969094,-7.559364319,26.13695717,-36.38131714,0.07159139216,-0.007121328264,-0.002865193645,-0.2545877993,0.1594867259,9.833031654 +5.10969094,-8.280281067,27.01271439,-36.4420166,0.06986922771,0.0109635517,-0.002792024519,-0.2140764892,0.1569391638,9.850317955 +5.11969094,-8.217372894,25.51366615,-36.48812866,0.06253593415,0.002094432712,-0.007235190365,-0.1947746426,0.2032549828,9.859428406 +5.12969094,-6.286819458,27.09403038,-37.36790466,0.06216876209,-0.008317800239,-0.009709886275,-0.2290931046,0.2211895436,9.904157639 +5.13969094,-8.077068329,26.05760765,-38.66372681,0.06461351365,-0.004909839481,-0.01018739119,-0.2145729065,0.2156002671,9.762316704 +5.14969094,-6.850280762,25.78923988,-38.34214783,0.05910928547,0.001064125914,-0.0116847232,-0.167883575,0.2063707858,9.694931984 +5.15969094,-7.17206955,25.34778023,-37.69958496,0.05363702774,0.01853247173,-0.01631854475,-0.156190142,0.1726144552,9.716248512 +5.16969094,-8.053226471,26.06991196,-37.59635925,0.05348046869,0.02741134167,-0.02060299739,-0.2086635679,0.2122661322,9.849188805 +5.17969094,-7.995639801,26.76731491,-36.88302612,0.053559497,0.02173338085,-0.01817750186,-0.3160272539,0.2369009852,9.909678459 +5.18969094,-7.801651001,27.60603905,-37.4009552,0.06455555558,0.02400907688,-0.01444878429,-0.3371770084,0.280346781,9.874191284 +5.19969094,-8.561702728,25.19411087,-37.22770691,0.07946416736,0.04503597319,-0.01281164959,-0.2494072318,0.2802865803,9.811919212 +5.20969094,-7.282344818,26.15469933,-37.90933228,0.08607848734,0.05437766761,-0.0133135058,-0.2424967289,0.2851865292,9.795174599 +5.21969094,-5.599964142,26.20069504,-38.83203125,0.09813603014,0.05297361314,-0.01501114666,-0.2714679539,0.2680800259,9.75396347 +5.22969094,-8.371612549,25.63021278,-36.80131531,0.09730117023,0.03567400575,-0.0161033757,-0.2618784308,0.2831623554,9.753846169 +5.23969094,-5.85345459,26.86277771,-38.11549377,0.1009470299,0.0124598816,-0.01290932298,-0.2666765749,0.3234401941,9.780581474 +5.24969094,-6.471401215,26.52723694,-37.17457581,0.1109568328,0.01610381715,-0.01498145796,-0.1893724501,0.3073857427,9.646884918 +5.25969094,-7.305873871,25.4748745,-37.09785461,0.09680746496,0.01358146127,-0.01551413164,-0.1401528865,0.2132546306,9.622281075 +5.26969094,-8.179725647,26.60138512,-37.78651428,0.07704415172,-0.02511924133,-0.01607831009,-0.2199209034,0.2536352575,9.718344688 +5.27969094,-7.139129639,26.29953575,-38.83563232,0.06522310525,-0.05784024298,-0.01338343509,-0.2406560779,0.289178431,9.901301384 +5.28969094,-7.139129639,26.29953575,-38.83563232,0.07847820967,-0.06008450687,-0.01284654625,-0.2131624073,0.3604699373,9.955468178 +5.29969094,-5.710235596,27.00761414,-39.04180908,0.09397917241,-0.03338656574,-0.01444436796,-0.2175605744,0.3692127168,9.880940437 +5.30969094,-5.710235596,27.00761414,-39.04180908,0.1072839499,-0.00890467409,-0.0123171173,-0.1554846913,0.338521868,9.983216286 +5.31969094,-8.555202484,25.86514664,-38.80158997,0.1280505955,-0.00145151047,-0.01125509851,-0.1884938926,0.3345931768,9.971286774 +5.32969094,-8.36139679,25.6354866,-36.34387207,0.1440444738,0.001271591522,-0.01606091298,-0.1713260263,0.4138234556,9.948322296 +5.33969094,-7.552864075,26.80799103,-37.9552002,0.1595576257,0.02187692188,-0.02512727119,-0.1057918295,0.3753095269,9.983222008 +5.34969094,-7.186187744,24.93988609,-37.212677,0.1767290533,0.005366987083,-0.0303742215,-0.2102942914,0.3615303338,9.934126854 +5.35969094,-7.898990631,25.95336533,-37.28318787,0.1936744601,0.02417157032,-0.02678085119,-0.1176252067,0.2720018625,10.04296494 +5.36969094,-7.449893951,25.59669495,-36.95359802,0.2405296117,0.06736565381,-0.03421706706,-0.1225322634,0.4234299064,10.36811256 +5.37969094,-8.820705414,25.98688316,-37.13090515,0.3403091133,0.08081629127,-0.03518076614,-0.107458882,0.4686262608,10.39834785 +5.38969094,-7.990322113,24.57086372,-37.64250183,0.4398308396,0.1258682907,-0.0389775075,-0.1128015518,0.5142008662,10.45117283 +5.39969094,-7.16185379,25.35305405,-37.24212646,0.5284068584,0.1784391701,-0.0449366197,-0.2035734504,0.5788798332,10.56637478 +5.40969094,-7.356647491,24.78098679,-37.50622559,0.6149481535,0.1743725836,-0.04687128216,-0.2832054794,0.6244323254,10.58554649 +5.41969094,-7.33052063,25.72922707,-38.94726562,0.7036159635,0.1237051487,-0.0392241925,-0.4429421723,0.6916079521,10.61947918 +5.42969094,-6.578952789,24.8692894,-37.51423645,0.8085047603,0.08715923876,-0.0232714992,-0.5601879358,0.8304725885,10.81384563 +5.43969094,-7.531322479,24.88698578,-38.73428345,0.938151896,0.08443415165,-0.007361578289,-0.6205343008,0.9866380692,10.99567223 +5.44969094,-8.279857635,22.61453819,-38.41836548,1.076931596,0.09234847128,0.01138667203,-0.7176730633,1.141917586,11.07694626 +5.45969094,-6.797901154,24.95243835,-40.7245636,1.21220386,0.142701149,0.01992633007,-0.5940642953,1.237163305,10.96197701 +5.46969094,-6.786567688,24.02353477,-37.60624695,1.331775427,0.2211852372,0.01148944534,-0.5794842839,1.406719089,10.83598232 +5.47969094,-6.683105469,23.21310043,-37.70143127,1.424222708,0.2731072903,0.001702721231,-0.6970831752,1.580493093,10.72273731 +5.48969094,-5.19291687,22.95544434,-39.51779175,1.489971876,0.2497478575,0.005910233129,-0.7620835304,1.679367185,10.78293896 +5.49969094,-7.543029785,22.68174744,-40.25616455,1.567991257,0.1823883057,0.01639992744,-0.7109425664,1.702432513,10.92615986 +5.50969094,-6.331970215,22.53879547,-41.01174927,1.654090762,0.1048861593,0.02590598166,-0.5987703204,1.934044361,10.9778614 +5.51969094,-6.872894287,21.11210251,-40.60383606,1.738529205,0.02646159567,0.03096565418,-0.4815279543,2.036860704,11.03164959 +5.52969094,-6.464668274,20.73433876,-42.10400391,1.831183553,-0.0371132046,0.03252334148,-0.2581321895,2.053145409,11.02364445 +5.53969094,-6.585845947,19.06935883,-43.05360413,1.945584893,-0.0588221848,0.02755689435,-0.03785359487,2.144049168,11.44700241 +5.54969094,-7.916595459,19.74729919,-42.18318176,2.122190714,-0.06661680341,-0.002210137434,-0.2162174582,2.293878794,11.10333824 +5.55969094,-7.169239044,19.15226173,-41.68467712,2.242797375,0.02907058969,0.01810688525,-0.6900045872,2.465847731,10.61191368 +5.56969094,-7.007083893,17.10767746,-42.76051331,2.365107298,0.07657755166,0.04666646942,-0.6271196008,2.857951403,11.23185539 +5.57969094,-7.567134857,15.80464458,-43.58227539,2.533013105,0.009588560089,0.06934992969,-0.6376622319,3.23949194,11.46477699 +5.58969094,-6.512111664,15.24316883,-43.23944092,2.682348967,-0.03812664747,0.1032418609,-1.011867642,3.570411205,11.33907413 +5.59969094,-6.6783638,14.81937218,-42.59846497,2.810805321,-0.02873550914,0.1526294798,-1.132974863,3.754922628,10.92838478 +5.60969094,-6.983627319,12.9884634,-44.45155334,2.896809578,0.02345578931,0.1785736531,-0.7919716239,3.861896276,10.60241222 +5.61969094,-8.335494995,11.18923187,-44.77839661,2.964149952,0.1522061974,0.1822080016,-0.3903231025,3.914002657,10.22689247 +5.62969094,-6.875339508,9.580715179,-44.20939636,3.014033556,0.314350307,0.1683944762,-0.1859156489,4.113828659,9.747583389 +5.63969094,-6.25868988,9.782049179,-44.83552551,3.032514095,0.4121232927,0.1518512666,-0.2616799474,4.384209156,9.284766197 +5.64969094,-5.253143311,8.660894394,-45.21585083,3.02897954,0.3880666196,0.1631702632,-0.4669449329,4.571393967,8.911646843 +5.65969094,-5.458652496,7.682689667,-44.84060669,2.998725176,0.1711447984,0.1893278956,-0.6494381428,4.91509676,8.661911964 +5.66969094,-4.898784637,6.919963837,-45.39804077,2.934651613,-0.1005303711,0.2150487304,-0.6185181737,5.301094055,9.058558464 +5.67969094,-6.250656128,5.120733261,-45.72488403,2.910330772,-0.2587017715,0.2318211645,-0.3392679691,5.375423908,9.396467209 +5.68969094,-5.879581451,4.056110382,-47.02351379,2.922665358,-0.2760679126,0.2243542522,-0.4822314382,5.503026485,9.01198101 +5.69969094,-7.555839539,1.547006607,-46.07824707,2.943333626,-0.2170758992,0.2124996334,-0.4549106658,5.76497364,8.254803658 +5.70969094,-7.756641388,0.7047691345,-45.86528015,2.945270777,-0.1781668216,0.206626907,-0.3903495967,5.746593952,7.337949276 +5.71969094,-7.067371368,-0.9885349274,-44.98329163,2.901698112,-0.208113566,0.1915666908,-0.1816526204,5.731616974,6.997162819 +5.72969094,-6.940868378,-1.520008087,-44.79315186,2.863831282,-0.2202323377,0.1786220968,0.2177357078,5.766057968,6.696406841 +5.73969094,-6.940868378,-1.520008087,-44.79315186,2.802832127,-0.2372323573,0.1332490295,0.4498233199,5.980806351,6.113747597 +5.74969094,-6.167266846,-3.900087357,-45.23603821,2.685050011,-0.3130146265,0.08385125548,0.2968164086,6.279119015,5.801239014 +5.75969094,-6.167266846,-3.900087357,-45.23603821,2.537742376,-0.4306736588,0.05320386589,-0.04167699441,6.515561581,5.803927898 +5.76969094,-6.593517303,-5.330215454,-46.65950012,2.41191411,-0.5239192843,0.04438148439,-0.1330732852,6.619808674,5.855363846 +5.77969094,-8.595588684,-5.950117111,-44.62696838,2.333110571,-0.5195303559,0.03090674616,-0.2117768675,6.696700573,5.642460823 +5.78969094,-8.628837585,-6.234352112,-45.36975098,2.297567368,-0.414770335,0.02381796204,-0.1566102207,6.806136608,5.84990263 +5.79969094,-7.396728516,-8.699171066,-45.80760193,2.302094221,-0.2918973565,0.008515642956,-0.2365027964,7.161421299,5.973455906 +5.80969094,-6.642562866,-9.290693283,-45.00413513,2.342935801,-0.1979716867,-0.003534507239,-0.3914331794,7.542293072,6.224736691 +5.81969094,-7.828483582,-9.00123024,-43.49595642,2.388193846,-0.1110058427,-0.01067978889,-0.2491026968,7.591664314,5.918228626 +5.82969094,-8.313922882,-9.598073959,-42.84197998,2.408067226,-0.01837301813,-0.0253147278,0.03190075234,7.624960423,5.320220947 +5.83969094,-8.342350006,-12.47961998,-43.24739075,2.358142614,0.03511781245,-0.05030852184,-0.1354587525,7.447844982,3.707296848 +5.84969094,-7.461193085,-13.20175362,-43.3506012,2.204473257,0.08235535771,-0.04534388334,-0.007207999472,7.318314552,3.282621861 +5.85969094,-7.363540649,-12.21660042,-41.58953857,2.055655241,0.08076247573,-0.0373577401,0.06377741694,7.392906189,3.514599085 +5.86969094,-7.110240936,-14.94444084,-43.68527222,1.952906013,0.0455320403,-0.03147932887,-0.04667448997,7.750877857,3.611784935 +5.87969094,-7.37494278,-15.08936119,-41.23254395,1.861353755,-0.06208071858,-0.02312056161,-0.1080982238,8.078206062,3.719119072 +5.88969094,-6.948883057,-15.72499657,-41.1882782,1.785762429,-0.1727042198,-0.0151411742,-0.1235443279,8.370152473,4.199866295 +5.89969094,-8.64748764,-16.0464344,-40.24595642,1.783481121,-0.1971811503,-0.02015489712,-0.1364535242,8.50699234,4.808917046 +5.90969094,-8.216724396,-16.54610634,-40.36398315,1.838792682,-0.07802446187,-0.03536086157,-0.06082001328,8.485049248,4.451487541 +5.91969094,-7.526954651,-17.83854485,-40.57881165,1.857732654,0.1300803274,-0.06967652589,-0.01184685621,8.531123161,3.72614336 +5.92969094,-8.303844452,-18.19350243,-39.78877258,1.814050555,0.2553527653,-0.08196844906,-0.06807902455,8.328253746,2.931307793 +5.93969094,-8.504646301,-19.03573799,-39.57580566,1.716382623,0.2538946271,-0.06764163077,0.005141078494,8.346729279,2.556385756 +5.94969094,-6.853099823,-20.07394981,-38.89517212,1.598608613,0.152466163,-0.05751124769,0.003872290719,8.425172806,2.134254694 +5.95969094,-7.664543152,-20.84735298,-38.53311157,1.460810065,0.05854824185,-0.04427852854,-0.007188765798,8.432364464,2.211527348 +5.96969094,-7.158172607,-19.83910179,-39.36903381,1.353563786,0.002514161868,-0.03926705942,0.1740697026,8.347842216,2.068900824 +5.97969094,-6.903877258,-20.76783943,-39.30352783,1.247846365,-0.0405799076,-0.04407382011,0.04491699114,8.569720268,1.811930776 +5.98969094,-7.849121094,-21.41414452,-38.33978271,1.123071313,-0.06583211571,-0.04931728542,0.1047966853,8.705016136,1.846391082 +5.99969094,-6.214603424,-22.4611454,-38.42155457,1.026422024,-0.04438321292,-0.07139761746,0.05338488892,8.847383499,2.146280289 +6.00969094,-8.489295959,-22.29530525,-36.90214539,0.9891057611,0.03876886517,-0.0867260173,-0.1419169158,9.085100174,2.551529169 +6.01969094,-7.745658875,-22.22458076,-38.43496704,0.9823154211,0.1017291918,-0.106370911,-0.1593304873,9.150740623,2.281132936 +6.02969094,-7.793209076,-23.98509407,-35.71522522,0.9616700411,0.1661918014,-0.1270233244,0.0574805215,9.073641777,2.047967672 +6.03969094,-6.944122314,-22.12397194,-37.37562561,0.9382100105,0.1993058175,-0.1547543406,0.01744905487,8.995657921,1.867096186 +6.04969094,-7.567459106,-25.06209755,-36.55479431,0.9021967649,0.1762719601,-0.1718251109,-0.1406121552,9.002772331,1.730701089 +6.05969094,-7.148822784,-23.36882973,-36.21832275,0.8519908786,0.1081412658,-0.1755056828,-0.3042657971,9.020177841,1.228797078 +6.06969094,-6.28729248,-24.36816597,-36.45439148,0.7535880208,0.05081130564,-0.1780438125,-0.07804404944,8.942026138,0.0622860454 +6.07969094,-6.615398407,-24.41227531,-37.21359253,0.618732214,0.1305778027,-0.1905168593,-0.1119370759,8.954662323,0.6950393319 +6.08969094,-6.468467712,-24.93319893,-36.10856628,0.5215619206,0.097046718,-0.2001017928,-0.1604000777,9.188315392,0.8780340552 +6.09969094,-8.278152466,-24.18106651,-36.15803528,0.4290464222,-0.01206605136,-0.1879246235,-0.4243336916,9.332118034,1.622178555 +6.10969094,-6.327850342,-25.05677986,-36.40531921,0.405993849,-0.07996894419,-0.1790645421,-0.552688241,9.564300537,2.605819941 +6.11969094,-7.102638245,-25.54419136,-35.147995,0.4912712276,-0.04473871738,-0.1838391423,-0.6350327134,9.846169472,3.193631172 +6.12969094,-6.550388336,-26.04377174,-36.79244995,0.6150547266,0.02244179323,-0.1928945929,-0.5246245861,10.11047459,2.911791801 +6.13969094,-6.669765472,-26.17630577,-34.79878235,0.6870937347,0.05554625392,-0.2030147314,-0.6148418188,9.993902206,2.150117159 +6.14969094,-5.846191406,-27.59583855,-35.61532593,0.6883915663,0.03711918741,-0.1955397874,-0.5256439447,9.68167305,1.30617404 +6.15969094,-5.604026794,-26.33164406,-35.09532166,0.6299076676,-0.03041126765,-0.1765921712,-0.3788752854,9.399337769,0.6339784861 +6.16969094,-8.064910889,-26.19928551,-34.94665527,0.5348986387,-0.09692849219,-0.1555133909,-0.3197306395,9.323526382,0.3968338072 +6.17969094,-7.614517212,-26.42174721,-34.93183899,0.4100218713,-0.1200562641,-0.1371923983,-0.2656853199,9.336148262,0.2756545842 +6.18969094,-7.253158569,-26.09340477,-33.42985535,0.2929925919,-0.1655155718,-0.119509913,-0.5380512476,9.51198101,1.004795432 +6.19969094,-7.253158569,-26.09340477,-33.42985535,0.2704642713,-0.2962380052,-0.09054698795,-0.5876882672,9.83070755,2.051366806 +6.20969094,-7.109138489,-26.21522713,-33.57411194,0.3218514025,-0.3892082572,-0.07513003796,-0.7501949668,10.15983486,1.972729325 +6.21969094,-7.109138489,-26.21522713,-33.57411194,0.3665290177,-0.3839081526,-0.07275354117,-0.8437544703,10.24689865,1.666616678 +6.22969094,-6.416275024,-26.83838844,-35.51531982,0.395961076,-0.362869978,-0.07692997903,-0.6962200403,10.24797153,1.79286015 +6.23969094,-6.924747467,-27.71418571,-35.14666748,0.4236700237,-0.2611028552,-0.08759007603,-0.5691984892,10.19368839,1.651011705 +6.24969094,-5.731018066,-26.20103264,-34.18865967,0.4270048141,-0.06775772572,-0.1009764075,-0.5052172542,10.05403423,1.090990543 +6.25969094,-5.955661774,-27.05557823,-35.04304504,0.3786482811,0.09604948014,-0.1041087434,-0.4335639477,9.900503159,0.7057359815 +6.26969094,-6.411750793,-27.77080727,-32.70195007,0.3053478301,0.1541505158,-0.09603412449,-0.3152265251,9.683514595,0.2176062912 +6.27969094,-7.398361206,-28.83907127,-32.47116089,0.2221469581,0.1793556064,-0.09244065732,-0.3450230062,9.64139843,0.03580936044 +6.28969094,-6.884685516,-27.42643929,-34.09892273,0.1321454048,0.1524278224,-0.07998012006,-0.4099330306,9.661970139,0.1432226747 +6.29969094,-5.973186493,-27.46523094,-34.70864868,0.06124486029,0.006182244048,-0.05034258217,-0.6426629424,9.841538429,0.8780437112 +6.30969094,-5.79510498,-27.56947136,-33.32810974,0.02109704167,-0.1478120834,-0.01445469074,-0.8502721786,10.12072086,1.450038433 +6.31969094,-6.100177765,-27.33461952,-33.80197144,0.02866151184,-0.1757359207,0.02049909532,-1.059395909,10.29624081,1.798726797 +6.32969094,-5.74773407,-26.87734032,-33.07220459,0.07471268624,-0.1548956931,0.04094931856,-0.6283707023,10.32818985,2.137936592 +6.33969094,-7.230003357,-28.54772377,-32.64486694,0.1274963319,-0.03663658351,0.03674643859,-0.6844533682,10.25029659,1.625554085 +6.34969094,-5.938632965,-27.04678917,-34.28063965,0.1503894925,0.09812860191,0.0308074709,-0.5885108113,10.05522156,1.04837501 +6.35969094,-6.94877243,-28.79487801,-33.2383728,0.141530484,0.1597932577,0.0212403778,-0.4865935445,9.90356636,0.6102195382 +6.36969094,-7.224311829,-27.61003304,-35.0007782,0.09064454585,0.1466042995,0.02438522689,-0.4241021872,9.743000031,0.1843403578 +6.37969094,-7.368019104,-28.15572929,-32.97767639,0.01164131891,0.07730738074,0.03112574108,-0.3331443667,9.599993706,0.2176162302 +6.38969094,-6.503890991,-28.88665199,-33.84329224,-0.05099488795,-0.004641776904,0.03543535247,-0.252383858,9.633149147,0.59193331 +6.39969094,-5.962162018,-27.726614,-33.46916199,-0.08003503084,-0.1150991321,0.04340262339,-0.4005373716,9.868338585,1.204803705 +6.40969094,-6.21163559,-29.39518929,-33.1973114,-0.07899376005,-0.1858911067,0.05741436034,-0.7340079546,10.20933914,1.619800568 +6.41969094,-5.45368576,-26.8508091,-33.83781433,-0.05616622418,-0.1541773379,0.07039058954,-1.079650044,10.29853725,1.836036086 +6.42969094,-5.764762878,-26.88612938,-33.83460999,-0.01166790351,-0.04989422113,0.08693824708,-0.7219535708,10.20710182,1.849469781 +6.43969094,-7.55210495,-28.321661,-33.88114929,0.04423505068,0.07162962109,0.07621535659,-0.5237618089,10.04287529,1.466649294 +6.44969094,-6.139930725,-28.28988838,-32.97085571,0.06462156773,0.1659917086,0.06487458199,-0.2693444788,9.804961205,1.003296137 +6.45969094,-6.900909424,-27.70188332,-34.07929993,0.03726745769,0.1448267251,0.05426616967,-0.3218414485,9.69636631,0.6625964046 +6.46969094,-7.200469971,-27.59772682,-33.9334259,-0.01479796506,0.08245497942,0.05262492597,-0.2694553733,9.55301857,0.2537037134 +6.47969094,-7.517551422,-27.90321922,-33.45314026,-0.08166196942,0.04908277839,0.05063142255,-0.2769313157,9.516850471,0.3263844252 +6.48969094,-6.802764893,-26.31586647,-33.41503906,-0.129857108,-0.02138678916,0.04803046212,-0.2548482716,9.549121857,0.8685886264 +6.49969094,-6.453117371,-28.19276619,-33.43490601,-0.1322575063,-0.1084113792,0.04938947782,-0.3309167624,9.744350433,1.420860767 +6.50969094,-6.421966553,-27.77607918,-33.15940857,-0.1049313694,-0.1264381558,0.046568349,-0.5277194381,9.95321846,1.722908378 +6.51969094,-5.795597076,-27.97033501,-32.23129272,-0.05866263807,-0.1087544486,0.05005582422,-0.7864149809,10.11928844,1.995217443 +6.52969094,-6.756572723,-28.49122429,-32.34472656,-0.003624816425,-0.06324600428,0.05518781021,-0.6161818504,10.06101227,1.923961639 +6.53969094,-7.125358582,-26.49067116,-33.55448914,0.04583896324,0.0206390284,0.04705470428,-0.5417268872,9.938585281,1.376885653 +6.54969094,-7.384243011,-28.43117332,-32.95803833,0.05019680783,0.09256529063,0.04150801897,-0.4252683222,9.765837669,0.8409014344 +6.55969094,-4.91444397,-28.69246864,-32.33450317,0.01591055095,0.1238697395,0.0377532728,-0.2604678869,9.612735748,0.5489522815 +6.56969094,-7.374832153,-28.15924644,-33.28263855,-0.03896613047,0.1192587167,0.03361805156,-0.2545866966,9.425396919,0.2876189351 +6.57969094,-7.552417755,-27.65414238,-35.75997925,-0.115347065,0.05623481423,0.03676060215,-0.2348897308,9.471121788,0.4286818206 +6.58969094,-5.604522705,-26.73250771,-33.9985199,-0.1771036536,-0.04492631555,0.04680872709,-0.2702373564,9.532647133,0.8573217988 +6.59969094,-6.436088562,-28.18397713,-32.67250061,-0.2030239701,-0.1211520582,0.05939222872,-0.3787891567,9.717459679,1.445369482 +6.60969094,-6.883880615,-27.69309425,-33.31689453,-0.1903610229,-0.1436105818,0.06776238978,-0.558552444,9.869405746,1.720998168 +6.61969094,-6.573608398,-27.391119,-34.10212708,-0.1610814184,-0.08932308853,0.07173191011,-0.6367165446,9.939030647,1.911174417 +6.62969094,-7.794269562,-29.58585548,-34.40113831,-0.1116924211,0.006465011742,0.06918103993,-0.5721738338,10.05697632,1.982402325 +6.63969094,-6.814472198,-28.52110863,-34.93688965,-0.06562864035,0.08441458642,0.07163098454,-0.5598268509,10.11370468,1.724575639 +6.64969094,-8.08763504,-27.1457653,-33.35314941,-0.04420860112,0.1142552868,0.07546826452,-0.5372830629,10.04286575,1.392329931 +6.65969094,-6.123214722,-27.6135807,-34.08731079,-0.0459260419,0.1182540208,0.07836209983,-0.4197402894,9.951440811,1.140536189 +6.66969094,-6.123214722,-27.6135807,-34.08731079,-0.05532723293,0.1020412371,0.07906202972,-0.3231778741,9.863641739,0.9318038821 +6.67969094,-6.894096375,-27.69836617,-33.77432251,-0.07831289619,0.07124064118,0.07983452082,-0.3302566707,9.701453209,0.7482259274 +6.68969094,-6.894096375,-27.69836617,-33.77432251,-0.1181097478,0.01705702953,0.08364310861,-0.3872544169,9.722757339,1.051870346 +6.69969094,-7.967948914,-27.68075752,-33.46795654,-0.1227426454,-0.05970519036,0.08897811919,-0.277425617,9.779336929,1.489115953 +6.70969094,-7.40127182,-28.43996239,-33.72044373,-0.0898701027,-0.0761885494,0.08000918478,-0.3281622827,9.89973259,1.708828926 +6.71969094,-7.944419861,-27.00093269,-34.2794342,-0.05175863951,-0.01853408106,0.06382262707,-0.288962692,9.960288048,1.617121935 +6.72969094,-7.350498199,-27.74607658,-33.31208801,-0.01620602794,0.05397556722,0.04388963431,-0.2301783711,9.98184967,1.649990201 +6.73969094,-6.850627899,-27.40886116,-32.57411194,0.02669902518,0.1087433919,0.02283645235,-0.2193958163,9.986762047,1.493821502 +6.74969094,-6.276763916,-25.03041267,-34.11810303,0.05053491145,0.1415589154,0.006427761633,-0.186055243,9.888465881,1.22418046 +6.75969094,-5.76556778,-26.61947441,-34.61665344,0.05468331277,0.1356552541,-0.004396722652,-0.2582173944,9.880655289,0.8945252299 +6.76969094,-6.412559509,-27.5041523,-33.48397827,0.02108596638,0.1019207016,-0.00492911879,-0.1788927615,9.746808052,0.8333583474 +6.77969094,-6.438995361,-27.78486824,-33.92181396,-0.01197361574,0.05054997653,-0.004860650748,-0.214070335,9.705836296,0.8822153211 +6.78969094,-7.23532486,-26.35127068,-31.88542175,-0.04006820545,0.009633807465,-0.007401582785,-0.2310826182,9.749238968,0.9326863885 +6.79969094,-5.922412872,-26.77134514,-34.30026245,-0.05779248476,-0.005850811489,-0.01066414267,-0.3249489665,9.765732765,1.428793669 +6.80969094,-6.624382019,-28.08500481,-34.51049805,-0.03070151433,-0.02131212316,-0.01599514671,-0.3499325216,9.840603828,1.784139752 +6.81969094,-7.216690063,-27.87317085,-33.9138031,0.01399721112,0.04281678796,-0.02650093473,-0.3876112998,10.02012634,1.671920061 +6.82969094,-7.334274292,-27.47063255,-33.33171082,0.03933909908,0.1012582257,-0.03255970404,-0.4020133018,9.993823051,1.556693912 +6.83969094,-6.422775269,-27.50942421,-33.94143677,0.04864115268,0.1026042551,-0.03411246836,-0.3927852213,9.927248001,1.404534221 +6.84969094,-6.099685669,-26.93375587,-34.89878845,0.04725361243,0.08464277536,-0.02633284219,-0.3983494043,9.882896423,1.130617261 +6.85969094,-6.709514618,-27.13157463,-33.96768188,0.03037458286,0.05510923266,-0.02556121349,-0.2641792893,9.814121246,1.064528465 +6.86969094,-8.128997803,-27.56772423,-34.08610535,0.01483618375,0.003363851458,-0.02506784908,-0.187411204,9.8035326,1.080051899 +6.87969094,-7.206474304,-27.86789894,-33.4563446,-0.002668799367,-0.01789128594,-0.02890080027,-0.320317775,9.793665886,1.104911685 +6.88969094,-6.774410248,-28.23335838,-33.88916016,-0.01878705993,-0.02112191916,-0.02776563354,-0.4035662711,9.821204185,1.327342987 +6.89969094,-5.018215179,-27.21451378,-34.1181488,-0.02795302868,-0.03217923641,-0.01834156923,-0.5014348626,9.811349869,1.555790424 +6.90969094,-7.20728302,-27.60124397,-34.2383728,-0.008743250743,-0.01979034208,-0.01190102287,-0.58278054,9.913967133,1.847940207 +6.91969094,-7.614204407,-27.08926582,-33.05297852,0.02492422983,0.0008906451985,-0.00287973159,-0.5595668554,10.02017021,1.774408937 +6.92969094,-7.233718872,-27.88195992,-34.6762085,0.04376917332,0.04712672532,-4.605692811e-05,-0.4242864549,9.926309586,1.59119153 +6.93969094,-6.364692688,-26.41115761,-34.3249054,0.05611082911,0.08474594355,0.001481917687,-0.3794151247,9.889209747,1.309559703 +6.94969094,-6.492179871,-26.68140984,-32.32142639,0.04988059774,0.0568838492,0.004740366712,-0.3320913613,9.857626915,1.161911249 +6.95969094,-6.750072479,-27.82018852,-33.91860962,0.03539325669,0.001818951452,0.01166409627,-0.2974179685,9.810185432,1.090349078 +6.96969094,-7.654762268,-27.77787971,-33.00390625,0.01885271445,-0.02488981374,0.01005619578,-0.2859608829,9.783588409,1.121622562 +6.97969094,-6.388221741,-27.09098244,-33.51342773,0.01086804736,-0.04192594439,0.01164975017,-0.3405780494,9.779755592,1.130196571 +6.98969094,-8.289241791,-27.72134972,-33.92221069,-0.001383066177,-0.04618999362,0.01165413111,-0.3816172481,9.75190258,1.178019404 +6.99969094,-7.193969727,-26.92669106,-35.50730896,-0.0115212854,-0.04669833183,0.01270983368,-0.328725487,9.718396187,1.172267914 +7.00969094,-7.662380219,-27.5147419,-34.09091187,-0.02117509581,-0.01347930171,0.006302303169,-0.3198303878,9.751620293,1.187064886 +7.01969094,-6.630390167,-28.35517693,-34.03343201,-0.03068202361,0.03722409159,-0.002729954664,-0.2822543681,9.780727386,1.300859928 +7.02969094,-5.79510498,-27.56947136,-33.32810974,-0.03604897112,0.08230222762,-0.009162247181,-0.2794805765,9.743423462,1.304435372 +7.03969094,-5.177967072,-26.96727562,-35.05105591,-0.03218114376,0.0754532665,-0.01454768702,-0.2370035499,9.778010368,1.412263632 +7.04969094,-7.679096222,-28.19104958,-32.97447205,-0.01724203117,0.04264059663,-0.01922232471,-0.3165919483,9.784212112,1.443166137 +7.05969094,-6.45602417,-27.7936573,-34.68421936,0.001956798136,0.03951912373,-0.01594966836,-0.4240784943,9.821266174,1.450664759 +7.06969094,-6.479553223,-28.47348213,-33.8727417,0.008233220316,0.02018114552,-0.008499246091,-0.3593118489,9.874123573,1.357286453 +7.07969094,-6.767910004,-27.56232262,-35.46304321,0.009127783589,-0.001021089964,-0.004468719475,-0.3498756886,9.895945549,1.244823337 +7.08969094,-6.692485809,-27.12278557,-33.20527649,-0.0003577759489,0.005629235413,-0.005507835187,-0.3065412641,9.852435112,1.167840958 +7.09969094,-6.924747467,-27.71418571,-35.14666748,-0.01182218362,0.001805877313,-0.01178541407,-0.3422503471,9.787656784,1.151168823 +7.10969094,-7.233718872,-27.88195992,-34.6762085,-0.02720049769,-0.03890308738,-0.006950148847,-0.3069644272,9.728578568,1.21667695 +7.11969094,-7.233718872,-27.88195992,-34.6762085,-0.03472160175,-0.08361277729,-0.002064191969,-0.3741668761,9.828631401,1.381771445 +7.12969094,-6.566795349,-27.38760185,-33.79716492,-0.02443219163,-0.1106951907,0.003613332286,-0.414747417,9.909883499,1.478235364 +7.13969094,-6.566795349,-27.38760185,-33.79716492,-0.009322667494,-0.08561228961,0.001425060909,-0.3364252448,9.950445175,1.520546079 +7.14969094,-6.894901276,-27.4317112,-34.55636597,0.01297305804,-0.02708912455,-0.01052638143,-0.2902875245,9.916857719,1.579128742 +7.15969094,-6.756885529,-27.82370567,-34.22355652,0.03051350266,0.02103862539,-0.02036703378,-0.3130198121,9.968650818,1.473050833 +7.16969094,-5.754547119,-26.88085747,-33.37718201,0.0321297273,0.03274782375,-0.02557382733,-0.2421809435,9.896290779,1.491528034 +7.17969094,-6.583019257,-27.66304588,-33.77752686,0.03285422921,0.03309166431,-0.0193908941,-0.3465489447,10.00986385,1.124078274 +7.18969094,-6.140735626,-28.02323341,-33.75289917,0.006991864182,0.03043413348,-0.01644375175,-0.3370473981,9.83026123,1.150320888 +7.19969094,-6.395530701,-27.49536324,-32.72157288,-0.008524950594,-0.003374490887,-0.008978554048,-0.3312490284,9.773057938,1.049988508 +7.20969094,-6.388221741,-27.09098244,-33.51342773,-0.02062888816,-0.03278618306,-0.003952762112,-0.3668813407,9.767832756,1.011610508 +7.21969094,-7.223503113,-27.876688,-34.21875,-0.04249082133,-0.0584564656,0.0001669055782,-0.3540610373,9.73944664,1.033432245 +7.22969094,-6.600048065,-27.67183495,-34.53993225,-0.05800957978,-0.07085700333,0.003791878931,-0.2489045262,9.71612072,1.233810782 +7.23969094,-5.110660553,-26.6654644,-32.78346252,-0.05563404411,-0.05858933181,-0.002280948451,-0.3057807386,9.793344498,1.323033094 +7.24969094,-7.309940338,-27.05746269,-33.36116028,-0.04836731032,-0.03795132041,-0.005654682405,-0.3357924521,10.0036459,1.809297442 +7.25969094,-6.61416626,-28.07973289,-34.05305481,-0.01423756406,0.006586294156,-0.01188713871,-0.3840130866,10.07378864,1.766835213 +7.26969094,-7.343997955,-27.07504082,-34.88597107,0.01365469862,0.07387618721,-0.01604680531,-0.4060422778,9.988751411,1.480861068 +7.27969094,-7.777240753,-29.57706642,-33.63873291,0.02453222126,0.1045350581,-0.01727712527,-0.4209952354,9.866679192,1.274744511 +7.28969094,-5.620742798,-27.00795174,-33.97889709,0.01804178953,0.06112404913,-0.005017064512,-0.3968960345,9.788542747,1.140993834 +7.29969094,-7.377742767,-27.76013756,-34.53192139,0.0008723521605,0.004055330064,0.007162726019,-0.3800239861,9.755570412,1.057734847 +7.30969094,-7.672595978,-27.52001381,-34.5483551,-0.01781889051,-0.02779907547,0.01464441419,-0.3330850601,9.757546425,1.02295351 +7.31969094,-7.233718872,-27.88195992,-34.6762085,-0.03380134702,-0.05228021741,0.01153825596,-0.3335008323,9.738282204,1.145313025 +7.32969094,-7.320156097,-27.0627346,-33.81858826,-0.04232557118,-0.0432979241,0.008302789181,-0.3039984107,9.838270187,1.414606929 +7.33969094,-7.190254211,-27.59245491,-33.47596741,-0.02935238183,-0.006282945164,-0.002957660705,-0.3414013386,9.905882835,1.408320904 +7.34969094,-7.477802277,-26.94795036,-34.28424072,-0.02123269252,0.01367532462,-0.01296658069,-0.4141909778,9.940719604,1.555158734 +7.35969094,-7.60969162,-29.01906395,-34.59448242,-0.001434227452,0.0439722389,-0.01752397791,-0.3848665655,9.939268112,1.577160716 +7.36969094,-6.877067566,-27.6895771,-33.01191711,0.01578740031,0.06906723976,-0.01977552287,-0.4487940073,9.894701958,1.458930373 +7.37969094,-7.281089783,-28.57409096,-34.93208313,0.01979922131,0.06542313844,-0.01498976164,-0.4290300012,9.838287354,1.364023685 +7.38969094,-8.161937714,-28.51947594,-32.95002747,0.01419570576,0.05275142938,-0.007790469099,-0.4366999269,9.831308365,1.314301133 +7.39969094,-5.692451477,-28.11325264,-34.20532227,0.006470880471,0.05087926984,-0.0004649830516,-0.4752577841,9.859540939,1.219009399 +7.40969094,-7.608882904,-29.28571892,-33.81245422,-0.007653800771,0.03567602485,0.007800285239,-0.4810006022,9.848795891,1.221743464 +7.41969094,-7.368019104,-28.15572929,-32.97767639,-0.01515169907,0.01380138658,0.012325285,-0.4593022466,9.826662064,1.251535416 +7.42969094,-7.377742767,-27.76013756,-34.53192139,-0.01542961784,0.001022662269,0.0169789847,-0.4755968451,9.779748917,1.37317872 +7.43969094,-7.240531921,-27.88547707,-34.9811554,-0.001768577844,-0.009118218906,0.0176607091,-0.4284687936,9.814570427,1.428025246 +7.44969094,-5.634056091,-27.68250465,-32.70996094,0.01253924798,-0.02427603118,0.01522912644,-0.4111814201,9.89552021,1.414922833 +7.45969094,-5.822349548,-27.58353233,-34.54794312,0.016501192,-0.02373738401,0.01423248276,-0.3876463473,9.8126688,1.245267987 +7.46969094,-4.953323364,-26.11273003,-34.19667053,0.01277748775,-0.00871932786,0.006232345942,-0.3258767724,9.745279312,1.207792163 +7.47969094,-8.162742615,-28.25282097,-33.73207092,0.008416143246,0.01032425929,-0.002889809664,-0.3021071851,9.780869484,1.24430275 +7.48969094,-7.350498199,-27.74607658,-33.31208801,0.002876439132,0.02547182329,-0.01436623745,-0.2963513732,9.847862244,1.273426533 +7.49969094,-7.305427551,-28.98726082,-34.90263367,-0.002952933777,0.0436315015,-0.02437200397,-0.324506253,9.83515358,1.275908351 +7.50969094,-6.436088562,-28.18397713,-32.67250061,-0.005305088125,0.0420396477,-0.02915145457,-0.347178787,9.725076675,1.296135187 +7.51969094,-6.781223297,-28.23687553,-34.19410706,-0.002636422403,0.03410620987,-0.03221314773,-0.3757326901,9.717552185,1.36000824 +7.52969094,-5.242053986,-28.33571434,-34.19050598,0.0007457425818,-0.01242156141,-0.02688090876,-0.4411390722,9.801218033,1.417131782 +7.53969094,-6.767910004,-27.56232262,-35.46304321,-0.001384953968,-0.05899711698,-0.01799323969,-0.4971121252,9.86701107,1.508045077 +7.54969094,-6.147857666,-27.35922813,-35.93670654,0.005357009359,-0.04358646274,-0.01161833853,-0.4639644921,9.821174622,1.404155374 +7.55969094,-4.976852417,-26.79255486,-33.38519287,0.0151279429,0.0234514568,-0.01500775106,-0.3858245611,9.831782341,1.389816284 +7.56969094,-7.960639954,-27.27637672,-34.2598114,0.0150560895,0.07433521748,-0.02114808559,-0.4165942073,9.863568306,1.26608634 +7.57969094,-7.960639954,-27.27637672,-34.2598114,-0.00113414228,0.0747378394,-0.01512921415,-0.4706613719,9.822451591,1.127860188 +7.58969094,-6.924438477,-28.38170815,-33.26782227,-0.01949587092,0.04672278464,-0.004416399635,-0.445856601,9.810827255,1.171207547 +7.59969094,-6.924438477,-28.38170815,-33.26782227,-0.02355873771,0.02254176326,0.001991390716,-0.4345932901,9.865891457,1.256605029 +7.60969094,-7.360713959,-27.7513485,-33.76951599,-0.01876131445,-0.01338596083,0.01086175442,-0.4302533865,9.892401695,1.399819732 +7.61969094,-6.099372864,-27.60127449,-33.01992798,-0.01351431571,-0.02890297584,0.01611890458,-0.4486133754,9.895947456,1.357090831 +7.62969094,-6.023464203,-27.7582531,-36.21383667,-0.006224627607,-0.03095378913,0.02272274345,-0.393653661,9.873376846,1.453101158 +7.63969094,-4.95381546,-26.51359367,-33.09985352,0.005430291407,-0.03075273894,0.02272888087,-0.3597887158,9.917237282,1.505781412 +7.64969094,-7.662380219,-27.5147419,-34.09091187,0.01662251353,-0.01261894777,0.02528715879,-0.3409255147,9.983776093,1.455637813 +7.65969094,-5.956157684,-27.45644188,-33.94624329,0.02580481023,0.01031739917,0.02337107435,-0.3538760841,9.961380005,1.392374516 +7.66969094,-5.945941925,-27.45116997,-33.48878479,0.02733973414,0.02243147232,0.02270804159,-0.3228459656,9.875902176,1.254824042 +7.67969094,-6.699794769,-27.52716637,-32.41342163,0.01501560863,0.04744583368,0.0235953778,-0.2549906671,9.883605957,1.205357552 +7.68969094,-5.870212555,-28.67652702,-33.70703125,-0.004503405653,0.07281056046,0.02109043114,-0.2139221281,9.83890152,1.22555697 +7.69969094,-6.106185913,-27.60479164,-33.3249054,-0.02125325799,0.07326879352,0.01964235678,-0.3086723089,9.874782562,1.160287142 +7.70969094,-5.586685181,-26.99037361,-32.4540863,-0.03068202361,0.07159326226,0.0173430033,-0.2877425849,9.871712685,1.175171494 +7.71969094,-4.659275055,-26.08619881,-34.96228027,-0.02949244902,0.07909663022,0.009707890451,-0.2861399055,9.862574577,1.186464429 +7.72969094,-6.55688858,-26.71480751,-35.21856689,-0.03317952901,0.09336777031,0.006730064284,-0.2911436856,9.858118057,1.322293639 +7.73969094,-5.501056671,-27.54294014,-34.09371948,-0.02859684452,0.08231816441,0.006812664215,-0.3232996762,9.870845795,1.490914941 +7.74969094,-6.781223297,-28.23687553,-34.19410706,-0.02269191667,0.05310622603,0.01530707069,-0.4537217319,9.846877098,1.393212676 +7.75969094,-6.124019623,-27.34692574,-34.86933899,-0.0262016058,0.02659603208,0.02568925545,-0.4360646009,9.820919991,1.411521673 +7.76969094,-5.696960449,-26.18345451,-32.66384888,-0.01927095279,0.02992433496,0.02766008116,-0.3731211722,9.859240532,1.493628025 +7.77969094,-6.423088074,-26.84190559,-35.82026672,-0.006756332237,0.06467999518,0.02580158599,-0.3479112685,9.877393723,1.410155177 +7.78969094,-6.002719879,-28.41522789,-33.42008972,-0.008050819859,0.1007210612,0.02127013728,-0.3521136343,9.825234413,1.321725845 +7.79969094,-7.713153839,-28.2086277,-34.49928284,-0.008586347103,0.1213297322,0.01726247743,-0.3854017854,9.753717422,1.385715365 +7.80969094,-6.133430481,-27.61885262,-34.54473877,0.006605016999,0.1322527677,0.01431554556,-0.3959612548,9.918157578,1.32136023 +7.81969094,-5.668609619,-28.10094643,-33.13796997,0.01407197956,0.1031682789,0.02043901943,-0.3845593333,9.779145241,1.264665008 +7.82969094,-7.165916443,-27.17928505,-33.50541687,0.01219318714,0.0370747447,0.02418522909,-0.3916911483,9.846901894,1.245322227 +7.83969094,-6.130828857,-27.35043907,-35.17430115,0.009833314456,0.002732901601,0.03058625944,-0.3888401389,9.822085381,1.323127627 +7.84969094,-6.675769806,-26.44647789,-34.32170105,0.01811700687,-0.005176926032,0.03133744374,-0.3480463326,9.801818848,1.435973644 +7.85969094,-6.683078766,-26.85085869,-33.52984619,0.0284155868,-0.004169987515,0.03186465427,-0.3630813062,9.866709709,1.449119687 +7.86969094,-5.778076172,-27.5606823,-32.56570435,0.03512119129,-0.008301069029,0.02844945155,-0.3229779601,9.843298912,1.415961981 +7.87969094,-4.823112488,-27.30996895,-31.97518921,0.0376309827,0.01536973473,0.02377301455,-0.2518242002,9.83313942,1.337736964 +7.88969094,-6.597137451,-28.07094383,-33.29064941,0.03478628397,0.03389872238,0.01552080922,-0.2680309415,9.835711479,1.253291488 +7.89969094,-6.525432587,-26.96564293,-33.06419373,0.0338845104,0.04535847902,0.006745200139,-0.2180101275,9.819023132,1.17667377 +7.90969094,-5.127689362,-26.67425346,-33.54586792,0.03140550107,0.03659430891,-0.001788613503,-0.1975850165,9.744873047,1.114180207 +7.91969094,-5.986499786,-28.13978386,-33.43971252,0.02867707983,0.01808444411,-0.009790910408,-0.2553076744,9.766321182,1.183923244 +7.92969094,-6.099372864,-27.60127449,-33.01992798,0.03164083511,-0.009051376022,-0.01414498687,-0.2725343108,9.800502777,1.266273379 +7.93969094,-7.190254211,-27.59245491,-33.47596741,0.03399694338,-0.03510866314,-0.01080191508,-0.3454096317,9.859103203,1.313657284 +7.94969094,-6.692485809,-27.12278557,-33.20527649,0.03248399124,-0.05040859431,-0.009788624942,-0.3541353345,9.83678627,1.314404488 +7.95969094,-5.701858521,-28.38517952,-33.88075256,0.02853907272,-0.04782148451,-0.009216323495,-0.3199444115,9.823339462,1.327843428 +7.96969094,-7.233718872,-27.88195992,-34.6762085,0.02739499882,-0.0317414254,-0.01145336591,-0.3068087101,9.891061783,1.347099781 +7.97969094,-5.948848724,-27.05206108,-34.73809814,0.02311804891,0.002707405714,-0.01562085561,-0.3483853638,9.85543251,1.401388407 +7.98969094,-6.965305328,-28.40279961,-35.09759521,0.02524874732,0.03096579574,-0.01465842687,-0.391481787,9.854180336,1.335046649 +7.99969094,-6.503890991,-28.88665199,-33.84329224,0.01820404828,0.03465368971,-0.005479752086,-0.3856081367,9.825998306,1.251428127 +8.00969094,-6.600048065,-27.67183495,-34.53993225,0.007291789167,0.007053038105,0.001987132709,-0.3335992694,9.810012817,1.201320887 +8.01969094,-6.163772583,-28.3021946,-34.03823853,-0.004180718213,-0.01456182078,0.01051915064,-0.3299972117,9.774536133,1.128297806 +8.02969094,-6.740352631,-28.21578026,-32.36434937,-0.01397203282,-0.03909219801,0.01338874549,-0.2939029634,9.800624847,1.181418419 +8.03969094,-6.740352631,-28.21578026,-32.36434937,-0.01755905338,-0.06164037436,0.01571976021,-0.3166171908,9.827580452,1.220766783 +8.04969094,-6.462524414,-28.46469307,-33.1103363,-0.02299508452,-0.07050529122,0.0169789847,-0.3287492394,9.846059799,1.265481591 +8.05969094,-6.462524414,-28.46469307,-33.1103363,-0.02086721361,-0.05611816049,0.01313410886,-0.3127599657,9.857430458,1.293400407 +8.06969094,-7.729377747,-28.48407173,-34.47964478,-0.01981415227,-0.02539752796,0.006699037272,-0.2972289622,9.890842438,1.254536748 +8.07969094,-7.350498199,-27.74607658,-33.31208801,-0.01718049124,0.008935067803,-0.003795302706,-0.2668244243,9.924371719,1.326421261 +8.08969094,-5.829654694,-27.98791313,-33.75610352,-0.009907738306,0.0356521979,-0.01852735132,-0.2779456675,9.923431396,1.423228025 +8.09969094,-7.517551422,-27.90321922,-33.45314026,0.006071233191,0.05359268934,-0.02314954251,-0.3708066344,9.927910805,1.405539036 +8.10969094,-6.399246216,-26.82959938,-34.75291443,0.01424688939,0.05802699178,-0.02333005331,-0.3841352463,9.899069786,1.362089515 +8.11969094,-6.709514618,-27.13157463,-33.96768188,0.01599690691,0.02361442149,-0.01315881126,-0.3848026097,9.839741707,1.206048846 +8.12969094,-4.843856812,-26.65299416,-34.76893616,0.002254735678,-0.01309334487,-0.003706273623,-0.3823072016,9.808428764,1.156294227 +8.13969094,-6.504203796,-28.21913338,-35.72212219,-0.0131037822,-0.02041620202,0.002272384241,-0.3847629428,9.844836235,1.307581306 +8.14969094,-6.73985672,-27.81491661,-33.46115112,-0.0191719532,-0.008650093339,0.006325504277,-0.3688550293,9.877473831,1.353151679 +8.15969094,-6.791439056,-28.24214745,-34.65156555,-0.01912099682,0.009797222912,0.006641102489,-0.3635028005,9.896050453,1.36118114 +8.16969094,-5.996219635,-27.74419212,-34.99397278,-0.01667619683,0.02679310925,0.003280967008,-0.3678926826,9.887942314,1.416732907 +8.17969094,-6.496582031,-28.48227119,-34.63514709,-0.004689696245,0.06497299671,0.005499317776,-0.3481041789,9.858240128,1.296657085 +8.18969094,-6.429588318,-27.51294136,-34.24638367,-0.004146645777,0.07617202401,0.001241869759,-0.3353248835,9.83586216,1.141688943 +8.19969094,-5.587493896,-26.72371864,-33.2361145,-0.01000920963,0.02543907054,0.004583748057,-0.3017803431,9.841454506,1.160948873 +8.20969094,-6.705497742,-29.46223259,-34.41235352,-0.01565810107,-0.03551977128,0.01044568978,-0.3462574482,9.866422653,1.207236767 +8.21969094,-6.877872467,-27.42292213,-33.79396057,-0.01856961846,-0.07280281931,0.01563544571,-0.3439301848,9.821113586,1.283706784 +8.22969094,-5.754547119,-26.88085747,-33.37718201,-0.01776750758,-0.05113984644,0.01072100736,-0.3152393103,9.849270821,1.368287325 +8.23969094,-6.790630341,-28.50880241,-33.86953735,-0.007758837193,0.0131536331,0.00110917585,-0.2948393226,9.888804436,1.375244021 +8.24969094,-6.883880615,-27.69309425,-33.31689453,0.0006529949605,0.05003567785,-0.007681441959,-0.3519371152,9.929426193,1.400822401 +8.25969094,-6.733852386,-27.54474449,-33.93823242,0.006397298537,0.0646423623,-0.01796964183,-0.3227308393,9.871684074,1.478527665 +8.26969094,-6.389030457,-26.82432747,-34.29545593,0.01365157124,0.08304973692,-0.02444793843,-0.285761565,9.80264473,1.286781788 +8.27969094,-5.979190826,-27.73540306,-34.23156738,0.01110325288,0.1031337157,-0.02889024839,-0.3004356325,9.809235573,1.281821251 +8.28969094,-7.199661255,-27.86438179,-33.15139771,0.01459526736,0.09770360589,-0.02966837026,-0.3339990079,9.804094315,1.184403181 +8.29969094,-6.924930573,-28.78257179,-32.17100525,0.001203035004,0.04889506102,-0.0212808568,-0.4237344265,9.790711403,1.148083925 +8.30969094,-6.60735321,-28.07621574,-33.74809265,-0.009375064634,0.01187157445,-0.01253232919,-0.3896429539,9.781116486,1.272933602 +8.31969094,-7.638046265,-27.10157204,-34.12036133,-0.00883369986,-0.007211517543,-0.005917307455,-0.4066072404,9.739402771,1.226894975 +8.32969094,-7.719966888,-28.21214485,-34.804245,-0.00851373747,-0.04851602763,0.003152085003,-0.4191360176,9.802324295,1.162682176 +8.33969094,-6.123214722,-27.6135807,-34.08731079,-0.01866781525,-0.08298770338,0.004629021045,-0.3646979332,9.822608948,1.243217111 +8.34969094,-6.573608398,-27.391119,-34.10212708,-0.01661939919,-0.05272687972,-0.001723809517,-0.3448917866,9.821504593,1.550055861 +8.35969094,-5.922412872,-26.77134514,-34.30026245,0.01482299808,0.01570285112,-0.01347478852,-0.4482457042,9.877696037,1.567859054 +8.36969094,-5.51127243,-27.54821205,-34.55114746,0.0364352949,0.05439424515,-0.01533631422,-0.5011907816,9.884627342,1.447217703 +8.37969094,-6.45602417,-27.7936573,-34.68421936,0.03358461335,0.05259715021,-0.007164225448,-0.4079911113,9.869935989,1.386832952 +8.38969094,-5.282611847,-29.02432823,-34.14143372,0.03009070083,0.02654754184,-0.0008480702527,-0.3597754538,9.862734795,1.254638791 +8.39969094,-6.476646423,-28.87259102,-32.62342834,0.02441980317,0.01135490555,0.004194807727,-0.3759404719,9.843818665,1.162875891 +8.40969094,-5.877025604,-28.68004417,-34.01199341,0.01330524031,-0.007301305421,0.01035590097,-0.272282958,9.803807259,1.169050694 +8.41969094,-7.271369934,-28.96968269,-33.37782288,0.004843432456,-0.02468059771,0.0119635351,-0.2605700195,9.832016945,1.192519903 +8.42969094,-6.976016998,-28.80893898,-34.45822144,-0.006202861667,-0.0435533002,0.01424292661,-0.2615356743,9.865633011,1.344079614 +8.43969094,-6.147548676,-28.02675056,-34.05786133,-0.004407898989,-0.04488246143,0.013322182,-0.2936043739,9.890439034,1.490942717 +8.44969094,-6.826293945,-26.9956913,-32.6035614,0.005539809354,-0.004841507412,0.0106570404,-0.2814545333,9.921515465,1.381560922 +8.45969094,-5.945941925,-27.45116997,-33.48878479,0.009740176611,0.0336917229,0.005443233531,-0.2583817542,9.897677422,1.307276845 +8.46969094,-8.233951569,-28.95725441,-35.05532837,0.006820478477,0.03775998205,0.0009987638332,-0.188798517,9.863088608,1.290988088 +8.47969094,-5.508361816,-27.94732094,-33.30186462,0.002396757714,0.02465282194,-0.002144287806,-0.2838674486,9.874014854,1.326351166 +8.48969094,-6.843322754,-27.00448036,-33.3659668,-0.005590040237,0.03597750515,-0.002197280526,-0.3121668994,9.947696686,1.387628078 +8.49969094,-6.843322754,-27.00448036,-33.3659668,6.277114153e-07,0.05783951283,-0.008056694642,-0.286522001,9.934962273,1.414525747 +8.50969094,-6.222164154,-28.73294258,-35.53359985,0.003852193244,0.06198479235,-0.01023218781,-0.3322080374,9.870047569,1.257705212 +8.51969094,-6.222164154,-28.73294258,-35.53359985,-0.007020583376,0.03898721188,-0.007140536793,-0.354721427,9.797861099,1.161445856 +8.52969094,-6.894096375,-27.69836617,-33.77432251,-0.02465378493,0.003852344118,-0.001664606389,-0.3568671644,9.820009232,1.262687206 +8.53969094,-6.123214722,-27.6135807,-34.08731079,-0.0191527456,-0.0115311034,0.00301573053,-0.4283187389,9.78444767,1.281093597 +8.54969094,-5.668922424,-27.43342781,-35.01679993,-0.02239307947,-0.01610159874,0.009372130036,-0.4523630142,9.761321068,1.180357814 +8.55969094,-5.701858521,-28.38517952,-33.88075256,-0.02748598531,-0.02391372249,0.01481722109,-0.3728437424,9.760118484,1.258087873 +8.56969094,-5.822349548,-27.58353233,-34.54794312,-0.02905605733,-0.02079217322,0.01221286692,-0.3384934366,9.839878082,1.264950633 +8.57969094,-7.391365051,-27.76716805,-35.14186096,-0.02644769847,0.004086437169,0.003770391457,-0.3486168683,9.821240425,1.333070636 +8.58969094,-6.013744354,-28.15384483,-34.65957642,-0.01537995227,0.01251769904,0.0009696688503,-0.3741804063,9.827664375,1.279482841 +8.59969094,-8.104663849,-27.15455437,-34.11555481,-0.01356030069,-0.002310473472,-0.002780725248,-0.3859480917,9.867848396,1.242322564 +8.60969094,-6.163772583,-28.3021946,-34.03823853,-0.02265218273,-0.0157774277,-0.00271670986,-0.3495922685,9.865961075,1.364074707 +8.61969094,-7.311927795,-29.65829659,-33.32875061,-0.01418382674,0.0006659564096,-0.005384140648,-0.3531033099,9.862417221,1.514775276 +8.62969094,-4.850357056,-27.32402992,-33.1950531,0.003424083814,0.03531191498,-0.006987604313,-0.4141439497,9.91368103,1.393144369 +8.63969094,-4.049133301,-26.55590248,-34.01454163,0.005545211025,0.02093706839,-0.003268030239,-0.388307482,9.895030975,1.247608185 +8.64969094,-6.623577118,-28.35165977,-33.72845459,-0.0003154044971,-0.02269215137,0.0004618884996,-0.3421718478,9.894777298,1.317503333 +8.65969094,-5.627555847,-27.01146889,-34.28384399,-0.001384953968,-0.04192937911,0.004192750901,-0.3671855927,9.879576683,1.335730672 +8.66969094,-6.40574646,-27.50063515,-33.17903137,0.006623237394,-0.02268284559,0.003680216148,-0.4353603423,9.886388779,1.367411256 +8.67969094,-7.719654083,-28.87966347,-32.92539978,0.01196025219,0.010412395,0.001041296404,-0.3877769411,9.898147583,1.325113058 +8.68969094,-6.09287262,-26.93023872,-34.59381104,0.01560688298,0.03164923191,-0.002251014113,-0.3476009667,9.874887466,1.177082181 +8.69969094,-5.185276031,-27.37165642,-34.25920105,0.007014569826,0.0320574306,-0.006441064179,-0.3255999684,9.796077728,1.179525852 +8.70969094,-5.887241364,-28.68531609,-34.46943665,-0.004665999673,0.02300379425,-0.007558021694,-0.3302562535,9.838270187,1.29654789 +8.71969094,-6.934654236,-28.38698006,-33.72525024,-0.0008134776726,0.01394113898,-0.008628170937,-0.4091928303,9.864145279,1.397688985 +8.72969094,-6.61447525,-27.41221046,-35.93190002,0.01304710936,0.0058097695,-0.01178541407,-0.3761585057,9.922281265,1.419453979 +8.73969094,-7.247840881,-28.28985786,-34.18930054,0.01882565767,-0.01711278409,-0.009561704472,-0.4335117936,9.864617348,1.383633018 +8.74969094,-6.140735626,-28.02323341,-33.75289917,0.01722362638,-0.01998676173,-0.004755612463,-0.3925135732,9.847726822,1.352542996 +8.75969094,-5.692451477,-28.11325264,-34.20532227,0.01413588692,-0.004737165757,-0.003758656094,-0.3975400031,9.840883255,1.424174428 +8.76969094,-6.374908447,-26.41642952,-34.78236389,0.01665059105,0.01422545873,0.001111822668,-0.4121716917,9.825641632,1.308867693 +8.77969094,-6.872554779,-29.61937523,-34.55342102,0.0079627214,0.00683412049,0.008576428518,-0.3998596966,9.780550003,1.15374434 +8.78969094,-8.128192902,-27.8343792,-33.30407715,-0.00830971729,-0.0006057955325,0.0137829408,-0.3708814979,9.795752525,1.336443067 +8.79969094,-6.156959534,-28.29867744,-33.73326111,-0.003319519572,0.03192749619,0.008939841762,-0.3640524149,9.828931808,1.51407516 +8.80969094,-4.970039368,-26.7890377,-33.08021545,0.0151279429,0.08334953338,0.004141870886,-0.40965572,9.86701107,1.33130312 +8.81969094,-6.123214722,-27.6135807,-34.08731079,0.01143238787,0.08572150022,0.005260156002,-0.3747372925,9.777318001,1.236672163 +8.82969094,-5.484832764,-27.26749611,-34.11334229,-0.0006647165865,0.04545711726,0.00868627429,-0.3375897408,9.728914261,1.210252285 +8.83969094,-5.79510498,-27.56947136,-33.32810974,-0.004408819135,0.004282972775,0.01372285746,-0.3406184614,9.721815109,1.191379905 +8.84969094,-6.901714325,-27.43522835,-34.86132812,-0.01009862404,-0.02514038049,0.0141247306,-0.3459747136,9.810904503,1.276666164 +8.85969094,-5.484027863,-27.53415108,-33.33131409,-0.01250343956,-0.009691631421,0.009318539873,-0.3982391655,9.863305092,1.43898356 +8.86969094,-5.594306946,-26.72723579,-33.5410614,-0.005750786047,0.02927107364,0.008384827524,-0.3999786675,9.804018974,1.47284627 +8.87969094,-5.6510849,-27.69129372,-33.47236633,0.001811090857,0.02968214266,0.01073560491,-0.4115684032,9.764409065,1.312410116 +8.88969094,-6.262722015,-29.42155647,-35.48452759,-0.007250172552,0.009495561942,0.01774243638,-0.3444252312,9.76662159,1.203037024 +8.89969094,-5.819438934,-27.98264122,-33.29866028,-0.01225235127,-0.03034259938,0.01879092306,-0.2967675924,9.789928436,1.379716516 +8.90969094,-6.181606293,-28.04432869,-35.58267212,0.003125378862,-0.04652474821,0.0152979847,-0.3290558457,9.828923225,1.316641212 +8.91969094,-7.777553558,-28.90954781,-35.51757812,0.008289773948,-0.05507510155,0.006511804182,-0.3192000985,9.854446411,1.306584358 +8.92969094,-6.726543427,-27.14036369,-34.73008728,0.006072483025,-0.03125399351,-0.001492372714,-0.2490886152,9.842761993,1.329090953 +8.93969094,-6.530639648,-28.49984932,-36.15995789,0.005286928266,0.004717913922,-0.01269114576,-0.2787717581,9.871692657,1.240097642 +8.94969094,-6.156959534,-28.29867744,-33.73326111,0.007722427137,0.00421890663,-0.01890247688,-0.341399461,9.860766411,1.212909102 +8.95969094,-5.195491791,-27.37692833,-34.71665955,-0.0005177315325,-0.02194965258,-0.01977552287,-0.3430656791,9.841578484,1.20852685 +8.96969094,-5.195491791,-27.37692833,-34.71665955,-0.004397911951,-0.01808477193,-0.02489776164,-0.2913993001,9.817040443,1.284796357 +8.97969094,-7.726776123,-28.21565819,-35.10920715,0.002665152773,0.001532091293,-0.02840395272,-0.3493477702,9.777391434,1.309384704 +8.98969094,-7.726776123,-28.21565819,-35.10920715,0.00724685844,-0.006472785026,-0.02936365642,-0.3285253942,9.861621857,1.359132767 +8.99969094,-6.80475235,-28.91670036,-33.38262939,0.007950174622,-0.02342860959,-0.02639209293,-0.4434536099,9.895025253,1.340307117 +9.00969094,-4.424610138,-27.29214287,-35.02961731,0.006259321235,-0.02674228698,-0.02167566307,-0.4321332276,9.925757408,1.370373964 +9.01969094,-5.539627075,-25.63072395,-34.07704163,0.01441753004,-0.003579446115,-0.02427353896,-0.3899079859,9.913691521,1.459052444 +9.02969094,-8.179279327,-27.86074638,-35.59129333,0.02454777062,0.02349377796,-0.02628907003,-0.4051021338,9.94682312,1.378842115 +9.03969094,-4.857170105,-27.32754707,-33.5,0.01992200688,0.02365491912,-0.02259735391,-0.429605186,9.887137413,1.335855365 +9.04969094,-5.644271851,-27.68777657,-33.16741943,0.00781902764,0.003750482807,-0.0154065527,-0.3775611222,9.861557007,1.329430461 +9.05969094,-4.808990479,-26.902071,-32.46209717,0.002876439132,0.002535451669,-0.01032625698,-0.3763277531,9.841564178,1.236484885 +9.06969094,-6.058815002,-26.9126606,-33.06900024,-0.002186387777,0.02572786249,-0.007384793833,-0.3992110789,9.797712326,1.18870461 +9.07969094,-4.641933441,-26.74492836,-32.3210144,-0.01085284352,0.03625140339,7.089274004e-05,-0.3808373511,9.743421555,1.078503132 +9.08969094,-5.76556778,-26.61947441,-34.61665344,-0.03301307559,0.01188983768,0.006888038013,-0.3624552786,9.697451591,1.048014522 +9.09969094,-6.100177765,-27.33461952,-33.80197144,-0.05799226463,-0.02245188132,0.01368184574,-0.3520770371,9.722191811,1.232402325 +9.10969094,-6.076648712,-26.65479469,-34.6134491,-0.05832531676,-0.02201962285,0.01837413386,-0.4136171639,9.800364494,1.344371796 +9.11969094,-7.182945251,-27.18807411,-34.26782227,-0.04887463897,-0.006774947047,0.01895935461,-0.4178379476,9.859547615,1.376471162 +9.12969094,-6.918430328,-28.11153603,-33.74488831,-0.03943052515,-0.01560469344,0.01933539659,-0.4469336271,9.893157959,1.409580708 +9.13969094,-6.422775269,-27.50942421,-33.94143677,-0.03463018686,-0.008655243553,0.01995558664,-0.3561683893,9.847137451,1.53296864 +9.14969094,-5.195491791,-27.37692833,-34.71665955,-0.01544791646,0.02348045073,0.0137829408,-0.3908901513,9.918141365,1.499109507 +9.15969094,-6.600048065,-27.67183495,-34.53993225,0.001441395842,0.03940471262,0.0152179841,-0.4149687886,9.856978416,1.277093053 +9.16969094,-5.989406586,-27.74067497,-34.68902588,-0.006609489676,0.004615615588,0.02280433103,-0.3392545581,9.758050919,1.146429181 +9.17969094,-5.484832764,-27.26749611,-34.11334229,-0.02656558156,-0.03210052848,0.02782632969,-0.2474892884,9.751603127,1.163014531 +9.18969094,-5.819747925,-27.31511879,-35.17750549,-0.02908400446,-0.04421976209,0.02460899949,-0.2521186173,9.782547951,1.296428919 +9.19969094,-5.76556778,-26.61947441,-34.61665344,-0.02535528317,-0.05329878628,0.02000474744,-0.2538339794,9.860228539,1.281494498 +9.20969094,-5.551830292,-28.23682594,-34.5020752,-0.0306779705,-0.04690035433,0.01591363735,-0.259737134,9.867933273,1.315501928 +9.21969094,-6.381721497,-26.41994667,-35.08731079,-0.03282633424,-0.03781130165,0.01075538062,-0.2662158608,9.842382431,1.342554212 +9.22969094,-5.979686737,-28.13626671,-33.13476562,-0.03420222551,-0.0125182569,0.005236934405,-0.2183810771,9.868021965,1.407692909 +9.23969094,-6.100177765,-27.33461952,-33.80197144,-0.02300329134,0.02829145826,-0.00561462203,-0.2840327621,9.901955605,1.485758662 +9.24969094,-4.625217438,-26.06862068,-33.43746948,-0.01911250688,0.04374434054,-0.008972733282,-0.3135461211,9.874196053,1.319430351 +9.25969094,-8.288433075,-27.98800468,-33.1401825,-0.02691411227,0.02197675779,-0.01033035852,-0.299005717,9.850079536,1.237532496 +9.26969094,-6.509208679,-26.6901989,-33.08383179,-0.03162460029,0.001549815293,-0.01018739119,-0.3111849427,9.796510696,1.378709555 +9.27969094,-6.473861694,-27.5357914,-36.22865295,-0.02315361984,-0.002986954525,-0.009323257953,-0.3509652317,9.8478508,1.414524198 +9.28969094,-7.360713959,-27.7513485,-33.76951599,-0.02178758755,-0.01910696179,-0.009859112091,-0.3926713765,9.908628464,1.373457074 +9.29969094,-6.951683044,-28.39576912,-34.48765564,-0.02419984341,-0.01715814695,-0.01178541407,-0.3376707137,9.898147583,1.401946902 +9.30969094,-6.590637207,-27.39990807,-34.86453247,-0.01450248435,0.01003692858,-0.01763336919,-0.3839448094,9.886221886,1.38350749 +9.31969094,-7.36151886,-27.48469353,-34.55155945,-0.01278505847,0.01857505366,-0.01977552287,-0.3988118172,9.814868927,1.286329269 +9.32969094,-6.484874725,-26.27702904,-33.11326599,-0.0293417424,-0.004705166444,-0.01006867364,-0.4420841634,9.828910828,1.158545732 +9.33969094,-6.422775269,-27.50942421,-33.94143677,-0.04848059639,-0.02031212486,-0.001976955682,-0.3750244081,9.772753716,1.376981616 +9.34969094,-6.510704041,-28.89016914,-34.14823914,-0.04332835972,-0.01426587068,-0.00082276389,-0.4337833822,9.7676754,1.373581648 +9.35969094,-7.736190796,-28.48758888,-34.78462219,-0.0376179181,-0.01224911399,0.002211536281,-0.4449275136,9.839629173,1.465065002 +9.36969094,-7.217002869,-27.20565224,-35.79263306,-0.02085287496,-0.03022374399,0.009145841002,-0.4059441388,9.8934021,1.544756889 +9.37969094,-5.001186371,-27.20572472,-33.35574341,-0.01117274538,-0.04885619879,0.01271759532,-0.3361937106,9.898633957,1.443880439 +9.38969094,-6.46333313,-28.1980381,-33.8923645,0.02227536216,-0.04157933593,-0.001544222352,-0.383582741,10.06635571,1.592002034 +9.39969094,-6.45602417,-27.7936573,-34.68421936,0.02985722572,0.008457273245,0.003183291759,-0.4190266728,10.04802799,1.19617939 +9.40969094,-6.100177765,-27.33461952,-33.80197144,0.02161491662,-0.01152697392,0.004680039361,-0.3310981691,9.888607979,1.307484984 +9.41969094,-6.100177765,-27.33461952,-33.80197144,0.02274901047,-0.02205886319,0.003949012142,-0.2644232512,9.895751953,1.281700253 +9.42969094,-8.001693726,-28.36585426,-33.11393738,0.0270957537,0.008429657668,-0.004258564673,-0.3135220706,9.886099815,1.322879076 +9.43969094,-8.001693726,-28.36585426,-33.11393738,0.03116482496,0.03424095362,-0.01175708137,-0.3184842765,9.87495327,1.208269715 +9.44969094,-5.972377777,-27.73188591,-33.92662048,0.0162592046,0.01626186259,-0.0105086565,-0.335937798,9.807515144,1.067642093 +9.45969094,-5.161434174,-27.3593502,-33.19184875,-0.01620616391,-0.01655917056,-0.004570898134,-0.3055139184,9.73563385,1.092912316 +9.46969094,-7.310745239,-26.79080772,-34.14318848,-0.02988763899,-0.03499742597,-0.003262628801,-0.2359486967,9.825713158,1.553684115 +9.47969094,-6.573112488,-26.99025154,-35.19894409,-0.01365076192,-0.01185967028,-0.008056694642,-0.3714031279,9.954780579,1.606928349 +9.48969094,-6.388221741,-27.09098244,-33.51342773,-0.0008522793651,0.03052273393,-0.008053194731,-0.4348410964,9.923695564,1.438187957 +9.49969094,-5.524894714,-27.55524254,-35.16108704,-0.002721569035,0.01259369962,-0.0008508043829,-0.3950788081,9.823553085,1.357761741 +9.50969094,-4.90422821,-28.68719673,-31.87704468,-0.009681989439,-0.01744466461,0.007116397377,-0.3847718537,9.837177277,1.353380203 +9.51969094,-5.419940948,-26.16571236,-34.19186401,-0.01018538047,-0.0034658527,0.009544158354,-0.4182123542,9.833841324,1.346598744 +9.52969094,-7.343997955,-27.07504082,-34.88597107,-0.005646346137,0.01587818377,0.009550519288,-0.3948255181,9.812287331,1.296076536 +9.53969094,-6.710323334,-26.86491966,-34.74971008,-0.006285015494,0.003204530338,0.009770220146,-0.3175331056,9.817604065,1.234001398 +9.54969094,-8.449485779,-27.87497139,-33.75830078,-0.02056121826,-0.02370020375,0.01402841881,-0.310777247,9.741884232,1.292146921 +9.55969094,-5.525390625,-27.95611,-34.06427002,-0.0177536048,-0.04929082096,0.01203878596,-0.3341468275,9.749916077,1.421624541 +9.56969094,-7.351303101,-27.47942162,-34.09411621,-0.005059229676,-0.07262051105,0.01338049769,-0.3519653678,9.822608948,1.402941823 +9.57969094,-6.428779602,-27.77959633,-33.46435547,-0.004816933535,-0.09561718255,0.01041176356,-0.3689287603,9.827497482,1.293786645 +9.58969094,-6.60735321,-28.07621574,-33.74809265,-0.006380548701,-0.09612983465,0.008321847767,-0.2538587749,9.772336006,1.353903651 +9.59969094,-6.934654236,-28.38698006,-33.72525024,0.002941088751,-0.0783181861,-0.002114825416,-0.2645348608,9.775900841,1.470979691 +9.60969094,-7.518360138,-27.63656425,-34.23516846,0.0110929301,-0.0527979359,-0.009588164277,-0.2304200828,9.846688271,1.339077234 +9.61969094,-6.428779602,-27.77959633,-33.46435547,0.00417023059,-0.02648497,-0.01985131018,-0.286404103,9.793714523,1.367927313 +9.62969094,-5.990215302,-27.47402,-35.47105408,0.0009704809636,-0.01529773325,-0.02053293586,-0.3193401098,9.735281944,1.396854877 +9.63969094,-5.915599823,-26.76782799,-33.99531555,0.002660098486,-0.01091999374,-0.02030819841,-0.3742132783,9.726406097,1.408692479 +9.64969094,-5.836467743,-27.99143028,-34.06106567,0.009704994969,0.00570339663,-0.02030819841,-0.396333158,9.782100677,1.37638545 +9.65969094,-5.075801849,-27.91191673,-34.83148193,0.01480308268,0.01089406572,-0.01835904643,-0.383464247,9.801027298,1.287954688 +9.66969094,-7.527767181,-27.90849113,-33.91059875,0.004126793705,-0.01640985347,-0.01433289424,-0.3254973888,9.808070183,1.299425244 +9.67969094,-5.753753662,-28.14489174,-36.94999695,-0.01064739004,-0.03583458811,-0.01477447897,-0.3483581841,9.893923759,1.421264291 +9.68969094,-7.688819885,-27.79545784,-34.52871704,-0.01350821275,-0.01939630695,-0.01784704253,-0.4041688144,9.913567543,1.498895407 +9.69969094,-5.795909882,-27.30281639,-34.11013794,-0.001301977783,0.01890260912,-0.02151327021,-0.4571124613,9.889979362,1.583774805 +9.70969094,-6.037273407,-28.83366966,-33.84809875,0.02016665787,0.03482097015,-0.02001620457,-0.3828421235,9.923582077,1.550175071 +9.71969094,-7.368331909,-27.48821068,-34.85652161,0.02689192817,0.01481494214,-0.01628457569,-0.4274457693,9.955366135,1.418853045 +9.72969094,-5.846683502,-27.99670219,-34.51850891,0.01808453351,0.0007289100904,-0.007230801042,-0.4414436519,9.913903236,1.362712622 +9.73969094,-7.233718872,-27.88195992,-34.6762085,0.006072483025,0.005902021192,-0.00551132299,-0.4905114472,9.913440704,1.355573893 +9.74969094,-7.385047913,-28.16451836,-33.74008179,-0.001548105851,0.03948125988,0.001368286088,-0.3877508342,9.898666382,1.246912837 +9.75969094,-6.509895325,-29.15682411,-33.36621094,-0.01327420678,0.06441155076,-0.002559530549,-0.3439078927,9.951795578,1.185796857 +9.76969094,-7.19997406,-27.19686317,-35.03022766,-0.02978560328,0.05697054416,-0.00752402097,-0.4116650224,9.954465866,1.257131696 +9.77969094,-6.428779602,-27.77959633,-33.46435547,-0.03334539384,0.07115680724,-0.006260839291,-0.5033277869,9.958221436,1.41427815 +9.78969094,-5.668113708,-27.70008278,-34.23477173,-0.02409242094,0.08291181177,-0.003795302706,-0.5041429996,9.987658501,1.488123059 +9.79969094,-5.535114288,-27.56051826,-35.61853027,-0.01179026719,0.08431231976,0.001658534165,-0.5291974545,9.954752922,1.494916201 +9.80969094,-6.589519501,-28.33408165,-32.2036438,0.003343363293,0.07458043098,0.00850411877,-0.4825110435,9.898130417,1.412799358 +9.81969094,-5.997028351,-27.47753716,-35.77600098,0.006553829648,0.05965533108,0.01489961706,-0.4234584868,9.782516479,1.332983136 +9.82969094,-6.059619904,-26.64600563,-33.8510437,0.01331350673,0.04850659519,0.01735352911,-0.3877936006,9.777862549,1.266014695 +9.83969094,-6.503890991,-28.88665199,-33.84329224,0.01824017987,0.03485743701,0.01993710175,-0.3487462103,9.775895119,1.266067386 +9.84969094,-7.19997406,-27.19686317,-35.03022766,0.02160441503,0.02122296393,0.01546534896,-0.311239928,9.760976791,1.272807956 +9.85969094,-6.55657959,-27.38232994,-33.33972168,0.01953481883,0.01390312053,0.01141053811,-0.3209119439,9.78553009,1.278277993 +9.86969094,-5.979190826,-27.73540306,-34.23156738,0.01899946108,0.01229704265,0.006715378258,-0.2684645653,9.816758156,1.335773826 +9.87969094,-7.206474304,-27.86789894,-33.4563446,0.02045468241,0.005114794243,0.002737503033,-0.3115752637,9.813545227,1.378985286 +9.88969094,-7.206474304,-27.86789894,-33.4563446,0.02057313174,-0.009556769393,0.003524060361,-0.2928525209,9.85481739,1.433280349 +9.89969094,-5.709480286,-28.1220417,-34.96772766,0.02471607178,-0.02593976445,-0.000195460394,-0.289963156,9.882286072,1.459529281 +9.90969094,-5.709480286,-28.1220417,-34.96772766,0.0349977091,-0.03940877318,-6.658444181e-05,-0.3231247663,9.801194191,1.448393464 +9.91969094,-6.100177765,-27.33461952,-33.80197144,0.04312978685,-0.04885619879,0.0009987638332,-0.32735309,9.733083725,1.397290349 +9.92969094,-5.444274902,-26.57888222,-34.16241455,0.05012412742,-0.04050931334,-0.001043979544,-0.2132692337,9.657939911,1.322166562 +9.93969094,-6.124019623,-27.34692574,-34.86933899,0.05335898697,-0.02532506175,-0.005103618838,-0.2339841872,9.683965683,1.298429251 +9.94969094,-5.822349548,-27.58353233,-34.54794312,0.04862793908,-0.005474109203,-0.009122043848,-0.2713183165,9.604672432,1.183119655 +9.95969094,-5.028430939,-27.21978569,-34.57559204,0.03629063815,-0.004140705802,-0.01083520055,-0.2865132093,9.572835922,1.108448267 +9.96969094,-6.019748688,-28.42401695,-34.18249512,0.01961249486,0.02772274427,-0.009543137625,-0.2350679785,9.573115349,1.162874699 +9.97969094,-5.610527039,-27.00267982,-33.5214386,0.008852970786,0.05212645978,-0.01561361179,-0.2903158665,9.632687569,1.220809221 +9.98969094,-4.843048096,-26.91964912,-33.98690796,-0.006395697594,0.07004146278,-0.01498145796,-0.3082864285,9.775126457,1.420338035 +9.99969094,-6.510704041,-28.89016914,-34.14823914,-0.01554161683,0.08230477571,-0.01358819567,-0.3808072507,9.88078022,1.6931777 +10.00969094,-5.558643341,-28.24034309,-34.80705261,-0.01750100963,0.09559768438,-0.003126793541,-0.4694359601,9.878305435,1.681256294 +10.01969094,-5.948848724,-27.05206108,-34.73809814,-0.01223013736,0.0947631076,0.01301613264,-0.520768702,9.835087776,1.616311669 +10.02969094,-5.524585724,-28.22276497,-33.28224182,-0.01229349431,0.08211163431,0.03143116459,-0.5171135068,9.89792347,1.515195131 +10.03969094,-5.634864807,-27.41584969,-33.49198914,-0.02304166183,0.07002359629,0.04795950279,-0.4607204795,9.916221619,1.462448239 +10.04969094,-5.86290741,-28.27214622,-34.49887085,-0.03202089667,0.06247267872,0.05862831324,-0.3976040781,9.907452583,1.388126731 +10.05969094,-5.812625885,-27.97912407,-32.99369812,-0.04750698432,0.0630890578,0.06102353334,-0.3217801452,9.843457222,1.314891696 +10.06969094,-6.058815002,-26.9126606,-33.06900024,-0.04895600677,0.05808627605,0.05451076478,-0.3295866549,9.846390724,1.25166893 From c418f4ab5295f304cc643e21aa7a82b6e30ba21e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 26 Aug 2024 14:04:15 +0800 Subject: [PATCH 072/213] update test script * dev2. --- applications/newton/llvm-ir/testMadgwick.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 27dddd3bc..fede1cef0 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -53,4 +53,4 @@ clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c # Step 10: Run the test executable echo "Step 10: Run the test executable" -/home/xyf/CoSense/applications/newton/llvm-ir/main_out +$USER_HOME/CoSense/applications/newton/llvm-ir/main_out From ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 26 Aug 2024 14:06:29 +0800 Subject: [PATCH 073/213] update test script * dev2. --- applications/newton/llvm-ir/run.sh | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/applications/newton/llvm-ir/run.sh b/applications/newton/llvm-ir/run.sh index 069d11f8e..f187ebab6 100755 --- a/applications/newton/llvm-ir/run.sh +++ b/applications/newton/llvm-ir/run.sh @@ -27,4 +27,4 @@ clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c # Step 10: Run the test executable echo "Step 10: Run the test executable" -/home/xyf/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file +$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file From 954fc8b6effd6620455fb3b51fcd181ff5eee72e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 13:05:59 +0800 Subject: [PATCH 074/213] update add ,sub with nsw * dev2. --- src/newton/newton-irPass-LLVMIR-quantization.cpp | 6 ++++-- 1 file changed, 4 insertions(+), 2 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 4301377a2..5f6ea6b7b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1773,7 +1773,8 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point addition - Value * newInst = Builder.CreateAdd(op0, op1); + //Value * newInst = Builder.CreateAdd(op0, op1); + Value *newInst = Builder.CreateNSWAdd(op0, op1, "nswadd"); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -1838,7 +1839,8 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point subtraction - Value * newInst = Builder.CreateSub(op0, op1); + //Value * newInst = Builder.CreateSub(op0, op1); + Value *newInst = Builder.CreateNSWSub(op0, op1, "nswsub"); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); From 035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 13:50:39 +0800 Subject: [PATCH 075/213] update mul nsw * dev2. --- ...a839299d6958488ea11f28a6d4b76d3d5496b0.txt | 48 ++++++++++++ ...18f4ab5295f304cc643e21aa7a82b6e30ba21e.txt | 48 ++++++++++++ ...c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt | 48 ++++++++++++ ...60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt | 48 ++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 77 ++++--------------- 5 files changed, 208 insertions(+), 61 deletions(-) create mode 100644 analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt create mode 100644 analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt create mode 100644 analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt create mode 100644 analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt diff --git a/analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt b/analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt new file mode 100644 index 000000000..d7d3d8895 --- /dev/null +++ b/analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt @@ -0,0 +1,48 @@ + +changeset: 1666:6ca839299d6958488ea11f28a6d4b76d3d5496b0 +char kNewtonVersion[] = "0.3-alpha-1666 (6ca839299d6958488ea11f28a6d4b76d3d5496b0) (build 08-26-2024-14:04-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt b/analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt new file mode 100644 index 000000000..d0d2e411b --- /dev/null +++ b/analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt @@ -0,0 +1,48 @@ + +changeset: 1667:c418f4ab5295f304cc643e21aa7a82b6e30ba21e +char kNewtonVersion[] = "0.3-alpha-1667 (c418f4ab5295f304cc643e21aa7a82b6e30ba21e) (build 08-26-2024-14:06-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt b/analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt new file mode 100644 index 000000000..212410fe2 --- /dev/null +++ b/analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt @@ -0,0 +1,48 @@ + +changeset: 1665:c4c45e21839a99f707dcde8a2fae9fd2cc5254f2 +char kNewtonVersion[] = "0.3-alpha-1665 (c4c45e21839a99f707dcde8a2fae9fd2cc5254f2) (build 08-26-2024-13:49-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt b/analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt new file mode 100644 index 000000000..3b5acafff --- /dev/null +++ b/analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt @@ -0,0 +1,48 @@ + +changeset: 1668:ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58 +char kNewtonVersion[] = "0.3-alpha-1668 (ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58) (build 08-27-2024-13:06-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 5f6ea6b7b..e5a14f9cb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -37,6 +37,7 @@ using namespace llvm; #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 + extern "C" { // check if the type is a floating type @@ -997,7 +998,8 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) if (instOpCode == Instruction::FMul) { llvm::errs() << "Handling FMul instruction\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + //newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newFisrtInst = Builder.CreateNSWMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) @@ -1308,7 +1310,9 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector(inInstruction->getOperand(1))) { llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + //newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newFirstInst = Builder.CreateNSWMul(nonConstOperand, compensateNumValue); newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) @@ -1774,7 +1780,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) // Create fixed-point addition //Value * newInst = Builder.CreateAdd(op0, op1); - Value *newInst = Builder.CreateNSWAdd(op0, op1, "nswadd"); + Value *newInst = Builder.CreateNSWAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -1840,7 +1846,7 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) // Create fixed-point subtraction //Value * newInst = Builder.CreateSub(op0, op1); - Value *newInst = Builder.CreateNSWSub(op0, op1, "nswsub"); + Value *newInst = Builder.CreateNSWSub(op0, op1 ); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -1963,7 +1969,8 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix if (isa(lhs) || isa(rhs)) { llvm::errs() << "One of the operands is an integer constant, using mul\n"; - Value * newInst = Builder.CreateMul(lhs, rhs); + //Value * newInst = Builder.CreateMul(lhs, rhs); + Value * newInst = Builder.CreateNSWMul(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); return; @@ -2002,58 +2009,7 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix // } } -// void -// handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) -//{ -// llvm::errs() << "Handling FMul\n"; -// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; -// IRBuilder<> Builder(llvmIrInstruction); -// -// // Ensure operands are correctly converted to fixed-point integers -// Value * lhs = llvmIrInstruction->getOperand(0); -// Value * rhs = llvmIrInstruction->getOperand(1); -// -// llvm::errs() << "LHS: " << *lhs << "\n"; -// llvm::errs() << "RHS: " << *rhs << "\n"; -// -// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); -// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); -// -// // If either operand is a float, convert both to fixed-point -// if (lhsIsFloat) -// { -// lhs = Builder.CreateFPToSI(lhs, quantizedType); -// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; -// } -// if (rhsIsFloat) -// { -// rhs = Builder.CreateFPToSI(rhs, quantizedType); -// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; -// } -// -// // Ensure both operands are now integers -// bool lhsIsInteger = lhs->getType()->isIntegerTy(); -// bool rhsIsInteger = rhs->getType()->isIntegerTy(); -// -// if (lhsIsInteger && rhsIsInteger) -// { -// if (isa(lhs) || isa(rhs)) -// { -// llvm::errs() << "One of the operands is a constant, simplifying...\n"; -// handleConstant(llvmIrInstruction, quantizedType); -// } -// else -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); -// llvmIrInstruction->replaceAllUsesWith(callInst); -// llvmIrInstruction->eraseFromParent(); -// } -// } -// } -// } + void handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixdiv) @@ -2097,9 +2053,8 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix } else { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::errs() << "Both operands are integers, substituting with fixdiv function...\n"; { - // Value * newInst = Builder.CreateMul(lhs, rhs); Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); From d15d558addacea96e02861eb25ec57a34d279064 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 13:52:37 +0800 Subject: [PATCH 076/213] update mul nsw * dev2. --- ...4fc8b6effd6620455fb3b51fcd181ff5eee72e.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt diff --git a/analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt b/analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt new file mode 100644 index 000000000..18ab1ddfd --- /dev/null +++ b/analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt @@ -0,0 +1,48 @@ + +changeset: 1669:954fc8b6effd6620455fb3b51fcd181ff5eee72e +char kNewtonVersion[] = "0.3-alpha-1669 (954fc8b6effd6620455fb3b51fcd181ff5eee72e) (build 08-27-2024-13:50-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From c4f46e8a0edb511b8ee8b4fb950213ba443d1c78 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 14:57:44 +0800 Subject: [PATCH 077/213] update some fmul case * dev2. --- ...5ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt | 48 +++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 99 +++++++++++++++++-- 2 files changed, 137 insertions(+), 10 deletions(-) create mode 100644 analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt diff --git a/analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt b/analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt new file mode 100644 index 000000000..d98e9a8df --- /dev/null +++ b/analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt @@ -0,0 +1,48 @@ + +changeset: 1670:035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8 +char kNewtonVersion[] = "0.3-alpha-1670 (035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8) (build 08-27-2024-13:52-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e5a14f9cb..cda8b1a34 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -47,6 +47,26 @@ isValidFloatingType(Type * type) return type->isFloatTy() || type->isDoubleTy(); } +bool checkForSpecialFloat(BinaryOperator *MulInst) { + // 确保指令是乘法 + if (MulInst->getOpcode() == Instruction::FMul) { + Value *lhs = MulInst->getOperand(0); + Value *rhs = MulInst->getOperand(1); + + // 检查右侧操作数是否为ConstantFP + if (ConstantFP *rhsConst = dyn_cast(rhs)) { + // 创建特定的浮点值 + APFloat targetValue = APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000)); + + // 比较右侧操作数是否等于0x3FA24924A0000000 + if (rhsConst->getValueAPF().bitwiseIsEqual(targetValue)) { + return true; // 发现目标浮点数 + } + } + } + return false; // 未发现目标浮点数 +} + bool isSpecialIEEE754(llvm::Value * value, llvm::IRBuilder<> & builder) { @@ -1941,6 +1961,68 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + + ConstantFP *lhsConst = dyn_cast(lhs); + ConstantFP *rhsConst = dyn_cast(rhs); + + // 检查是否存在浮点数常数0.5 + bool lhsIsHalf = lhsConst && lhsConst->getValueAPF().convertToDouble() == 0.5; + bool rhsIsHalf = rhsConst && rhsConst->getValueAPF().convertToDouble() == 0.5; + + //TODO HARDCODE + if (lhsIsHalf || rhsIsHalf) { + llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using sdiv by 2\n"; + + Value *otherOperand = lhsIsHalf ? rhs : lhs; + Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); + + // 创建常数2 + Value *two = ConstantInt::get(intType, 2); + + // 创建除法指令 + Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand, two, "divbytwo", llvmIrInstruction); + //Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand,two); + // 替换原来的乘法指令 + llvmIrInstruction->replaceAllUsesWith(divInst); + llvmIrInstruction->eraseFromParent(); + + return; // 防止后续代码执行 + } + + // 检查是否有乘以特定浮点数的操作,即乘以 0x3FA24924A0000000(大约等于 1/28) + //bool rhsIsDiv28 = lhsConst && lhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); + //bool lhsIsDiv28 = rhsConst && rhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); + + + bool rhsIsDiv28 = lhsConst && lhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); + bool lhsIsDiv28 = rhsConst && rhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); + + if (lhsIsDiv28 || rhsIsDiv28) { + llvm::errs() << "One operand is a floating-point constant 0x3FA24924A0000000, simplifying using sdiv by 28\n"; + + // 获取另一个操作数 + Value *otherOperand = lhsIsDiv28 ? rhs : lhs; + IRBuilder<> Builder(llvmIrInstruction); + + // 创建浮点型常数 28.0 + Value *twentyEight = ConstantFP::get(llvmIrInstruction->getContext(), APFloat(28.0)); + + // 创建除法指令 + Value *divInstValue = Builder.CreateFDiv(otherOperand, twentyEight, "divFrequency"); + Instruction *divInst = cast(divInstValue); // 将 Value* 转换为 Instruction* + + // 替换原来的乘法指令 + llvmIrInstruction->replaceAllUsesWith(divInst); + llvmIrInstruction->eraseFromParent(); + + return; // 防止后续代码执行 + } + + + + + // If either operand is a float constant, convert it to fixed-point using handleConstant if (isa(lhs)) { @@ -1960,10 +2042,7 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion } - // if (isa(llvmIrInstruction->getOperand(0)) || - // isa(llvmIrInstruction->getOperand(1))) { - // simplifyConstant(llvmIrInstruction, quantizedType); - // } + // If either operand is an integer constant, directly use mul if (isa(lhs) || isa(rhs)) @@ -2001,12 +2080,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvmIrInstruction->eraseFromParent(); } - // if (allOperandsAreInt && llvmIrInstruction->getOpcode() == Instruction::FMul) { - // llvm::errs() << "Both operands are integers, converting FMul to Mul\n"; - // Value *mulInst = Builder.CreateMul(lhs, rhs); - // llvmIrInstruction->replaceAllUsesWith(mulInst); - // llvmIrInstruction->eraseFromParent(); - // } } @@ -2028,6 +2101,12 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + + + + + // If either operand is a float, convert both to fixed-point if (lhsIsFloat) { From d323aa59ba738ab8a2f71713088a49f77b2d758e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 16:29:52 +0800 Subject: [PATCH 078/213] update Makefile * dev2. --- .../newton/llvm-ir/performance_test/Makefile | 44 +++++++------------ 1 file changed, 17 insertions(+), 27 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index fc20ab602..d7724f3be 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -11,7 +11,7 @@ CHStone_DIR = ../CHStone_test/ NEWTON_BIN_DIR = ../../../../src/newton CC = clang -OUT_FILE = out.llperformace +OUT_FILE = out.ll OUT_BC = out.bc OUT_S = out.s OUT_OBJ = out.o @@ -19,11 +19,9 @@ OUT_LIB = libout.a # DEBUG_MODE=true make ... ifdef DEBUG_MODE -#CC_OPT_LEVEL = -O0 -g -CC_OPT_LEVEL = -O0 -g -fno-builtin +CC_OPT_LEVEL = -O0 -g else -#CC_OPT_LEVEL = -O3 -Os -CC_OPT_LEVEL = -O3 -Os -g -fno-builtin +CC_OPT_LEVEL = -O3 -Os endif # CROSS_COMPILE=true make ... @@ -59,7 +57,7 @@ endif endif # ENABLE_OVERLOAD=true ENABLE_BUILTIN_ASSUME=true make ... -NEWTON_FLAG=--llvm-ir-liveness-check --llvm-ir-auto-quantization +NEWTON_FLAG=--llvm-ir-liveness-check ifdef ENABLE_OVERLOAD NEWTON_FLAG+=--llvm-ir-enable-overload endif @@ -264,7 +262,7 @@ compile_arm_sqrt_q15: $(call compile_main_fn,ARM_SQRT_Q15) madgwick_non_opt: -ifdef AUTO_FIX_QUANT +ifdef AUTO_QUANT $(call max_opt_fn,MadgwickAHRSfix) else ifdef SOFT_FLOAT_LIB $(call max_opt_fn,MadgwickAHRS_softfloat) @@ -274,9 +272,9 @@ else endif madgwick_opt: - cd $(NEWTON_BIN_DIR) && $(call newton_opt_fn,MadgwickAHRS) - llvm-dis ../MadgwickAHRS_output.bc - $(call max_opt_fn,MadgwickAHRS_output) + cd $(NEWTON_BIN_DIR) && $(call newton_opt_fn,MadgwickAHRSfix) + llvm-dis ../MadgwickAHRSfix_output.bc + $(call max_opt_fn,MadgwickAHRSfix_output) inferBoundControlFlow_non_opt: $(call max_opt_fn,inferBoundControlFlow) @@ -295,17 +293,17 @@ perf_exp_opt: clean make_ll exp_opt compile_lib compile_exp perf_log: clean make_ll log_non_opt compile_lib compile_log -perf_log_opt: clean make_ll log_opt compile_lib compile_log +perf_log_opt: clean make_ll log_opt compile_lib compile_log -perf_acosh: clean make_ll acosh_non_opt compile_lib compile_acosh +perf_acosh: clean make_ll acosh_non_opt compile_lib compile_acosh -perf_acosh_opt: clean make_ll acosh_opt compile_lib compile_acosh +perf_acosh_opt: clean make_ll acosh_opt compile_lib compile_acosh -perf_j0: clean make_ll j0_non_opt compile_lib compile_j0 +perf_j0: clean make_ll j0_non_opt compile_lib compile_j0 -perf_j0_opt: clean make_ll j0_opt compile_lib compile_j0 +perf_j0_opt: clean make_ll j0_opt compile_lib compile_j0 -perf_y0: clean make_ll y0_non_opt compile_lib compile_y0 +perf_y0: clean make_ll y0_non_opt compile_lib compile_y0 perf_y0_opt: clean make_ll y0_opt compile_lib compile_y0 @@ -345,7 +343,7 @@ perf_benchmark_suite_double_opt: clean make_ll benchmark_suite_opt compile_lib c perf_benchmark_suite_float: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_float -perf_benchmark_suite_float_opt: clean make_ll benchmark_suite_opt compile_lib compile_benchmark_suite_floa et +perf_benchmark_suite_float_opt: clean make_ll benchmark_suite_opt compile_lib compile_benchmark_suite_float perf_benchmark_suite_asuint: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_asuint @@ -353,7 +351,7 @@ perf_benchmark_suite_asuint_opt: clean make_ll benchmark_suite_opt compile_lib c perf_benchmark_suite_quant: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_quant -perf_benchmark_suite_fixedpoint: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_fixe ed_point +perf_benchmark_suite_fixedpoint: clean make_ll benchmark_suite_non_opt compile_lib compile_benchmark_suite_fixed_point perf_madgwick: clean make_ll madgwick_non_opt compile_lib compile_test_madgwick @@ -376,17 +374,9 @@ auto_test_compile: all: default - - - default: perf_exp perf_exp_opt perf_log perf_log_opt perf_acosh perf_acosh_opt perf_j0 perf_j0_opt perf_y0 perf_y0_opt perf_rem_pio2 perf_rem_pio2_opt perf_sincosf perf_sincosf_opt perf_float64_add perf_float64_add_opt perf_float64_div perf_float64_div_opt perf_float64_mul perf_float64_mul_opt perf_float64_sin perf_float64_sin_opt perf_arm_sqrt_q15 perf_arm_sqrt_q15_opt auto_test_compile clean: $(QUIET)rm -f *.ll *.o *.s *.txt out.* libout.a main_out auto_test cd $(CHStone_DIR) && $(MAKE_CLEAN) - cd $(SUBDIR) && $(MAKE_CLEAN) - - - - - + cd $(SUBDIR) && $(MAKE_CLEAN) \ No newline at end of file From ac1bad9ede204cbcaf22ae227319f98635350c8f Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 21:39:04 +0800 Subject: [PATCH 079/213] update test script * dev2. --- applications/newton/llvm-ir/run.sh | 6 +++--- applications/newton/llvm-ir/testMadgwick.sh | 10 ++++++---- applications/newton/llvm-ir/testOriginal.sh | 6 +++--- 3 files changed, 12 insertions(+), 10 deletions(-) diff --git a/applications/newton/llvm-ir/run.sh b/applications/newton/llvm-ir/run.sh index f187ebab6..3ceaac273 100755 --- a/applications/newton/llvm-ir/run.sh +++ b/applications/newton/llvm-ir/run.sh @@ -3,11 +3,11 @@ # Get the current user's home directory USER_HOME=$HOME -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" -llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly echo "Step 6: Compile the bitcode file to assembly" @@ -23,7 +23,7 @@ ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSens # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D INT_DATA_TYPE -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index fede1cef0..b0af6dad9 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -7,7 +7,7 @@ FILE_PATH="$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" #Step 1: Generate LLVM IR file echo "Step 1: Generate LLVM IR file" -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c #clang -g -O0 -Xclang -disable-O0-optnone -fno-math-errno -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -29,11 +29,13 @@ python3 replace.py # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll + + # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" -llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly echo "Step 6: Compile the bitcode file to assembly" @@ -49,7 +51,7 @@ ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSens # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D INT_DATA_TYPE -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index 0b5ae535d..c73083bc0 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -2,14 +2,14 @@ USER_HOME=$HOME # Step 1: Generate LLVM IR file -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c #clang -O0 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 4: Optimize the generated LLVM IR file -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --simplifycfg --instsimplify -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.llperformace +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode -llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $HOME/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s From fd0d35fca0fc55e00c998de3cf02138684d02c1e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 27 Aug 2024 21:46:08 +0800 Subject: [PATCH 080/213] update test file * dev2. --- .../llvm-ir/c-files/test_MadgwickAHRS.c | 631 +++++++----------- 1 file changed, 245 insertions(+), 386 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c index ab5b0fcc8..fa47a7e9f 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -1,411 +1,270 @@ -#include -#include +/* +* Madgwick test case (run locally) +* Compilation command in applications/newton/llvm-ir/performance_test/Makefile +* +* How to compile and run? +* 1. `make perf_madgwick` FP hardware (by default) +* 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) +* 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) +* 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format +* */ + #include +#include #include -#include #include #include #include -#define FRAC_Q 10 - -#define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 32 - +#if defined(INT_DATA_TYPE) +extern volatile int32_t q0, q1, q2, q3; +//#include "c-files/MadgwickAHRSfix.h" +#elif defined(FP_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#if defined(SOFT_FLOAT_LIB) +#include "c-files/MadgwickAHRS_softfloat.h" +#else +#include "c-files/MadgwickAHRS.h" +#endif + +#else +#error "Must set data type: FP or INT" +#endif + +#define DATA_SIZE 1000 +#define FRAC_Q 10 +#define FRAC_BASE (1<current = stop - p_tbl->start; -// if (p_tbl->max < p_tbl->current) { -// p_tbl->max = p_tbl->current; -// } -// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { -// p_tbl->min = p_tbl->current; -// } -//} +* Timer functions of the test framework +***************************************/ typedef struct timespec timespec; -timespec -diff(timespec start, timespec end) +timespec diff(timespec start, timespec end) { - timespec temp; - if ((end.tv_nsec - start.tv_nsec) < 0) - { - temp.tv_sec = end.tv_sec - start.tv_sec - 1; - temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; - } - else - { - temp.tv_sec = end.tv_sec - start.tv_sec; - temp.tv_nsec = end.tv_nsec - start.tv_nsec; - } - return temp; + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + return temp; } -timespec -sum(timespec t1, timespec t2) -{ - timespec temp; - if (t1.tv_nsec + t2.tv_nsec >= 1000000000) - { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } - else - { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; +timespec sum(timespec t1, timespec t2) { + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } else { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; } -void -printTimeSpec(timespec t, const char * prefix) -{ - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); -} - -// Quantization function -int -quantize(float value, int frac_base) -{ - return round(value * frac_base); +void printTimeSpec(timespec t, const char* prefix) { + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); } -// Dequantization function -float -dequantize(int quantized_value) +timespec tic( ) { - return (float)(quantized_value >> FRAC_Q); + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; } -QuantizedValues -quantize4(float value1, float value2, float value3, float value4, int frac_base) +timespec toc( timespec* start_time, const char* prefix ) { - QuantizedValues result; - result.quantized1 = round(value1 * frac_base); - result.quantized2 = round(value2 * frac_base); - result.quantized3 = round(value3 * frac_base); - result.quantized4 = round(value4 * frac_base); - return result; + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff( *start_time, current_time ); + printTimeSpec(time_consump, prefix ); + *start_time = current_time; + return time_consump; } -DequantizedValues -dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) -{ - DequantizedValues result; - result.dequantized1 = (float)(quantized1 >> FRAC_Q); - result.dequantized2 = (float)(quantized2 >> FRAC_Q); - result.dequantized3 = (float)(quantized3 >> FRAC_Q); - result.dequantized4 = (float)(quantized4 >> FRAC_Q); - return result; +int main() { + FILE* fp = fopen("input.csv", "r"); + + if (!fp) { + printf("Can't open file\n"); + return -1; + } + + double time[DATA_SIZE]; +#if defined(INT_DATA_TYPE) + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) { + q0[i] = (0.64306622f*FRAC_BASE); + q1[i] = (0.02828862f*FRAC_BASE); + q2[i] = (-0.00567953f*FRAC_BASE); + q3[i] = (-0.76526684f*FRAC_BASE); + } + + int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; +#elif defined(FP_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; +#else +#error "Must set data type: FP or INT" +#endif + + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) { + column = 0; + row++; + + if (row == 1) + continue; + + char* value = strtok(buffer, ", "); + + while (value) { + switch (column) { + case 0: + time[row-2] = atof(value); + break; + case 1: +#if defined(INT_DATA_TYPE) + mag_x[row-2] = round(atof(value)*FRAC_BASE); +#elif defined(FP_DATA_TYPE) + mag_x[row-2] = atof(value); +#endif + break; + case 2: +#if defined(INT_DATA_TYPE) + mag_y[row-2] = round(atof(value)*FRAC_BASE); +#elif defined(FP_DATA_TYPE) + mag_y[row-2] = atof(value); +#endif + break; + case 3: +#if defined(INT_DATA_TYPE) + mag_z[row-2] = round(atof(value)*FRAC_BASE); +#elif defined(FP_DATA_TYPE) + mag_z[row-2] = atof(value); +#endif + break; + case 4: +#if defined(INT_DATA_TYPE) + gyr_x[row-2] = round(atof(value)*FRAC_BASE)*61; +#elif defined(FP_DATA_TYPE) + gyr_x[row-2] = atof(value)*61; +#endif + break; + case 5: +#if defined(INT_DATA_TYPE) + gyr_y[row-2] = round(atof(value)*FRAC_BASE)*61; +#elif defined(FP_DATA_TYPE) + gyr_y[row-2] = atof(value)*61; +#endif + break; + case 6: +#if defined(INT_DATA_TYPE) + gyr_z[row-2] = round(atof(value)*FRAC_BASE)*61; +#elif defined(FP_DATA_TYPE) + gyr_z[row-2] = atof(value)*61; +#endif + break; + case 7: +#if defined(INT_DATA_TYPE) + acc_x[row-2] = round(atof(value)*FRAC_BASE)*2; +#elif defined(FP_DATA_TYPE) + acc_x[row-2] = atof(value)*2; +#endif + break; + case 8: +#if defined(INT_DATA_TYPE) + acc_y[row-2] = round(atof(value)*FRAC_BASE)*2; +#elif defined(FP_DATA_TYPE) + acc_y[row-2] = atof(value)*2; +#endif + break; + case 9: +#if defined(INT_DATA_TYPE) + acc_z[row-2] = round(atof(value)*FRAC_BASE)*2; +#elif defined(FP_DATA_TYPE) + acc_z[row-2] = atof(value)*2; +#endif + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } + + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) { + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); + +#if defined(FP_DATA_TYPE) + FILE *fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); +#elif defined(INT_DATA_TYPE) + FILE *fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, (double)q0[ts]/FRAC_BASE, + ts, (double)q1[ts]/FRAC_BASE, + ts, (double)q2[ts]/FRAC_BASE, + ts, (double)q3[ts]/FRAC_BASE); + } + fclose(fptr); +// printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", +// DISPLAY_INT(q0), DISPLAY_FRAC(q0), +// DISPLAY_INT(q1), DISPLAY_FRAC(q1), +// DISPLAY_INT(q2), DISPLAY_FRAC(q2), +// DISPLAY_INT(q3), DISPLAY_FRAC(q3)); +#endif + return 0; } - -extern int perform_addition(float a, float b); - -timespec -tic() -{ - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; -} - -timespec -toc(timespec * start_time, const char * prefix) -{ - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - timespec time_consump = diff(*start_time, current_time); - printTimeSpec(time_consump, prefix); - *start_time = current_time; - return time_consump; -} - - int - main() -{ - FILE * fp = fopen("input.csv", "r"); - - if (!fp) - { - printf("Can't open file\n"); - return -1; - } - double time[DATA_SIZE]; - - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) - { - q0[i] = (0.64306622f * FRAC_BASE); - q1[i] = (0.02828862f * FRAC_BASE); - q2[i] = (-0.00567953f * FRAC_BASE); - q3[i] = (-0.76526684f * FRAC_BASE); - } - - int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; - - char buffer[1024]; - int row = 0, column = 0; - while (fgets(buffer, 1024, fp)) - { - column = 0; - row++; - - if (row == 1) - continue; - - char * value = strtok(buffer, ", "); - - while (value) - { - switch (column) - { - case 0: - time[row - 2] = atof(value); - break; - case 1: - mag_x[row - 2] = round(atof(value) * FRAC_BASE); - break; - case 2: - mag_y[row - 2] = round(atof(value) * FRAC_BASE); - break; - case 3: - mag_z[row - 2] = round(atof(value) * FRAC_BASE); - break; - case 4: - gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; - break; - case 5: - gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; - break; - case 6: - gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; - break; - case 7: - acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; - break; - case 8: - acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; - break; - case 9: - acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; - break; - default: - break; - } - - value = strtok(NULL, ", "); - column++; - } - } - - fclose(fp); - - u_int64_t time_slots[ITERATION]; - - for (size_t idx = 0; idx < ITERATION; idx++) { - //elapsed_time_start(idx); // 开始计时 - timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - - - - } - //elapsed_time_stop(idx); // 结束计时 - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - } - - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); - -// // 打印出每次迭代的最大、最小和当前时间 -// for (size_t idx = 0; idx < ITERATION; idx++) { -// printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", -// idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); -// } - - FILE *fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, (double)q0[ts]/FRAC_BASE, - // ts, (double)q1[ts]/FRAC_BASE, - // ts, (double)q2[ts]/FRAC_BASE, - // ts, (double)q3[ts]/FRAC_BASE); - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, (double)q0[ts]/FRAC_BASE, - ts, (double)q1[ts]/FRAC_BASE, - ts, (double)q2[ts]/FRAC_BASE, - ts, (double)q3[ts]/FRAC_BASE); - } - fclose(fptr); - return 0; - } - - -//int -//main() -//{ -// -// -// { -// /* -// * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) -// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -// */ -// -// float num1 = -7.249095917; -// float num2 = 26.43893433; -// float num3 = -37.16656494; -// float num4 = -0.1184487343; -// float num5 = 0.001258035656; -// float num6 = 0.008988874033; -// float num7 = -0.3570917249; -// float num8 = 0.3941296637; -// float num9 = 9.963726044; -// -// float num10 = 0.64306622; -// float num11 = 0.02828862; -// float num12 = -0.00567953; -// float num13 = -0.76526684; -// -// int32_t mag_x = quantize(num1, FRAC_BASE); -// int32_t mag_y = quantize(num2, FRAC_BASE); -// int32_t mag_z = quantize(num3, FRAC_BASE); -// int32_t gyr_x = quantize(num4, FRAC_BASE); -// int32_t gyr_y = quantize(num5, FRAC_BASE); -// int32_t gyr_z = quantize(num6, FRAC_BASE); -// int32_t acc_x = quantize(num7, FRAC_BASE); -// int32_t acc_y = quantize(num8, FRAC_BASE); -// int32_t acc_z = quantize(num9, FRAC_BASE); -// int32_t q0 = quantize(num10, FRAC_BASE); -// int32_t q1 = quantize(num11, FRAC_BASE); -// int32_t q2 = quantize(num12, FRAC_BASE); -// int32_t q3 = quantize(num13, FRAC_BASE); -// // float q0 = num10; -// // float q1 = num11; -// // float q2 = num12; -// // float q3 = num13; -// -// // result -// int num14 = 657; -// int num15 = 28; -// int num16 = -6; -// int num17 = -785; -// -// u_int64_t time_slots[ITERATION]; -// -// for (size_t idx = 0; idx < ITERATION; idx++) -// { -// timespec timer = tic(); -// -// for (size_t ts = 0; ts < DATA_SIZE; ts++) -// { -// MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], -// acc_x[ts], acc_y[ts], acc_z[ts], -// mag_x[ts], mag_y[ts], mag_z[ts], -// &q0[ts], &q1[ts], &q2[ts], &q3[ts]); -// } -// } -// -// // MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, -// // acc_x, acc_y, acc_z, -// // mag_x, mag_y, mag_z, -// // &q0, &q1, &q2, &q3); -// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; -// -// -// -// // tic toc quantize4耗时 -// timespec timer = tic(); -// QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); -// timespec durationQuantize = toc(&timer, "Quantize"); -// printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); -// -// // tic toc dequantize4耗时 -// timer = tic(); -// DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); -// timespec durationDequantize = toc(&timer, "Dequantize"); -// printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); -// -// u_int64_t average_time = 0; -// for (size_t idx = 0; idx < ITERATION; idx++) -// { -// average_time += time_slots[idx]; -// } -// average_time /= ITERATION; -// printf("average time = %lu nm\n", average_time); -// -// // 计算total time, quantize+dequantize+average -// u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; -// printf("total time = %lu nm\n", totalTime); -// } -// -//} \ No newline at end of file From d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 28 Aug 2024 10:34:30 +0800 Subject: [PATCH 081/213] save before change * dev2. --- ...1bad9ede204cbcaf22ae227319f98635350c8f.txt | 48 ++++ ...f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt | 48 ++++ ...5d558addacea96e02861eb25ec57a34d279064.txt | 48 ++++ ...23aa59ba738ab8a2f71713088a49f77b2d758e.txt | 48 ++++ .../newton-irPass-LLVMIR-quantization.cpp | 272 +++++++++--------- 5 files changed, 324 insertions(+), 140 deletions(-) create mode 100644 analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt create mode 100644 analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt create mode 100644 analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt create mode 100644 analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt diff --git a/analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt b/analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt new file mode 100644 index 000000000..3efd28155 --- /dev/null +++ b/analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt @@ -0,0 +1,48 @@ + +changeset: 1674:ac1bad9ede204cbcaf22ae227319f98635350c8f +char kNewtonVersion[] = "0.3-alpha-1674 (ac1bad9ede204cbcaf22ae227319f98635350c8f) (build 08-27-2024-21:46-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt b/analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt new file mode 100644 index 000000000..237f2970a --- /dev/null +++ b/analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt @@ -0,0 +1,48 @@ + +changeset: 1672:c4f46e8a0edb511b8ee8b4fb950213ba443d1c78 +char kNewtonVersion[] = "0.3-alpha-1672 (c4f46e8a0edb511b8ee8b4fb950213ba443d1c78) (build 08-27-2024-16:29-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt b/analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt new file mode 100644 index 000000000..726fc02f9 --- /dev/null +++ b/analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt @@ -0,0 +1,48 @@ + +changeset: 1671:d15d558addacea96e02861eb25ec57a34d279064 +char kNewtonVersion[] = "0.3-alpha-1671 (d15d558addacea96e02861eb25ec57a34d279064) (build 08-27-2024-14:57-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt b/analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt new file mode 100644 index 000000000..ed9314506 --- /dev/null +++ b/analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt @@ -0,0 +1,48 @@ + +changeset: 1673:d323aa59ba738ab8a2f71713088a49f77b2d758e +char kNewtonVersion[] = "0.3-alpha-1673 (d323aa59ba738ab8a2f71713088a49f77b2d758e) (build 08-27-2024-21:39-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index cda8b1a34..fedb7022a 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -37,7 +37,6 @@ using namespace llvm; #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 - extern "C" { // check if the type is a floating type @@ -47,19 +46,24 @@ isValidFloatingType(Type * type) return type->isFloatTy() || type->isDoubleTy(); } -bool checkForSpecialFloat(BinaryOperator *MulInst) { +bool +checkForSpecialFloat(BinaryOperator * MulInst) +{ // 确保指令是乘法 - if (MulInst->getOpcode() == Instruction::FMul) { - Value *lhs = MulInst->getOperand(0); - Value *rhs = MulInst->getOperand(1); + if (MulInst->getOpcode() == Instruction::FMul) + { + Value * lhs = MulInst->getOperand(0); + Value * rhs = MulInst->getOperand(1); // 检查右侧操作数是否为ConstantFP - if (ConstantFP *rhsConst = dyn_cast(rhs)) { + if (ConstantFP * rhsConst = dyn_cast(rhs)) + { // 创建特定的浮点值 APFloat targetValue = APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000)); // 比较右侧操作数是否等于0x3FA24924A0000000 - if (rhsConst->getValueAPF().bitwiseIsEqual(targetValue)) { + if (rhsConst->getValueAPF().bitwiseIsEqual(targetValue)) + { return true; // 发现目标浮点数 } } @@ -262,7 +266,6 @@ replaceFunctionUses(Function & oldFunc, Function * newFunc) CallInst * newCall = CallInst::Create(newFunc, args, "", callInst); newCall->setCallingConv(callInst->getCallingConv()); newCall->setDebugLoc(callInst->getDebugLoc()); - llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; callInst->replaceAllUsesWith(newCall); callInst->eraseFromParent(); } @@ -290,7 +293,6 @@ shouldSkipFunction(const std::string & functionName) "llvm.dbg.value", "llvm.dbg.label", "fixmul", - "floatIntMul", "fixdiv", "fixsqrt", "fixrsqrt" @@ -1018,7 +1020,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) if (instOpCode == Instruction::FMul) { llvm::errs() << "Handling FMul instruction\n"; - //newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); newFisrtInst = Builder.CreateNSWMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } @@ -1085,8 +1087,6 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->removeFromParent(); } - - llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1326,15 +1326,15 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorarg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - //llvm::Value * mulInst = builder.CreateMul(sext1, sext2); - //nsw - llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); - llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + // nsw + llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); + llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); builder.CreateRet(truncInst); functionsToInsert.emplace_back(func); @@ -1404,68 +1404,63 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFixRsqrt\n"; + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } - //Create a fixed-point reversed square root function - llvm::Function* createFixRsqrt(llvm::Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { - - llvm::errs() << "Entering createFixRsqrt\n"; - - // Check if irModule is valid - if (!irModule) - { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } - - std::string fixrsqrtFuncName = "fixrsqrt"; - for (auto & function : *irModule) - { - if (function.getName() == fixrsqrtFuncName) - { - llvm::errs() << "fixrsqrt already exists\n"; - return &function; - } - } + std::string fixrsqrtFuncName = "fixrsqrt"; + for (auto & function : *irModule) + { + if (function.getName() == fixrsqrtFuncName) + { + llvm::errs() << "fixrsqrt already exists\n"; + return &function; + } + } // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) - llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); // Create the function and insert it into the module - llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); - llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); // Get the function argument (x) llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value* x = &*args++; + llvm::Value * x = &*args++; // Create the fixed-point multiplication function - llvm::Function* fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); + llvm::Function * fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); - llvm::Value* halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); + llvm::Value * halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); // Step 2: Convert x to floating-point and perform the initial approximation - llvm::Value* fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); - //fdiv - - + llvm::Value * fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); + // fdiv - - llvm::Value* i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); + i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; - llvm::Value* int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - llvm::Value* mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - llvm::Value* mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - llvm::Value* correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value* final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - + llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); // Return the final fixed-point result builder.CreateRet(final_y); @@ -1624,14 +1619,14 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, if (instOpCode == Instruction::FMul) { llvm::errs() << "Handling FMul instruction\n"; - //newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + // newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); newFirstInst = Builder.CreateNSWMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - //newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + // newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); newFirstInst = Builder.CreateNSWMul(nonConstOperand, compensateNumValue); newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); } @@ -1799,8 +1794,8 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point addition - //Value * newInst = Builder.CreateAdd(op0, op1); - Value *newInst = Builder.CreateNSWAdd(op0, op1); + // Value * newInst = Builder.CreateAdd(op0, op1); + Value * newInst = Builder.CreateNSWAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -1865,8 +1860,8 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point subtraction - //Value * newInst = Builder.CreateSub(op0, op1); - Value *newInst = Builder.CreateNSWSub(op0, op1 ); + // Value * newInst = Builder.CreateSub(op0, op1); + Value * newInst = Builder.CreateNSWSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -1961,66 +1956,72 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + ConstantFP * lhsConst = dyn_cast(lhs); + ConstantFP * rhsConst = dyn_cast(rhs); - - ConstantFP *lhsConst = dyn_cast(lhs); - ConstantFP *rhsConst = dyn_cast(rhs); + bool rhsIsDiv28 = false; + bool lhsIsDiv28 = false; // 检查是否存在浮点数常数0.5 bool lhsIsHalf = lhsConst && lhsConst->getValueAPF().convertToDouble() == 0.5; bool rhsIsHalf = rhsConst && rhsConst->getValueAPF().convertToDouble() == 0.5; - //TODO HARDCODE - if (lhsIsHalf || rhsIsHalf) { - llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using sdiv by 2\n"; - - Value *otherOperand = lhsIsHalf ? rhs : lhs; - Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); - - // 创建常数2 - Value *two = ConstantInt::get(intType, 2); - - // 创建除法指令 - Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand, two, "divbytwo", llvmIrInstruction); - //Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand,two); - // 替换原来的乘法指令 - llvmIrInstruction->replaceAllUsesWith(divInst); - llvmIrInstruction->eraseFromParent(); - - return; // 防止后续代码执行 + if (auto rhsConst = dyn_cast(rhs)) { + // 直接检查常量值是否等于硬编码的特定浮点数 + if (rhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) { + rhsIsDiv28 = true; + } } - // 检查是否有乘以特定浮点数的操作,即乘以 0x3FA24924A0000000(大约等于 1/28) - //bool rhsIsDiv28 = lhsConst && lhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); - //bool lhsIsDiv28 = rhsConst && rhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); - + if (auto lhsConst = dyn_cast(lhs)) { + if (lhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) { + lhsIsDiv28 = true; + } + } - bool rhsIsDiv28 = lhsConst && lhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); - bool lhsIsDiv28 = rhsConst && rhsConst->getValueAPF().bitwiseIsEqual(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000))); + // TODO HARDCODE - if (lhsIsDiv28 || rhsIsDiv28) { + if (lhsIsDiv28 || rhsIsDiv28) + { llvm::errs() << "One operand is a floating-point constant 0x3FA24924A0000000, simplifying using sdiv by 28\n"; // 获取另一个操作数 - Value *otherOperand = lhsIsDiv28 ? rhs : lhs; + Value * otherOperand = lhsIsDiv28 ? rhs : lhs; IRBuilder<> Builder(llvmIrInstruction); // 创建浮点型常数 28.0 - Value *twentyEight = ConstantFP::get(llvmIrInstruction->getContext(), APFloat(28.0)); + Value * twentyEight = ConstantFP::get(llvmIrInstruction->getContext(), APFloat(28.0)); // 创建除法指令 - Value *divInstValue = Builder.CreateFDiv(otherOperand, twentyEight, "divFrequency"); - Instruction *divInst = cast(divInstValue); // 将 Value* 转换为 Instruction* + Value * divInstValue = Builder.CreateFDiv(otherOperand, twentyEight, "divFrequency"); + Instruction * divInst = cast(divInstValue); // 将 Value* 转换为 Instruction* // 替换原来的乘法指令 llvmIrInstruction->replaceAllUsesWith(divInst); llvmIrInstruction->eraseFromParent(); - return; // 防止后续代码执行 + return; // 防止后续代码执行 } + if (lhsIsHalf || rhsIsHalf) + { + llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using sdiv by 2\n"; + + Value * otherOperand = lhsIsHalf ? rhs : lhs; + Type * intType = Type::getInt32Ty(llvmIrInstruction->getContext()); + + // 创建常数2 + Value * two = ConstantInt::get(intType, 2); + // 创建除法指令 + Instruction * divInst = BinaryOperator::CreateSDiv(otherOperand, two, "divbytwo", llvmIrInstruction); + // Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand,two); + // 替换原来的乘法指令 + llvmIrInstruction->replaceAllUsesWith(divInst); + llvmIrInstruction->eraseFromParent(); + return; // 防止后续代码执行 + } // If either operand is a float constant, convert it to fixed-point using handleConstant @@ -2042,13 +2043,11 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion } - - // If either operand is an integer constant, directly use mul if (isa(lhs) || isa(rhs)) { llvm::errs() << "One of the operands is an integer constant, using mul\n"; - //Value * newInst = Builder.CreateMul(lhs, rhs); + // Value * newInst = Builder.CreateMul(lhs, rhs); Value * newInst = Builder.CreateNSWMul(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); @@ -2079,11 +2078,8 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix llvmIrInstruction->replaceAllUsesWith(callInst); llvmIrInstruction->eraseFromParent(); } - } - - void handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixdiv) { @@ -2101,12 +2097,6 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - - - - - - // If either operand is a float, convert both to fixed-point if (lhsIsFloat) { @@ -2186,7 +2176,8 @@ handleFNeg(Instruction * inInstruction, Type * quantizedType) // Perform the negation in fixed-point arithmetic // Fixed-point negation is equivalent to integer negation - Value * newInst = Builder.CreateNeg(operand); + // Value * newInst = Builder.CreateNeg(operand); + Value * newInst = Builder.CreateNSWNeg(operand); // Replace the original FNeg instruction with the new fixed-point negation inInstruction->replaceAllUsesWith(newInst); @@ -2317,7 +2308,7 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) // } void -//handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) +// handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt) { Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); @@ -2338,7 +2329,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "sqrt" || funcName == "sqrtf" ||funcName =="llvm.sqrt.f64" ) + if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64") { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); @@ -2350,11 +2341,11 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetOperand(0), quantizedType); quantizeConstant(llvmIrInstruction, quantizedType); } - -//void -//handleStore(Instruction * llvmIrInstruction, Type * quantizedType) +// void +// handleStore(Instruction * llvmIrInstruction, Type * quantizedType) //{ // if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) // { @@ -2456,7 +2448,7 @@ void handleStore(Instruction* llvmIrInstruction, Type* quantizedType) { // llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); // } // } -//} +// } // Function to handle loads involving pointer types void @@ -2779,15 +2771,15 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) switch (llvmIrInstruction->getOpcode()) { -// case Instruction::Alloca: -// handleAlloca(llvmIrInstruction, quantizedType); -// break; -// case Instruction::Store: -// handleStore(llvmIrInstruction, quantizedType); -// break; -// case Instruction::Load: -// handleLoad(llvmIrInstruction, quantizedType); -// break; + // case Instruction::Alloca: + // handleAlloca(llvmIrInstruction, quantizedType); + // break; + // case Instruction::Store: + // handleStore(llvmIrInstruction, quantizedType); + // break; + // case Instruction::Load: + // handleLoad(llvmIrInstruction, quantizedType); + // break; case Instruction::FPToUI: case Instruction::FPToSI: @@ -2905,7 +2897,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); - //llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + // llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); /* * quantize the arguments type @@ -2928,7 +2920,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt,fixrsqrt); + // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt,fixrsqrt); handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt); break; case Instruction::GetElementPtr: From e706af854809196eb5688ca158e41cfbf5cde2db Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 28 Aug 2024 17:36:18 +0800 Subject: [PATCH 082/213] update fixrsqrt * dev2. --- applications/newton/llvm-ir/replace.py | 58 ++---------- .../newton-irPass-LLVMIR-quantization.cpp | 92 ++++++------------- 2 files changed, 36 insertions(+), 114 deletions(-) diff --git a/applications/newton/llvm-ir/replace.py b/applications/newton/llvm-ir/replace.py index a52314e21..40c16ed6f 100644 --- a/applications/newton/llvm-ir/replace.py +++ b/applications/newton/llvm-ir/replace.py @@ -1,19 +1,13 @@ import re -def replace_function_content(filepath, old_content, new_content): +def replace_invSqrt_call(filepath): # 读取文件内容 with open(filepath, 'r', encoding='utf-8') as file: content = file.read() - # 替换函数定义 - #updated_content = re.sub(re.escape(old_content), new_content, content, flags=re.DOTALL) - - # 定义要匹配的模式 - pattern = re.compile(r'(define dso_local i32 @invSqrt\(i32 %0\) #1 .*?\{.*?^\})', re.DOTALL | re.MULTILINE) - - # 替换函数定义 - updated_content = pattern.sub(new_content, content) + # 替换 invSqrt 调用为 fixrsqrt + updated_content = re.sub(r'call i32 @invSqrt', 'call i32 @fixrsqrt', content) # 将更新后的内容写回文件 with open(filepath, 'w', encoding='utf-8') as file: @@ -22,51 +16,11 @@ def replace_function_content(filepath, old_content, new_content): -# 定义新函数内容 -new_content = """define dso_local i32 @invSqrt(i32 %0) #0 { - %2 = alloca i32, align 4 - %3 = alloca i32, align 4 - %4 = alloca float, align 4 - %5 = alloca i64, align 8 - %6 = alloca i32, align 4 - store i32 %0, i32* %2, align 4 - %7 = load i32, i32* %2, align 4 - %8 = call i32 @fixmul(i32 512, i32 %7) - store i32 %8, i32* %3, align 4 - %9 = load i32, i32* %2, align 4 - %10 = sitofp i32 %9 to float - %11 = fdiv float %10, 1.024000e+03 - store float %11, float* %4, align 4 - %12 = bitcast float* %4 to i64* - %13 = load i64, i64* %12, align 4 - store i64 %13, i64* %5, align 8 - %14 = load i64, i64* %5, align 8 - %15 = ashr i64 %14, 1 - %16 = sub nsw i64 1597463007, %15 - store i64 %16, i64* %5, align 8 - %17 = bitcast i64* %5 to float* - %18 = load float, float* %17, align 8 - store float %18, float* %4, align 4 - %19 = load float, float* %4, align 4 - %20 = fmul float %19, 1.024000e+03 - %21 = fptosi float %20 to i32 - store i32 %21, i32* %6, align 4 - %22 = load i32, i32* %6, align 4 - %23 = load i32, i32* %3, align 4 - %24 = load i32, i32* %6, align 4 - %25 = call i32 @fixmul(i32 %23, i32 %24) - %26 = load i32, i32* %6, align 4 - %27 = call i32 @fixmul(i32 %25, i32 %26) - %28 = sub nsw i32 1536, %27 - %29 = call i32 @fixmul(i32 %22, i32 %28) - store i32 %29, i32* %6, align 4 - %30 = load i32, i32* %6, align 4 - ret i32 %30 -}""" - # 文件路径 filepath = 'MadgwickAHRS_opt.ll' -replace_function_content(filepath, 'define dso_local i32 @invSqrt\(i32 %0\) #1', new_content) +# 替换 invSqrt 函数的调用 +replace_invSqrt_call(filepath) + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index fedb7022a..2b4e37a35 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -295,7 +295,7 @@ shouldSkipFunction(const std::string & functionName) "fixmul", "fixdiv", "fixsqrt", - "fixrsqrt" + "fixrsqrt", "constantMulDiv", "sinf", "llvm.sqrt.f64", @@ -464,6 +464,10 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function // Create call to the fixed-point sqrt function llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + if (auto * call = dyn_cast(sqrtResult)) + { + call->setTailCall(true); + } // No need to apply shl and compensation if it's already done in createFixSqrt llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); @@ -1152,54 +1156,6 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -{ - llvm::errs() << "Entering createFloatIntMul\n"; - - // Check if irModule is valid - if (!irModule) - { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } - - std::string floatIntMulFuncName = "floatIntMul"; - for (auto & function : *irModule) - { - if (function.getName() == floatIntMulFuncName) - { - llvm::errs() << "floatIntMul already exists\n"; - return &function; - } - } - - llvm::FunctionType * funcType = llvm::FunctionType::get(intType, {floatType, intType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, floatIntMulFuncName, irModule); - - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); - - // Get function arguments - - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value * floatArg = &*arg1; - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * intArg = &*arg2; - - // Generate float * int multiplication instruction - llvm::Value * intToFloat = builder.CreateSIToFP(intArg, floatType); - llvm::Value * mulInst = builder.CreateFMul(floatArg, intToFloat); - llvm::Value * floatToInt = builder.CreateFPToSI(mulInst, intType); - - builder.CreateRet(floatToInt); - - functionsToInsert.emplace_back(func); - llvm::errs() << "Created floatIntMul function: " << func->getName() << "\n"; - return func; -} - // llvm::Function* createFixPow(Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { // llvm::errs() << "Entering createFixPow\n"; // @@ -1307,6 +1263,9 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorsetCallingConv(llvm::CallingConv::Fast); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); llvm::IRBuilder<> builder(entryBB); builder.SetInsertPoint(entryBB); @@ -1332,8 +1291,10 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorgetContext())); - // fdiv + + // Added step: fp_y = fp_y / 1024.0; + fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); @@ -1966,15 +1929,19 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsHalf = lhsConst && lhsConst->getValueAPF().convertToDouble() == 0.5; bool rhsIsHalf = rhsConst && rhsConst->getValueAPF().convertToDouble() == 0.5; - if (auto rhsConst = dyn_cast(rhs)) { + if (auto rhsConst = dyn_cast(rhs)) + { // 直接检查常量值是否等于硬编码的特定浮点数 - if (rhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) { + if (rhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) + { rhsIsDiv28 = true; } } - if (auto lhsConst = dyn_cast(lhs)) { - if (lhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) { + if (auto lhsConst = dyn_cast(lhs)) + { + if (lhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) + { lhsIsDiv28 = true; } } @@ -2023,7 +1990,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix return; // 防止后续代码执行 } - // If either operand is a float constant, convert it to fixed-point using handleConstant if (isa(lhs)) { @@ -2075,6 +2041,8 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix { llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + // callInst->setCallingConv(llvm::CallingConv::Fast); + // callInst->setTailCall(true); llvmIrInstruction->replaceAllUsesWith(callInst); llvmIrInstruction->eraseFromParent(); } @@ -2894,10 +2862,10 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * generate hardcode function - fixmul and fixdiv * */ - llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); - llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); - // llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); + llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); /* * quantize the arguments type @@ -2920,7 +2888,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt,fixrsqrt); + //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt); break; case Instruction::GetElementPtr: From 12d68d7c7d92a4b59d5d116fbc576b285e4e255e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 28 Aug 2024 18:11:25 +0800 Subject: [PATCH 083/213] update test scripts * dev2. --- .../newton/llvm-ir/c-files/test_MadgwickAHRSfix.c | 10 +++++----- .../newton/llvm-ir/c-files/test_Original.c | 2 +- applications/newton/llvm-ir/run.sh | 3 ++- applications/newton/llvm-ir/testMadgwick.sh | 3 ++- applications/newton/llvm-ir/testMadgwickfix.sh | 7 ++++--- applications/newton/llvm-ir/testOriginal.sh | 7 ++++--- src/newton/newton-irPass-LLVMIR-quantization.cpp | 15 ++++++++++----- 7 files changed, 28 insertions(+), 19 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index 3c2e36147..e2445c954 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -8,7 +8,7 @@ #define FRAC_Q 10 #define BIT_WIDTH 32 #define ITERATION 10 -#define DATA_SIZE 10000 +#define DATA_SIZE 1000 #include "MadgwickAHRSfix.h" extern volatile int32_t q0, q1, q2, q3; extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); @@ -211,12 +211,12 @@ toc(timespec * start_time, const char * prefix) time[row - 2] = atof(value); break; case 1: - //mag_x[row - 2] = round(atof(value) * FRAC_BASE); - mag_x[row - 2] = quantize(atof(value), FRAC_BASE); + mag_x[row - 2] = round(atof(value) * FRAC_BASE); + //mag_x[row - 2] = quantize(atof(value), FRAC_BASE); break; case 2: - //mag_y[row - 2] = round(atof(value) * FRAC_BASE); - mag_y[row - 2] = quantize(atof(value), FRAC_BASE); + mag_y[row - 2] = round(atof(value) * FRAC_BASE); + //mag_y[row - 2] = quantize(atof(value), FRAC_BASE); break; case 3: mag_z[row - 2] = round(atof(value) * FRAC_BASE); diff --git a/applications/newton/llvm-ir/c-files/test_Original.c b/applications/newton/llvm-ir/c-files/test_Original.c index ed245adf7..132408a85 100644 --- a/applications/newton/llvm-ir/c-files/test_Original.c +++ b/applications/newton/llvm-ir/c-files/test_Original.c @@ -11,7 +11,7 @@ #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 #define ITERATION 10 -#define DATA_SIZE 10000 +#define DATA_SIZE 1000 extern volatile float q0, q1, q2, q3; extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, diff --git a/applications/newton/llvm-ir/run.sh b/applications/newton/llvm-ir/run.sh index 3ceaac273..af65bbbc3 100755 --- a/applications/newton/llvm-ir/run.sh +++ b/applications/newton/llvm-ir/run.sh @@ -3,7 +3,8 @@ # Get the current user's home directory USER_HOME=$HOME -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index b0af6dad9..8c0b54a2c 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -29,7 +29,8 @@ python3 replace.py # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll diff --git a/applications/newton/llvm-ir/testMadgwickfix.sh b/applications/newton/llvm-ir/testMadgwickfix.sh index 227a52fab..c2e881bce 100755 --- a/applications/newton/llvm-ir/testMadgwickfix.sh +++ b/applications/newton/llvm-ir/testMadgwickfix.sh @@ -13,10 +13,11 @@ clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOM # Step 4: Optimize the generated LLVM IR file -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll --simplifycfg --instsimplify -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.llperformace +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode -llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $HOME/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s @@ -28,7 +29,7 @@ clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applic ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library -clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable $HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index c73083bc0..a1d3acd31 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -2,11 +2,12 @@ USER_HOME=$HOME # Step 1: Generate LLVM IR file -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c -#clang -O0 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + # Step 4: Optimize the generated LLVM IR file -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 2b4e37a35..6ef27ba73 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1129,6 +1129,11 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector(sqrtResult)) + { + call->setTailCall(true); + } // Convert the result back to a fixed-point integer llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); @@ -1264,7 +1269,7 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorsetCallingConv(llvm::CallingConv::Fast); + func->setCallingConv(llvm::CallingConv::Fast); llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); llvm::IRBuilder<> builder(entryBB); @@ -1292,9 +1297,9 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorsetCallingConv(llvm::CallingConv::Fast); - // callInst->setTailCall(true); + callInst->setCallingConv(llvm::CallingConv::Fast); + callInst->setTailCall(true); llvmIrInstruction->replaceAllUsesWith(callInst); llvmIrInstruction->eraseFromParent(); } From 36a536429b6d7ffbe2f5a3e51cb07392c966f76f Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Thu, 29 Aug 2024 18:29:19 +0800 Subject: [PATCH 084/213] small update on fixrsqrt * dev2. --- ...05b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt | 48 +++++++++++++++++++ ...06af854809196eb5688ca158e41cfbf5cde2db.txt | 48 +++++++++++++++++++ ...0d35fca0fc55e00c998de3cf02138684d02c1e.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 11 ++--- 4 files changed, 149 insertions(+), 6 deletions(-) create mode 100644 analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt create mode 100644 analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt create mode 100644 analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt diff --git a/analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt b/analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt new file mode 100644 index 000000000..94eb2e034 --- /dev/null +++ b/analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt @@ -0,0 +1,48 @@ + +changeset: 1676:d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669 +char kNewtonVersion[] = "0.3-alpha-1676 (d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669) (build 08-28-2024-17:36-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt b/analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt new file mode 100644 index 000000000..242f5c819 --- /dev/null +++ b/analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt @@ -0,0 +1,48 @@ + +changeset: 1677:e706af854809196eb5688ca158e41cfbf5cde2db +char kNewtonVersion[] = "0.3-alpha-1677 (e706af854809196eb5688ca158e41cfbf5cde2db) (build 08-28-2024-18:11-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt b/analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt new file mode 100644 index 000000000..f703a4994 --- /dev/null +++ b/analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt @@ -0,0 +1,48 @@ + +changeset: 1675:fd0d35fca0fc55e00c998de3cf02138684d02c1e +char kNewtonVersion[] = "0.3-alpha-1675 (fd0d35fca0fc55e00c998de3cf02138684d02c1e) (build 08-28-2024-10:34-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 6ef27ba73..350ddcb33 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1129,11 +1129,6 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector(sqrtResult)) - { - call->setTailCall(true); - } // Convert the result back to a fixed-point integer llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); @@ -1415,7 +1410,11 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext())); // Added step: fp_y = fp_y / 1024.0; - fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); + //fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); + + // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 + fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); + llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); From a64086b2fe57ccd9a3dff399376cf66a9c753ba4 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 2 Sep 2024 10:39:34 +0800 Subject: [PATCH 085/213] add more ashr * dev2. --- ...d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 23 +++++---- 2 files changed, 63 insertions(+), 8 deletions(-) create mode 100644 analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt diff --git a/analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt b/analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt new file mode 100644 index 000000000..7b522f36b --- /dev/null +++ b/analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt @@ -0,0 +1,48 @@ + +changeset: 1678:12d68d7c7d92a4b59d5d116fbc576b285e4e255e +char kNewtonVersion[] = "0.3-alpha-1678 (12d68d7c7d92a4b59d5d116fbc576b285e4e255e) (build 08-29-2024-18:29-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 350ddcb33..76c4b8985 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1404,7 +1404,9 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext())); @@ -1976,19 +1978,24 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix if (lhsIsHalf || rhsIsHalf) { - llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using sdiv by 2\n"; + //llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using sdiv by 2\n"; + llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using logical shift right by 1\n"; Value * otherOperand = lhsIsHalf ? rhs : lhs; Type * intType = Type::getInt32Ty(llvmIrInstruction->getContext()); - // 创建常数2 - Value * two = ConstantInt::get(intType, 2); + // 创建常数1,用于右移 + Value *one = ConstantInt::get(intType, 1); // 创建除法指令 - Instruction * divInst = BinaryOperator::CreateSDiv(otherOperand, two, "divbytwo", llvmIrInstruction); + //Instruction * divInst = BinaryOperator::CreateSDiv(otherOperand, two, "divbytwo", llvmIrInstruction); + + // 创建算术右移指令 + Instruction *shrInst = BinaryOperator::CreateAShr(otherOperand, one, "divbytwo", llvmIrInstruction); // Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand,two); // 替换原来的乘法指令 - llvmIrInstruction->replaceAllUsesWith(divInst); + //llvmIrInstruction->replaceAllUsesWith(divInst); + llvmIrInstruction->replaceAllUsesWith(shrInst); llvmIrInstruction->eraseFromParent(); return; // 防止后续代码执行 @@ -2045,8 +2052,8 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix { llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - callInst->setCallingConv(llvm::CallingConv::Fast); - callInst->setTailCall(true); + //callInst->setCallingConv(llvm::CallingConv::Fast); + //callInst->setTailCall(true); llvmIrInstruction->replaceAllUsesWith(callInst); llvmIrInstruction->eraseFromParent(); } From bca911370c42ab769501a69e1de57f832a4c8c58 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 10 Sep 2024 09:59:10 +0800 Subject: [PATCH 086/213] update i32 fixmul and fixsqrt * dev2. --- applications/newton/llvm-ir/replace.py | 30 ++- applications/newton/llvm-ir/testMadgwick.sh | 37 ++- applications/newton/llvm-ir/testOriginal.sh | 6 +- .../newton-irPass-LLVMIR-quantization.cpp | 236 ++++++++++++++---- 4 files changed, 226 insertions(+), 83 deletions(-) diff --git a/applications/newton/llvm-ir/replace.py b/applications/newton/llvm-ir/replace.py index 40c16ed6f..89ff3ff79 100644 --- a/applications/newton/llvm-ir/replace.py +++ b/applications/newton/llvm-ir/replace.py @@ -1,26 +1,34 @@ import re +import os +def replace_text_in_file(filepath, patterns_replacements): + # 检查文件是否存在 + if not os.path.isfile(filepath): + print(f"Error: File '{filepath}' does not exist.") + return -def replace_invSqrt_call(filepath): # 读取文件内容 with open(filepath, 'r', encoding='utf-8') as file: content = file.read() - # 替换 invSqrt 调用为 fixrsqrt - updated_content = re.sub(r'call i32 @invSqrt', 'call i32 @fixrsqrt', content) + # 对所有模式和替换进行操作 + for pattern, replacement in patterns_replacements: + updated_content = re.sub(pattern, replacement, content) # 将更新后的内容写回文件 with open(filepath, 'w', encoding='utf-8') as file: file.write(updated_content) - - - -# 文件路径 +# 定义文件路径和替换规则 filepath = 'MadgwickAHRS_opt.ll' +patterns_replacements = [ + (r'declare dso_local i32 @printf\(i8\*\)', 'declare dso_local i32 @printf(i8*, i32)'), + (r'float\*\*', 'i32**'), + (r'float\*', 'i32*'), + (r'call i32 @invSqrt', 'call i32 @fixrsqrt') +] +# 执行替换 +replace_text_in_file(filepath, patterns_replacements) - -# 替换 invSqrt 函数的调用 -replace_invSqrt_call(filepath) - +print("Replacement complete.") \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 8c0b54a2c..0884fc61f 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -3,57 +3,50 @@ # Get the current user's home directory USER_HOME=$HOME -FILE_PATH="$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" +FILE_PATH="$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" #Step 1: Generate LLVM IR file echo "Step 1: Generate LLVM IR file" -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c - -#clang -g -O0 -Xclang -disable-O0-optnone -fno-math-errno -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c - -#clang -O3 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -#$HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 2: Use newton for optimization and quantization echo "Step 2: Use newton for optimization and quantization" -cd $USER_HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $USER_HOME/CoSense/applications/newton/sensors/test.nt +cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/test.nt # # Step 3: Convert generated bytecode file to LLVM IR file echo "Step 3: Convert generated bytecode file to LLVM IR file" -#llvm-dis $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -cd $USER_HOME/CoSense/applications/newton/llvm-ir/&& -./replace.sh $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll +#llvm-dis $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.bc -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll +cd $HOME/CoSense/applications/newton/llvm-ir/&& +./replace.sh $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -python3 replace.py +#python3 replace.py # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -#opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll - - +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" -llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc # Step 6: Compile the bitcode file to assembly echo "Step 6: Compile the bitcode file to assembly" -llc $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.s +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s # Step 7: Compile the assembly file to object file echo "Step 7: Compile the assembly file to object file" -clang -c $USER_HOME/CoSense/applications/newton/llvm-ir/out.s -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.o +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 8: Package the object file into a static library echo "Step 8: Package the object file into a static library" -ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSense/applications/newton/llvm-ir/out.o +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D INT_DATA_TYPE -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" -$USER_HOME/CoSense/applications/newton/llvm-ir/main_out +$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index a1d3acd31..79e0861b1 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -2,12 +2,12 @@ USER_HOME=$HOME # Step 1: Generate LLVM IR file -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 4: Optimize the generated LLVM IR file #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc @@ -25,4 +25,4 @@ ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applicat clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_Original.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable -$HOME/CoSense/applications/newton/llvm-ir/main_out +$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 76c4b8985..7f919d87b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -299,6 +299,7 @@ shouldSkipFunction(const std::string & functionName) "constantMulDiv", "sinf", "llvm.sqrt.f64", + "llvm.sqrt.f32", "sqrt", "sqrtf"}; @@ -1090,11 +1091,75 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->replaceAllUsesWith(callInst); inInstruction->removeFromParent(); } +//TODO : Origianl double fixsqrt +//llvm::Function * +//createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//{ +// // Check if irModule is valid +// if (!irModule) +// { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixSqrtFuncName = "fixsqrt"; +// for (auto & function : *irModule) +// { +// if (function.getName() == fixSqrtFuncName) +// { +// llvm::errs() << "fixsqrt already exists\n"; +// return &function; +// } +// } +// +// llvm::LLVMContext & context = irModule->getContext(); +// +// // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType +// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); +// +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// +// llvm::Function::arg_iterator args = func->arg_begin(); +// llvm::Value * x = &*args++; +// +// // Convert the fixed-point integer to a floating-point number for sqrt computation +// llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); +// +// // Call sqrt on the floating-point value +// llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); +// llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); +// +// // Convert the result back to a fixed-point integer +// llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); +// +// // Perform a left shift to scale the result +// llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); +// +// // Apply compensation if FRAC_Q is odd +// llvm::Value * finalRes = shlRes; +// if (FRAC_Q % 2 != 0) +// { +// llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); +// llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); +// llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); +// finalRes = builder.CreateFPToSI(compensated, quantizedType); +// } +// +// builder.CreateRet(finalRes); +// +// // Insert the newly created function into the list +// functionsToInsert.push_back(func); +// +// llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; +// +// return func; +//} -llvm::Function * -createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//TODO : float version rsqrt +llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { - // Check if irModule is valid if (!irModule) { llvm::errs() << "Error: irModule is nullptr\n"; @@ -1112,23 +1177,20 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext(); - - // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); - + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::IRBuilder<> builder(entryBB); llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value * x = &*args++; + llvm::Value * x = &*args++; // Convert the fixed-point integer to a floating-point number for sqrt computation - llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); + llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getFloatTy(context)); // Call sqrt on the floating-point value - llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); - llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); // Convert the result back to a fixed-point integer llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); @@ -1140,17 +1202,14 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetName() << "\n"; return func; @@ -1238,6 +1297,74 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//{ +// llvm::errs() << "Entering createFixMul\n"; +// +// // Check if irModule is valid +// if (!irModule) +// { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixmulFuncName = "fixmul"; +// for (auto & function : *irModule) +// { +// if (function.getName() == fixmulFuncName) +// { +// llvm::errs() << "fixmul already exists\n"; +// return &function; +// } +// } +// +// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); +// +// // 设置调用约定为 fastcc +// func->setCallingConv(llvm::CallingConv::Fast); +// +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// builder.SetInsertPoint(entryBB); +// +// // Create fixed-point multiplication instruction +// Type * higherQuantizedType; +// switch (BIT_WIDTH) +// { +// case 8: +// higherQuantizedType = Type::getInt16Ty(irModule->getContext()); +// break; +// case 16: +// higherQuantizedType = Type::getInt32Ty(irModule->getContext()); +// break; +// default: +// higherQuantizedType = Type::getInt64Ty(irModule->getContext()); +// break; +// } +// +// llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); +// llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); +// llvm::Function::arg_iterator arg2 = &*(++arg1); +// llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); +// // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); +// // nsw +// llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); +// //llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); +// // lshr +// llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); +// llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); +// builder.CreateRet(truncInst); +// +// functionsToInsert.emplace_back(func); +// llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; +// return func; +//} + + +//TODO: i32 version fixmul llvm::Function * createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1260,49 +1387,41 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorsetCallingConv(llvm::CallingConv::Fast); + // Create entry basic block llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); llvm::IRBuilder<> builder(entryBB); builder.SetInsertPoint(entryBB); - // Create fixed-point multiplication instruction - Type * higherQuantizedType; - switch (BIT_WIDTH) - { - case 8: - higherQuantizedType = Type::getInt16Ty(irModule->getContext()); - break; - case 16: - higherQuantizedType = Type::getInt32Ty(irModule->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(irModule->getContext()); - break; - } + // Get the two arguments of the function + llvm::Function::arg_iterator args = func->arg_begin(); + llvm::Value * arg1 = args++; // Get the first argument + llvm::Value * arg2 = args; // Get the second argument - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); - // nsw - llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); - //llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - // lshr - llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); - builder.CreateRet(truncInst); + // Perform i32 multiplication + llvm::Value * mulInst = builder.CreateMul(arg1, arg2); + + // Logical shift right by 10 + llvm::Value * lshrInst = builder.CreateAShr(mulInst, ConstantInt::get(quantizedType, 10)); + + // Return the result + builder.CreateRet(lshrInst); + // Add the function to the list of functions to insert functionsToInsert.emplace_back(func); + llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; return func; } + + // Create a fixed-point division function llvm::Function * createFixDiv(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1934,6 +2053,9 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix // 检查是否存在浮点数常数0.5 bool lhsIsHalf = lhsConst && lhsConst->getValueAPF().convertToDouble() == 0.5; bool rhsIsHalf = rhsConst && rhsConst->getValueAPF().convertToDouble() == 0.5; + bool lhsIsTwo = lhsConst && lhsConst->getValueAPF().convertToDouble() == 2.0; + bool rhsIsTwo = rhsConst && rhsConst->getValueAPF().convertToDouble() == 2.0; + if (auto rhsConst = dyn_cast(rhs)) { @@ -2001,6 +2123,26 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix return; // 防止后续代码执行 } + if (lhsIsTwo || rhsIsTwo) + { + llvm::errs() << "One operand is a floating-point constant 2.0, simplifying using logical shift left by 1\n"; + + Value *otherOperand = lhsIsTwo ? rhs : lhs; + Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); + + // 创建常数1,用于左移 + Value *one = ConstantInt::get(intType, 1); + + // 创建逻辑左移指令 + Instruction *shlInst = BinaryOperator::CreateShl(otherOperand, one, "mulbytwo", llvmIrInstruction); + + // 替换原来的乘法指令 + llvmIrInstruction->replaceAllUsesWith(shlInst); + llvmIrInstruction->eraseFromParent(); + + return; // 防止后续代码执行 + } + // If either operand is a float constant, convert it to fixed-point using handleConstant if (isa(lhs)) { @@ -2308,7 +2450,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64") + if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64"|| funcName == "llvm.sqrt.f32") { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); From 7d92b1b0f8bfd75adb3706278af53ceeb6ff1de8 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 14 Sep 2024 17:49:28 +0800 Subject: [PATCH 087/213] update fixmul * dev2. --- ...a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt | 48 + ...4086b2fe57ccd9a3dff399376cf66a9c753ba4.txt | 48 + applications/newton/llvm-ir/input.csv | 1001 ----------------- .../newton-irPass-LLVMIR-quantization.cpp | 126 ++- 4 files changed, 213 insertions(+), 1010 deletions(-) create mode 100644 analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt create mode 100644 analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt delete mode 100644 applications/newton/llvm-ir/input.csv diff --git a/analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt b/analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt new file mode 100644 index 000000000..5c2cafe47 --- /dev/null +++ b/analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt @@ -0,0 +1,48 @@ + +changeset: 1679:36a536429b6d7ffbe2f5a3e51cb07392c966f76f +char kNewtonVersion[] = "0.3-alpha-1679 (36a536429b6d7ffbe2f5a3e51cb07392c966f76f) (build 09-02-2024-10:39-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt b/analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt new file mode 100644 index 000000000..188c9a6c7 --- /dev/null +++ b/analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt @@ -0,0 +1,48 @@ + +changeset: 1680:a64086b2fe57ccd9a3dff399376cf66a9c753ba4 +char kNewtonVersion[] = "0.3-alpha-1680 (a64086b2fe57ccd9a3dff399376cf66a9c753ba4) (build 09-10-2024-09:59-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/input.csv b/applications/newton/llvm-ir/input.csv deleted file mode 100644 index ce7c7c2b3..000000000 --- a/applications/newton/llvm-ir/input.csv +++ /dev/null @@ -1,1001 +0,0 @@ -Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) -0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.08969094,-6.654678345,26.09465027,-37.29600525,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.09969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.10969094,-7.548351288,24.87819672,-39.49668884,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.11969094,-7.311084747,23.94067001,-40.19360352,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.12969094,-5.958099365,24.8057251,-37.20587158,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.13969094,-8.100597382,25.37778282,-37.85224915,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 -0.14969094,-6.394374847,25.43608284,-37.70759583,-0.1151283234,-0.00251355581,0.01010152325,-0.2793728709,0.4129949808,10.27848434 -0.15969094,-7.610450745,26.11058998,-38.66853333,-0.08246348053,0.00447106408,0.004323757719,-0.2367789149,0.4112356901,10.43229961 -0.16969094,-6.729293823,25.38845825,-38.77174377,-0.03473320603,0.04428362101,0.0009580431506,-0.2995947599,0.416141957,10.24356842 -0.17969094,-6.832756042,26.19889259,-38.67654419,0.002444048412,0.07756645232,-0.003223320469,-0.2556326687,0.3563099205,9.911549568 -0.18969094,-7.576393127,26.12816811,-37.14372253,0.007744635455,0.09112588316,-0.008515236899,-0.1705993563,0.3450968266,9.673620224 -0.19969094,-5.582935333,26.2094841,-38.06962585,-0.007077907212,0.1003924161,-0.01438220032,-0.2904628515,0.4137096405,9.693481445 -0.20969094,-7.26531601,26.16348839,-37.14692688,-0.0214833729,0.07035970688,-0.01663675718,-0.4015436172,0.4240173995,9.765337944 -0.21969094,-7.610450745,26.11058998,-38.66853333,-0.02840357646,0.0412395969,-0.01503071003,-0.4420829713,0.4474136829,9.710318565 -0.22969094,-5.917541504,25.49433899,-37.25494385,-0.04069363698,0.0300014019,-0.01120906137,-0.5065568686,0.4349407554,9.780290604 -0.23969094,-5.917541504,25.49433899,-37.25494385,-0.04557721689,0.01997687295,-0.003380747745,-0.4558943808,0.4060772061,9.982199669 -0.24969094,-7.248287201,26.17227745,-36.38452148,-0.03875789419,-0.002376349643,-0.001149083488,-0.4019374549,0.4131945372,10.07145596 -0.25969094,-5.484790802,27.59550095,-37.40536499,-0.02963040024,-0.007826571353,-0.0005718094762,-0.3436793387,0.4178147614,10.06400871 -0.26969094,-7.59992218,25.44834328,-36.33224487,-0.01951537654,-0.001477465499,-0.004841145594,-0.2960692346,0.3756780624,9.936094284 -0.27969094,-7.536643982,27.08343887,-37.97483826,-0.01999245957,-0.006206189282,-0.007511992939,-0.3121205568,0.3395512402,9.855197906 -0.28969094,-5.894504547,25.77330017,-36.96961975,-0.02960216627,-0.0179349836,-0.009124945849,-0.3137557507,0.3006750047,9.795846939 -0.29969094,-5.791351318,25.63038635,-38.94366455,-0.04506304488,-0.02701679431,-0.006459381431,-0.3230510056,0.3795295656,9.721902847 -0.30969094,-6.642169952,27.03585815,-39.3469696,-0.06745177507,-0.03394386917,-0.005393324886,-0.3160211146,0.3835323155,9.728869438 -0.31969094,-8.179725647,26.60138512,-37.78651428,-0.07811312377,-0.03392977268,-0.003795302706,-0.3117079139,0.3867061734,9.83324337 -0.32969094,-6.671707153,26.08586121,-38.05841064,-0.07162205875,-0.02754923515,-0.002687801374,-0.3693574667,0.4109267592,9.983849525 -0.33969094,-7.483146667,25.31245995,-37.69638062,-0.05240149051,-0.02055237256,0.006894228514,-0.4506206214,0.425347209,10.05073643 -0.34969094,-7.178882599,25.34426498,-38.00453186,-0.01982024126,-0.008340923116,0.01644631103,-0.424624294,0.4272369146,10.15809345 -0.35969094,-5.974441528,27.26355934,-37.68589783,0.01485640649,-0.002841048408,0.0174930077,-0.322455287,0.4224477112,10.03915787 -0.36969094,-5.750793457,26.31900024,-38.99273682,0.03709132969,0.01580360159,0.01747041196,-0.2883752882,0.3374709487,9.90159893 -0.37969094,-5.870170593,26.18646812,-36.99906921,0.03729007766,0.02409679629,0.01276430115,-0.2753283978,0.2819014192,9.6376791 -0.38969094,-7.402835846,26.9563446,-38.57655334,0.01491038781,0.01591248065,0.01207613945,-0.2228651792,0.2963768244,9.694525719 -0.39969094,-8.981754303,26.09991455,-37.74905396,-0.008029373363,-0.004363908432,0.009553089738,-0.3137707412,0.3058240712,9.756849289 -0.40969094,-7.179374695,24.94340134,-36.9077301,-0.0325601548,-0.01107135322,0.008988874033,-0.347237438,0.2963377833,9.893450737 -0.41969094,-6.377658844,26.11239243,-38.82402039,-0.0477651991,-0.02031742781,0.01180266216,-0.2945395708,0.2534381747,10.08242321 -0.42969094,-7.576393127,26.12816811,-37.14372253,-0.03290509433,-0.03066522814,0.01591363735,-0.3064144552,0.2915987968,10.09379768 -0.43969094,-6.654678345,26.09465027,-37.29600525,-0.007921610959,-0.01810567081,0.01840451546,-0.220240429,0.3167188168,10.02454567 -0.44969094,-7.910011292,26.21474838,-38.52267456,0.01434707176,0.005370598286,0.01262276806,-0.2308089137,0.3418518007,9.914393425 -0.45969094,-7.616950989,25.43955421,-37.09465027,0.02976943925,0.02135305665,0.01048317552,-0.2660395503,0.3493344486,9.844152451 -0.46969094,-6.706256866,25.66741943,-38.48641968,0.03813938051,0.02182977088,0.005633147899,-0.216332078,0.3326821923,9.803571701 -0.47969094,-6.727798462,27.58842468,-37.70732117,0.0336618945,0.00669084629,-0.002252099337,-0.3407348692,0.3455455303,9.784972191 -0.48969094,-6.272701263,27.50192642,-37.85481262,0.02714404091,-0.02482098155,-7.733469829e-06,-0.4290958643,0.3308064342,9.846453667 -0.49969094,-7.497264862,24.9045639,-37.20947266,0.02085882053,-0.04777231812,0.005195887294,-0.4415936172,0.3619870543,9.872394562 -0.50969094,-6.638454437,26.37009621,-37.31562805,0.01971525326,-0.06770654023,0.007923526689,-0.3541632593,0.3599542379,9.863398552 -0.51969094,-7.459617615,25.99228477,-38.50785828,0.02144834772,-0.05833188444,0.005967103411,-0.3571015596,0.2838820815,9.942513466 -0.52969094,-6.602794647,24.85698509,-38.581604,0.03338546306,-0.04018680751,0.002523483243,-0.3252090514,0.3449226618,9.981822014 -0.53969094,-6.37815094,25.71152878,-37.72721863,0.0395555906,-0.03105190583,0.002672136296,-0.3542988896,0.3391857445,9.916908264 -0.54969094,-7.507480621,24.89929199,-37.66693115,0.04480022192,-0.009855329059,0.002203115728,-0.2928415239,0.3543523848,9.862686157 -0.55969094,-7.940353394,25.53140831,-38.0161438,0.04905298352,0.03105917946,-0.004327977076,-0.2486828268,0.414686501,9.692631721 -0.56969094,-6.474308014,26.92634201,-38.42385864,0.04452182725,0.05528639257,-0.009296388365,-0.2784487903,0.3446758687,9.670860291 -0.57969094,-6.825942993,26.20240784,-38.37158203,0.0319024995,0.02479611523,-0.006458672695,-0.3158861697,0.380629003,9.788747787 -0.58969094,-6.840065002,25.7945137,-37.88470459,0.02158891037,-0.03473115712,-0.001664606389,-0.3231438696,0.411952734,9.852827072 -0.59969094,-7.162658691,25.61971092,-38.02415466,0.01061961707,-0.06082195044,-0.003890550463,-0.2853017449,0.4193702936,9.816333771 -0.60969094,-6.856781006,25.11820412,-36.76826477,0.007375336252,-0.03639098257,-0.006122777238,-0.2694948912,0.4136307836,9.676003456 -0.61969094,-6.896842957,24.83045387,-37.81599426,0.002672176808,-0.001061701681,-0.01018739119,-0.3234909773,0.3764295876,9.812074661 -0.62969094,-6.792694092,26.48664284,-37.6288147,0.005003871396,0.01006212085,-0.008162576705,-0.4266119301,0.3720042706,10.06733036 -0.63969094,-6.353816986,26.12469673,-37.75666809,0.02046790347,-0.01681301743,0.0007936262991,-0.3262727559,0.3997677267,10.05027294 -0.64969094,-7.610450745,26.11058998,-38.66853333,0.03206149116,-0.02037302963,0.0003540727776,-0.2678869963,0.4512276053,9.878255844 -0.65969094,-6.743099213,24.31304169,-36.40600586,0.0311081633,0.002321796725,-0.003910094965,-0.3650930524,0.3835353553,9.755164146 -0.66969094,-7.426364899,26.27651978,-37.76507568,0.01962944865,0.01086440869,0.0001691966318,-0.2833980918,0.3181568384,9.629114151 -0.67969094,-8.06734848,25.66201782,-37.10948181,-0.003784938715,-0.009464945644,0.004316138569,-0.2693619728,0.3249111176,9.749714851 -0.68969094,-8.06734848,25.66201782,-37.10948181,-0.01996190473,-0.01890161261,0.004569322802,-0.2541345954,0.3612316251,9.785536766 -0.69969094,-6.71957016,24.99286652,-37.21748352,-0.03055388108,-0.002701988444,0.001403293107,-0.2876216173,0.42038095,9.840220451 -0.70969094,-6.71957016,24.99286652,-37.21748352,-0.02469141968,0.02064591087,0.0002037035301,-0.3182426989,0.4196154475,9.82704258 -0.71969094,-6.593383789,25.12891579,-38.90620422,-0.01536074467,0.02345331945,-0.0008636235725,-0.3334631324,0.4254368544,9.891711235 -0.72969094,-6.681922913,26.08058739,-38.51586914,0.0003667054698,0.02293391153,0.00113602588,-0.3242745101,0.4562799931,9.878070831 -0.73969094,-6.498645782,26.51317406,-38.39442444,0.01448426675,0.03026067652,0.000355088152,-0.3454433978,0.4558205009,9.850791931 -0.74969094,-6.310348511,26.41420555,-36.556427,0.02660195902,0.02360223606,0.002740718424,-0.3830767274,0.4391977191,9.813574791 -0.75969094,-7.249095917,26.43893433,-37.16656494,0.03125537932,-0.009130412713,0.006767154206,-0.3287489414,0.4146726429,9.829097748 -0.76969094,-8.217372894,25.51366615,-36.48812866,0.02221588045,-0.04891215265,0.01043653488,-0.2150225341,0.3947498798,9.832125664 -0.77969094,-8.854763031,25.96930504,-38.65571594,0.01269038301,-0.05699107051,0.007309804205,-0.2531299889,0.4025529325,9.883623123 -0.78969094,-7.472930908,25.31773376,-37.23892212,0.009331553243,-0.0362610966,0.007923526689,-0.2762732506,0.3899558783,9.84967804 -0.79969094,-5.784229279,24.96638107,-36.75984192,0.001331721433,-0.02163651586,0.007390852552,-0.234718129,0.4027428627,9.921022415 -0.80969094,-7.957382202,25.52261925,-38.77854919,-0.001178442501,-0.02419010922,0.007390852552,-0.1688663065,0.3537279963,9.899970055 -0.81969094,-7.96679306,25.25068855,-38.45397949,-0.0002859253436,-0.02208984457,0.003662134986,-0.1612079442,0.3765885234,9.860362053 -0.82969094,-6.607620239,27.45429993,-38.91896057,-0.005500952248,-0.00137518486,0.002451392356,-0.2259380519,0.4388239086,9.777145386 -0.83969094,-6.706256866,25.66741943,-38.48641968,-0.008655028418,0.03205142915,0.001516730525,-0.2650729716,0.4043938816,9.713522911 -0.84969094,-6.545207977,25.55438805,-37.86827087,-0.01504812483,0.05091909319,0.004732759669,-0.2684932947,0.3750241399,9.674304008 -0.85969094,-7.96679306,25.25068855,-38.45397949,-0.02855567634,0.04647941142,0.006329850759,-0.2255412191,0.4341191947,9.723275185 -0.86969094,-7.983821869,25.24189949,-39.21638489,-0.03407438844,0.03285332024,0.009157052264,-0.2946357727,0.4097974002,9.758426666 -0.87969094,-6.569046021,25.54208374,-38.93565369,-0.04476465285,0.03424095362,0.007229163777,-0.3785450459,0.4446446896,9.875383377 -0.88969094,-7.569580078,26.13168335,-36.83876038,-0.04716154933,0.02489669435,0.01244034618,-0.3616573513,0.4302352071,10.03662205 -0.89969094,-7.520793915,24.22473907,-36.397995,-0.03513527289,0.009162386879,0.01834226586,-0.3369231224,0.354245156,9.933807373 -0.90969094,-6.706256866,25.66741943,-38.48641968,-0.03387806937,5.956785753e-05,0.01586125046,-0.2603001595,0.413443327,9.827401161 -0.91969094,-8.26845932,25.48729897,-38.77534485,-0.0300873816,0.01511435304,0.008654415607,-0.2779739201,0.3738090396,9.869602203 -0.92969094,-5.997970581,26.58373451,-36.87442017,-0.02735616267,0.04325161129,0.004194807727,-0.3075370789,0.3841251135,9.859921455 -0.93969094,-6.103237152,25.86172104,-39.7224884,-0.02637853473,0.05735059083,0.003129459452,-0.320104301,0.373578757,9.733283043 -0.94969094,-7.90920639,25.94809151,-37.74064636,-0.03651535884,0.0361250788,0.005883715581,-0.3417606056,0.3402200043,9.643527985 -0.95969094,-7.259311676,26.43366051,-37.62400818,-0.05156690255,0.009104821831,0.006214913446,-0.3017233014,0.362526238,9.718540192 -0.96969094,-6.360630035,26.12118149,-38.06161499,-0.05347704142,0.01731107384,0.002591064665,-0.2577753067,0.3784553707,9.827540398 -0.97969094,-6.320072174,26.80979538,-38.11068726,-0.03843300045,0.03169715032,-0.0004796904977,-0.3363775015,0.3911237717,9.874797821 -0.98969094,-8.060348511,26.73391724,-39.78018188,-0.02594162524,0.03712494671,0.0003746603616,-0.4213152528,0.352683723,9.925812721 -0.99969094,-6.769851685,24.69984436,-38.72267151,-0.01860057376,0.03343068063,0.002202911768,-0.4492088258,0.3678621352,10.10691547 -1.00969094,-5.576122284,26.21299934,-37.76467896,-0.001166775823,0.01629613154,0.006246880163,-0.4433344305,0.3575173914,10.19894409 -1.01969094,-7.339931488,25.45729637,-38.62266541,0.02069848031,0.005792571232,0.009291321039,-0.3940068483,0.3785350621,9.979031563 -1.02969094,-4.853103638,25.20479393,-37.23670959,0.02874404564,0.01429859176,0.008222779259,-0.3645473719,0.3816692829,9.829014778 -1.03969094,-6.648670197,26.36482239,-37.77308655,0.02584087476,0.01577451825,0.008752264082,-0.3319935501,0.391680181,9.704668045 -1.04969094,-7.364269257,25.04412842,-38.5932312,0.01320957113,0.02140369639,0.004434604198,-0.2616887093,0.3081431389,9.435370445 -1.05969094,-5.186531067,27.35713577,-37.23646545,-0.01679551601,0.01554101706,0.00070909434,-0.3204491436,0.3072315753,9.469326973 -1.06969094,-6.095619202,25.59858131,-38.63548279,-0.04527019337,-0.02895073779,0.003868098371,-0.3079995215,0.3426241875,9.777582169 -1.07969094,-8.571918488,25.18883705,-37.68516541,-0.05302047729,-0.07532060146,0.007323132362,-0.3083209097,0.3275064826,9.900536537 -1.08969094,-6.433750153,27.6149559,-38.47293091,-0.04696979001,-0.09256293625,0.009241476655,-0.3125772476,0.3005108833,9.937273979 -1.09969094,-7.12210083,26.30832481,-38.07322693,-0.03368562087,-0.08957409114,0.008200418204,-0.2579268813,0.3488419354,9.958529472 -1.10969094,-7.961097717,26.1883812,-40.80989075,-0.01956947893,-0.11456047,0.01081642881,-0.3258158267,0.3308687806,10.03633881 -1.11969094,-6.585765839,24.86577415,-37.81919861,0.004703424871,-0.1317160726,0.01432392001,-0.2255369574,0.315831691,10.02883244 -1.12969094,-6.65839386,26.76041222,-39.3273468,0.03163328022,-0.09282163531,0.007392741274,-0.2747437358,0.4104028642,9.873939514 -1.13969094,-8.267654419,25.22064209,-37.99331665,0.03962652758,-0.04242435843,0.003125044052,-0.2230227441,0.3918129504,9.700381279 -1.14969094,-8.267654419,25.22064209,-37.99331665,0.0334565714,-0.01739601418,-0.0003491293173,-0.2507834136,0.3877517879,9.647592545 -1.15969094,-6.522174835,25.83334923,-37.58294678,0.01399359573,-0.01048641745,-0.0005820093211,-0.2868594825,0.3809897602,9.612992287 -1.16969094,-6.522174835,25.83334923,-37.58294678,-0.009211212397,0.002303284593,-0.002197280526,-0.2456134111,0.3624066412,9.478822708 -1.17969094,-7.220245361,24.92230797,-38.73748779,-0.03906954825,-0.005264308304,-0.002449053805,-0.223413676,0.3052503765,9.675016403 -1.18969094,-8.320838928,26.32410049,-36.39294434,-0.04616601393,-0.01693219133,0.0009987638332,-0.2595686018,0.3547726274,9.831622124 -1.19969094,-6.297035217,27.08875656,-37.82536316,-0.04363083839,-0.01177432761,0.006406869274,-0.3021802008,0.4283012152,9.923987389 -1.20969094,-5.802066803,25.22424889,-38.30429077,-0.03484881297,-0.002998926677,0.005745526869,-0.3050480485,0.3657292128,10.12125111 -1.21969094,-8.227901459,26.17591286,-38.82441711,-0.01179379877,-0.006921730004,0.009910203516,-0.3035609126,0.3256982863,10.10582829 -1.22969094,-6.744827271,27.57963562,-38.46972656,-0.0006591975689,-0.01147360541,0.0132014323,-0.2544778585,0.2881220877,9.779053688 -1.23969094,-7.299373627,26.14591026,-38.67173767,-0.0105999019,-0.00578986574,0.01048056968,-0.1460589617,0.283655107,9.60635376 -1.24969094,-7.473735809,25.58439064,-38.02095032,-0.03279230744,0.02829709277,-0.0002586811315,-0.1317581385,0.2602887452,9.617964745 -1.25969094,-6.430034637,26.94919395,-36.44160461,-0.04902238026,0.0402559936,-0.007753454149,-0.2250817716,0.3019514382,9.776272774 -1.26969094,-6.785385132,26.89102173,-38.4206543,-0.04932562262,0.03157758713,-0.007301884238,-0.2387727499,0.3511135578,9.836483002 -1.27969094,-7.3736763,24.77219772,-38.26863098,-0.04486268014,0.04647804797,-0.007624792866,-0.2069167346,0.3134610355,9.70259285 -1.28969094,-6.866500854,25.51379395,-38.32250977,-0.04943052307,0.0468153283,-0.008807333186,-0.2923846543,0.272872448,9.821870804 -1.29969094,-6.055557251,25.88633156,-37.5877533,-0.04847415164,0.01401153393,3.832438961e-05,-0.2815147936,0.2507653534,9.938878059 -1.30969094,-8.090877533,24.98219299,-36.29800415,-0.0390965566,-0.007415878586,0.002172368579,-0.3992255032,0.3018830121,10.052248 -1.31969094,-6.433750153,27.6149559,-38.47293091,-0.01272503659,0.01556072105,0.006858177949,-0.3525747061,0.3768803477,10.04895401 -1.32969094,-5.842926025,26.20053101,-35.77920532,0.009118192829,0.05159369111,0.003864748403,-0.3418606818,0.3400193453,9.91121769 -1.33969094,-6.688735962,26.07707214,-38.82081604,0.02072456852,0.05234477669,0.00173030328,-0.3416000307,0.3463162482,9.807590485 -1.34969094,-7.080738068,26.73028183,-37.340271,0.02005904168,0.03351046145,0.001531437505,-0.2878206968,0.3056016266,9.60395813 -1.35969094,-8.100597382,25.37778282,-37.85224915,0.005226348527,0.03811069578,-0.003207825124,-0.2599823773,0.2751262188,9.588534355 -1.36969094,-6.607307434,26.7867794,-37.04013062,-0.01296652295,0.01011377014,-0.003455437953,-0.2150486112,0.2730696797,9.68800354 -1.37969094,-7.91601944,25.94457626,-38.04559326,-0.02920342982,-0.03429959714,-0.003262628801,-0.1792165935,0.256131053,9.581644058 -1.38969094,-6.480808258,26.25530624,-36.84997559,-0.05070129409,-0.048120372,-0.005925999023,-0.1703465581,0.202443108,9.596740723 -1.39969094,-5.802066803,25.22424889,-38.30429077,-0.06068585813,-0.06144545227,-0.00397459697,-0.2702684999,0.2518742383,9.811048508 -1.40969094,-6.799507141,26.48312759,-37.93377686,-0.05394846946,-0.08207222819,0.003652619664,-0.3534995615,0.319263339,9.85598278 -1.41969094,-5.760700226,25.6462059,-37.57131958,-0.03585629165,-0.04909897596,0.01270254329,-0.3109368086,0.2954011559,9.816308975 -1.42969094,-7.402030945,26.68968773,-37.79452515,-0.02535336465,-0.01209592633,0.01307334937,-0.3471710384,0.3598883748,9.775583267 -1.43969094,-7.899482727,25.55250168,-36.18638611,-0.01951367408,-0.001619831193,0.0187486317,-0.3248218298,0.3403574526,9.822806358 -1.44969094,-7.186187744,24.93988609,-37.212677,-0.01519426517,-0.008781705052,0.02070770226,-0.3171883821,0.308766067,9.850348473 -1.45969094,-6.514865875,26.23772812,-38.37478638,-0.02264731191,-0.0292735938,0.01713754982,-0.2401963323,0.2161890417,9.744996071 -1.46969094,-7.224758148,26.85210228,-37.19599915,-0.03933265805,-0.05343721062,0.01005422324,-0.0907349363,0.2035675794,9.581429482 -1.47969094,-6.823036194,25.80330276,-37.12229919,-0.06534571946,-0.08001222461,0.001245157328,-0.05353847891,0.21554254,9.619607925 -1.48969094,-5.802066803,25.22424889,-38.30429077,-0.08012887836,-0.1155415028,-0.008874082938,-0.07394784689,0.2216080427,9.712301254 -1.49969094,-7.926235199,25.93930244,-38.50305176,-0.08614850789,-0.1406992674,-0.01109797321,-0.1761151403,0.2405264229,9.942374229 -1.50969094,-8.060848236,26.33305359,-38.68336487,-0.06363983452,-0.131477356,-0.008736706339,-0.1644631177,0.2284149528,10.12247753 -1.51969094,-5.934570312,25.48554993,-38.01734924,-0.03686951846,-0.1067624614,-0.009261385538,-0.2282977104,0.1928204149,10.15298653 -1.52969094,-6.481929779,27.18948364,-39.51086426,-0.01791328192,-0.08309015632,-0.00313524832,-0.2743725479,0.21198605,10.15187454 -1.53969094,-7.875953674,26.23232651,-36.99786377,-0.008983654901,-0.07694666088,0.002590309829,-0.2886238396,0.1790893525,10.07465935 -1.54969094,-7.529022217,26.82029724,-36.88783264,-0.009366864339,-0.07829668373,0.01138180681,-0.1872850209,0.1983563304,9.777256966 -1.55969094,-7.105072021,26.31711388,-37.31082153,-0.03254691884,-0.04712443799,0.01258446462,-0.1739229262,0.2279125303,9.533390045 -1.56969094,-7.194416046,27.53544235,-37.70251465,-0.05746591091,-0.001762664411,0.009799225256,-0.1670654416,0.1957996339,9.661688805 -1.57969094,-7.11448288,26.04518318,-36.98625183,-0.06772604585,0.003953762818,0.0142403841,-0.2083566934,0.2039169073,9.748625755 -1.58969094,-7.978923798,27.4436245,-37.99946594,-0.07317771018,-0.02151427604,0.02289812267,-0.2312756628,0.2643887997,9.760647774 -1.59969094,-5.639026642,27.71204758,-37.71853638,-0.06665766984,-0.04223006219,0.02425481752,-0.1424741298,0.2968740463,9.914733887 -1.60969094,-5.639026642,27.71204758,-37.71853638,-0.04367075861,-0.04056430608,0.0234865211,-0.1608794183,0.3256749511,10.09056568 -1.61969094,-7.868648529,26.6367054,-37.78971863,-0.008530608378,-0.04159038514,0.02200041711,-0.189670369,0.2899492383,10.21168613 -1.62969094,-7.868648529,26.6367054,-37.78971863,0.02628235519,-0.05120419711,0.02283840254,-0.2218288481,0.3283816576,10.20733452 -1.63969094,-6.303535461,26.41772079,-36.2514801,0.05489103496,-0.03783921897,0.02347395197,-0.07787474245,0.2735392451,9.849228859 -1.64969094,-8.169509888,26.60665894,-37.32905579,0.04711384699,-0.003802698106,0.01128105074,-0.0208355207,0.2059793174,9.5058918 -1.65969094,-6.614925385,27.04992104,-38.12710571,0.01760414243,0.01516226307,0.0005188670475,-0.01495215017,0.2007467449,9.477257729 -1.66969094,-7.610450745,26.11058998,-38.66853333,-0.0118032163,0.003432350932,-0.004136003554,-0.1004913151,0.2752893269,9.617282867 -1.67969094,-5.836917877,26.47070312,-36.25628662,-0.03128357977,-0.01314315572,-0.003888061969,-0.1696598381,0.2897028029,9.776549339 -1.68969094,-8.07756424,25.656744,-37.56692505,-0.03902820498,-0.031104194,0.0003777854145,-0.1696388274,0.3016649187,9.855332375 -1.69969094,-8.01266861,26.75852585,-37.64543152,-0.0361819528,-0.04390206188,0.004108215682,-0.08676551282,0.2340180129,9.961453438 -1.70969094,-6.815727234,26.20768166,-37.91413879,-0.02684909478,-0.06165890396,0.004194807727,-0.08061584085,0.2390247434,10.00907612 -1.71969094,-8.774143219,26.94566917,-37.65705872,-0.006290666759,-0.06052558869,0.005260156002,-0.09469732642,0.2780058086,9.99105072 -1.72969094,-6.751327515,26.90859985,-36.89584351,0.009505183436,-0.03102665953,0.002138116863,-0.1390231103,0.2646279931,9.884099007 -1.73969094,-7.415344238,26.01513672,-36.52558899,0.005679448135,0.004524962511,0.001461617649,-0.008892144077,0.1571245342,9.586317062 -1.74969094,-6.648670197,26.36482239,-37.77308655,-0.01966399699,0.02597477287,-0.003588251071,-0.02148039639,0.06291545182,9.521533966 -1.75969094,-7.916511536,25.54371262,-36.9487915,-0.04305823147,0.0223974213,-0.003466613824,-0.0427948162,0.1390912533,9.611943245 -1.76969094,-8.504920959,26.15816879,-37.29640198,-0.05958180875,0.0147998305,-0.003130199853,-0.0402473025,0.1930487752,9.696281433 -1.77969094,-7.408531189,26.01865196,-36.22064209,-0.06862412393,0.02192832902,-0.002607707866,-0.1067857593,0.1676802784,9.801758766 -1.78969094,-7.651504517,25.02111244,-37.5226593,-0.06073463336,0.009087075479,-0.00118764746,-0.1356327534,0.1706100255,9.846673965 -1.79969094,-7.519615173,27.09222794,-37.21243286,-0.05577040464,-0.02627890185,0.006700108293,-0.1201330051,0.2078993767,9.865870476 -1.80969094,-7.292560577,26.14942551,-38.36677551,-0.045699507,-0.05088428408,0.009521549568,-0.1055382341,0.194249332,9.946352005 -1.81969094,-8.941196442,26.78852844,-37.79812622,-0.02589777857,-0.03927788138,0.007972843945,-0.1263555437,0.2195701152,9.982694626 -1.82969094,-8.136257172,26.89089394,-36.58627319,-0.0022355197,-0.007897994481,0.00841078721,-0.13340047,0.2100664824,9.810973167 -1.83969094,-7.91601944,25.94457626,-38.04559326,0.006072483025,0.02714213356,0.008006671444,-0.08557648957,0.1570303142,9.632401466 -1.84969094,-6.8097229,26.47785378,-38.39122009,-0.003367578145,0.0620284602,0.001605473459,-0.1225117221,0.1410100162,9.67570591 -1.85969094,-7.31608963,25.46960068,-37.55529785,-0.0129718259,0.06267061085,0.002064112108,-0.1651064754,0.1498811394,9.766771317 -1.86969094,-8.440029144,27.25995255,-37.37492371,-0.01939735189,0.02667776495,0.001498597208,-0.07774727792,0.1837403625,9.869438171 -1.87969094,-6.269790649,27.10281944,-36.60549927,-0.01915593445,-0.02103452012,-0.003069892991,-0.06515015662,0.1889760047,9.92991066 -1.88969094,-6.245456696,27.5159874,-36.63494873,-0.006940102205,-0.03827885538,-0.005393324886,-0.1165964454,0.1897324771,9.984901428 -1.89969094,-8.234401703,25.50487709,-37.25053406,0.01909381524,-0.03864673525,-0.004890202545,-0.1061906591,0.1626597643,10.02290344 -1.90969094,-6.614612579,26.38240051,-36.24827576,0.04109552875,-0.06041933596,-0.003773062024,-0.1020373926,0.171957165,9.990610123 -1.91969094,-7.17206955,25.34778023,-37.69958496,0.05656939,-0.05699628592,-0.003241203493,-0.09723923355,0.2134001702,9.856469154 -1.92969094,-8.266967773,27.6872654,-37.7109375,0.05981186032,-0.0220338013,-0.0090006385,-0.09895791858,0.1922320724,9.676743507 -1.93969094,-7.080738068,26.73028183,-37.340271,0.04462248087,0.01481336262,-0.01446673647,-0.07513602078,0.1447238624,9.591789246 -1.94969094,-5.692897797,26.34888268,-36.40054321,0.01682722196,0.01619235054,-0.01550634205,-0.04328014329,0.08844234794,9.693587303 -1.95969094,-8.033290863,25.67959595,-35.58467102,-0.004610041622,-0.01159483008,-0.01657947898,-0.06921155751,0.1747955829,9.817505836 -1.96969094,-6.930095673,24.54621887,-38.55877686,-0.01155486237,-0.02273061872,-0.01285894588,-0.1380582452,0.2311763912,9.925704956 -1.97969094,-7.033367157,27.42241287,-37.08439636,-0.007763602771,-0.007799980231,-0.0128642004,-0.08171288669,0.2393242121,9.866889954 -1.98969094,-8.105110168,27.30757713,-36.31077576,0.000226826407,0.007088335231,-0.01391610876,-0.07211033255,0.1989753842,9.786602974 -1.99969094,-8.221088409,26.1794281,-38.51947021,0.007151844911,0.01721406542,-0.0149721168,-0.1541464329,0.2528064251,9.78149128 -2.00969094,-6.014190674,26.30828857,-36.8547821,0.01787129417,0.0273047667,-0.01124131307,-0.2166621983,0.3024373949,9.920562744 -2.01969094,-6.497837067,26.24651718,-37.61238098,0.02909519151,0.02771151997,-0.009713578969,-0.1204545349,0.2341998667,10.05337238 -2.02969094,-7.641288757,25.02638626,-37.06521606,0.03541889042,0.009614607319,-0.01449812017,-0.0005609099171,0.1977932751,9.951425552 -2.03969094,-6.648670197,26.36482239,-37.77308655,0.03854234144,-0.002443780191,-0.02357402071,0.02601853013,0.1741227955,9.71210289 -2.04969094,-6.823036194,25.80330276,-37.12229919,0.03266156837,0.002902421635,-0.03311468288,-0.06614656001,0.2000939846,9.64405632 -2.05969094,-8.481391907,26.83799362,-38.10787964,0.03341981024,0.004892181139,-0.03618502244,-0.1074707657,0.162389487,9.802295685 -2.06969094,-8.481391907,26.83799362,-38.10787964,0.04172888771,-0.004742521793,-0.03478867188,-0.08618438244,0.1765760481,9.902980804 -2.07969094,-9.125282288,26.62259865,-38.70158386,0.04610145465,-0.01111476123,-0.03636684269,-0.1150004193,0.2032267451,9.933595657 -2.08969094,-7.097454071,26.05397224,-36.22384644,0.04376486316,-0.005667108111,-0.03837662563,-0.1557287276,0.1533870399,9.807321548 -2.09969094,-8.169509888,26.60665894,-37.32905579,0.03125278652,-0.001980882138,-0.03598660603,-0.1652607918,0.2026120722,9.855483055 -2.10969094,-7.881961823,25.96215439,-36.52078247,0.0225853771,-0.01276575774,-0.03193943202,-0.1723911315,0.2648062408,9.907776833 -2.11969094,-8.129444122,26.89440918,-36.28132629,0.0193426609,-0.01047625113,-0.02931698225,-0.1844214499,0.2498556525,9.777618408 -2.12969094,-8.090877533,24.98219299,-36.29800415,0.01299724448,0.03212872893,-0.02499904484,-0.1594483554,0.185912326,9.599106789 -2.13969094,-6.583778381,27.46660423,-37.85160828,0.003714879043,0.0774788782,-0.02019474469,-0.1376511902,0.1288460642,9.618491173 -2.14969094,-8.394954681,26.01877022,-38.96549988,-0.004882628098,0.07974929363,-0.01858952455,-0.1264213771,0.2041591108,9.738189697 -2.15969094,-7.352062225,27.65023232,-38.16816711,-0.01172669791,0.06241033226,-0.01871017553,-0.1282311976,0.2556934357,9.854340553 -2.16969094,-6.314064026,27.0799675,-38.58776855,-0.007096226327,0.06087465584,-0.02018002048,-0.140379414,0.2431407869,9.895014763 -2.17969094,-7.603637695,26.11410522,-38.36357117,-0.0009169559926,0.05228283256,-0.02110611834,-0.2296079248,0.2668908536,9.884092331 -2.18969094,-7.875953674,26.23232651,-36.99786377,0.01122065168,0.03513076901,-0.0161176268,-0.2607826889,0.2880160809,9.85874939 -2.19969094,-7.865737915,26.23760033,-36.54040527,0.01991503313,0.01621211134,-0.01369080879,-0.2125987709,0.2349860221,9.828289986 -2.20969094,-7.449401855,25.99755859,-38.05039978,0.02593856305,0.001058021793,-0.01391610876,-0.1139675379,0.1974376589,9.821710587 -2.21969094,-8.971538544,26.10518837,-37.29159546,0.03127063811,-0.00808400847,-0.01858368888,-0.07830224186,0.1740580052,9.894181252 -2.22969094,-8.855258942,25.56844139,-37.55891418,0.04016362131,-0.01468131132,-0.02297156677,-0.08382807672,0.1723902375,9.791069031 -2.23969094,-6.840065002,25.7945137,-37.88470459,0.03510941565,-0.005808715709,-0.02527568862,-0.09655064344,0.2753678262,9.775838852 -2.24969094,-6.344406128,26.39662743,-38.08123779,0.03261594847,-0.001005770639,-0.0267905239,-0.1564678401,0.3032001257,9.954015732 -2.25969094,-6.631149292,26.7744751,-38.10748291,0.03750025481,-0.0004138355143,-0.02738986723,-0.2111902684,0.2499201596,9.906594276 -2.26969094,-5.894012451,26.17416382,-38.06642151,0.03352537006,-0.006534338929,-0.02287421189,-0.185904786,0.256785661,9.880761147 -2.27969094,-7.282344818,26.15469933,-37.90933228,0.02651475742,-0.01083567925,-0.01977552287,-0.1777724028,0.3054589629,9.881853104 -2.28969094,-7.128601074,25.63728905,-36.49934387,0.02215582877,-0.01636308059,-0.01840080321,-0.200542137,0.2789077759,9.947379112 -2.29969094,-8.145980835,27.28648376,-38.14053345,0.0253528133,-0.01774885505,-0.01157522202,-0.2354736328,0.327449441,10.0650425 -2.30969094,-7.559364319,26.13695717,-36.38131714,0.03911292553,-0.01410826668,-0.0002708618995,-0.2110538632,0.3184655607,10.03411579 -2.31969094,-8.043819427,26.34184265,-37.92095947,0.05202747509,0.004895074293,0.007309973706,-0.1512760371,0.3286831975,9.895553589 -2.32969094,-8.00245285,26.76379967,-37.18798828,0.04810490087,0.02173821256,0.008107297122,-0.08198977262,0.3075869679,9.735380173 -2.33969094,-7.408843994,26.68617249,-38.09947205,0.03104375303,0.02506607771,0.008456200361,-0.1293141246,0.3014782965,9.657446861 -2.34969094,-7.115287781,26.31184006,-37.76828003,0.008865046315,0.01515081618,0.01111957058,-0.1489645094,0.3126883209,9.760924339 -2.35969094,-8.885910034,25.55262184,-38.9312439,-0.01044041198,0.005883761216,0.01086863503,-0.1335579604,0.3199432492,9.854743004 -2.36969094,-8.196754456,26.59259605,-38.54891968,-0.01020090934,0.02274373919,0.009521549568,-0.1970662028,0.3293652236,9.818229675 -2.37969094,-8.354896545,26.30652237,-37.91775513,-0.004453454632,0.0365036875,0.01297717914,-0.2366603762,0.3252747357,9.787967682 -2.38969094,-8.15328598,26.88210487,-37.34867859,-0.002847630531,0.03556828201,0.01924502663,-0.2097179592,0.2747732401,9.877931595 -2.39969094,-7.432373047,26.00634766,-37.28799438,0.001555474475,0.03062932193,0.02191158198,-0.1824806482,0.2579643428,9.950833321 -2.40969094,-7.249095917,26.43893433,-37.16656494,0.006780474447,0.03764603287,0.02351267263,-0.1464302391,0.268522054,9.896903038 -2.41969094,-7.10426712,26.050457,-36.52879333,0.01062327344,0.04378296435,0.01857700758,-0.1064542532,0.2074498385,9.702510834 -2.42969094,-7.85161972,26.64549637,-37.02731323,0.002669626847,0.02873747237,0.0129544735,-0.04887576029,0.2210505307,9.567022324 -2.43969094,-7.363460541,24.77747154,-37.81118774,-0.0103612449,0.005181383342,0.009140042588,-0.1335268766,0.2172933221,9.608876228 -2.44969094,-6.644954681,25.69906044,-35.741745,-0.01882448047,-0.004420454614,0.008147323504,-0.09323616326,0.2520536184,9.751316071 -2.45969094,-7.416149139,26.28179359,-37.30761719,-0.02168631367,-0.001290564891,0.004412197508,-0.103556484,0.2825931907,9.850436211 -2.46969094,-8.162696838,26.61017418,-37.02410889,-0.01086263545,0.006069350056,-0.0008704911452,-0.1312154382,0.2413202971,9.851606369 -2.47969094,-7.145629883,25.62849998,-37.26174927,-0.003843489569,0.01436809357,-0.001623692689,-0.1820041239,0.2070912719,9.713765144 -2.48969094,-7.275531769,26.15821457,-37.60437012,-0.009312344715,0.01892914996,-0.001100572641,-0.1220566928,0.1783026159,9.680235863 -2.49969094,-7.507480621,24.89929199,-37.66693115,-0.01717316359,0.02942520566,-0.003816983197,-0.06498123705,0.1999483556,9.725384712 -2.50969094,-7.90920639,25.94809151,-37.74064636,-0.02303904109,0.02805838361,-0.005925999023,-0.1256715059,0.2209081352,9.832509995 -2.51969094,-7.90920639,25.94809151,-37.74064636,-0.02268951386,0.01364582684,-0.003972060047,-0.2051095217,0.225720942,9.763577461 -2.52969094,-6.614120483,26.78326416,-37.34507751,-0.02658636495,0.01437338069,0.002244035713,-0.1648696512,0.2123297453,9.703748703 -2.53969094,-6.614120483,26.78326416,-37.34507751,-0.02037812956,0.02540178038,0.003479044419,-0.1159800962,0.2158698142,9.719527245 -2.54969094,-7.148540497,26.02760506,-38.51106262,-0.0118260067,0.03690432012,0.003315735608,-0.1407853812,0.261910826,9.750429153 -2.55969094,-8.043819427,26.34184265,-37.92095947,-0.006829578429,0.02317268588,0.004384300672,-0.1386529505,0.3286429048,9.795340538 -2.56969094,-8.094593048,25.64795494,-38.32933044,0.003840013407,0.007176350802,0.001100537833,-0.06916922331,0.3458430767,9.878037453 -2.57969094,-6.825942993,26.20240784,-38.37158203,0.008876708336,0.0003457232378,-0.006905544549,-0.08735194057,0.3348065913,9.828860283 -2.58969094,-8.531360626,25.87745094,-37.73423767,0.005340719596,-0.00217997143,-0.01371701993,-0.126806885,0.3439081907,9.871929169 -2.59969094,-6.774856567,26.22877502,-36.08436584,0.0004153251648,-0.01131269149,-0.01645131782,-0.1413100064,0.3308411539,9.938947678 -2.60969094,-7.85161972,26.64549637,-37.02731323,-0.004786434583,-0.003533433191,-0.02084087208,-0.1905422211,0.3367064893,9.995604515 -2.61969094,-6.839752197,25.12699318,-36.00585938,-0.003631051164,0.02111057006,-0.02646948397,-0.1596919447,0.3178349733,9.962801933 -2.62969094,-6.727798462,27.58842468,-37.70732117,-0.002874007449,0.04105066508,-0.02904283628,-0.1408151686,0.3369486332,9.898715019 -2.63969094,-7.459617615,25.99228477,-38.50785828,-0.008197206073,0.04637994617,-0.03437278792,-0.1868165284,0.3002360463,9.948604584 -2.64969094,-7.563079834,26.80271912,-38.41264343,-0.009471255355,0.04070337862,-0.03384329379,-0.1625285,0.3253901303,10.03444958 -2.65969094,-6.765449524,26.50070572,-36.40896606,0.001410008408,0.02944688685,-0.03491182253,-0.1994553506,0.3648633659,9.999612808 -2.66969094,-6.336788177,26.13348579,-36.9942627,0.009185058065,0.03163524717,-0.03544767573,-0.2142654508,0.3202452958,9.846342087 -2.67969094,-7.402030945,26.68968773,-37.79452515,0.01375771593,0.03424095362,-0.03149435297,-0.1759790331,0.2779944837,9.759856224 -2.68969094,-6.60439682,26.38767433,-35.79081726,0.0170276463,0.04406008124,-0.02959464677,-0.1392083019,0.281003803,9.720905304 -2.69969094,-8.084377289,25.65322876,-37.87188721,0.01672596484,0.04845323414,-0.03155862913,-0.1621757448,0.2420970947,9.75135231 -2.70969094,-6.504650116,26.24300194,-37.91734314,0.009563808329,0.04708301276,-0.02664239891,-0.1740728915,0.2218662202,9.769033432 -2.71969094,-7.81186676,27.60076523,-37.8584137,-7.901713252e-05,0.03207566217,-0.02283397503,-0.26397264,0.2458071113,9.813800812 -2.72969094,-7.875461578,26.63319016,-38.09466553,-0.007686767727,0.004682029597,-0.0156044066,-0.2840597332,0.180323258,9.903401375 -2.73969094,-6.792198181,26.88750458,-38.72561646,-0.008024005219,-0.02341856062,-0.004444222432,-0.2712144554,0.2306922525,9.956364632 -2.74969094,-7.576889038,25.72730446,-36.04692078,0.001746371388,-0.02758159675,0.001749235205,-0.2706443071,0.2705234885,9.986332893 -2.75969094,-7.259311676,26.43366051,-37.62400818,0.01849942282,-0.009009528905,0.01289303042,-0.2955932617,0.2673327327,9.963009834 -2.76969094,-7.399120331,26.29058266,-36.54521179,0.03639570996,0.008357273415,0.01962275989,-0.2361007482,0.2875300348,9.913796425 -2.77969094,-7.133922577,27.83374023,-35.73989868,0.04758138955,0.0147917401,0.02016180754,-0.1789459288,0.2186049521,9.904644012 -2.78969094,-7.132316589,26.30305099,-38.53068542,0.05693943799,0.003899033181,0.02071453631,-0.09112010896,0.210464716,9.896371841 -2.79969094,-7.495277405,27.50539398,-37.24186707,0.0550789088,-0.01156693138,0.01165349409,-0.1010814682,0.2988709211,9.751430511 -2.80969094,-7.276340485,26.42487144,-38.38641357,0.04573579505,-0.02328784391,0.003650189843,-0.1644459963,0.2800166309,9.548920631 -2.81969094,-7.063709259,26.73907089,-36.5778656,0.01850998029,-0.01182917133,-0.004340337589,-0.1469102055,0.2510396242,9.466347694 -2.82969094,-6.741111755,26.91387177,-36.43840027,-0.004324021749,-0.01375587657,-0.008865067735,-0.1834320128,0.2701443136,9.711741447 -2.83969094,-6.631641388,26.37361145,-37.01068115,-0.001867462881,-0.02513966337,-0.0110240262,-0.1669787318,0.3215048611,9.896787643 -2.84969094,-7.425559998,26.0098629,-36.98304749,0.01628770307,-0.02488586679,-0.01466791332,-0.2579631507,0.3567141593,9.860242844 -2.85969094,-8.243812561,25.2329464,-36.92596436,0.02688464522,-0.004169604741,-0.01387823559,-0.2582223117,0.2771977484,9.736753464 -2.86969094,-8.193843842,26.19349098,-37.29960632,0.02200842649,0.03450661153,-0.01309495978,-0.272795558,0.2449849695,9.676970482 -2.87969094,-6.823036194,25.80330276,-37.12229919,0.006453162991,0.05085517466,-0.006890017539,-0.3061911166,0.2199545354,9.631800652 -2.88969094,-7.40933609,26.28530884,-37.00267029,-0.01606200263,0.04909879714,0.003005686682,-0.2995644808,0.201525405,9.73643589 -2.89969094,-7.641288757,25.02638626,-37.06521606,-0.02808204293,0.04346767068,0.006027473602,-0.1448681653,0.272339046,9.628669739 -2.90969094,-7.46692276,25.58790588,-37.71600342,-0.04177628458,0.04816025496,0.002225785982,-0.116304405,0.2411215901,9.453680038 -2.91969094,-6.624336243,26.77799034,-37.80253601,-0.05434793979,0.06665814668,-0.006078326143,-0.2214545161,0.2327720374,9.527388573 -2.92969094,-5.72095108,26.60147667,-38.4024353,-0.06135207415,0.06435563415,-0.004860650748,-0.2514836788,0.2456260026,9.681171417 -2.93969094,-7.189098358,25.33899117,-38.46199036,-0.05704882741,0.0275369063,0.0003332057968,-0.2850147784,0.2860251069,9.833475113 -2.94969094,-7.529335022,27.48781586,-38.76667786,-0.04562484473,0.002622469561,0.00259678578,-0.2471337616,0.2965653837,9.949759483 -2.95969094,-8.473773956,26.5748539,-37.02090454,-0.02654643729,0.0005687286612,0.001860674936,-0.2698851526,0.3216832578,10.0168438 -2.96969094,-7.249095917,26.43893433,-37.16656494,-0.006175159011,-6.634369493e-05,0.001427219715,-0.2811920643,0.2962718606,10.01611328 -2.97969094,-7.450206757,26.26421547,-38.83242798,0.006415885873,-0.007441923022,0.002711253706,-0.2567391992,0.20896779,9.862981796 -2.98969094,-7.450206757,26.26421547,-38.83242798,0.0108665498,-0.009281657636,0.00197905954,-0.1455981433,0.2174957097,9.758725166 -2.99969094,-6.447872162,27.20706177,-37.98605347,0.004828017205,0.01278313715,-0.00765129365,-0.09340839088,0.2501848638,9.700984001 -3.00969094,-6.447872162,27.20706177,-37.98605347,-0.003581494559,0.02978005633,-0.01544828713,-0.1543234587,0.2795874476,9.821495056 -3.01969094,-8.49810791,26.16168594,-36.99145508,-0.009767705575,0.02843782678,-0.01857014373,-0.2008584142,0.3034571409,9.921107292 -3.02969094,-8.06734848,25.66201782,-37.10948181,-0.01092633884,0.01583724841,-0.01798436232,-0.2255139798,0.2753494382,9.935453415 -3.03969094,-6.695236206,25.40603638,-37.24693298,-0.01146858186,0.002507640515,-0.01536135934,-0.2470640689,0.2939111292,9.952013969 -3.04969094,-7.316898346,25.73625755,-38.33734131,-0.01291702595,-0.005841163918,-0.01194457337,-0.2390746474,0.2554622889,9.902705193 -3.05969094,-7.374786377,26.70375061,-36.57466125,-0.01152374595,-0.02042293362,-0.009471164085,-0.2179525644,0.2555968761,9.93608284 -3.06969094,-7.333118439,25.46081161,-38.31770325,-0.008825613186,-0.03340864927,-0.007532409392,-0.2380346656,0.3015532494,9.908273697 -3.07969094,-8.468456268,24.37840271,-37.78034973,-0.005647540092,-0.02399409376,-0.006282707676,-0.1862966716,0.2948144078,9.761417389 -3.08969094,-6.474308014,26.92634201,-38.42385864,-0.008505098522,0.01370482426,-0.008589369245,-0.2072754353,0.290933311,9.65144825 -3.09969094,-7.194416046,27.53544235,-37.70251465,-0.01222948264,0.04708583653,-0.009292855859,-0.1612217426,0.2714120448,9.722549438 -3.10969094,-7.488464355,27.50891113,-36.93690491,-0.01520463172,0.05751100183,-0.009822325781,-0.2179809064,0.2705034912,9.771174431 -3.11969094,-7.842208862,26.91742516,-37.35188293,-0.01752957702,0.04657791555,-0.01018739119,-0.20446226,0.3115225434,9.746862411 -3.12969094,-6.607307434,26.7867794,-37.04013062,-0.02082055621,0.03212425858,-0.01252835803,-0.284610033,0.3318831623,9.818239212 -3.13969094,-8.050319672,25.67080688,-36.34707642,-0.02162656561,0.006840092596,-0.01146937907,-0.2710173726,0.335179776,9.85300827 -3.14969094,-6.631641388,26.37361145,-37.01068115,-0.01494333707,-0.003114380408,-0.01216324605,-0.293641299,0.3532663584,9.856822014 -3.15969094,-8.227588654,25.50839233,-36.94558716,-0.00701501593,0.003271889873,-0.01338343509,-0.3079952896,0.2997109592,9.897614479 -3.16969094,-7.115287781,26.31184006,-37.76828003,0.0001519322395,0.005005017854,-0.01125273854,-0.2283413708,0.3019617796,9.842337608 -3.17969094,-7.956577301,25.25596237,-37.996521,0.008300120942,-0.003143170848,-0.01512670144,-0.1571785808,0.2411979139,9.994022369 -3.18969094,-6.794185638,24.2866745,-38.69322205,0.01882113516,-0.01369971037,-0.02005962282,-0.1790753752,0.2787266076,10.03287792 -3.19969094,-7.600730896,25.71500015,-37.11428833,0.0226082243,-0.01807713695,-0.01910396852,-0.2966942191,0.2534326017,9.856265068 -3.20969094,-7.275531769,26.15821457,-37.60437012,0.01845968887,-0.02622262016,-0.01432321966,-0.2260305434,0.2392146587,9.849489212 -3.21969094,-8.507831573,26.55727577,-38.54571533,0.01486025844,-0.02195211686,-0.01205309667,-0.1015152261,0.2238649428,9.757564545 -3.22969094,-8.193843842,26.19349098,-37.29960632,0.007541217841,-0.009552078322,-0.01419021189,-0.1901240647,0.2519818842,9.725886345 -3.23969094,-8.170314789,26.87331581,-38.11108398,-0.002043720335,-0.002106974367,-0.01097225025,-0.3190883398,0.2920241356,9.870922089 -3.24969094,-7.138324738,26.03287888,-38.05360413,-0.002122517675,0.001215161756,-0.002811893821,-0.3439500332,0.3194630742,9.970935822 -3.25969094,-5.583427429,25.80862045,-36.9728241,0.004528208636,0.01517218351,0.0009987638332,-0.246371001,0.294268012,9.926848412 -3.26969094,-6.74079895,26.24635315,-34.55955505,0.01069999952,0.0292138122,0.003129459452,-0.2038469911,0.2858913839,9.858620644 -3.27969094,-7.333118439,25.46081161,-38.31770325,0.01235124376,0.03107888252,0.0003867314663,-0.1960114241,0.3041076958,9.8361063 -3.28969094,-6.695236206,25.40603638,-37.24693298,0.01681809872,0.02263225801,4.35502734e-05,-0.2625229657,0.3344432414,9.877408981 -3.29969094,-8.365112305,26.30124855,-38.37519836,0.01864273101,0.02571816929,-0.001450675773,-0.3248307705,0.3089620173,9.92305851 -3.30969094,-5.703922272,26.61026573,-37.64002991,0.0246123001,0.02688729018,-0.004978229757,-0.1456692666,0.2753237486,9.839014053 -3.31969094,-6.840065002,25.7945137,-37.88470459,0.01809304953,0.03596933186,-0.01544332877,-0.1127016768,0.1825166345,9.618488312 -3.32969094,-7.235782623,27.11348534,-38.43548584,0.0004593646154,0.0279977899,-0.0206976831,-0.2280323654,0.2231822312,9.626499176 -3.33969094,-8.49810791,26.16168594,-36.99145508,-0.01300951093,-0.008212786168,-0.01676802337,-0.2872509062,0.2894925773,9.755972862 -3.34969094,-8.084686279,26.32074928,-39.75073242,-0.01604039222,-0.02611956932,-0.01259132661,-0.3058491349,0.2855690122,9.846001625 -3.35969094,-7.926235199,25.93930244,-38.50305176,-0.01576715335,-0.02967993356,-0.007701023482,-0.2758152187,0.2458155155,9.883844376 -3.36969094,-7.593421936,26.11937904,-37.90612793,-0.0165593382,-0.02559154853,-0.004947154783,-0.2677181363,0.2292198688,9.824416161 -3.37969094,-7.408531189,26.01865196,-36.22064209,-0.01147498749,-0.008676798083,-0.000233884668,-0.288110882,0.2577656209,9.797032356 -3.38969094,-7.148540497,26.02760506,-38.51106262,-0.006633196957,0.01344986074,-0.0008964412846,-0.3024396598,0.3181247711,9.880328178 -3.39969094,-6.470592499,26.26058006,-36.39253235,0.00531923119,0.03014460765,0.0009987638332,-0.2945103049,0.3188195527,9.94841671 -3.40969094,-8.007350922,24.56207466,-38.40490723,0.01756862178,0.03873810172,0.00289370399,-0.2079023421,0.2359249741,9.747755051 -3.41969094,-10.57120132,25.9057312,-39.25784302,0.01010619756,0.05189544708,-0.001664606389,-0.1738738865,0.1913950294,9.680673599 -3.42969094,-7.552864075,26.80799103,-37.9552002,-0.0005048532039,0.03880349547,-0.008658854291,-0.194183588,0.2359473258,9.722563744 -3.43969094,-8.193843842,26.19349098,-37.29960632,-0.01316022221,0.007196725346,-0.008648898453,-0.3240816891,0.297080785,9.751264572 -3.44969094,-8.193843842,26.19349098,-37.29960632,-0.01896319911,0.002648235299,-0.00752402097,-0.348402977,0.307802856,9.702807426 -3.45969094,-7.282344818,26.15469933,-37.90933228,-0.02673238888,0.003566776402,-0.004272747319,-0.3012737632,0.2431114316,9.759227753 -3.46969094,-7.282344818,26.15469933,-37.90933228,-0.02919545397,-0.002234932035,-6.658444181e-05,-0.3082113266,0.2360342592,9.839511871 -3.47969094,-7.601039886,26.38251877,-38.99313354,-0.02647368982,-0.0003828592598,0.004247863777,-0.3241005838,0.2539158165,9.896888733 -3.48969094,-7.70747757,23.79039574,-36.67192078,-0.01391642354,0.005569877103,0.005652846303,-0.3335336745,0.2924467623,9.974705696 -3.49969094,-7.466117859,25.32124901,-36.93397522,-0.002039574087,0.006009228528,0.008374901488,-0.3174402714,0.2626022398,9.936785698 -3.50969094,-6.774856567,26.22877502,-36.08436584,0.005044433288,-0.002327063121,0.009446952492,-0.3287468851,0.2626532614,9.900091171 -3.51969094,-6.638454437,26.37009621,-37.31562805,0.009231860749,-0.0183471106,0.01005422324,-0.2419033647,0.2037884295,9.751329422 -3.52969094,-8.237312317,25.90398216,-38.49984741,-0.0002129487693,-0.0351838246,0.006396608893,-0.2397907376,0.2444081306,9.79660511 -3.53969094,-7.794345856,28.01041794,-38.19281006,-0.001384953968,-0.05765299499,0.004162475467,-0.2784003317,0.2528136969,9.876300812 -3.54969094,-6.01499939,26.57494545,-37.63682556,0.01022412907,-0.04971811175,0.004277116619,-0.2988288105,0.2813760936,9.848402023 -3.55969094,-7.399120331,26.29058266,-36.54521179,0.02244941518,-0.01563763618,0.004172147717,-0.3169019222,0.3055851161,9.799267769 -3.56969094,-7.289653778,25.75032043,-37.11749268,0.0274567008,0.02185425535,0.001589378342,-0.233324185,0.2500683963,9.69851017 -3.57969094,-7.165569305,26.01881599,-39.27346802,0.02480689436,0.02791698091,-0.003749892116,-0.3112936914,0.2143467516,9.725136757 -3.58969094,-6.753314972,24.30776978,-36.86346436,0.01731849089,0.004670565017,-0.003262628801,-0.3077295125,0.2493745536,9.750574112 -3.59969094,-6.712265015,25.39724731,-38.00933838,0.004657322541,-0.01840234734,0.002560213674,-0.3492403328,0.2538228631,9.572223663 -3.60969094,-8.330558777,26.71968842,-37.94718933,-0.01725715771,-0.02274436876,0.00684737647,-0.3248106241,0.2503971457,9.603363991 -3.61969094,-6.785385132,26.89102173,-38.4206543,-0.02854960784,-0.01157763973,0.008985424414,-0.313901335,0.2334174663,9.79224205 -3.62969094,-6.785385132,26.89102173,-38.4206543,-0.03440004215,0.01084966771,0.01005065627,-0.2761927247,0.2305997759,9.864484787 -3.63969094,-7.546051025,26.81150818,-37.65023804,-0.02321261168,0.03533025831,0.0105829034,-0.2884257436,0.258186996,9.792014122 -3.64969094,-7.489959717,25.3089447,-38.00132751,-0.01417162456,0.04542586207,0.01005422324,-0.3501100838,0.2739596069,9.894083023 -3.65969094,-7.131511688,26.03639412,-37.74865723,-0.0024527614,0.04169920832,0.01005422324,-0.3122108281,0.283588022,9.821797371 -3.66969094,-8.243812561,25.2329464,-36.92596436,0.001286050305,0.04755017161,0.006820009556,-0.3645493984,0.2892889977,9.780883789 -3.67969094,-8.11762619,25.36899376,-38.61465454,0.002876439132,0.03516138345,0.006307389122,-0.3239471614,0.2848519981,9.735665321 -3.68969094,-6.440250397,26.94392014,-36.89904785,-0.0002477457747,0.0002992372029,0.007414806169,-0.2544038296,0.3007819951,9.809139252 -3.69969094,-7.536643982,27.08343887,-37.97483826,0.003989266232,-0.05071535707,0.00947406888,-0.2319714129,0.3080909848,9.878863335 -3.70969094,-6.672199249,25.68499756,-36.96160889,0.008822058327,-0.06696711481,0.008434649557,-0.2179051787,0.2917186916,9.934447289 -3.71969094,-7.923324585,25.54019737,-37.2537384,0.0168600902,-0.05562448502,0.00521544693,-0.1962778419,0.2554365098,9.943925858 -3.72969094,-7.080738068,26.73028183,-37.340271,0.02695737779,-0.04445654154,0.00677522039,-0.2058451921,0.2460807711,9.855082512 -3.73969094,-7.258815765,26.83452415,-38.72080994,0.0257118158,-0.03880500048,0.009023677558,-0.2229058892,0.2702342272,9.8239851 -3.74969094,-8.029697418,26.74973679,-38.40783691,0.01767116413,-0.03354083747,0.01173234172,-0.1730276793,0.293876797,9.859820366 -3.75969094,-8.277870178,25.21536827,-38.45077515,0.00750257913,-0.0134898033,0.01595561951,-0.2170909047,0.2744894028,9.763518333 -3.76969094,-7.784622192,27.61482811,-36.6385498,-0.008115497418,0.004707356449,0.01857700758,-0.1322013289,0.2862198949,9.706252098 -3.77969094,-7.408531189,26.01865196,-36.22064209,-0.01740895212,0.02163187601,0.02066392824,-0.200186044,0.2886405289,9.775326729 -3.78969094,-7.276340485,26.42487144,-38.38641357,-0.01349348295,0.02156052925,0.0227907449,-0.05037189275,0.2798060477,10.02560616 -3.79969094,-8.060535431,25.66553307,-36.80451965,-0.006179019809,0.01282461267,0.01740583777,-0.1050582752,0.304669559,9.953327179 -3.80969094,-6.792694092,26.48664284,-37.6288147,-0.008539781906,0.04109317809,0.01307771914,-0.1367159933,0.2985886037,9.855304718 -3.81969094,-6.528675079,25.16231346,-36.00906372,-0.01666204073,0.06206073612,0.009280072525,-0.1876604408,0.2774381042,9.739678383 -3.82969094,-6.840065002,25.7945137,-37.88470459,-0.03033562005,0.04966237396,0.009583637118,-0.1894158125,0.2180558145,9.681078911 -3.83969094,-8.82151413,26.25354004,-37.91294861,-0.0442554988,0.02925441973,0.007262540516,-0.1306591481,0.2705550194,9.748282433 -3.84969094,-7.875461578,26.63319016,-38.09466553,-0.04479436576,0.01652778126,0.002259462141,-0.1970492601,0.2646980286,9.908373833 -3.85969094,-8.313529968,26.72847748,-37.18478394,-0.03184996918,0.02280884422,-0.00311926892,-0.2717116773,0.2997603416,9.914752007 -3.86969094,-6.546016693,25.82104492,-38.65031433,-0.02147505246,0.03477363288,-0.0003719876986,-0.347087115,0.2509671748,9.840457916 -3.87969094,-5.744480133,25.92165184,-37.59095764,-0.01409151033,0.03734870255,0.003584513441,-0.2748141289,0.2386694402,9.767773628 -3.88969094,-7.956577301,25.25596237,-37.996521,-0.005113671999,0.02281517908,0.005340028089,-0.303516686,0.2496834397,9.829120636 -3.89969094,-7.333118439,25.46081161,-38.31770325,0.003993850201,-0.002983287908,0.00775645813,-0.1925490499,0.3337683082,9.893627167 -3.90969094,-7.333118439,25.46081161,-38.31770325,0.01015300211,-0.02382051758,0.0009108139202,-0.2402744293,0.2796029747,9.859054565 -3.91969094,-8.14566803,26.61896324,-36.26170349,0.01105037984,-0.01546274498,-0.003354544286,-0.2564902306,0.254522115,9.739465714 -3.92969094,-8.180530548,26.86804199,-38.56854248,0.005029072054,-0.003389192745,-0.003890158376,-0.2920319736,0.2168327123,9.626190186 -3.93969094,-8.244125366,25.90046692,-38.80479431,-0.0132009536,-0.01026250795,-0.003068285529,-0.2643445432,0.226190418,9.728908539 -3.94969094,-7.898990631,25.95336533,-37.28318787,-0.01736517623,-0.01826028526,0.0005658168811,-0.2710779905,0.2723842859,9.839493752 -3.95969094,-7.899795532,26.2200222,-38.06521606,-0.01492538489,-0.01595095545,0.0008957325481,-0.2685719728,0.2795063257,9.843836784 -3.96969094,-7.566673279,25.73257828,-35.58947754,-0.01235895604,0.003454227,0.001958799548,-0.3106808662,0.2576106787,9.792434692 -3.97969094,-6.982593536,28.11629868,-36.67601013,-0.01192790549,0.006320843473,0.00643603364,-0.2947559953,0.2017954588,9.794699669 -3.98969094,-7.617759705,25.70621109,-37.87669373,-0.01215213537,-0.005099052563,0.00940784812,-0.251095444,0.2411850542,9.778581619 -3.99969094,-6.648178101,26.76568604,-38.86988831,-0.01203843486,-0.008605884388,0.007923526689,-0.2290615439,0.2813150585,9.819487572 -4.00969094,-7.11448288,26.04518318,-36.98625183,-0.006584511138,-0.001146110706,0.008336946368,-0.2567182481,0.3083212674,9.838685036 -4.01969094,-7.385807037,26.96513367,-37.81414795,0.004388485104,0.01485629193,0.002841629088,-0.2343084961,0.2451559454,9.847247124 -4.02969094,-7.097766876,26.72149277,-38.10267639,0.008203179576,0.02133086324,0.00259678578,-0.2072534859,0.2421934903,9.698073387 -4.03969094,-8.01266861,26.75852585,-37.64543152,-0.0003045490012,0.01985875331,-0.003262628801,-0.1555505842,0.1782817245,9.711576462 -4.04969094,-6.823036194,25.80330276,-37.12229919,-0.01336695347,0.005080743693,-0.006058639847,-0.1943382621,0.2094749659,9.714632034 -4.05969094,-8.01997757,26.35414696,-36.85359192,-0.03109871596,-0.01130758598,-0.008986420929,-0.2392482162,0.2315007001,9.763028145 -4.06969094,-7.651504517,25.02111244,-37.5226593,-0.0400146544,-0.004739507101,-0.01046456024,-0.2673299909,0.2497864366,9.792757988 -4.07969094,-7.242595673,27.10997009,-38.740448,-0.03906309232,0.006150948815,-0.01100350544,-0.2492612153,0.2543903887,9.845363617 -4.08969094,-7.074729919,27.00045395,-37.81735229,-0.03663897514,0.004508738406,-0.01430373639,-0.2727160156,0.224393189,9.859981537 -4.09969094,-6.320072174,26.80979538,-38.11068726,-0.02902301773,-0.007368606515,-0.01376769505,-0.2551684082,0.249186188,9.86097908 -4.10969094,-7.587417603,26.38955116,-38.38320923,-0.02162656561,-0.005713349208,-0.0148298014,-0.2201159149,0.2390990704,9.782792091 -4.11969094,-7.859237671,26.90863609,-38.11428833,-0.02336135507,0.003191094613,-0.01513622142,-0.2662590444,0.2411587536,9.972793579 -4.12969094,-7.305873871,25.4748745,-37.09785461,-0.009118622169,-0.002612174489,-0.01077926904,-0.282861501,0.2332253903,9.922781944 -4.13969094,-6.574367523,27.73853493,-38.17617798,-0.005163447931,-0.009760250337,-0.007363054901,-0.211932838,0.212636292,9.728738785 -4.14969094,-8.435512543,25.33015633,-38.91642761,-0.007328464184,0.003674271284,-0.008220901713,-0.20717749,0.1956729591,9.695152283 -4.15969094,-6.055557251,25.88633156,-37.5877533,-0.01590443403,0.01273629628,-0.009684925899,-0.2054186314,0.205473423,9.685699463 -4.16969094,-7.834903717,27.32180405,-38.14373779,-0.02339531854,-0.001536996569,-0.005243080668,-0.2339800149,0.2187104374,9.73903656 -4.17969094,-8.187030792,26.19700623,-36.99465942,-0.02800761536,-0.02326576225,0.001716356725,-0.2859913707,0.2686445117,9.796771049 -4.18969094,-7.242282867,26.44244957,-36.86160278,-0.02908233926,-0.03056327812,0.003839137033,-0.2978691161,0.2407368124,9.87355423 -4.19969094,-7.145629883,25.62849998,-37.26174927,-0.02445448563,-0.04781426489,0.01024218835,-0.2982214987,0.2228002399,9.95925045 -4.20969094,-8.521144867,25.88272476,-37.27677917,-0.01644831337,-0.06270572543,0.01859445311,-0.3007469773,0.2295433432,9.89234066 -4.21969094,-7.385002136,26.69847679,-37.03211975,-0.01185182575,-0.04533778131,0.02409035899,-0.2922000289,0.2343971729,9.830682755 -4.22969094,-7.899482727,25.55250168,-36.18638611,-0.009681108408,-0.02127336524,0.02458944358,-0.1705660373,0.2193717957,9.817026138 -4.23969094,-6.296230316,26.82210159,-37.04333496,-0.008695719764,-0.004451250657,0.01799800247,-0.1621389687,0.2197939306,9.757343292 -4.24969094,-6.509967804,28.43945312,-37.15789795,-0.01495374739,0.002140143886,0.01305411384,-0.2275846601,0.2546940744,9.810265541 -4.25969094,-7.916511536,25.54371262,-36.9487915,-0.02169186994,8.451147005e-05,0.008854851127,-0.2209734768,0.2321222872,9.868536949 -4.26969094,-7.392620087,26.96161842,-38.11909485,-0.02849860862,-0.001703454647,0.006528029684,-0.1220632419,0.1541925073,9.71141243 -4.27969094,-6.614120483,26.78326416,-37.34507751,-0.04428917542,0.01810290664,-0.002197280526,-0.2801389396,0.1237404421,9.710455894 -4.28969094,-7.392307281,26.2940979,-36.24026489,-0.04955516383,0.02923793532,0.0008839904331,-0.2690411806,0.03041800112,9.910918236 -4.29969094,-7.131511688,26.03639412,-37.74865723,-0.03079982474,0.03252982348,-0.002942085499,-0.226893425,0.06968209893,10.19825363 -4.30969094,-8.054035187,26.33656883,-38.37840271,0.008452025242,0.07177698612,0.002925334033,-0.2675082386,0.2150973082,10.1730938 -4.31969094,-7.242595673,27.10997009,-38.740448,0.04162428528,0.1026687622,0.005137396511,-0.3166666329,0.2443601489,10.15234661 -4.32969094,-7.234973907,26.84682846,-37.65344238,0.07101240754,0.09248439223,0.005703500006,-0.2230912894,0.2666797042,9.999241829 -4.33969094,-9.125778198,26.221735,-37.6047821,0.0769181326,0.04123746604,0.003129459452,-0.1260693818,0.2381351441,9.691967964 -4.34969094,-6.775665283,26.4954319,-36.8664093,0.05713566393,-0.01057719067,-0.007675570901,-0.1283408254,0.208796829,9.449485779 -4.35969094,-7.322902679,25.46608543,-37.86026001,0.021940846,-0.03525879979,-0.0164392069,-0.2080651373,0.2594524622,9.481989861 -4.36969094,-7.322902679,25.46608543,-37.86026001,-0.012359038,-0.04465859383,-0.02090466022,-0.33076033,0.2426670343,9.544941902 -4.37969094,-6.873313904,25.5102787,-38.62747192,-0.03849985823,-0.04358688742,-0.01385076717,-0.4688956439,0.2643616199,9.787895203 -4.38969094,-6.873313904,25.5102787,-38.62747192,-0.04186818004,-0.04309883714,-0.0009931600653,-0.4567618966,0.2665080726,9.873948097 -4.39969094,-6.815727234,26.20768166,-37.91413879,-0.03789544106,-0.02908178978,0.01127431542,-0.4167534411,0.3044225276,9.863181114 -4.40969094,-6.782478333,26.49191666,-37.17137146,-0.03473445773,-0.001430485398,0.01775887236,-0.3834793866,0.3153073788,9.803701401 -4.41969094,-8.043819427,26.34184265,-37.92095947,-0.03422413021,0.01866585761,0.01907779463,-0.2802246213,0.277962029,9.730218887 -4.42969094,-7.369091034,27.64144325,-38.93057251,-0.03385256976,0.02039143257,0.01885609329,-0.2379066348,0.2587588131,9.743079185 -4.43969094,-8.297309875,27.00392532,-37.204422,-0.04078369215,0.01425610762,0.01538096368,-0.2097538263,0.237317726,9.7883358 -4.44969094,-6.798698425,26.21647072,-37.1517334,-0.04215364158,0.007632749155,0.0129903052,-0.2162558734,0.2124440819,9.842716217 -4.45969094,-6.679012299,25.68148232,-37.26655579,-0.03894165531,0.002023714595,0.01219128072,-0.21327281,0.2051690519,9.822431564 -4.46969094,-7.881961823,25.96215439,-36.52078247,-0.03920482472,-0.004644265398,0.01271759532,-0.1758301556,0.1789844632,9.79445076 -4.47969094,-9.782485962,25.73265076,-38.02636719,-0.04025738314,-0.007038090378,0.006575869862,-0.1418743432,0.1429290026,9.867178917 -4.48969094,-8.773334503,26.6790123,-36.87501526,-0.03090364486,-0.01158179156,0.001804163679,-0.1442653984,0.1189981252,9.951881409 -4.49969094,-6.671707153,26.08586121,-38.05841064,-0.01513878815,-0.02521918528,-6.658444181e-05,-0.1738433093,0.1544630975,9.960613251 -4.50969094,-6.0623703,25.88281631,-37.89271545,-0.0008522793651,-0.02589283884,0.0004660896957,-0.1996041238,0.1565450281,9.95832634 -4.51969094,-7.868648529,26.6367054,-37.78971863,0.01431652624,-0.004012350924,-0.002729954664,-0.2186203599,0.1420396417,9.912900925 -4.52969094,-7.892982483,26.22353745,-37.76026917,0.018323984,0.02168132737,-0.0004264845047,-0.2232411653,0.1151062325,9.794009209 -4.53969094,-8.274959564,24.81626129,-37.20146179,0.008296172135,0.03482315689,0.0001749894582,-0.1810871363,0.09140643477,9.71138382 -4.54969094,-6.775169373,26.89629364,-37.96321106,-0.0138578536,0.0290421769,0.0007601014804,-0.1903544068,0.133223936,9.736455917 -4.55969094,-6.423221588,26.9527092,-36.13664246,-0.03204216808,0.02157061175,0.001588354819,-0.215617761,0.2027130872,9.723331451 -4.56969094,-8.013477325,27.02518272,-38.42747498,-0.04305369407,0.02441652864,0.00265686959,-0.21561867,0.1740983278,9.768782616 -4.57969094,-8.473773956,26.5748539,-37.02090454,-0.04656537622,0.01722446829,0.005860616919,-0.2287943214,0.1416486502,9.826238632 -4.58969094,-7.249095917,26.43893433,-37.16656494,-0.0395822376,0.01232369244,0.008911250159,-0.2248601615,0.1444966495,9.854561806 -4.59969094,-7.443393707,26.26773071,-38.52748108,-0.03166176751,0.00800199993,0.008988874033,-0.2275328785,0.1459973603,9.916668892 -4.60969094,-8.831729889,26.24826622,-38.37039185,-0.0206013117,0.009386268444,0.008898375556,-0.2268878073,0.1908883601,9.917303085 -4.61969094,-5.772026062,27.57248497,-36.33480835,-0.01100313012,0.01832520589,0.006325504277,-0.2854395807,0.2194937319,9.911237717 -4.62969094,-7.928646088,27.73664856,-36.49429321,-0.003731844481,0.04050150514,0.003878329415,-0.3024586439,0.1961129755,9.810222626 -4.63969094,-6.263290405,27.77385521,-38.17938232,-0.0005319332704,0.05672128499,-0.001344260294,-0.22144261,0.1550287455,9.70290947 -4.64969094,-8.314025879,26.32761574,-36.08798218,-0.002774584573,0.05401900411,-0.006458672695,-0.1730443388,0.1541241705,9.64126873 -4.65969094,-6.654678345,26.09465027,-37.29600525,-0.01445338782,0.03875076026,-0.01371094957,-0.2011986077,0.1363305449,9.544471741 -4.66969094,-7.570388794,26.39834023,-37.62080383,-0.02568543702,0.01708991826,-0.01378848217,-0.29481107,0.1495176256,9.718362808 -4.67969094,-6.825942993,26.20240784,-38.37158203,-0.02655274048,-0.0216173958,-0.01138909534,-0.296559602,0.1946366876,9.950929642 -4.68969094,-7.906295776,25.54898643,-36.49133301,-0.01781709492,-0.04153759778,-0.005925999023,-0.3151355982,0.1484638304,10.02096558 -4.69969094,-7.559364319,26.13695717,-36.38131714,-0.002749661915,-0.03404572606,-0.003847503802,-0.3055528998,0.1347839087,10.00041008 -4.70969094,-8.094593048,25.64795494,-38.32933044,0.00945944991,-0.008453158662,-0.0002174130641,-0.3440961838,0.1186381653,9.896161079 -4.71969094,-6.487621307,26.251791,-37.15493774,0.01406259369,-0.001046618447,0.005578093696,-0.3545964956,0.1277017146,9.871976852 -4.72969094,-7.586608887,26.12289429,-37.60116577,0.007003881969,-0.01737609878,0.01359666139,-0.3624377251,0.1338391006,9.84253788 -4.73969094,-6.487621307,26.251791,-37.15493774,-0.007547943387,-0.01550474204,0.01591363735,-0.2639065683,0.1462768465,9.776966095 -4.74969094,-6.751327515,26.90859985,-36.89584351,-0.02195086703,-0.001110700425,0.01218491793,-0.2222563624,0.1621421576,9.738617897 -4.75969094,-7.249095917,26.43893433,-37.16656494,-0.03246286511,-0.008031003177,0.01146942563,-0.248240903,0.115946807,9.743156433 -4.76969094,-6.457279205,26.93513107,-37.66145325,-0.03635413572,-0.02225174196,0.01254490949,-0.2521977723,0.102270402,9.834392548 -4.77969094,-5.980941772,26.59252357,-36.11201477,-0.02922541276,-0.03147359192,0.01022274233,-0.190122053,0.1274154633,9.831977844 -4.78969094,-7.479057312,27.78084183,-37.26150513,-0.01552046463,-0.0149680879,0.005340931471,-0.2009503394,0.1568839103,9.832667351 -4.79969094,-7.26581192,25.76262474,-36.05012512,-0.0007052477449,0.01664851978,0.001047774218,-0.2484272271,0.1699940115,9.815591812 -4.80969094,-9.155311584,25.27173805,-36.31622314,0.006605156697,0.02571816929,9.230547585e-05,-0.2894585431,0.1414472759,9.803195 -4.81969094,-9.155311584,25.27173805,-36.31622314,0.003320282325,0.007851457223,0.0008433877956,-0.2730394006,0.1397527158,9.857546806 -4.82969094,-7.358562469,26.97919655,-36.59428406,-0.003061691765,-0.01043106336,0.0001573381014,-0.258321166,0.1311504543,9.84120369 -4.83969094,-7.358562469,26.97919655,-36.59428406,-0.004729580134,-0.01538466103,-0.0008347684052,-0.263558358,0.1183589473,9.855064392 -4.84969094,-8.036197662,26.07870102,-36.83395386,-0.004580997862,-0.01142347418,-0.002197280526,-0.2746455669,0.1134366319,9.814276695 -4.85969094,-6.672199249,25.68499756,-36.96160889,-0.007733293343,-0.0003262530081,-0.0008874703199,-0.1970769614,0.08623953164,9.628912926 -4.86969094,-8.81470108,26.25705528,-37.60798645,-0.01761743426,0.01848483458,-0.005533532239,-0.259354353,0.09551355988,9.633827209 -4.87969094,-7.85843277,26.64197922,-37.33226013,-0.02588795871,0.01906336285,-0.0006064993795,-0.2534437478,0.1723184586,9.753379822 -4.88969094,-7.25850296,26.16700363,-36.84196472,-0.03109328821,0.003598091658,0.001400619745,-0.2143057436,0.1092486531,9.908551216 -4.89969094,-7.898990631,25.95336533,-37.28318787,-0.02612610161,-0.01706181467,-0.001536136726,-0.2153220028,0.1003462896,10.02652073 -4.90969094,-6.45356369,26.26936913,-35.63012695,-0.01295110211,-0.02876726352,-0.002070615999,-0.2522668839,0.1105454043,10.04345798 -4.91969094,-9.015007019,25.81567955,-38.49183655,0.002009376884,-0.0287384484,-0.001379664754,-0.2668540478,0.1403126717,9.930277824 -4.92969094,-7.378501892,27.36951256,-38.60600281,0.01198495831,-0.00673567038,-0.003675399348,-0.2398301959,0.1118507236,9.92348671 -4.93969094,-7.471748352,28.18522072,-38.05334473,0.02573502436,0.02284619026,-0.004629021976,-0.224476397,0.1067397967,9.81465435 -4.94969094,-6.574054718,27.0710144,-36.29734802,0.02536131069,0.03054238297,-0.004635522142,-0.2303179353,0.0717818737,9.722005844 -4.95969094,-6.296230316,26.82210159,-37.04333496,0.01556797791,0.004868612625,-0.002214650391,-0.2246153355,0.1043654531,9.742161751 -4.96969094,-7.560173035,26.40361404,-37.1633606,2.473592758e-06,-0.0173169449,-0.0008139780257,-0.2097203881,0.1449587941,9.785895348 -4.97969094,-6.471401215,26.52723694,-37.17457581,-0.008842390031,-0.02489814535,-0.0002747424878,-0.3028791845,0.1854895651,9.777700424 -4.98969094,-7.569580078,26.13168335,-36.83876038,-0.01287231874,-0.02937872149,0.003762538079,-0.271209687,0.129128173,9.908959389 -4.99969094,-6.624336243,26.77799034,-37.80253601,-0.004194155335,-0.04173757136,0.004533623811,-0.1912932694,0.1316759884,10.00805187 -5.00969094,-6.457279205,26.93513107,-37.66145325,0.01475238521,-0.03149639815,0.003035570029,-0.2443001717,0.1331238598,9.971121788 -5.01969094,-8.210559845,25.5171814,-36.18318176,0.03028877825,-0.006620025262,-0.0008588049095,-0.2268170714,0.1420782506,9.910667419 -5.02969094,-7.385002136,26.69847679,-37.03211975,0.04052030668,0.01364735421,-0.004151983652,-0.237571016,0.158927694,9.847308159 -5.03969094,-7.442901611,26.66859436,-39.62428284,0.03705263138,0.0003825714812,-0.006458672695,-0.2139079124,0.1738214344,9.855632782 -5.04969094,-7.299869537,25.74504662,-37.57493591,0.03569614887,-0.0260367766,-0.007442372385,-0.2120704055,0.182754904,9.782229424 -5.05969094,-7.995639801,26.76731491,-36.88302612,0.03453819454,-0.03720206767,-0.00965471752,-0.2041972578,0.1758445352,9.763034821 -5.06969094,-8.423805237,27.53539658,-37.39454651,0.03948170319,-0.04894115031,-0.007673261221,-0.2060015351,0.1358015239,9.880899429 -5.07969094,-7.416149139,26.28179359,-37.30761719,0.04444842041,-0.05113239586,-0.00752402097,-0.2362748682,0.1675562263,9.965304375 -5.08969094,-8.899833679,27.21048546,-37.06517029,0.06157614291,-0.0353282243,-0.003937673289,-0.2722059488,0.193922624,9.950833321 -5.09969094,-7.559364319,26.13695717,-36.38131714,0.07159139216,-0.007121328264,-0.002865193645,-0.2545877993,0.1594867259,9.833031654 -5.10969094,-8.280281067,27.01271439,-36.4420166,0.06986922771,0.0109635517,-0.002792024519,-0.2140764892,0.1569391638,9.850317955 -5.11969094,-8.217372894,25.51366615,-36.48812866,0.06253593415,0.002094432712,-0.007235190365,-0.1947746426,0.2032549828,9.859428406 -5.12969094,-6.286819458,27.09403038,-37.36790466,0.06216876209,-0.008317800239,-0.009709886275,-0.2290931046,0.2211895436,9.904157639 -5.13969094,-8.077068329,26.05760765,-38.66372681,0.06461351365,-0.004909839481,-0.01018739119,-0.2145729065,0.2156002671,9.762316704 -5.14969094,-6.850280762,25.78923988,-38.34214783,0.05910928547,0.001064125914,-0.0116847232,-0.167883575,0.2063707858,9.694931984 -5.15969094,-7.17206955,25.34778023,-37.69958496,0.05363702774,0.01853247173,-0.01631854475,-0.156190142,0.1726144552,9.716248512 -5.16969094,-8.053226471,26.06991196,-37.59635925,0.05348046869,0.02741134167,-0.02060299739,-0.2086635679,0.2122661322,9.849188805 -5.17969094,-7.995639801,26.76731491,-36.88302612,0.053559497,0.02173338085,-0.01817750186,-0.3160272539,0.2369009852,9.909678459 -5.18969094,-7.801651001,27.60603905,-37.4009552,0.06455555558,0.02400907688,-0.01444878429,-0.3371770084,0.280346781,9.874191284 -5.19969094,-8.561702728,25.19411087,-37.22770691,0.07946416736,0.04503597319,-0.01281164959,-0.2494072318,0.2802865803,9.811919212 -5.20969094,-7.282344818,26.15469933,-37.90933228,0.08607848734,0.05437766761,-0.0133135058,-0.2424967289,0.2851865292,9.795174599 -5.21969094,-5.599964142,26.20069504,-38.83203125,0.09813603014,0.05297361314,-0.01501114666,-0.2714679539,0.2680800259,9.75396347 -5.22969094,-8.371612549,25.63021278,-36.80131531,0.09730117023,0.03567400575,-0.0161033757,-0.2618784308,0.2831623554,9.753846169 -5.23969094,-5.85345459,26.86277771,-38.11549377,0.1009470299,0.0124598816,-0.01290932298,-0.2666765749,0.3234401941,9.780581474 -5.24969094,-6.471401215,26.52723694,-37.17457581,0.1109568328,0.01610381715,-0.01498145796,-0.1893724501,0.3073857427,9.646884918 -5.25969094,-7.305873871,25.4748745,-37.09785461,0.09680746496,0.01358146127,-0.01551413164,-0.1401528865,0.2132546306,9.622281075 -5.26969094,-8.179725647,26.60138512,-37.78651428,0.07704415172,-0.02511924133,-0.01607831009,-0.2199209034,0.2536352575,9.718344688 -5.27969094,-7.139129639,26.29953575,-38.83563232,0.06522310525,-0.05784024298,-0.01338343509,-0.2406560779,0.289178431,9.901301384 -5.28969094,-7.139129639,26.29953575,-38.83563232,0.07847820967,-0.06008450687,-0.01284654625,-0.2131624073,0.3604699373,9.955468178 -5.29969094,-5.710235596,27.00761414,-39.04180908,0.09397917241,-0.03338656574,-0.01444436796,-0.2175605744,0.3692127168,9.880940437 -5.30969094,-5.710235596,27.00761414,-39.04180908,0.1072839499,-0.00890467409,-0.0123171173,-0.1554846913,0.338521868,9.983216286 -5.31969094,-8.555202484,25.86514664,-38.80158997,0.1280505955,-0.00145151047,-0.01125509851,-0.1884938926,0.3345931768,9.971286774 -5.32969094,-8.36139679,25.6354866,-36.34387207,0.1440444738,0.001271591522,-0.01606091298,-0.1713260263,0.4138234556,9.948322296 -5.33969094,-7.552864075,26.80799103,-37.9552002,0.1595576257,0.02187692188,-0.02512727119,-0.1057918295,0.3753095269,9.983222008 -5.34969094,-7.186187744,24.93988609,-37.212677,0.1767290533,0.005366987083,-0.0303742215,-0.2102942914,0.3615303338,9.934126854 -5.35969094,-7.898990631,25.95336533,-37.28318787,0.1936744601,0.02417157032,-0.02678085119,-0.1176252067,0.2720018625,10.04296494 -5.36969094,-7.449893951,25.59669495,-36.95359802,0.2405296117,0.06736565381,-0.03421706706,-0.1225322634,0.4234299064,10.36811256 -5.37969094,-8.820705414,25.98688316,-37.13090515,0.3403091133,0.08081629127,-0.03518076614,-0.107458882,0.4686262608,10.39834785 -5.38969094,-7.990322113,24.57086372,-37.64250183,0.4398308396,0.1258682907,-0.0389775075,-0.1128015518,0.5142008662,10.45117283 -5.39969094,-7.16185379,25.35305405,-37.24212646,0.5284068584,0.1784391701,-0.0449366197,-0.2035734504,0.5788798332,10.56637478 -5.40969094,-7.356647491,24.78098679,-37.50622559,0.6149481535,0.1743725836,-0.04687128216,-0.2832054794,0.6244323254,10.58554649 -5.41969094,-7.33052063,25.72922707,-38.94726562,0.7036159635,0.1237051487,-0.0392241925,-0.4429421723,0.6916079521,10.61947918 -5.42969094,-6.578952789,24.8692894,-37.51423645,0.8085047603,0.08715923876,-0.0232714992,-0.5601879358,0.8304725885,10.81384563 -5.43969094,-7.531322479,24.88698578,-38.73428345,0.938151896,0.08443415165,-0.007361578289,-0.6205343008,0.9866380692,10.99567223 -5.44969094,-8.279857635,22.61453819,-38.41836548,1.076931596,0.09234847128,0.01138667203,-0.7176730633,1.141917586,11.07694626 -5.45969094,-6.797901154,24.95243835,-40.7245636,1.21220386,0.142701149,0.01992633007,-0.5940642953,1.237163305,10.96197701 -5.46969094,-6.786567688,24.02353477,-37.60624695,1.331775427,0.2211852372,0.01148944534,-0.5794842839,1.406719089,10.83598232 -5.47969094,-6.683105469,23.21310043,-37.70143127,1.424222708,0.2731072903,0.001702721231,-0.6970831752,1.580493093,10.72273731 -5.48969094,-5.19291687,22.95544434,-39.51779175,1.489971876,0.2497478575,0.005910233129,-0.7620835304,1.679367185,10.78293896 -5.49969094,-7.543029785,22.68174744,-40.25616455,1.567991257,0.1823883057,0.01639992744,-0.7109425664,1.702432513,10.92615986 -5.50969094,-6.331970215,22.53879547,-41.01174927,1.654090762,0.1048861593,0.02590598166,-0.5987703204,1.934044361,10.9778614 -5.51969094,-6.872894287,21.11210251,-40.60383606,1.738529205,0.02646159567,0.03096565418,-0.4815279543,2.036860704,11.03164959 -5.52969094,-6.464668274,20.73433876,-42.10400391,1.831183553,-0.0371132046,0.03252334148,-0.2581321895,2.053145409,11.02364445 -5.53969094,-6.585845947,19.06935883,-43.05360413,1.945584893,-0.0588221848,0.02755689435,-0.03785359487,2.144049168,11.44700241 -5.54969094,-7.916595459,19.74729919,-42.18318176,2.122190714,-0.06661680341,-0.002210137434,-0.2162174582,2.293878794,11.10333824 -5.55969094,-7.169239044,19.15226173,-41.68467712,2.242797375,0.02907058969,0.01810688525,-0.6900045872,2.465847731,10.61191368 -5.56969094,-7.007083893,17.10767746,-42.76051331,2.365107298,0.07657755166,0.04666646942,-0.6271196008,2.857951403,11.23185539 -5.57969094,-7.567134857,15.80464458,-43.58227539,2.533013105,0.009588560089,0.06934992969,-0.6376622319,3.23949194,11.46477699 -5.58969094,-6.512111664,15.24316883,-43.23944092,2.682348967,-0.03812664747,0.1032418609,-1.011867642,3.570411205,11.33907413 -5.59969094,-6.6783638,14.81937218,-42.59846497,2.810805321,-0.02873550914,0.1526294798,-1.132974863,3.754922628,10.92838478 -5.60969094,-6.983627319,12.9884634,-44.45155334,2.896809578,0.02345578931,0.1785736531,-0.7919716239,3.861896276,10.60241222 -5.61969094,-8.335494995,11.18923187,-44.77839661,2.964149952,0.1522061974,0.1822080016,-0.3903231025,3.914002657,10.22689247 -5.62969094,-6.875339508,9.580715179,-44.20939636,3.014033556,0.314350307,0.1683944762,-0.1859156489,4.113828659,9.747583389 -5.63969094,-6.25868988,9.782049179,-44.83552551,3.032514095,0.4121232927,0.1518512666,-0.2616799474,4.384209156,9.284766197 -5.64969094,-5.253143311,8.660894394,-45.21585083,3.02897954,0.3880666196,0.1631702632,-0.4669449329,4.571393967,8.911646843 -5.65969094,-5.458652496,7.682689667,-44.84060669,2.998725176,0.1711447984,0.1893278956,-0.6494381428,4.91509676,8.661911964 -5.66969094,-4.898784637,6.919963837,-45.39804077,2.934651613,-0.1005303711,0.2150487304,-0.6185181737,5.301094055,9.058558464 -5.67969094,-6.250656128,5.120733261,-45.72488403,2.910330772,-0.2587017715,0.2318211645,-0.3392679691,5.375423908,9.396467209 -5.68969094,-5.879581451,4.056110382,-47.02351379,2.922665358,-0.2760679126,0.2243542522,-0.4822314382,5.503026485,9.01198101 -5.69969094,-7.555839539,1.547006607,-46.07824707,2.943333626,-0.2170758992,0.2124996334,-0.4549106658,5.76497364,8.254803658 -5.70969094,-7.756641388,0.7047691345,-45.86528015,2.945270777,-0.1781668216,0.206626907,-0.3903495967,5.746593952,7.337949276 -5.71969094,-7.067371368,-0.9885349274,-44.98329163,2.901698112,-0.208113566,0.1915666908,-0.1816526204,5.731616974,6.997162819 -5.72969094,-6.940868378,-1.520008087,-44.79315186,2.863831282,-0.2202323377,0.1786220968,0.2177357078,5.766057968,6.696406841 -5.73969094,-6.940868378,-1.520008087,-44.79315186,2.802832127,-0.2372323573,0.1332490295,0.4498233199,5.980806351,6.113747597 -5.74969094,-6.167266846,-3.900087357,-45.23603821,2.685050011,-0.3130146265,0.08385125548,0.2968164086,6.279119015,5.801239014 -5.75969094,-6.167266846,-3.900087357,-45.23603821,2.537742376,-0.4306736588,0.05320386589,-0.04167699441,6.515561581,5.803927898 -5.76969094,-6.593517303,-5.330215454,-46.65950012,2.41191411,-0.5239192843,0.04438148439,-0.1330732852,6.619808674,5.855363846 -5.77969094,-8.595588684,-5.950117111,-44.62696838,2.333110571,-0.5195303559,0.03090674616,-0.2117768675,6.696700573,5.642460823 -5.78969094,-8.628837585,-6.234352112,-45.36975098,2.297567368,-0.414770335,0.02381796204,-0.1566102207,6.806136608,5.84990263 -5.79969094,-7.396728516,-8.699171066,-45.80760193,2.302094221,-0.2918973565,0.008515642956,-0.2365027964,7.161421299,5.973455906 -5.80969094,-6.642562866,-9.290693283,-45.00413513,2.342935801,-0.1979716867,-0.003534507239,-0.3914331794,7.542293072,6.224736691 -5.81969094,-7.828483582,-9.00123024,-43.49595642,2.388193846,-0.1110058427,-0.01067978889,-0.2491026968,7.591664314,5.918228626 -5.82969094,-8.313922882,-9.598073959,-42.84197998,2.408067226,-0.01837301813,-0.0253147278,0.03190075234,7.624960423,5.320220947 -5.83969094,-8.342350006,-12.47961998,-43.24739075,2.358142614,0.03511781245,-0.05030852184,-0.1354587525,7.447844982,3.707296848 -5.84969094,-7.461193085,-13.20175362,-43.3506012,2.204473257,0.08235535771,-0.04534388334,-0.007207999472,7.318314552,3.282621861 -5.85969094,-7.363540649,-12.21660042,-41.58953857,2.055655241,0.08076247573,-0.0373577401,0.06377741694,7.392906189,3.514599085 -5.86969094,-7.110240936,-14.94444084,-43.68527222,1.952906013,0.0455320403,-0.03147932887,-0.04667448997,7.750877857,3.611784935 -5.87969094,-7.37494278,-15.08936119,-41.23254395,1.861353755,-0.06208071858,-0.02312056161,-0.1080982238,8.078206062,3.719119072 -5.88969094,-6.948883057,-15.72499657,-41.1882782,1.785762429,-0.1727042198,-0.0151411742,-0.1235443279,8.370152473,4.199866295 -5.89969094,-8.64748764,-16.0464344,-40.24595642,1.783481121,-0.1971811503,-0.02015489712,-0.1364535242,8.50699234,4.808917046 -5.90969094,-8.216724396,-16.54610634,-40.36398315,1.838792682,-0.07802446187,-0.03536086157,-0.06082001328,8.485049248,4.451487541 -5.91969094,-7.526954651,-17.83854485,-40.57881165,1.857732654,0.1300803274,-0.06967652589,-0.01184685621,8.531123161,3.72614336 -5.92969094,-8.303844452,-18.19350243,-39.78877258,1.814050555,0.2553527653,-0.08196844906,-0.06807902455,8.328253746,2.931307793 -5.93969094,-8.504646301,-19.03573799,-39.57580566,1.716382623,0.2538946271,-0.06764163077,0.005141078494,8.346729279,2.556385756 -5.94969094,-6.853099823,-20.07394981,-38.89517212,1.598608613,0.152466163,-0.05751124769,0.003872290719,8.425172806,2.134254694 -5.95969094,-7.664543152,-20.84735298,-38.53311157,1.460810065,0.05854824185,-0.04427852854,-0.007188765798,8.432364464,2.211527348 -5.96969094,-7.158172607,-19.83910179,-39.36903381,1.353563786,0.002514161868,-0.03926705942,0.1740697026,8.347842216,2.068900824 -5.97969094,-6.903877258,-20.76783943,-39.30352783,1.247846365,-0.0405799076,-0.04407382011,0.04491699114,8.569720268,1.811930776 -5.98969094,-7.849121094,-21.41414452,-38.33978271,1.123071313,-0.06583211571,-0.04931728542,0.1047966853,8.705016136,1.846391082 -5.99969094,-6.214603424,-22.4611454,-38.42155457,1.026422024,-0.04438321292,-0.07139761746,0.05338488892,8.847383499,2.146280289 -6.00969094,-8.489295959,-22.29530525,-36.90214539,0.9891057611,0.03876886517,-0.0867260173,-0.1419169158,9.085100174,2.551529169 -6.01969094,-7.745658875,-22.22458076,-38.43496704,0.9823154211,0.1017291918,-0.106370911,-0.1593304873,9.150740623,2.281132936 -6.02969094,-7.793209076,-23.98509407,-35.71522522,0.9616700411,0.1661918014,-0.1270233244,0.0574805215,9.073641777,2.047967672 -6.03969094,-6.944122314,-22.12397194,-37.37562561,0.9382100105,0.1993058175,-0.1547543406,0.01744905487,8.995657921,1.867096186 -6.04969094,-7.567459106,-25.06209755,-36.55479431,0.9021967649,0.1762719601,-0.1718251109,-0.1406121552,9.002772331,1.730701089 -6.05969094,-7.148822784,-23.36882973,-36.21832275,0.8519908786,0.1081412658,-0.1755056828,-0.3042657971,9.020177841,1.228797078 -6.06969094,-6.28729248,-24.36816597,-36.45439148,0.7535880208,0.05081130564,-0.1780438125,-0.07804404944,8.942026138,0.0622860454 -6.07969094,-6.615398407,-24.41227531,-37.21359253,0.618732214,0.1305778027,-0.1905168593,-0.1119370759,8.954662323,0.6950393319 -6.08969094,-6.468467712,-24.93319893,-36.10856628,0.5215619206,0.097046718,-0.2001017928,-0.1604000777,9.188315392,0.8780340552 -6.09969094,-8.278152466,-24.18106651,-36.15803528,0.4290464222,-0.01206605136,-0.1879246235,-0.4243336916,9.332118034,1.622178555 -6.10969094,-6.327850342,-25.05677986,-36.40531921,0.405993849,-0.07996894419,-0.1790645421,-0.552688241,9.564300537,2.605819941 -6.11969094,-7.102638245,-25.54419136,-35.147995,0.4912712276,-0.04473871738,-0.1838391423,-0.6350327134,9.846169472,3.193631172 -6.12969094,-6.550388336,-26.04377174,-36.79244995,0.6150547266,0.02244179323,-0.1928945929,-0.5246245861,10.11047459,2.911791801 -6.13969094,-6.669765472,-26.17630577,-34.79878235,0.6870937347,0.05554625392,-0.2030147314,-0.6148418188,9.993902206,2.150117159 -6.14969094,-5.846191406,-27.59583855,-35.61532593,0.6883915663,0.03711918741,-0.1955397874,-0.5256439447,9.68167305,1.30617404 -6.15969094,-5.604026794,-26.33164406,-35.09532166,0.6299076676,-0.03041126765,-0.1765921712,-0.3788752854,9.399337769,0.6339784861 -6.16969094,-8.064910889,-26.19928551,-34.94665527,0.5348986387,-0.09692849219,-0.1555133909,-0.3197306395,9.323526382,0.3968338072 -6.17969094,-7.614517212,-26.42174721,-34.93183899,0.4100218713,-0.1200562641,-0.1371923983,-0.2656853199,9.336148262,0.2756545842 -6.18969094,-7.253158569,-26.09340477,-33.42985535,0.2929925919,-0.1655155718,-0.119509913,-0.5380512476,9.51198101,1.004795432 -6.19969094,-7.253158569,-26.09340477,-33.42985535,0.2704642713,-0.2962380052,-0.09054698795,-0.5876882672,9.83070755,2.051366806 -6.20969094,-7.109138489,-26.21522713,-33.57411194,0.3218514025,-0.3892082572,-0.07513003796,-0.7501949668,10.15983486,1.972729325 -6.21969094,-7.109138489,-26.21522713,-33.57411194,0.3665290177,-0.3839081526,-0.07275354117,-0.8437544703,10.24689865,1.666616678 -6.22969094,-6.416275024,-26.83838844,-35.51531982,0.395961076,-0.362869978,-0.07692997903,-0.6962200403,10.24797153,1.79286015 -6.23969094,-6.924747467,-27.71418571,-35.14666748,0.4236700237,-0.2611028552,-0.08759007603,-0.5691984892,10.19368839,1.651011705 -6.24969094,-5.731018066,-26.20103264,-34.18865967,0.4270048141,-0.06775772572,-0.1009764075,-0.5052172542,10.05403423,1.090990543 -6.25969094,-5.955661774,-27.05557823,-35.04304504,0.3786482811,0.09604948014,-0.1041087434,-0.4335639477,9.900503159,0.7057359815 -6.26969094,-6.411750793,-27.77080727,-32.70195007,0.3053478301,0.1541505158,-0.09603412449,-0.3152265251,9.683514595,0.2176062912 -6.27969094,-7.398361206,-28.83907127,-32.47116089,0.2221469581,0.1793556064,-0.09244065732,-0.3450230062,9.64139843,0.03580936044 -6.28969094,-6.884685516,-27.42643929,-34.09892273,0.1321454048,0.1524278224,-0.07998012006,-0.4099330306,9.661970139,0.1432226747 -6.29969094,-5.973186493,-27.46523094,-34.70864868,0.06124486029,0.006182244048,-0.05034258217,-0.6426629424,9.841538429,0.8780437112 -6.30969094,-5.79510498,-27.56947136,-33.32810974,0.02109704167,-0.1478120834,-0.01445469074,-0.8502721786,10.12072086,1.450038433 -6.31969094,-6.100177765,-27.33461952,-33.80197144,0.02866151184,-0.1757359207,0.02049909532,-1.059395909,10.29624081,1.798726797 -6.32969094,-5.74773407,-26.87734032,-33.07220459,0.07471268624,-0.1548956931,0.04094931856,-0.6283707023,10.32818985,2.137936592 -6.33969094,-7.230003357,-28.54772377,-32.64486694,0.1274963319,-0.03663658351,0.03674643859,-0.6844533682,10.25029659,1.625554085 -6.34969094,-5.938632965,-27.04678917,-34.28063965,0.1503894925,0.09812860191,0.0308074709,-0.5885108113,10.05522156,1.04837501 -6.35969094,-6.94877243,-28.79487801,-33.2383728,0.141530484,0.1597932577,0.0212403778,-0.4865935445,9.90356636,0.6102195382 -6.36969094,-7.224311829,-27.61003304,-35.0007782,0.09064454585,0.1466042995,0.02438522689,-0.4241021872,9.743000031,0.1843403578 -6.37969094,-7.368019104,-28.15572929,-32.97767639,0.01164131891,0.07730738074,0.03112574108,-0.3331443667,9.599993706,0.2176162302 -6.38969094,-6.503890991,-28.88665199,-33.84329224,-0.05099488795,-0.004641776904,0.03543535247,-0.252383858,9.633149147,0.59193331 -6.39969094,-5.962162018,-27.726614,-33.46916199,-0.08003503084,-0.1150991321,0.04340262339,-0.4005373716,9.868338585,1.204803705 -6.40969094,-6.21163559,-29.39518929,-33.1973114,-0.07899376005,-0.1858911067,0.05741436034,-0.7340079546,10.20933914,1.619800568 -6.41969094,-5.45368576,-26.8508091,-33.83781433,-0.05616622418,-0.1541773379,0.07039058954,-1.079650044,10.29853725,1.836036086 -6.42969094,-5.764762878,-26.88612938,-33.83460999,-0.01166790351,-0.04989422113,0.08693824708,-0.7219535708,10.20710182,1.849469781 -6.43969094,-7.55210495,-28.321661,-33.88114929,0.04423505068,0.07162962109,0.07621535659,-0.5237618089,10.04287529,1.466649294 -6.44969094,-6.139930725,-28.28988838,-32.97085571,0.06462156773,0.1659917086,0.06487458199,-0.2693444788,9.804961205,1.003296137 -6.45969094,-6.900909424,-27.70188332,-34.07929993,0.03726745769,0.1448267251,0.05426616967,-0.3218414485,9.69636631,0.6625964046 -6.46969094,-7.200469971,-27.59772682,-33.9334259,-0.01479796506,0.08245497942,0.05262492597,-0.2694553733,9.55301857,0.2537037134 -6.47969094,-7.517551422,-27.90321922,-33.45314026,-0.08166196942,0.04908277839,0.05063142255,-0.2769313157,9.516850471,0.3263844252 -6.48969094,-6.802764893,-26.31586647,-33.41503906,-0.129857108,-0.02138678916,0.04803046212,-0.2548482716,9.549121857,0.8685886264 -6.49969094,-6.453117371,-28.19276619,-33.43490601,-0.1322575063,-0.1084113792,0.04938947782,-0.3309167624,9.744350433,1.420860767 -6.50969094,-6.421966553,-27.77607918,-33.15940857,-0.1049313694,-0.1264381558,0.046568349,-0.5277194381,9.95321846,1.722908378 -6.51969094,-5.795597076,-27.97033501,-32.23129272,-0.05866263807,-0.1087544486,0.05005582422,-0.7864149809,10.11928844,1.995217443 -6.52969094,-6.756572723,-28.49122429,-32.34472656,-0.003624816425,-0.06324600428,0.05518781021,-0.6161818504,10.06101227,1.923961639 -6.53969094,-7.125358582,-26.49067116,-33.55448914,0.04583896324,0.0206390284,0.04705470428,-0.5417268872,9.938585281,1.376885653 -6.54969094,-7.384243011,-28.43117332,-32.95803833,0.05019680783,0.09256529063,0.04150801897,-0.4252683222,9.765837669,0.8409014344 -6.55969094,-4.91444397,-28.69246864,-32.33450317,0.01591055095,0.1238697395,0.0377532728,-0.2604678869,9.612735748,0.5489522815 -6.56969094,-7.374832153,-28.15924644,-33.28263855,-0.03896613047,0.1192587167,0.03361805156,-0.2545866966,9.425396919,0.2876189351 -6.57969094,-7.552417755,-27.65414238,-35.75997925,-0.115347065,0.05623481423,0.03676060215,-0.2348897308,9.471121788,0.4286818206 -6.58969094,-5.604522705,-26.73250771,-33.9985199,-0.1771036536,-0.04492631555,0.04680872709,-0.2702373564,9.532647133,0.8573217988 -6.59969094,-6.436088562,-28.18397713,-32.67250061,-0.2030239701,-0.1211520582,0.05939222872,-0.3787891567,9.717459679,1.445369482 -6.60969094,-6.883880615,-27.69309425,-33.31689453,-0.1903610229,-0.1436105818,0.06776238978,-0.558552444,9.869405746,1.720998168 -6.61969094,-6.573608398,-27.391119,-34.10212708,-0.1610814184,-0.08932308853,0.07173191011,-0.6367165446,9.939030647,1.911174417 -6.62969094,-7.794269562,-29.58585548,-34.40113831,-0.1116924211,0.006465011742,0.06918103993,-0.5721738338,10.05697632,1.982402325 -6.63969094,-6.814472198,-28.52110863,-34.93688965,-0.06562864035,0.08441458642,0.07163098454,-0.5598268509,10.11370468,1.724575639 -6.64969094,-8.08763504,-27.1457653,-33.35314941,-0.04420860112,0.1142552868,0.07546826452,-0.5372830629,10.04286575,1.392329931 -6.65969094,-6.123214722,-27.6135807,-34.08731079,-0.0459260419,0.1182540208,0.07836209983,-0.4197402894,9.951440811,1.140536189 -6.66969094,-6.123214722,-27.6135807,-34.08731079,-0.05532723293,0.1020412371,0.07906202972,-0.3231778741,9.863641739,0.9318038821 -6.67969094,-6.894096375,-27.69836617,-33.77432251,-0.07831289619,0.07124064118,0.07983452082,-0.3302566707,9.701453209,0.7482259274 -6.68969094,-6.894096375,-27.69836617,-33.77432251,-0.1181097478,0.01705702953,0.08364310861,-0.3872544169,9.722757339,1.051870346 -6.69969094,-7.967948914,-27.68075752,-33.46795654,-0.1227426454,-0.05970519036,0.08897811919,-0.277425617,9.779336929,1.489115953 -6.70969094,-7.40127182,-28.43996239,-33.72044373,-0.0898701027,-0.0761885494,0.08000918478,-0.3281622827,9.89973259,1.708828926 -6.71969094,-7.944419861,-27.00093269,-34.2794342,-0.05175863951,-0.01853408106,0.06382262707,-0.288962692,9.960288048,1.617121935 -6.72969094,-7.350498199,-27.74607658,-33.31208801,-0.01620602794,0.05397556722,0.04388963431,-0.2301783711,9.98184967,1.649990201 -6.73969094,-6.850627899,-27.40886116,-32.57411194,0.02669902518,0.1087433919,0.02283645235,-0.2193958163,9.986762047,1.493821502 -6.74969094,-6.276763916,-25.03041267,-34.11810303,0.05053491145,0.1415589154,0.006427761633,-0.186055243,9.888465881,1.22418046 -6.75969094,-5.76556778,-26.61947441,-34.61665344,0.05468331277,0.1356552541,-0.004396722652,-0.2582173944,9.880655289,0.8945252299 -6.76969094,-6.412559509,-27.5041523,-33.48397827,0.02108596638,0.1019207016,-0.00492911879,-0.1788927615,9.746808052,0.8333583474 -6.77969094,-6.438995361,-27.78486824,-33.92181396,-0.01197361574,0.05054997653,-0.004860650748,-0.214070335,9.705836296,0.8822153211 -6.78969094,-7.23532486,-26.35127068,-31.88542175,-0.04006820545,0.009633807465,-0.007401582785,-0.2310826182,9.749238968,0.9326863885 -6.79969094,-5.922412872,-26.77134514,-34.30026245,-0.05779248476,-0.005850811489,-0.01066414267,-0.3249489665,9.765732765,1.428793669 -6.80969094,-6.624382019,-28.08500481,-34.51049805,-0.03070151433,-0.02131212316,-0.01599514671,-0.3499325216,9.840603828,1.784139752 -6.81969094,-7.216690063,-27.87317085,-33.9138031,0.01399721112,0.04281678796,-0.02650093473,-0.3876112998,10.02012634,1.671920061 -6.82969094,-7.334274292,-27.47063255,-33.33171082,0.03933909908,0.1012582257,-0.03255970404,-0.4020133018,9.993823051,1.556693912 -6.83969094,-6.422775269,-27.50942421,-33.94143677,0.04864115268,0.1026042551,-0.03411246836,-0.3927852213,9.927248001,1.404534221 -6.84969094,-6.099685669,-26.93375587,-34.89878845,0.04725361243,0.08464277536,-0.02633284219,-0.3983494043,9.882896423,1.130617261 -6.85969094,-6.709514618,-27.13157463,-33.96768188,0.03037458286,0.05510923266,-0.02556121349,-0.2641792893,9.814121246,1.064528465 -6.86969094,-8.128997803,-27.56772423,-34.08610535,0.01483618375,0.003363851458,-0.02506784908,-0.187411204,9.8035326,1.080051899 -6.87969094,-7.206474304,-27.86789894,-33.4563446,-0.002668799367,-0.01789128594,-0.02890080027,-0.320317775,9.793665886,1.104911685 -6.88969094,-6.774410248,-28.23335838,-33.88916016,-0.01878705993,-0.02112191916,-0.02776563354,-0.4035662711,9.821204185,1.327342987 -6.89969094,-5.018215179,-27.21451378,-34.1181488,-0.02795302868,-0.03217923641,-0.01834156923,-0.5014348626,9.811349869,1.555790424 -6.90969094,-7.20728302,-27.60124397,-34.2383728,-0.008743250743,-0.01979034208,-0.01190102287,-0.58278054,9.913967133,1.847940207 -6.91969094,-7.614204407,-27.08926582,-33.05297852,0.02492422983,0.0008906451985,-0.00287973159,-0.5595668554,10.02017021,1.774408937 -6.92969094,-7.233718872,-27.88195992,-34.6762085,0.04376917332,0.04712672532,-4.605692811e-05,-0.4242864549,9.926309586,1.59119153 -6.93969094,-6.364692688,-26.41115761,-34.3249054,0.05611082911,0.08474594355,0.001481917687,-0.3794151247,9.889209747,1.309559703 -6.94969094,-6.492179871,-26.68140984,-32.32142639,0.04988059774,0.0568838492,0.004740366712,-0.3320913613,9.857626915,1.161911249 -6.95969094,-6.750072479,-27.82018852,-33.91860962,0.03539325669,0.001818951452,0.01166409627,-0.2974179685,9.810185432,1.090349078 -6.96969094,-7.654762268,-27.77787971,-33.00390625,0.01885271445,-0.02488981374,0.01005619578,-0.2859608829,9.783588409,1.121622562 -6.97969094,-6.388221741,-27.09098244,-33.51342773,0.01086804736,-0.04192594439,0.01164975017,-0.3405780494,9.779755592,1.130196571 -6.98969094,-8.289241791,-27.72134972,-33.92221069,-0.001383066177,-0.04618999362,0.01165413111,-0.3816172481,9.75190258,1.178019404 -6.99969094,-7.193969727,-26.92669106,-35.50730896,-0.0115212854,-0.04669833183,0.01270983368,-0.328725487,9.718396187,1.172267914 -7.00969094,-7.662380219,-27.5147419,-34.09091187,-0.02117509581,-0.01347930171,0.006302303169,-0.3198303878,9.751620293,1.187064886 -7.01969094,-6.630390167,-28.35517693,-34.03343201,-0.03068202361,0.03722409159,-0.002729954664,-0.2822543681,9.780727386,1.300859928 -7.02969094,-5.79510498,-27.56947136,-33.32810974,-0.03604897112,0.08230222762,-0.009162247181,-0.2794805765,9.743423462,1.304435372 -7.03969094,-5.177967072,-26.96727562,-35.05105591,-0.03218114376,0.0754532665,-0.01454768702,-0.2370035499,9.778010368,1.412263632 -7.04969094,-7.679096222,-28.19104958,-32.97447205,-0.01724203117,0.04264059663,-0.01922232471,-0.3165919483,9.784212112,1.443166137 -7.05969094,-6.45602417,-27.7936573,-34.68421936,0.001956798136,0.03951912373,-0.01594966836,-0.4240784943,9.821266174,1.450664759 -7.06969094,-6.479553223,-28.47348213,-33.8727417,0.008233220316,0.02018114552,-0.008499246091,-0.3593118489,9.874123573,1.357286453 -7.07969094,-6.767910004,-27.56232262,-35.46304321,0.009127783589,-0.001021089964,-0.004468719475,-0.3498756886,9.895945549,1.244823337 -7.08969094,-6.692485809,-27.12278557,-33.20527649,-0.0003577759489,0.005629235413,-0.005507835187,-0.3065412641,9.852435112,1.167840958 -7.09969094,-6.924747467,-27.71418571,-35.14666748,-0.01182218362,0.001805877313,-0.01178541407,-0.3422503471,9.787656784,1.151168823 -7.10969094,-7.233718872,-27.88195992,-34.6762085,-0.02720049769,-0.03890308738,-0.006950148847,-0.3069644272,9.728578568,1.21667695 -7.11969094,-7.233718872,-27.88195992,-34.6762085,-0.03472160175,-0.08361277729,-0.002064191969,-0.3741668761,9.828631401,1.381771445 -7.12969094,-6.566795349,-27.38760185,-33.79716492,-0.02443219163,-0.1106951907,0.003613332286,-0.414747417,9.909883499,1.478235364 -7.13969094,-6.566795349,-27.38760185,-33.79716492,-0.009322667494,-0.08561228961,0.001425060909,-0.3364252448,9.950445175,1.520546079 -7.14969094,-6.894901276,-27.4317112,-34.55636597,0.01297305804,-0.02708912455,-0.01052638143,-0.2902875245,9.916857719,1.579128742 -7.15969094,-6.756885529,-27.82370567,-34.22355652,0.03051350266,0.02103862539,-0.02036703378,-0.3130198121,9.968650818,1.473050833 -7.16969094,-5.754547119,-26.88085747,-33.37718201,0.0321297273,0.03274782375,-0.02557382733,-0.2421809435,9.896290779,1.491528034 -7.17969094,-6.583019257,-27.66304588,-33.77752686,0.03285422921,0.03309166431,-0.0193908941,-0.3465489447,10.00986385,1.124078274 -7.18969094,-6.140735626,-28.02323341,-33.75289917,0.006991864182,0.03043413348,-0.01644375175,-0.3370473981,9.83026123,1.150320888 -7.19969094,-6.395530701,-27.49536324,-32.72157288,-0.008524950594,-0.003374490887,-0.008978554048,-0.3312490284,9.773057938,1.049988508 -7.20969094,-6.388221741,-27.09098244,-33.51342773,-0.02062888816,-0.03278618306,-0.003952762112,-0.3668813407,9.767832756,1.011610508 -7.21969094,-7.223503113,-27.876688,-34.21875,-0.04249082133,-0.0584564656,0.0001669055782,-0.3540610373,9.73944664,1.033432245 -7.22969094,-6.600048065,-27.67183495,-34.53993225,-0.05800957978,-0.07085700333,0.003791878931,-0.2489045262,9.71612072,1.233810782 -7.23969094,-5.110660553,-26.6654644,-32.78346252,-0.05563404411,-0.05858933181,-0.002280948451,-0.3057807386,9.793344498,1.323033094 -7.24969094,-7.309940338,-27.05746269,-33.36116028,-0.04836731032,-0.03795132041,-0.005654682405,-0.3357924521,10.0036459,1.809297442 -7.25969094,-6.61416626,-28.07973289,-34.05305481,-0.01423756406,0.006586294156,-0.01188713871,-0.3840130866,10.07378864,1.766835213 -7.26969094,-7.343997955,-27.07504082,-34.88597107,0.01365469862,0.07387618721,-0.01604680531,-0.4060422778,9.988751411,1.480861068 -7.27969094,-7.777240753,-29.57706642,-33.63873291,0.02453222126,0.1045350581,-0.01727712527,-0.4209952354,9.866679192,1.274744511 -7.28969094,-5.620742798,-27.00795174,-33.97889709,0.01804178953,0.06112404913,-0.005017064512,-0.3968960345,9.788542747,1.140993834 -7.29969094,-7.377742767,-27.76013756,-34.53192139,0.0008723521605,0.004055330064,0.007162726019,-0.3800239861,9.755570412,1.057734847 -7.30969094,-7.672595978,-27.52001381,-34.5483551,-0.01781889051,-0.02779907547,0.01464441419,-0.3330850601,9.757546425,1.02295351 -7.31969094,-7.233718872,-27.88195992,-34.6762085,-0.03380134702,-0.05228021741,0.01153825596,-0.3335008323,9.738282204,1.145313025 -7.32969094,-7.320156097,-27.0627346,-33.81858826,-0.04232557118,-0.0432979241,0.008302789181,-0.3039984107,9.838270187,1.414606929 -7.33969094,-7.190254211,-27.59245491,-33.47596741,-0.02935238183,-0.006282945164,-0.002957660705,-0.3414013386,9.905882835,1.408320904 -7.34969094,-7.477802277,-26.94795036,-34.28424072,-0.02123269252,0.01367532462,-0.01296658069,-0.4141909778,9.940719604,1.555158734 -7.35969094,-7.60969162,-29.01906395,-34.59448242,-0.001434227452,0.0439722389,-0.01752397791,-0.3848665655,9.939268112,1.577160716 -7.36969094,-6.877067566,-27.6895771,-33.01191711,0.01578740031,0.06906723976,-0.01977552287,-0.4487940073,9.894701958,1.458930373 -7.37969094,-7.281089783,-28.57409096,-34.93208313,0.01979922131,0.06542313844,-0.01498976164,-0.4290300012,9.838287354,1.364023685 -7.38969094,-8.161937714,-28.51947594,-32.95002747,0.01419570576,0.05275142938,-0.007790469099,-0.4366999269,9.831308365,1.314301133 -7.39969094,-5.692451477,-28.11325264,-34.20532227,0.006470880471,0.05087926984,-0.0004649830516,-0.4752577841,9.859540939,1.219009399 -7.40969094,-7.608882904,-29.28571892,-33.81245422,-0.007653800771,0.03567602485,0.007800285239,-0.4810006022,9.848795891,1.221743464 -7.41969094,-7.368019104,-28.15572929,-32.97767639,-0.01515169907,0.01380138658,0.012325285,-0.4593022466,9.826662064,1.251535416 -7.42969094,-7.377742767,-27.76013756,-34.53192139,-0.01542961784,0.001022662269,0.0169789847,-0.4755968451,9.779748917,1.37317872 -7.43969094,-7.240531921,-27.88547707,-34.9811554,-0.001768577844,-0.009118218906,0.0176607091,-0.4284687936,9.814570427,1.428025246 -7.44969094,-5.634056091,-27.68250465,-32.70996094,0.01253924798,-0.02427603118,0.01522912644,-0.4111814201,9.89552021,1.414922833 -7.45969094,-5.822349548,-27.58353233,-34.54794312,0.016501192,-0.02373738401,0.01423248276,-0.3876463473,9.8126688,1.245267987 -7.46969094,-4.953323364,-26.11273003,-34.19667053,0.01277748775,-0.00871932786,0.006232345942,-0.3258767724,9.745279312,1.207792163 -7.47969094,-8.162742615,-28.25282097,-33.73207092,0.008416143246,0.01032425929,-0.002889809664,-0.3021071851,9.780869484,1.24430275 -7.48969094,-7.350498199,-27.74607658,-33.31208801,0.002876439132,0.02547182329,-0.01436623745,-0.2963513732,9.847862244,1.273426533 -7.49969094,-7.305427551,-28.98726082,-34.90263367,-0.002952933777,0.0436315015,-0.02437200397,-0.324506253,9.83515358,1.275908351 -7.50969094,-6.436088562,-28.18397713,-32.67250061,-0.005305088125,0.0420396477,-0.02915145457,-0.347178787,9.725076675,1.296135187 -7.51969094,-6.781223297,-28.23687553,-34.19410706,-0.002636422403,0.03410620987,-0.03221314773,-0.3757326901,9.717552185,1.36000824 -7.52969094,-5.242053986,-28.33571434,-34.19050598,0.0007457425818,-0.01242156141,-0.02688090876,-0.4411390722,9.801218033,1.417131782 -7.53969094,-6.767910004,-27.56232262,-35.46304321,-0.001384953968,-0.05899711698,-0.01799323969,-0.4971121252,9.86701107,1.508045077 -7.54969094,-6.147857666,-27.35922813,-35.93670654,0.005357009359,-0.04358646274,-0.01161833853,-0.4639644921,9.821174622,1.404155374 -7.55969094,-4.976852417,-26.79255486,-33.38519287,0.0151279429,0.0234514568,-0.01500775106,-0.3858245611,9.831782341,1.389816284 -7.56969094,-7.960639954,-27.27637672,-34.2598114,0.0150560895,0.07433521748,-0.02114808559,-0.4165942073,9.863568306,1.26608634 -7.57969094,-7.960639954,-27.27637672,-34.2598114,-0.00113414228,0.0747378394,-0.01512921415,-0.4706613719,9.822451591,1.127860188 -7.58969094,-6.924438477,-28.38170815,-33.26782227,-0.01949587092,0.04672278464,-0.004416399635,-0.445856601,9.810827255,1.171207547 -7.59969094,-6.924438477,-28.38170815,-33.26782227,-0.02355873771,0.02254176326,0.001991390716,-0.4345932901,9.865891457,1.256605029 -7.60969094,-7.360713959,-27.7513485,-33.76951599,-0.01876131445,-0.01338596083,0.01086175442,-0.4302533865,9.892401695,1.399819732 -7.61969094,-6.099372864,-27.60127449,-33.01992798,-0.01351431571,-0.02890297584,0.01611890458,-0.4486133754,9.895947456,1.357090831 -7.62969094,-6.023464203,-27.7582531,-36.21383667,-0.006224627607,-0.03095378913,0.02272274345,-0.393653661,9.873376846,1.453101158 -7.63969094,-4.95381546,-26.51359367,-33.09985352,0.005430291407,-0.03075273894,0.02272888087,-0.3597887158,9.917237282,1.505781412 -7.64969094,-7.662380219,-27.5147419,-34.09091187,0.01662251353,-0.01261894777,0.02528715879,-0.3409255147,9.983776093,1.455637813 -7.65969094,-5.956157684,-27.45644188,-33.94624329,0.02580481023,0.01031739917,0.02337107435,-0.3538760841,9.961380005,1.392374516 -7.66969094,-5.945941925,-27.45116997,-33.48878479,0.02733973414,0.02243147232,0.02270804159,-0.3228459656,9.875902176,1.254824042 -7.67969094,-6.699794769,-27.52716637,-32.41342163,0.01501560863,0.04744583368,0.0235953778,-0.2549906671,9.883605957,1.205357552 -7.68969094,-5.870212555,-28.67652702,-33.70703125,-0.004503405653,0.07281056046,0.02109043114,-0.2139221281,9.83890152,1.22555697 -7.69969094,-6.106185913,-27.60479164,-33.3249054,-0.02125325799,0.07326879352,0.01964235678,-0.3086723089,9.874782562,1.160287142 -7.70969094,-5.586685181,-26.99037361,-32.4540863,-0.03068202361,0.07159326226,0.0173430033,-0.2877425849,9.871712685,1.175171494 -7.71969094,-4.659275055,-26.08619881,-34.96228027,-0.02949244902,0.07909663022,0.009707890451,-0.2861399055,9.862574577,1.186464429 -7.72969094,-6.55688858,-26.71480751,-35.21856689,-0.03317952901,0.09336777031,0.006730064284,-0.2911436856,9.858118057,1.322293639 -7.73969094,-5.501056671,-27.54294014,-34.09371948,-0.02859684452,0.08231816441,0.006812664215,-0.3232996762,9.870845795,1.490914941 -7.74969094,-6.781223297,-28.23687553,-34.19410706,-0.02269191667,0.05310622603,0.01530707069,-0.4537217319,9.846877098,1.393212676 -7.75969094,-6.124019623,-27.34692574,-34.86933899,-0.0262016058,0.02659603208,0.02568925545,-0.4360646009,9.820919991,1.411521673 -7.76969094,-5.696960449,-26.18345451,-32.66384888,-0.01927095279,0.02992433496,0.02766008116,-0.3731211722,9.859240532,1.493628025 -7.77969094,-6.423088074,-26.84190559,-35.82026672,-0.006756332237,0.06467999518,0.02580158599,-0.3479112685,9.877393723,1.410155177 -7.78969094,-6.002719879,-28.41522789,-33.42008972,-0.008050819859,0.1007210612,0.02127013728,-0.3521136343,9.825234413,1.321725845 -7.79969094,-7.713153839,-28.2086277,-34.49928284,-0.008586347103,0.1213297322,0.01726247743,-0.3854017854,9.753717422,1.385715365 -7.80969094,-6.133430481,-27.61885262,-34.54473877,0.006605016999,0.1322527677,0.01431554556,-0.3959612548,9.918157578,1.32136023 -7.81969094,-5.668609619,-28.10094643,-33.13796997,0.01407197956,0.1031682789,0.02043901943,-0.3845593333,9.779145241,1.264665008 -7.82969094,-7.165916443,-27.17928505,-33.50541687,0.01219318714,0.0370747447,0.02418522909,-0.3916911483,9.846901894,1.245322227 -7.83969094,-6.130828857,-27.35043907,-35.17430115,0.009833314456,0.002732901601,0.03058625944,-0.3888401389,9.822085381,1.323127627 -7.84969094,-6.675769806,-26.44647789,-34.32170105,0.01811700687,-0.005176926032,0.03133744374,-0.3480463326,9.801818848,1.435973644 -7.85969094,-6.683078766,-26.85085869,-33.52984619,0.0284155868,-0.004169987515,0.03186465427,-0.3630813062,9.866709709,1.449119687 -7.86969094,-5.778076172,-27.5606823,-32.56570435,0.03512119129,-0.008301069029,0.02844945155,-0.3229779601,9.843298912,1.415961981 -7.87969094,-4.823112488,-27.30996895,-31.97518921,0.0376309827,0.01536973473,0.02377301455,-0.2518242002,9.83313942,1.337736964 -7.88969094,-6.597137451,-28.07094383,-33.29064941,0.03478628397,0.03389872238,0.01552080922,-0.2680309415,9.835711479,1.253291488 -7.89969094,-6.525432587,-26.96564293,-33.06419373,0.0338845104,0.04535847902,0.006745200139,-0.2180101275,9.819023132,1.17667377 -7.90969094,-5.127689362,-26.67425346,-33.54586792,0.03140550107,0.03659430891,-0.001788613503,-0.1975850165,9.744873047,1.114180207 -7.91969094,-5.986499786,-28.13978386,-33.43971252,0.02867707983,0.01808444411,-0.009790910408,-0.2553076744,9.766321182,1.183923244 -7.92969094,-6.099372864,-27.60127449,-33.01992798,0.03164083511,-0.009051376022,-0.01414498687,-0.2725343108,9.800502777,1.266273379 -7.93969094,-7.190254211,-27.59245491,-33.47596741,0.03399694338,-0.03510866314,-0.01080191508,-0.3454096317,9.859103203,1.313657284 -7.94969094,-6.692485809,-27.12278557,-33.20527649,0.03248399124,-0.05040859431,-0.009788624942,-0.3541353345,9.83678627,1.314404488 -7.95969094,-5.701858521,-28.38517952,-33.88075256,0.02853907272,-0.04782148451,-0.009216323495,-0.3199444115,9.823339462,1.327843428 -7.96969094,-7.233718872,-27.88195992,-34.6762085,0.02739499882,-0.0317414254,-0.01145336591,-0.3068087101,9.891061783,1.347099781 -7.97969094,-5.948848724,-27.05206108,-34.73809814,0.02311804891,0.002707405714,-0.01562085561,-0.3483853638,9.85543251,1.401388407 -7.98969094,-6.965305328,-28.40279961,-35.09759521,0.02524874732,0.03096579574,-0.01465842687,-0.391481787,9.854180336,1.335046649 -7.99969094,-6.503890991,-28.88665199,-33.84329224,0.01820404828,0.03465368971,-0.005479752086,-0.3856081367,9.825998306,1.251428127 -8.00969094,-6.600048065,-27.67183495,-34.53993225,0.007291789167,0.007053038105,0.001987132709,-0.3335992694,9.810012817,1.201320887 -8.01969094,-6.163772583,-28.3021946,-34.03823853,-0.004180718213,-0.01456182078,0.01051915064,-0.3299972117,9.774536133,1.128297806 -8.02969094,-6.740352631,-28.21578026,-32.36434937,-0.01397203282,-0.03909219801,0.01338874549,-0.2939029634,9.800624847,1.181418419 -8.03969094,-6.740352631,-28.21578026,-32.36434937,-0.01755905338,-0.06164037436,0.01571976021,-0.3166171908,9.827580452,1.220766783 -8.04969094,-6.462524414,-28.46469307,-33.1103363,-0.02299508452,-0.07050529122,0.0169789847,-0.3287492394,9.846059799,1.265481591 -8.05969094,-6.462524414,-28.46469307,-33.1103363,-0.02086721361,-0.05611816049,0.01313410886,-0.3127599657,9.857430458,1.293400407 -8.06969094,-7.729377747,-28.48407173,-34.47964478,-0.01981415227,-0.02539752796,0.006699037272,-0.2972289622,9.890842438,1.254536748 -8.07969094,-7.350498199,-27.74607658,-33.31208801,-0.01718049124,0.008935067803,-0.003795302706,-0.2668244243,9.924371719,1.326421261 -8.08969094,-5.829654694,-27.98791313,-33.75610352,-0.009907738306,0.0356521979,-0.01852735132,-0.2779456675,9.923431396,1.423228025 -8.09969094,-7.517551422,-27.90321922,-33.45314026,0.006071233191,0.05359268934,-0.02314954251,-0.3708066344,9.927910805,1.405539036 -8.10969094,-6.399246216,-26.82959938,-34.75291443,0.01424688939,0.05802699178,-0.02333005331,-0.3841352463,9.899069786,1.362089515 -8.11969094,-6.709514618,-27.13157463,-33.96768188,0.01599690691,0.02361442149,-0.01315881126,-0.3848026097,9.839741707,1.206048846 -8.12969094,-4.843856812,-26.65299416,-34.76893616,0.002254735678,-0.01309334487,-0.003706273623,-0.3823072016,9.808428764,1.156294227 -8.13969094,-6.504203796,-28.21913338,-35.72212219,-0.0131037822,-0.02041620202,0.002272384241,-0.3847629428,9.844836235,1.307581306 -8.14969094,-6.73985672,-27.81491661,-33.46115112,-0.0191719532,-0.008650093339,0.006325504277,-0.3688550293,9.877473831,1.353151679 -8.15969094,-6.791439056,-28.24214745,-34.65156555,-0.01912099682,0.009797222912,0.006641102489,-0.3635028005,9.896050453,1.36118114 -8.16969094,-5.996219635,-27.74419212,-34.99397278,-0.01667619683,0.02679310925,0.003280967008,-0.3678926826,9.887942314,1.416732907 -8.17969094,-6.496582031,-28.48227119,-34.63514709,-0.004689696245,0.06497299671,0.005499317776,-0.3481041789,9.858240128,1.296657085 -8.18969094,-6.429588318,-27.51294136,-34.24638367,-0.004146645777,0.07617202401,0.001241869759,-0.3353248835,9.83586216,1.141688943 -8.19969094,-5.587493896,-26.72371864,-33.2361145,-0.01000920963,0.02543907054,0.004583748057,-0.3017803431,9.841454506,1.160948873 -8.20969094,-6.705497742,-29.46223259,-34.41235352,-0.01565810107,-0.03551977128,0.01044568978,-0.3462574482,9.866422653,1.207236767 -8.21969094,-6.877872467,-27.42292213,-33.79396057,-0.01856961846,-0.07280281931,0.01563544571,-0.3439301848,9.821113586,1.283706784 -8.22969094,-5.754547119,-26.88085747,-33.37718201,-0.01776750758,-0.05113984644,0.01072100736,-0.3152393103,9.849270821,1.368287325 -8.23969094,-6.790630341,-28.50880241,-33.86953735,-0.007758837193,0.0131536331,0.00110917585,-0.2948393226,9.888804436,1.375244021 -8.24969094,-6.883880615,-27.69309425,-33.31689453,0.0006529949605,0.05003567785,-0.007681441959,-0.3519371152,9.929426193,1.400822401 -8.25969094,-6.733852386,-27.54474449,-33.93823242,0.006397298537,0.0646423623,-0.01796964183,-0.3227308393,9.871684074,1.478527665 -8.26969094,-6.389030457,-26.82432747,-34.29545593,0.01365157124,0.08304973692,-0.02444793843,-0.285761565,9.80264473,1.286781788 -8.27969094,-5.979190826,-27.73540306,-34.23156738,0.01110325288,0.1031337157,-0.02889024839,-0.3004356325,9.809235573,1.281821251 -8.28969094,-7.199661255,-27.86438179,-33.15139771,0.01459526736,0.09770360589,-0.02966837026,-0.3339990079,9.804094315,1.184403181 -8.29969094,-6.924930573,-28.78257179,-32.17100525,0.001203035004,0.04889506102,-0.0212808568,-0.4237344265,9.790711403,1.148083925 -8.30969094,-6.60735321,-28.07621574,-33.74809265,-0.009375064634,0.01187157445,-0.01253232919,-0.3896429539,9.781116486,1.272933602 -8.31969094,-7.638046265,-27.10157204,-34.12036133,-0.00883369986,-0.007211517543,-0.005917307455,-0.4066072404,9.739402771,1.226894975 -8.32969094,-7.719966888,-28.21214485,-34.804245,-0.00851373747,-0.04851602763,0.003152085003,-0.4191360176,9.802324295,1.162682176 -8.33969094,-6.123214722,-27.6135807,-34.08731079,-0.01866781525,-0.08298770338,0.004629021045,-0.3646979332,9.822608948,1.243217111 -8.34969094,-6.573608398,-27.391119,-34.10212708,-0.01661939919,-0.05272687972,-0.001723809517,-0.3448917866,9.821504593,1.550055861 -8.35969094,-5.922412872,-26.77134514,-34.30026245,0.01482299808,0.01570285112,-0.01347478852,-0.4482457042,9.877696037,1.567859054 -8.36969094,-5.51127243,-27.54821205,-34.55114746,0.0364352949,0.05439424515,-0.01533631422,-0.5011907816,9.884627342,1.447217703 -8.37969094,-6.45602417,-27.7936573,-34.68421936,0.03358461335,0.05259715021,-0.007164225448,-0.4079911113,9.869935989,1.386832952 -8.38969094,-5.282611847,-29.02432823,-34.14143372,0.03009070083,0.02654754184,-0.0008480702527,-0.3597754538,9.862734795,1.254638791 -8.39969094,-6.476646423,-28.87259102,-32.62342834,0.02441980317,0.01135490555,0.004194807727,-0.3759404719,9.843818665,1.162875891 -8.40969094,-5.877025604,-28.68004417,-34.01199341,0.01330524031,-0.007301305421,0.01035590097,-0.272282958,9.803807259,1.169050694 -8.41969094,-7.271369934,-28.96968269,-33.37782288,0.004843432456,-0.02468059771,0.0119635351,-0.2605700195,9.832016945,1.192519903 -8.42969094,-6.976016998,-28.80893898,-34.45822144,-0.006202861667,-0.0435533002,0.01424292661,-0.2615356743,9.865633011,1.344079614 -8.43969094,-6.147548676,-28.02675056,-34.05786133,-0.004407898989,-0.04488246143,0.013322182,-0.2936043739,9.890439034,1.490942717 -8.44969094,-6.826293945,-26.9956913,-32.6035614,0.005539809354,-0.004841507412,0.0106570404,-0.2814545333,9.921515465,1.381560922 -8.45969094,-5.945941925,-27.45116997,-33.48878479,0.009740176611,0.0336917229,0.005443233531,-0.2583817542,9.897677422,1.307276845 -8.46969094,-8.233951569,-28.95725441,-35.05532837,0.006820478477,0.03775998205,0.0009987638332,-0.188798517,9.863088608,1.290988088 -8.47969094,-5.508361816,-27.94732094,-33.30186462,0.002396757714,0.02465282194,-0.002144287806,-0.2838674486,9.874014854,1.326351166 -8.48969094,-6.843322754,-27.00448036,-33.3659668,-0.005590040237,0.03597750515,-0.002197280526,-0.3121668994,9.947696686,1.387628078 -8.49969094,-6.843322754,-27.00448036,-33.3659668,6.277114153e-07,0.05783951283,-0.008056694642,-0.286522001,9.934962273,1.414525747 -8.50969094,-6.222164154,-28.73294258,-35.53359985,0.003852193244,0.06198479235,-0.01023218781,-0.3322080374,9.870047569,1.257705212 -8.51969094,-6.222164154,-28.73294258,-35.53359985,-0.007020583376,0.03898721188,-0.007140536793,-0.354721427,9.797861099,1.161445856 -8.52969094,-6.894096375,-27.69836617,-33.77432251,-0.02465378493,0.003852344118,-0.001664606389,-0.3568671644,9.820009232,1.262687206 -8.53969094,-6.123214722,-27.6135807,-34.08731079,-0.0191527456,-0.0115311034,0.00301573053,-0.4283187389,9.78444767,1.281093597 -8.54969094,-5.668922424,-27.43342781,-35.01679993,-0.02239307947,-0.01610159874,0.009372130036,-0.4523630142,9.761321068,1.180357814 -8.55969094,-5.701858521,-28.38517952,-33.88075256,-0.02748598531,-0.02391372249,0.01481722109,-0.3728437424,9.760118484,1.258087873 -8.56969094,-5.822349548,-27.58353233,-34.54794312,-0.02905605733,-0.02079217322,0.01221286692,-0.3384934366,9.839878082,1.264950633 -8.57969094,-7.391365051,-27.76716805,-35.14186096,-0.02644769847,0.004086437169,0.003770391457,-0.3486168683,9.821240425,1.333070636 -8.58969094,-6.013744354,-28.15384483,-34.65957642,-0.01537995227,0.01251769904,0.0009696688503,-0.3741804063,9.827664375,1.279482841 -8.59969094,-8.104663849,-27.15455437,-34.11555481,-0.01356030069,-0.002310473472,-0.002780725248,-0.3859480917,9.867848396,1.242322564 -8.60969094,-6.163772583,-28.3021946,-34.03823853,-0.02265218273,-0.0157774277,-0.00271670986,-0.3495922685,9.865961075,1.364074707 -8.61969094,-7.311927795,-29.65829659,-33.32875061,-0.01418382674,0.0006659564096,-0.005384140648,-0.3531033099,9.862417221,1.514775276 -8.62969094,-4.850357056,-27.32402992,-33.1950531,0.003424083814,0.03531191498,-0.006987604313,-0.4141439497,9.91368103,1.393144369 -8.63969094,-4.049133301,-26.55590248,-34.01454163,0.005545211025,0.02093706839,-0.003268030239,-0.388307482,9.895030975,1.247608185 -8.64969094,-6.623577118,-28.35165977,-33.72845459,-0.0003154044971,-0.02269215137,0.0004618884996,-0.3421718478,9.894777298,1.317503333 -8.65969094,-5.627555847,-27.01146889,-34.28384399,-0.001384953968,-0.04192937911,0.004192750901,-0.3671855927,9.879576683,1.335730672 -8.66969094,-6.40574646,-27.50063515,-33.17903137,0.006623237394,-0.02268284559,0.003680216148,-0.4353603423,9.886388779,1.367411256 -8.67969094,-7.719654083,-28.87966347,-32.92539978,0.01196025219,0.010412395,0.001041296404,-0.3877769411,9.898147583,1.325113058 -8.68969094,-6.09287262,-26.93023872,-34.59381104,0.01560688298,0.03164923191,-0.002251014113,-0.3476009667,9.874887466,1.177082181 -8.69969094,-5.185276031,-27.37165642,-34.25920105,0.007014569826,0.0320574306,-0.006441064179,-0.3255999684,9.796077728,1.179525852 -8.70969094,-5.887241364,-28.68531609,-34.46943665,-0.004665999673,0.02300379425,-0.007558021694,-0.3302562535,9.838270187,1.29654789 -8.71969094,-6.934654236,-28.38698006,-33.72525024,-0.0008134776726,0.01394113898,-0.008628170937,-0.4091928303,9.864145279,1.397688985 -8.72969094,-6.61447525,-27.41221046,-35.93190002,0.01304710936,0.0058097695,-0.01178541407,-0.3761585057,9.922281265,1.419453979 -8.73969094,-7.247840881,-28.28985786,-34.18930054,0.01882565767,-0.01711278409,-0.009561704472,-0.4335117936,9.864617348,1.383633018 -8.74969094,-6.140735626,-28.02323341,-33.75289917,0.01722362638,-0.01998676173,-0.004755612463,-0.3925135732,9.847726822,1.352542996 -8.75969094,-5.692451477,-28.11325264,-34.20532227,0.01413588692,-0.004737165757,-0.003758656094,-0.3975400031,9.840883255,1.424174428 -8.76969094,-6.374908447,-26.41642952,-34.78236389,0.01665059105,0.01422545873,0.001111822668,-0.4121716917,9.825641632,1.308867693 -8.77969094,-6.872554779,-29.61937523,-34.55342102,0.0079627214,0.00683412049,0.008576428518,-0.3998596966,9.780550003,1.15374434 -8.78969094,-8.128192902,-27.8343792,-33.30407715,-0.00830971729,-0.0006057955325,0.0137829408,-0.3708814979,9.795752525,1.336443067 -8.79969094,-6.156959534,-28.29867744,-33.73326111,-0.003319519572,0.03192749619,0.008939841762,-0.3640524149,9.828931808,1.51407516 -8.80969094,-4.970039368,-26.7890377,-33.08021545,0.0151279429,0.08334953338,0.004141870886,-0.40965572,9.86701107,1.33130312 -8.81969094,-6.123214722,-27.6135807,-34.08731079,0.01143238787,0.08572150022,0.005260156002,-0.3747372925,9.777318001,1.236672163 -8.82969094,-5.484832764,-27.26749611,-34.11334229,-0.0006647165865,0.04545711726,0.00868627429,-0.3375897408,9.728914261,1.210252285 -8.83969094,-5.79510498,-27.56947136,-33.32810974,-0.004408819135,0.004282972775,0.01372285746,-0.3406184614,9.721815109,1.191379905 -8.84969094,-6.901714325,-27.43522835,-34.86132812,-0.01009862404,-0.02514038049,0.0141247306,-0.3459747136,9.810904503,1.276666164 -8.85969094,-5.484027863,-27.53415108,-33.33131409,-0.01250343956,-0.009691631421,0.009318539873,-0.3982391655,9.863305092,1.43898356 -8.86969094,-5.594306946,-26.72723579,-33.5410614,-0.005750786047,0.02927107364,0.008384827524,-0.3999786675,9.804018974,1.47284627 -8.87969094,-5.6510849,-27.69129372,-33.47236633,0.001811090857,0.02968214266,0.01073560491,-0.4115684032,9.764409065,1.312410116 -8.88969094,-6.262722015,-29.42155647,-35.48452759,-0.007250172552,0.009495561942,0.01774243638,-0.3444252312,9.76662159,1.203037024 -8.89969094,-5.819438934,-27.98264122,-33.29866028,-0.01225235127,-0.03034259938,0.01879092306,-0.2967675924,9.789928436,1.379716516 -8.90969094,-6.181606293,-28.04432869,-35.58267212,0.003125378862,-0.04652474821,0.0152979847,-0.3290558457,9.828923225,1.316641212 -8.91969094,-7.777553558,-28.90954781,-35.51757812,0.008289773948,-0.05507510155,0.006511804182,-0.3192000985,9.854446411,1.306584358 -8.92969094,-6.726543427,-27.14036369,-34.73008728,0.006072483025,-0.03125399351,-0.001492372714,-0.2490886152,9.842761993,1.329090953 -8.93969094,-6.530639648,-28.49984932,-36.15995789,0.005286928266,0.004717913922,-0.01269114576,-0.2787717581,9.871692657,1.240097642 -8.94969094,-6.156959534,-28.29867744,-33.73326111,0.007722427137,0.00421890663,-0.01890247688,-0.341399461,9.860766411,1.212909102 -8.95969094,-5.195491791,-27.37692833,-34.71665955,-0.0005177315325,-0.02194965258,-0.01977552287,-0.3430656791,9.841578484,1.20852685 -8.96969094,-5.195491791,-27.37692833,-34.71665955,-0.004397911951,-0.01808477193,-0.02489776164,-0.2913993001,9.817040443,1.284796357 -8.97969094,-7.726776123,-28.21565819,-35.10920715,0.002665152773,0.001532091293,-0.02840395272,-0.3493477702,9.777391434,1.309384704 -8.98969094,-7.726776123,-28.21565819,-35.10920715,0.00724685844,-0.006472785026,-0.02936365642,-0.3285253942,9.861621857,1.359132767 -8.99969094,-6.80475235,-28.91670036,-33.38262939,0.007950174622,-0.02342860959,-0.02639209293,-0.4434536099,9.895025253,1.340307117 -9.00969094,-4.424610138,-27.29214287,-35.02961731,0.006259321235,-0.02674228698,-0.02167566307,-0.4321332276,9.925757408,1.370373964 -9.01969094,-5.539627075,-25.63072395,-34.07704163,0.01441753004,-0.003579446115,-0.02427353896,-0.3899079859,9.913691521,1.459052444 -9.02969094,-8.179279327,-27.86074638,-35.59129333,0.02454777062,0.02349377796,-0.02628907003,-0.4051021338,9.94682312,1.378842115 -9.03969094,-4.857170105,-27.32754707,-33.5,0.01992200688,0.02365491912,-0.02259735391,-0.429605186,9.887137413,1.335855365 -9.04969094,-5.644271851,-27.68777657,-33.16741943,0.00781902764,0.003750482807,-0.0154065527,-0.3775611222,9.861557007,1.329430461 -9.05969094,-4.808990479,-26.902071,-32.46209717,0.002876439132,0.002535451669,-0.01032625698,-0.3763277531,9.841564178,1.236484885 -9.06969094,-6.058815002,-26.9126606,-33.06900024,-0.002186387777,0.02572786249,-0.007384793833,-0.3992110789,9.797712326,1.18870461 -9.07969094,-4.641933441,-26.74492836,-32.3210144,-0.01085284352,0.03625140339,7.089274004e-05,-0.3808373511,9.743421555,1.078503132 -9.08969094,-5.76556778,-26.61947441,-34.61665344,-0.03301307559,0.01188983768,0.006888038013,-0.3624552786,9.697451591,1.048014522 -9.09969094,-6.100177765,-27.33461952,-33.80197144,-0.05799226463,-0.02245188132,0.01368184574,-0.3520770371,9.722191811,1.232402325 -9.10969094,-6.076648712,-26.65479469,-34.6134491,-0.05832531676,-0.02201962285,0.01837413386,-0.4136171639,9.800364494,1.344371796 -9.11969094,-7.182945251,-27.18807411,-34.26782227,-0.04887463897,-0.006774947047,0.01895935461,-0.4178379476,9.859547615,1.376471162 -9.12969094,-6.918430328,-28.11153603,-33.74488831,-0.03943052515,-0.01560469344,0.01933539659,-0.4469336271,9.893157959,1.409580708 -9.13969094,-6.422775269,-27.50942421,-33.94143677,-0.03463018686,-0.008655243553,0.01995558664,-0.3561683893,9.847137451,1.53296864 -9.14969094,-5.195491791,-27.37692833,-34.71665955,-0.01544791646,0.02348045073,0.0137829408,-0.3908901513,9.918141365,1.499109507 -9.15969094,-6.600048065,-27.67183495,-34.53993225,0.001441395842,0.03940471262,0.0152179841,-0.4149687886,9.856978416,1.277093053 -9.16969094,-5.989406586,-27.74067497,-34.68902588,-0.006609489676,0.004615615588,0.02280433103,-0.3392545581,9.758050919,1.146429181 -9.17969094,-5.484832764,-27.26749611,-34.11334229,-0.02656558156,-0.03210052848,0.02782632969,-0.2474892884,9.751603127,1.163014531 -9.18969094,-5.819747925,-27.31511879,-35.17750549,-0.02908400446,-0.04421976209,0.02460899949,-0.2521186173,9.782547951,1.296428919 -9.19969094,-5.76556778,-26.61947441,-34.61665344,-0.02535528317,-0.05329878628,0.02000474744,-0.2538339794,9.860228539,1.281494498 -9.20969094,-5.551830292,-28.23682594,-34.5020752,-0.0306779705,-0.04690035433,0.01591363735,-0.259737134,9.867933273,1.315501928 -9.21969094,-6.381721497,-26.41994667,-35.08731079,-0.03282633424,-0.03781130165,0.01075538062,-0.2662158608,9.842382431,1.342554212 -9.22969094,-5.979686737,-28.13626671,-33.13476562,-0.03420222551,-0.0125182569,0.005236934405,-0.2183810771,9.868021965,1.407692909 -9.23969094,-6.100177765,-27.33461952,-33.80197144,-0.02300329134,0.02829145826,-0.00561462203,-0.2840327621,9.901955605,1.485758662 -9.24969094,-4.625217438,-26.06862068,-33.43746948,-0.01911250688,0.04374434054,-0.008972733282,-0.3135461211,9.874196053,1.319430351 -9.25969094,-8.288433075,-27.98800468,-33.1401825,-0.02691411227,0.02197675779,-0.01033035852,-0.299005717,9.850079536,1.237532496 -9.26969094,-6.509208679,-26.6901989,-33.08383179,-0.03162460029,0.001549815293,-0.01018739119,-0.3111849427,9.796510696,1.378709555 -9.27969094,-6.473861694,-27.5357914,-36.22865295,-0.02315361984,-0.002986954525,-0.009323257953,-0.3509652317,9.8478508,1.414524198 -9.28969094,-7.360713959,-27.7513485,-33.76951599,-0.02178758755,-0.01910696179,-0.009859112091,-0.3926713765,9.908628464,1.373457074 -9.29969094,-6.951683044,-28.39576912,-34.48765564,-0.02419984341,-0.01715814695,-0.01178541407,-0.3376707137,9.898147583,1.401946902 -9.30969094,-6.590637207,-27.39990807,-34.86453247,-0.01450248435,0.01003692858,-0.01763336919,-0.3839448094,9.886221886,1.38350749 -9.31969094,-7.36151886,-27.48469353,-34.55155945,-0.01278505847,0.01857505366,-0.01977552287,-0.3988118172,9.814868927,1.286329269 -9.32969094,-6.484874725,-26.27702904,-33.11326599,-0.0293417424,-0.004705166444,-0.01006867364,-0.4420841634,9.828910828,1.158545732 -9.33969094,-6.422775269,-27.50942421,-33.94143677,-0.04848059639,-0.02031212486,-0.001976955682,-0.3750244081,9.772753716,1.376981616 -9.34969094,-6.510704041,-28.89016914,-34.14823914,-0.04332835972,-0.01426587068,-0.00082276389,-0.4337833822,9.7676754,1.373581648 -9.35969094,-7.736190796,-28.48758888,-34.78462219,-0.0376179181,-0.01224911399,0.002211536281,-0.4449275136,9.839629173,1.465065002 -9.36969094,-7.217002869,-27.20565224,-35.79263306,-0.02085287496,-0.03022374399,0.009145841002,-0.4059441388,9.8934021,1.544756889 -9.37969094,-5.001186371,-27.20572472,-33.35574341,-0.01117274538,-0.04885619879,0.01271759532,-0.3361937106,9.898633957,1.443880439 -9.38969094,-6.46333313,-28.1980381,-33.8923645,0.02227536216,-0.04157933593,-0.001544222352,-0.383582741,10.06635571,1.592002034 -9.39969094,-6.45602417,-27.7936573,-34.68421936,0.02985722572,0.008457273245,0.003183291759,-0.4190266728,10.04802799,1.19617939 -9.40969094,-6.100177765,-27.33461952,-33.80197144,0.02161491662,-0.01152697392,0.004680039361,-0.3310981691,9.888607979,1.307484984 -9.41969094,-6.100177765,-27.33461952,-33.80197144,0.02274901047,-0.02205886319,0.003949012142,-0.2644232512,9.895751953,1.281700253 -9.42969094,-8.001693726,-28.36585426,-33.11393738,0.0270957537,0.008429657668,-0.004258564673,-0.3135220706,9.886099815,1.322879076 -9.43969094,-8.001693726,-28.36585426,-33.11393738,0.03116482496,0.03424095362,-0.01175708137,-0.3184842765,9.87495327,1.208269715 -9.44969094,-5.972377777,-27.73188591,-33.92662048,0.0162592046,0.01626186259,-0.0105086565,-0.335937798,9.807515144,1.067642093 -9.45969094,-5.161434174,-27.3593502,-33.19184875,-0.01620616391,-0.01655917056,-0.004570898134,-0.3055139184,9.73563385,1.092912316 -9.46969094,-7.310745239,-26.79080772,-34.14318848,-0.02988763899,-0.03499742597,-0.003262628801,-0.2359486967,9.825713158,1.553684115 -9.47969094,-6.573112488,-26.99025154,-35.19894409,-0.01365076192,-0.01185967028,-0.008056694642,-0.3714031279,9.954780579,1.606928349 -9.48969094,-6.388221741,-27.09098244,-33.51342773,-0.0008522793651,0.03052273393,-0.008053194731,-0.4348410964,9.923695564,1.438187957 -9.49969094,-5.524894714,-27.55524254,-35.16108704,-0.002721569035,0.01259369962,-0.0008508043829,-0.3950788081,9.823553085,1.357761741 -9.50969094,-4.90422821,-28.68719673,-31.87704468,-0.009681989439,-0.01744466461,0.007116397377,-0.3847718537,9.837177277,1.353380203 -9.51969094,-5.419940948,-26.16571236,-34.19186401,-0.01018538047,-0.0034658527,0.009544158354,-0.4182123542,9.833841324,1.346598744 -9.52969094,-7.343997955,-27.07504082,-34.88597107,-0.005646346137,0.01587818377,0.009550519288,-0.3948255181,9.812287331,1.296076536 -9.53969094,-6.710323334,-26.86491966,-34.74971008,-0.006285015494,0.003204530338,0.009770220146,-0.3175331056,9.817604065,1.234001398 -9.54969094,-8.449485779,-27.87497139,-33.75830078,-0.02056121826,-0.02370020375,0.01402841881,-0.310777247,9.741884232,1.292146921 -9.55969094,-5.525390625,-27.95611,-34.06427002,-0.0177536048,-0.04929082096,0.01203878596,-0.3341468275,9.749916077,1.421624541 -9.56969094,-7.351303101,-27.47942162,-34.09411621,-0.005059229676,-0.07262051105,0.01338049769,-0.3519653678,9.822608948,1.402941823 -9.57969094,-6.428779602,-27.77959633,-33.46435547,-0.004816933535,-0.09561718255,0.01041176356,-0.3689287603,9.827497482,1.293786645 -9.58969094,-6.60735321,-28.07621574,-33.74809265,-0.006380548701,-0.09612983465,0.008321847767,-0.2538587749,9.772336006,1.353903651 -9.59969094,-6.934654236,-28.38698006,-33.72525024,0.002941088751,-0.0783181861,-0.002114825416,-0.2645348608,9.775900841,1.470979691 -9.60969094,-7.518360138,-27.63656425,-34.23516846,0.0110929301,-0.0527979359,-0.009588164277,-0.2304200828,9.846688271,1.339077234 -9.61969094,-6.428779602,-27.77959633,-33.46435547,0.00417023059,-0.02648497,-0.01985131018,-0.286404103,9.793714523,1.367927313 -9.62969094,-5.990215302,-27.47402,-35.47105408,0.0009704809636,-0.01529773325,-0.02053293586,-0.3193401098,9.735281944,1.396854877 -9.63969094,-5.915599823,-26.76782799,-33.99531555,0.002660098486,-0.01091999374,-0.02030819841,-0.3742132783,9.726406097,1.408692479 -9.64969094,-5.836467743,-27.99143028,-34.06106567,0.009704994969,0.00570339663,-0.02030819841,-0.396333158,9.782100677,1.37638545 -9.65969094,-5.075801849,-27.91191673,-34.83148193,0.01480308268,0.01089406572,-0.01835904643,-0.383464247,9.801027298,1.287954688 -9.66969094,-7.527767181,-27.90849113,-33.91059875,0.004126793705,-0.01640985347,-0.01433289424,-0.3254973888,9.808070183,1.299425244 -9.67969094,-5.753753662,-28.14489174,-36.94999695,-0.01064739004,-0.03583458811,-0.01477447897,-0.3483581841,9.893923759,1.421264291 -9.68969094,-7.688819885,-27.79545784,-34.52871704,-0.01350821275,-0.01939630695,-0.01784704253,-0.4041688144,9.913567543,1.498895407 -9.69969094,-5.795909882,-27.30281639,-34.11013794,-0.001301977783,0.01890260912,-0.02151327021,-0.4571124613,9.889979362,1.583774805 -9.70969094,-6.037273407,-28.83366966,-33.84809875,0.02016665787,0.03482097015,-0.02001620457,-0.3828421235,9.923582077,1.550175071 -9.71969094,-7.368331909,-27.48821068,-34.85652161,0.02689192817,0.01481494214,-0.01628457569,-0.4274457693,9.955366135,1.418853045 -9.72969094,-5.846683502,-27.99670219,-34.51850891,0.01808453351,0.0007289100904,-0.007230801042,-0.4414436519,9.913903236,1.362712622 -9.73969094,-7.233718872,-27.88195992,-34.6762085,0.006072483025,0.005902021192,-0.00551132299,-0.4905114472,9.913440704,1.355573893 -9.74969094,-7.385047913,-28.16451836,-33.74008179,-0.001548105851,0.03948125988,0.001368286088,-0.3877508342,9.898666382,1.246912837 -9.75969094,-6.509895325,-29.15682411,-33.36621094,-0.01327420678,0.06441155076,-0.002559530549,-0.3439078927,9.951795578,1.185796857 -9.76969094,-7.19997406,-27.19686317,-35.03022766,-0.02978560328,0.05697054416,-0.00752402097,-0.4116650224,9.954465866,1.257131696 -9.77969094,-6.428779602,-27.77959633,-33.46435547,-0.03334539384,0.07115680724,-0.006260839291,-0.5033277869,9.958221436,1.41427815 -9.78969094,-5.668113708,-27.70008278,-34.23477173,-0.02409242094,0.08291181177,-0.003795302706,-0.5041429996,9.987658501,1.488123059 -9.79969094,-5.535114288,-27.56051826,-35.61853027,-0.01179026719,0.08431231976,0.001658534165,-0.5291974545,9.954752922,1.494916201 -9.80969094,-6.589519501,-28.33408165,-32.2036438,0.003343363293,0.07458043098,0.00850411877,-0.4825110435,9.898130417,1.412799358 -9.81969094,-5.997028351,-27.47753716,-35.77600098,0.006553829648,0.05965533108,0.01489961706,-0.4234584868,9.782516479,1.332983136 -9.82969094,-6.059619904,-26.64600563,-33.8510437,0.01331350673,0.04850659519,0.01735352911,-0.3877936006,9.777862549,1.266014695 -9.83969094,-6.503890991,-28.88665199,-33.84329224,0.01824017987,0.03485743701,0.01993710175,-0.3487462103,9.775895119,1.266067386 -9.84969094,-7.19997406,-27.19686317,-35.03022766,0.02160441503,0.02122296393,0.01546534896,-0.311239928,9.760976791,1.272807956 -9.85969094,-6.55657959,-27.38232994,-33.33972168,0.01953481883,0.01390312053,0.01141053811,-0.3209119439,9.78553009,1.278277993 -9.86969094,-5.979190826,-27.73540306,-34.23156738,0.01899946108,0.01229704265,0.006715378258,-0.2684645653,9.816758156,1.335773826 -9.87969094,-7.206474304,-27.86789894,-33.4563446,0.02045468241,0.005114794243,0.002737503033,-0.3115752637,9.813545227,1.378985286 -9.88969094,-7.206474304,-27.86789894,-33.4563446,0.02057313174,-0.009556769393,0.003524060361,-0.2928525209,9.85481739,1.433280349 -9.89969094,-5.709480286,-28.1220417,-34.96772766,0.02471607178,-0.02593976445,-0.000195460394,-0.289963156,9.882286072,1.459529281 -9.90969094,-5.709480286,-28.1220417,-34.96772766,0.0349977091,-0.03940877318,-6.658444181e-05,-0.3231247663,9.801194191,1.448393464 -9.91969094,-6.100177765,-27.33461952,-33.80197144,0.04312978685,-0.04885619879,0.0009987638332,-0.32735309,9.733083725,1.397290349 -9.92969094,-5.444274902,-26.57888222,-34.16241455,0.05012412742,-0.04050931334,-0.001043979544,-0.2132692337,9.657939911,1.322166562 -9.93969094,-6.124019623,-27.34692574,-34.86933899,0.05335898697,-0.02532506175,-0.005103618838,-0.2339841872,9.683965683,1.298429251 -9.94969094,-5.822349548,-27.58353233,-34.54794312,0.04862793908,-0.005474109203,-0.009122043848,-0.2713183165,9.604672432,1.183119655 -9.95969094,-5.028430939,-27.21978569,-34.57559204,0.03629063815,-0.004140705802,-0.01083520055,-0.2865132093,9.572835922,1.108448267 -9.96969094,-6.019748688,-28.42401695,-34.18249512,0.01961249486,0.02772274427,-0.009543137625,-0.2350679785,9.573115349,1.162874699 -9.97969094,-5.610527039,-27.00267982,-33.5214386,0.008852970786,0.05212645978,-0.01561361179,-0.2903158665,9.632687569,1.220809221 -9.98969094,-4.843048096,-26.91964912,-33.98690796,-0.006395697594,0.07004146278,-0.01498145796,-0.3082864285,9.775126457,1.420338035 -9.99969094,-6.510704041,-28.89016914,-34.14823914,-0.01554161683,0.08230477571,-0.01358819567,-0.3808072507,9.88078022,1.6931777 -10.00969094,-5.558643341,-28.24034309,-34.80705261,-0.01750100963,0.09559768438,-0.003126793541,-0.4694359601,9.878305435,1.681256294 -10.01969094,-5.948848724,-27.05206108,-34.73809814,-0.01223013736,0.0947631076,0.01301613264,-0.520768702,9.835087776,1.616311669 -10.02969094,-5.524585724,-28.22276497,-33.28224182,-0.01229349431,0.08211163431,0.03143116459,-0.5171135068,9.89792347,1.515195131 -10.03969094,-5.634864807,-27.41584969,-33.49198914,-0.02304166183,0.07002359629,0.04795950279,-0.4607204795,9.916221619,1.462448239 -10.04969094,-5.86290741,-28.27214622,-34.49887085,-0.03202089667,0.06247267872,0.05862831324,-0.3976040781,9.907452583,1.388126731 -10.05969094,-5.812625885,-27.97912407,-32.99369812,-0.04750698432,0.0630890578,0.06102353334,-0.3217801452,9.843457222,1.314891696 -10.06969094,-6.058815002,-26.9126606,-33.06900024,-0.04895600677,0.05808627605,0.05451076478,-0.3295866549,9.846390724,1.25166893 diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 7f919d87b..9ad7009e3 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1557,6 +1557,55 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector &functionsToInsert) { +// llvm::errs() << "Entering createFixRsqrt\n"; +// +// if (!irModule) { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixrsqrtFuncName = "fixrsqrt"; +// for (auto &function : *irModule) { +// if (function.getName() == fixrsqrtFuncName) { +// llvm::errs() << "fixrsqrt already exists\n"; +// return &function; +// } +// } +// +// llvm::FunctionType *funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixrsqrtFuncName, irModule); +// llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// +// llvm::Function::arg_iterator args = func->arg_begin(); +// llvm::Value *x = &*args++; +// +// llvm::Function *fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); +// +// llvm::Value *halfX = builder.CreateLShr(x, 1); +// +// llvm::Value *fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); +// llvm::Value *mulConst = llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625); +// llvm::Value *approximation = builder.CreateFMul(fpX, mulConst); +// +// llvm::Value *intApproximation = builder.CreateBitCast(approximation, llvm::Type::getInt32Ty(irModule->getContext())); +// llvm::Value *shiftedApproximation = builder.CreateLShr(intApproximation, 1); +// llvm::Value *magicNumberSub = builder.CreateSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 1597463007), shiftedApproximation); +// +// llvm::Value *scaledApproximation = builder.CreateShl(magicNumberSub, 10); +// llvm::Value *firstMul = builder.CreateCall(fixMulFunc, {halfX, scaledApproximation}); +// llvm::Value *secondMul = builder.CreateCall(fixMulFunc, {firstMul, scaledApproximation}); +// llvm::Value *correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, 1536), secondMul); +// +// llvm::Value *finalResult = builder.CreateCall(fixMulFunc, {scaledApproximation, correction}); +// +// builder.CreateRet(finalResult); +// functionsToInsert.push_back(func); +// +// return func; +//} + // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -2055,6 +2104,10 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool rhsIsHalf = rhsConst && rhsConst->getValueAPF().convertToDouble() == 0.5; bool lhsIsTwo = lhsConst && lhsConst->getValueAPF().convertToDouble() == 2.0; bool rhsIsTwo = rhsConst && rhsConst->getValueAPF().convertToDouble() == 2.0; + bool lhsIsFour =lhsConst && lhsConst->getValueAPF().convertToDouble() == 4.0; + bool rhsIsFour = rhsConst && rhsConst->getValueAPF().convertToDouble() == 4.0; + bool lhsIsEight =lhsConst && lhsConst->getValueAPF().convertToDouble() == 8.0; + bool rhsIsEight = rhsConst && rhsConst->getValueAPF().convertToDouble() == 8.0; if (auto rhsConst = dyn_cast(rhs)) @@ -2143,6 +2196,47 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix return; // 防止后续代码执行 } + + if (lhsIsFour || rhsIsFour) + { + llvm::errs() << "One operand is a floating-point constant 4.0, simplifying using logical shift left by 1\n"; + + Value *otherOperand = lhsIsFour ? rhs : lhs; + Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); + + // 创建常数1,用于左移 + Value *one = ConstantInt::get(intType, 2); + + // 创建逻辑左移指令 + Instruction *shlInst = BinaryOperator::CreateShl(otherOperand, one, "mulbyfour", llvmIrInstruction); + + // 替换原来的乘法指令 + llvmIrInstruction->replaceAllUsesWith(shlInst); + llvmIrInstruction->eraseFromParent(); + + return; // 防止后续代码执行 + } + + if (lhsIsEight || rhsIsEight) + { + llvm::errs() << "One operand is a floating-point constant 8.0, simplifying using logical shift left by 1\n"; + + Value *otherOperand = lhsIsEight ? rhs : lhs; + Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); + + // 创建常数1,用于左移 + Value *one = ConstantInt::get(intType, 3); + + // 创建逻辑左移指令 + Instruction *shlInst = BinaryOperator::CreateShl(otherOperand, one, "mulbyfour", llvmIrInstruction); + + // 替换原来的乘法指令 + llvmIrInstruction->replaceAllUsesWith(shlInst); + llvmIrInstruction->eraseFromParent(); + + return; // 防止后续代码执行 + } + // If either operand is a float constant, convert it to fixed-point using handleConstant if (isa(lhs)) { @@ -2190,15 +2284,29 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); - if (lhsIsInteger && rhsIsInteger) - { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - //callInst->setCallingConv(llvm::CallingConv::Fast); - //callInst->setTailCall(true); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - } +// if (lhsIsInteger && rhsIsInteger) +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); +// //callInst->setCallingConv(llvm::CallingConv::Fast); +// //callInst->setTailCall(true); +// llvmIrInstruction->replaceAllUsesWith(callInst); +// llvmIrInstruction->eraseFromParent(); +// } + +if (lhsIsInteger && rhsIsInteger) { + llvm::errs() << "Both operands are integers, performing inline multiplication and shifting...\n"; + + // Perform multiplication directly + llvm::Value *mulResult = Builder.CreateMul(lhs, rhs); + + // Perform right arithmetic shift + llvm::Value *shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), 10)); + + // Replace all uses of the original instruction with the result of the shift + llvmIrInstruction->replaceAllUsesWith(shiftResult); + llvmIrInstruction->eraseFromParent(); +} } void From e80b481ed410101933bac45110bc96966e694473 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 14 Sep 2024 17:57:53 +0800 Subject: [PATCH 088/213] add input.csv * dev2. --- ...a911370c42ab769501a69e1de57f832a4c8c58.txt | 48 + applications/newton/llvm-ir/input.csv | 1001 +++++++++++++++++ 2 files changed, 1049 insertions(+) create mode 100644 analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt create mode 100644 applications/newton/llvm-ir/input.csv diff --git a/analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt b/analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt new file mode 100644 index 000000000..93e96b960 --- /dev/null +++ b/analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt @@ -0,0 +1,48 @@ + +changeset: 1681:bca911370c42ab769501a69e1de57f832a4c8c58 +char kNewtonVersion[] = "0.3-alpha-1681 (bca911370c42ab769501a69e1de57f832a4c8c58) (build 09-14-2024-17:49-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/input.csv b/applications/newton/llvm-ir/input.csv new file mode 100644 index 000000000..ce7c7c2b3 --- /dev/null +++ b/applications/newton/llvm-ir/input.csv @@ -0,0 +1,1001 @@ +Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.08969094,-6.654678345,26.09465027,-37.29600525,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.09969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.10969094,-7.548351288,24.87819672,-39.49668884,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.11969094,-7.311084747,23.94067001,-40.19360352,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.12969094,-5.958099365,24.8057251,-37.20587158,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.13969094,-8.100597382,25.37778282,-37.85224915,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.14969094,-6.394374847,25.43608284,-37.70759583,-0.1151283234,-0.00251355581,0.01010152325,-0.2793728709,0.4129949808,10.27848434 +0.15969094,-7.610450745,26.11058998,-38.66853333,-0.08246348053,0.00447106408,0.004323757719,-0.2367789149,0.4112356901,10.43229961 +0.16969094,-6.729293823,25.38845825,-38.77174377,-0.03473320603,0.04428362101,0.0009580431506,-0.2995947599,0.416141957,10.24356842 +0.17969094,-6.832756042,26.19889259,-38.67654419,0.002444048412,0.07756645232,-0.003223320469,-0.2556326687,0.3563099205,9.911549568 +0.18969094,-7.576393127,26.12816811,-37.14372253,0.007744635455,0.09112588316,-0.008515236899,-0.1705993563,0.3450968266,9.673620224 +0.19969094,-5.582935333,26.2094841,-38.06962585,-0.007077907212,0.1003924161,-0.01438220032,-0.2904628515,0.4137096405,9.693481445 +0.20969094,-7.26531601,26.16348839,-37.14692688,-0.0214833729,0.07035970688,-0.01663675718,-0.4015436172,0.4240173995,9.765337944 +0.21969094,-7.610450745,26.11058998,-38.66853333,-0.02840357646,0.0412395969,-0.01503071003,-0.4420829713,0.4474136829,9.710318565 +0.22969094,-5.917541504,25.49433899,-37.25494385,-0.04069363698,0.0300014019,-0.01120906137,-0.5065568686,0.4349407554,9.780290604 +0.23969094,-5.917541504,25.49433899,-37.25494385,-0.04557721689,0.01997687295,-0.003380747745,-0.4558943808,0.4060772061,9.982199669 +0.24969094,-7.248287201,26.17227745,-36.38452148,-0.03875789419,-0.002376349643,-0.001149083488,-0.4019374549,0.4131945372,10.07145596 +0.25969094,-5.484790802,27.59550095,-37.40536499,-0.02963040024,-0.007826571353,-0.0005718094762,-0.3436793387,0.4178147614,10.06400871 +0.26969094,-7.59992218,25.44834328,-36.33224487,-0.01951537654,-0.001477465499,-0.004841145594,-0.2960692346,0.3756780624,9.936094284 +0.27969094,-7.536643982,27.08343887,-37.97483826,-0.01999245957,-0.006206189282,-0.007511992939,-0.3121205568,0.3395512402,9.855197906 +0.28969094,-5.894504547,25.77330017,-36.96961975,-0.02960216627,-0.0179349836,-0.009124945849,-0.3137557507,0.3006750047,9.795846939 +0.29969094,-5.791351318,25.63038635,-38.94366455,-0.04506304488,-0.02701679431,-0.006459381431,-0.3230510056,0.3795295656,9.721902847 +0.30969094,-6.642169952,27.03585815,-39.3469696,-0.06745177507,-0.03394386917,-0.005393324886,-0.3160211146,0.3835323155,9.728869438 +0.31969094,-8.179725647,26.60138512,-37.78651428,-0.07811312377,-0.03392977268,-0.003795302706,-0.3117079139,0.3867061734,9.83324337 +0.32969094,-6.671707153,26.08586121,-38.05841064,-0.07162205875,-0.02754923515,-0.002687801374,-0.3693574667,0.4109267592,9.983849525 +0.33969094,-7.483146667,25.31245995,-37.69638062,-0.05240149051,-0.02055237256,0.006894228514,-0.4506206214,0.425347209,10.05073643 +0.34969094,-7.178882599,25.34426498,-38.00453186,-0.01982024126,-0.008340923116,0.01644631103,-0.424624294,0.4272369146,10.15809345 +0.35969094,-5.974441528,27.26355934,-37.68589783,0.01485640649,-0.002841048408,0.0174930077,-0.322455287,0.4224477112,10.03915787 +0.36969094,-5.750793457,26.31900024,-38.99273682,0.03709132969,0.01580360159,0.01747041196,-0.2883752882,0.3374709487,9.90159893 +0.37969094,-5.870170593,26.18646812,-36.99906921,0.03729007766,0.02409679629,0.01276430115,-0.2753283978,0.2819014192,9.6376791 +0.38969094,-7.402835846,26.9563446,-38.57655334,0.01491038781,0.01591248065,0.01207613945,-0.2228651792,0.2963768244,9.694525719 +0.39969094,-8.981754303,26.09991455,-37.74905396,-0.008029373363,-0.004363908432,0.009553089738,-0.3137707412,0.3058240712,9.756849289 +0.40969094,-7.179374695,24.94340134,-36.9077301,-0.0325601548,-0.01107135322,0.008988874033,-0.347237438,0.2963377833,9.893450737 +0.41969094,-6.377658844,26.11239243,-38.82402039,-0.0477651991,-0.02031742781,0.01180266216,-0.2945395708,0.2534381747,10.08242321 +0.42969094,-7.576393127,26.12816811,-37.14372253,-0.03290509433,-0.03066522814,0.01591363735,-0.3064144552,0.2915987968,10.09379768 +0.43969094,-6.654678345,26.09465027,-37.29600525,-0.007921610959,-0.01810567081,0.01840451546,-0.220240429,0.3167188168,10.02454567 +0.44969094,-7.910011292,26.21474838,-38.52267456,0.01434707176,0.005370598286,0.01262276806,-0.2308089137,0.3418518007,9.914393425 +0.45969094,-7.616950989,25.43955421,-37.09465027,0.02976943925,0.02135305665,0.01048317552,-0.2660395503,0.3493344486,9.844152451 +0.46969094,-6.706256866,25.66741943,-38.48641968,0.03813938051,0.02182977088,0.005633147899,-0.216332078,0.3326821923,9.803571701 +0.47969094,-6.727798462,27.58842468,-37.70732117,0.0336618945,0.00669084629,-0.002252099337,-0.3407348692,0.3455455303,9.784972191 +0.48969094,-6.272701263,27.50192642,-37.85481262,0.02714404091,-0.02482098155,-7.733469829e-06,-0.4290958643,0.3308064342,9.846453667 +0.49969094,-7.497264862,24.9045639,-37.20947266,0.02085882053,-0.04777231812,0.005195887294,-0.4415936172,0.3619870543,9.872394562 +0.50969094,-6.638454437,26.37009621,-37.31562805,0.01971525326,-0.06770654023,0.007923526689,-0.3541632593,0.3599542379,9.863398552 +0.51969094,-7.459617615,25.99228477,-38.50785828,0.02144834772,-0.05833188444,0.005967103411,-0.3571015596,0.2838820815,9.942513466 +0.52969094,-6.602794647,24.85698509,-38.581604,0.03338546306,-0.04018680751,0.002523483243,-0.3252090514,0.3449226618,9.981822014 +0.53969094,-6.37815094,25.71152878,-37.72721863,0.0395555906,-0.03105190583,0.002672136296,-0.3542988896,0.3391857445,9.916908264 +0.54969094,-7.507480621,24.89929199,-37.66693115,0.04480022192,-0.009855329059,0.002203115728,-0.2928415239,0.3543523848,9.862686157 +0.55969094,-7.940353394,25.53140831,-38.0161438,0.04905298352,0.03105917946,-0.004327977076,-0.2486828268,0.414686501,9.692631721 +0.56969094,-6.474308014,26.92634201,-38.42385864,0.04452182725,0.05528639257,-0.009296388365,-0.2784487903,0.3446758687,9.670860291 +0.57969094,-6.825942993,26.20240784,-38.37158203,0.0319024995,0.02479611523,-0.006458672695,-0.3158861697,0.380629003,9.788747787 +0.58969094,-6.840065002,25.7945137,-37.88470459,0.02158891037,-0.03473115712,-0.001664606389,-0.3231438696,0.411952734,9.852827072 +0.59969094,-7.162658691,25.61971092,-38.02415466,0.01061961707,-0.06082195044,-0.003890550463,-0.2853017449,0.4193702936,9.816333771 +0.60969094,-6.856781006,25.11820412,-36.76826477,0.007375336252,-0.03639098257,-0.006122777238,-0.2694948912,0.4136307836,9.676003456 +0.61969094,-6.896842957,24.83045387,-37.81599426,0.002672176808,-0.001061701681,-0.01018739119,-0.3234909773,0.3764295876,9.812074661 +0.62969094,-6.792694092,26.48664284,-37.6288147,0.005003871396,0.01006212085,-0.008162576705,-0.4266119301,0.3720042706,10.06733036 +0.63969094,-6.353816986,26.12469673,-37.75666809,0.02046790347,-0.01681301743,0.0007936262991,-0.3262727559,0.3997677267,10.05027294 +0.64969094,-7.610450745,26.11058998,-38.66853333,0.03206149116,-0.02037302963,0.0003540727776,-0.2678869963,0.4512276053,9.878255844 +0.65969094,-6.743099213,24.31304169,-36.40600586,0.0311081633,0.002321796725,-0.003910094965,-0.3650930524,0.3835353553,9.755164146 +0.66969094,-7.426364899,26.27651978,-37.76507568,0.01962944865,0.01086440869,0.0001691966318,-0.2833980918,0.3181568384,9.629114151 +0.67969094,-8.06734848,25.66201782,-37.10948181,-0.003784938715,-0.009464945644,0.004316138569,-0.2693619728,0.3249111176,9.749714851 +0.68969094,-8.06734848,25.66201782,-37.10948181,-0.01996190473,-0.01890161261,0.004569322802,-0.2541345954,0.3612316251,9.785536766 +0.69969094,-6.71957016,24.99286652,-37.21748352,-0.03055388108,-0.002701988444,0.001403293107,-0.2876216173,0.42038095,9.840220451 +0.70969094,-6.71957016,24.99286652,-37.21748352,-0.02469141968,0.02064591087,0.0002037035301,-0.3182426989,0.4196154475,9.82704258 +0.71969094,-6.593383789,25.12891579,-38.90620422,-0.01536074467,0.02345331945,-0.0008636235725,-0.3334631324,0.4254368544,9.891711235 +0.72969094,-6.681922913,26.08058739,-38.51586914,0.0003667054698,0.02293391153,0.00113602588,-0.3242745101,0.4562799931,9.878070831 +0.73969094,-6.498645782,26.51317406,-38.39442444,0.01448426675,0.03026067652,0.000355088152,-0.3454433978,0.4558205009,9.850791931 +0.74969094,-6.310348511,26.41420555,-36.556427,0.02660195902,0.02360223606,0.002740718424,-0.3830767274,0.4391977191,9.813574791 +0.75969094,-7.249095917,26.43893433,-37.16656494,0.03125537932,-0.009130412713,0.006767154206,-0.3287489414,0.4146726429,9.829097748 +0.76969094,-8.217372894,25.51366615,-36.48812866,0.02221588045,-0.04891215265,0.01043653488,-0.2150225341,0.3947498798,9.832125664 +0.77969094,-8.854763031,25.96930504,-38.65571594,0.01269038301,-0.05699107051,0.007309804205,-0.2531299889,0.4025529325,9.883623123 +0.78969094,-7.472930908,25.31773376,-37.23892212,0.009331553243,-0.0362610966,0.007923526689,-0.2762732506,0.3899558783,9.84967804 +0.79969094,-5.784229279,24.96638107,-36.75984192,0.001331721433,-0.02163651586,0.007390852552,-0.234718129,0.4027428627,9.921022415 +0.80969094,-7.957382202,25.52261925,-38.77854919,-0.001178442501,-0.02419010922,0.007390852552,-0.1688663065,0.3537279963,9.899970055 +0.81969094,-7.96679306,25.25068855,-38.45397949,-0.0002859253436,-0.02208984457,0.003662134986,-0.1612079442,0.3765885234,9.860362053 +0.82969094,-6.607620239,27.45429993,-38.91896057,-0.005500952248,-0.00137518486,0.002451392356,-0.2259380519,0.4388239086,9.777145386 +0.83969094,-6.706256866,25.66741943,-38.48641968,-0.008655028418,0.03205142915,0.001516730525,-0.2650729716,0.4043938816,9.713522911 +0.84969094,-6.545207977,25.55438805,-37.86827087,-0.01504812483,0.05091909319,0.004732759669,-0.2684932947,0.3750241399,9.674304008 +0.85969094,-7.96679306,25.25068855,-38.45397949,-0.02855567634,0.04647941142,0.006329850759,-0.2255412191,0.4341191947,9.723275185 +0.86969094,-7.983821869,25.24189949,-39.21638489,-0.03407438844,0.03285332024,0.009157052264,-0.2946357727,0.4097974002,9.758426666 +0.87969094,-6.569046021,25.54208374,-38.93565369,-0.04476465285,0.03424095362,0.007229163777,-0.3785450459,0.4446446896,9.875383377 +0.88969094,-7.569580078,26.13168335,-36.83876038,-0.04716154933,0.02489669435,0.01244034618,-0.3616573513,0.4302352071,10.03662205 +0.89969094,-7.520793915,24.22473907,-36.397995,-0.03513527289,0.009162386879,0.01834226586,-0.3369231224,0.354245156,9.933807373 +0.90969094,-6.706256866,25.66741943,-38.48641968,-0.03387806937,5.956785753e-05,0.01586125046,-0.2603001595,0.413443327,9.827401161 +0.91969094,-8.26845932,25.48729897,-38.77534485,-0.0300873816,0.01511435304,0.008654415607,-0.2779739201,0.3738090396,9.869602203 +0.92969094,-5.997970581,26.58373451,-36.87442017,-0.02735616267,0.04325161129,0.004194807727,-0.3075370789,0.3841251135,9.859921455 +0.93969094,-6.103237152,25.86172104,-39.7224884,-0.02637853473,0.05735059083,0.003129459452,-0.320104301,0.373578757,9.733283043 +0.94969094,-7.90920639,25.94809151,-37.74064636,-0.03651535884,0.0361250788,0.005883715581,-0.3417606056,0.3402200043,9.643527985 +0.95969094,-7.259311676,26.43366051,-37.62400818,-0.05156690255,0.009104821831,0.006214913446,-0.3017233014,0.362526238,9.718540192 +0.96969094,-6.360630035,26.12118149,-38.06161499,-0.05347704142,0.01731107384,0.002591064665,-0.2577753067,0.3784553707,9.827540398 +0.97969094,-6.320072174,26.80979538,-38.11068726,-0.03843300045,0.03169715032,-0.0004796904977,-0.3363775015,0.3911237717,9.874797821 +0.98969094,-8.060348511,26.73391724,-39.78018188,-0.02594162524,0.03712494671,0.0003746603616,-0.4213152528,0.352683723,9.925812721 +0.99969094,-6.769851685,24.69984436,-38.72267151,-0.01860057376,0.03343068063,0.002202911768,-0.4492088258,0.3678621352,10.10691547 +1.00969094,-5.576122284,26.21299934,-37.76467896,-0.001166775823,0.01629613154,0.006246880163,-0.4433344305,0.3575173914,10.19894409 +1.01969094,-7.339931488,25.45729637,-38.62266541,0.02069848031,0.005792571232,0.009291321039,-0.3940068483,0.3785350621,9.979031563 +1.02969094,-4.853103638,25.20479393,-37.23670959,0.02874404564,0.01429859176,0.008222779259,-0.3645473719,0.3816692829,9.829014778 +1.03969094,-6.648670197,26.36482239,-37.77308655,0.02584087476,0.01577451825,0.008752264082,-0.3319935501,0.391680181,9.704668045 +1.04969094,-7.364269257,25.04412842,-38.5932312,0.01320957113,0.02140369639,0.004434604198,-0.2616887093,0.3081431389,9.435370445 +1.05969094,-5.186531067,27.35713577,-37.23646545,-0.01679551601,0.01554101706,0.00070909434,-0.3204491436,0.3072315753,9.469326973 +1.06969094,-6.095619202,25.59858131,-38.63548279,-0.04527019337,-0.02895073779,0.003868098371,-0.3079995215,0.3426241875,9.777582169 +1.07969094,-8.571918488,25.18883705,-37.68516541,-0.05302047729,-0.07532060146,0.007323132362,-0.3083209097,0.3275064826,9.900536537 +1.08969094,-6.433750153,27.6149559,-38.47293091,-0.04696979001,-0.09256293625,0.009241476655,-0.3125772476,0.3005108833,9.937273979 +1.09969094,-7.12210083,26.30832481,-38.07322693,-0.03368562087,-0.08957409114,0.008200418204,-0.2579268813,0.3488419354,9.958529472 +1.10969094,-7.961097717,26.1883812,-40.80989075,-0.01956947893,-0.11456047,0.01081642881,-0.3258158267,0.3308687806,10.03633881 +1.11969094,-6.585765839,24.86577415,-37.81919861,0.004703424871,-0.1317160726,0.01432392001,-0.2255369574,0.315831691,10.02883244 +1.12969094,-6.65839386,26.76041222,-39.3273468,0.03163328022,-0.09282163531,0.007392741274,-0.2747437358,0.4104028642,9.873939514 +1.13969094,-8.267654419,25.22064209,-37.99331665,0.03962652758,-0.04242435843,0.003125044052,-0.2230227441,0.3918129504,9.700381279 +1.14969094,-8.267654419,25.22064209,-37.99331665,0.0334565714,-0.01739601418,-0.0003491293173,-0.2507834136,0.3877517879,9.647592545 +1.15969094,-6.522174835,25.83334923,-37.58294678,0.01399359573,-0.01048641745,-0.0005820093211,-0.2868594825,0.3809897602,9.612992287 +1.16969094,-6.522174835,25.83334923,-37.58294678,-0.009211212397,0.002303284593,-0.002197280526,-0.2456134111,0.3624066412,9.478822708 +1.17969094,-7.220245361,24.92230797,-38.73748779,-0.03906954825,-0.005264308304,-0.002449053805,-0.223413676,0.3052503765,9.675016403 +1.18969094,-8.320838928,26.32410049,-36.39294434,-0.04616601393,-0.01693219133,0.0009987638332,-0.2595686018,0.3547726274,9.831622124 +1.19969094,-6.297035217,27.08875656,-37.82536316,-0.04363083839,-0.01177432761,0.006406869274,-0.3021802008,0.4283012152,9.923987389 +1.20969094,-5.802066803,25.22424889,-38.30429077,-0.03484881297,-0.002998926677,0.005745526869,-0.3050480485,0.3657292128,10.12125111 +1.21969094,-8.227901459,26.17591286,-38.82441711,-0.01179379877,-0.006921730004,0.009910203516,-0.3035609126,0.3256982863,10.10582829 +1.22969094,-6.744827271,27.57963562,-38.46972656,-0.0006591975689,-0.01147360541,0.0132014323,-0.2544778585,0.2881220877,9.779053688 +1.23969094,-7.299373627,26.14591026,-38.67173767,-0.0105999019,-0.00578986574,0.01048056968,-0.1460589617,0.283655107,9.60635376 +1.24969094,-7.473735809,25.58439064,-38.02095032,-0.03279230744,0.02829709277,-0.0002586811315,-0.1317581385,0.2602887452,9.617964745 +1.25969094,-6.430034637,26.94919395,-36.44160461,-0.04902238026,0.0402559936,-0.007753454149,-0.2250817716,0.3019514382,9.776272774 +1.26969094,-6.785385132,26.89102173,-38.4206543,-0.04932562262,0.03157758713,-0.007301884238,-0.2387727499,0.3511135578,9.836483002 +1.27969094,-7.3736763,24.77219772,-38.26863098,-0.04486268014,0.04647804797,-0.007624792866,-0.2069167346,0.3134610355,9.70259285 +1.28969094,-6.866500854,25.51379395,-38.32250977,-0.04943052307,0.0468153283,-0.008807333186,-0.2923846543,0.272872448,9.821870804 +1.29969094,-6.055557251,25.88633156,-37.5877533,-0.04847415164,0.01401153393,3.832438961e-05,-0.2815147936,0.2507653534,9.938878059 +1.30969094,-8.090877533,24.98219299,-36.29800415,-0.0390965566,-0.007415878586,0.002172368579,-0.3992255032,0.3018830121,10.052248 +1.31969094,-6.433750153,27.6149559,-38.47293091,-0.01272503659,0.01556072105,0.006858177949,-0.3525747061,0.3768803477,10.04895401 +1.32969094,-5.842926025,26.20053101,-35.77920532,0.009118192829,0.05159369111,0.003864748403,-0.3418606818,0.3400193453,9.91121769 +1.33969094,-6.688735962,26.07707214,-38.82081604,0.02072456852,0.05234477669,0.00173030328,-0.3416000307,0.3463162482,9.807590485 +1.34969094,-7.080738068,26.73028183,-37.340271,0.02005904168,0.03351046145,0.001531437505,-0.2878206968,0.3056016266,9.60395813 +1.35969094,-8.100597382,25.37778282,-37.85224915,0.005226348527,0.03811069578,-0.003207825124,-0.2599823773,0.2751262188,9.588534355 +1.36969094,-6.607307434,26.7867794,-37.04013062,-0.01296652295,0.01011377014,-0.003455437953,-0.2150486112,0.2730696797,9.68800354 +1.37969094,-7.91601944,25.94457626,-38.04559326,-0.02920342982,-0.03429959714,-0.003262628801,-0.1792165935,0.256131053,9.581644058 +1.38969094,-6.480808258,26.25530624,-36.84997559,-0.05070129409,-0.048120372,-0.005925999023,-0.1703465581,0.202443108,9.596740723 +1.39969094,-5.802066803,25.22424889,-38.30429077,-0.06068585813,-0.06144545227,-0.00397459697,-0.2702684999,0.2518742383,9.811048508 +1.40969094,-6.799507141,26.48312759,-37.93377686,-0.05394846946,-0.08207222819,0.003652619664,-0.3534995615,0.319263339,9.85598278 +1.41969094,-5.760700226,25.6462059,-37.57131958,-0.03585629165,-0.04909897596,0.01270254329,-0.3109368086,0.2954011559,9.816308975 +1.42969094,-7.402030945,26.68968773,-37.79452515,-0.02535336465,-0.01209592633,0.01307334937,-0.3471710384,0.3598883748,9.775583267 +1.43969094,-7.899482727,25.55250168,-36.18638611,-0.01951367408,-0.001619831193,0.0187486317,-0.3248218298,0.3403574526,9.822806358 +1.44969094,-7.186187744,24.93988609,-37.212677,-0.01519426517,-0.008781705052,0.02070770226,-0.3171883821,0.308766067,9.850348473 +1.45969094,-6.514865875,26.23772812,-38.37478638,-0.02264731191,-0.0292735938,0.01713754982,-0.2401963323,0.2161890417,9.744996071 +1.46969094,-7.224758148,26.85210228,-37.19599915,-0.03933265805,-0.05343721062,0.01005422324,-0.0907349363,0.2035675794,9.581429482 +1.47969094,-6.823036194,25.80330276,-37.12229919,-0.06534571946,-0.08001222461,0.001245157328,-0.05353847891,0.21554254,9.619607925 +1.48969094,-5.802066803,25.22424889,-38.30429077,-0.08012887836,-0.1155415028,-0.008874082938,-0.07394784689,0.2216080427,9.712301254 +1.49969094,-7.926235199,25.93930244,-38.50305176,-0.08614850789,-0.1406992674,-0.01109797321,-0.1761151403,0.2405264229,9.942374229 +1.50969094,-8.060848236,26.33305359,-38.68336487,-0.06363983452,-0.131477356,-0.008736706339,-0.1644631177,0.2284149528,10.12247753 +1.51969094,-5.934570312,25.48554993,-38.01734924,-0.03686951846,-0.1067624614,-0.009261385538,-0.2282977104,0.1928204149,10.15298653 +1.52969094,-6.481929779,27.18948364,-39.51086426,-0.01791328192,-0.08309015632,-0.00313524832,-0.2743725479,0.21198605,10.15187454 +1.53969094,-7.875953674,26.23232651,-36.99786377,-0.008983654901,-0.07694666088,0.002590309829,-0.2886238396,0.1790893525,10.07465935 +1.54969094,-7.529022217,26.82029724,-36.88783264,-0.009366864339,-0.07829668373,0.01138180681,-0.1872850209,0.1983563304,9.777256966 +1.55969094,-7.105072021,26.31711388,-37.31082153,-0.03254691884,-0.04712443799,0.01258446462,-0.1739229262,0.2279125303,9.533390045 +1.56969094,-7.194416046,27.53544235,-37.70251465,-0.05746591091,-0.001762664411,0.009799225256,-0.1670654416,0.1957996339,9.661688805 +1.57969094,-7.11448288,26.04518318,-36.98625183,-0.06772604585,0.003953762818,0.0142403841,-0.2083566934,0.2039169073,9.748625755 +1.58969094,-7.978923798,27.4436245,-37.99946594,-0.07317771018,-0.02151427604,0.02289812267,-0.2312756628,0.2643887997,9.760647774 +1.59969094,-5.639026642,27.71204758,-37.71853638,-0.06665766984,-0.04223006219,0.02425481752,-0.1424741298,0.2968740463,9.914733887 +1.60969094,-5.639026642,27.71204758,-37.71853638,-0.04367075861,-0.04056430608,0.0234865211,-0.1608794183,0.3256749511,10.09056568 +1.61969094,-7.868648529,26.6367054,-37.78971863,-0.008530608378,-0.04159038514,0.02200041711,-0.189670369,0.2899492383,10.21168613 +1.62969094,-7.868648529,26.6367054,-37.78971863,0.02628235519,-0.05120419711,0.02283840254,-0.2218288481,0.3283816576,10.20733452 +1.63969094,-6.303535461,26.41772079,-36.2514801,0.05489103496,-0.03783921897,0.02347395197,-0.07787474245,0.2735392451,9.849228859 +1.64969094,-8.169509888,26.60665894,-37.32905579,0.04711384699,-0.003802698106,0.01128105074,-0.0208355207,0.2059793174,9.5058918 +1.65969094,-6.614925385,27.04992104,-38.12710571,0.01760414243,0.01516226307,0.0005188670475,-0.01495215017,0.2007467449,9.477257729 +1.66969094,-7.610450745,26.11058998,-38.66853333,-0.0118032163,0.003432350932,-0.004136003554,-0.1004913151,0.2752893269,9.617282867 +1.67969094,-5.836917877,26.47070312,-36.25628662,-0.03128357977,-0.01314315572,-0.003888061969,-0.1696598381,0.2897028029,9.776549339 +1.68969094,-8.07756424,25.656744,-37.56692505,-0.03902820498,-0.031104194,0.0003777854145,-0.1696388274,0.3016649187,9.855332375 +1.69969094,-8.01266861,26.75852585,-37.64543152,-0.0361819528,-0.04390206188,0.004108215682,-0.08676551282,0.2340180129,9.961453438 +1.70969094,-6.815727234,26.20768166,-37.91413879,-0.02684909478,-0.06165890396,0.004194807727,-0.08061584085,0.2390247434,10.00907612 +1.71969094,-8.774143219,26.94566917,-37.65705872,-0.006290666759,-0.06052558869,0.005260156002,-0.09469732642,0.2780058086,9.99105072 +1.72969094,-6.751327515,26.90859985,-36.89584351,0.009505183436,-0.03102665953,0.002138116863,-0.1390231103,0.2646279931,9.884099007 +1.73969094,-7.415344238,26.01513672,-36.52558899,0.005679448135,0.004524962511,0.001461617649,-0.008892144077,0.1571245342,9.586317062 +1.74969094,-6.648670197,26.36482239,-37.77308655,-0.01966399699,0.02597477287,-0.003588251071,-0.02148039639,0.06291545182,9.521533966 +1.75969094,-7.916511536,25.54371262,-36.9487915,-0.04305823147,0.0223974213,-0.003466613824,-0.0427948162,0.1390912533,9.611943245 +1.76969094,-8.504920959,26.15816879,-37.29640198,-0.05958180875,0.0147998305,-0.003130199853,-0.0402473025,0.1930487752,9.696281433 +1.77969094,-7.408531189,26.01865196,-36.22064209,-0.06862412393,0.02192832902,-0.002607707866,-0.1067857593,0.1676802784,9.801758766 +1.78969094,-7.651504517,25.02111244,-37.5226593,-0.06073463336,0.009087075479,-0.00118764746,-0.1356327534,0.1706100255,9.846673965 +1.79969094,-7.519615173,27.09222794,-37.21243286,-0.05577040464,-0.02627890185,0.006700108293,-0.1201330051,0.2078993767,9.865870476 +1.80969094,-7.292560577,26.14942551,-38.36677551,-0.045699507,-0.05088428408,0.009521549568,-0.1055382341,0.194249332,9.946352005 +1.81969094,-8.941196442,26.78852844,-37.79812622,-0.02589777857,-0.03927788138,0.007972843945,-0.1263555437,0.2195701152,9.982694626 +1.82969094,-8.136257172,26.89089394,-36.58627319,-0.0022355197,-0.007897994481,0.00841078721,-0.13340047,0.2100664824,9.810973167 +1.83969094,-7.91601944,25.94457626,-38.04559326,0.006072483025,0.02714213356,0.008006671444,-0.08557648957,0.1570303142,9.632401466 +1.84969094,-6.8097229,26.47785378,-38.39122009,-0.003367578145,0.0620284602,0.001605473459,-0.1225117221,0.1410100162,9.67570591 +1.85969094,-7.31608963,25.46960068,-37.55529785,-0.0129718259,0.06267061085,0.002064112108,-0.1651064754,0.1498811394,9.766771317 +1.86969094,-8.440029144,27.25995255,-37.37492371,-0.01939735189,0.02667776495,0.001498597208,-0.07774727792,0.1837403625,9.869438171 +1.87969094,-6.269790649,27.10281944,-36.60549927,-0.01915593445,-0.02103452012,-0.003069892991,-0.06515015662,0.1889760047,9.92991066 +1.88969094,-6.245456696,27.5159874,-36.63494873,-0.006940102205,-0.03827885538,-0.005393324886,-0.1165964454,0.1897324771,9.984901428 +1.89969094,-8.234401703,25.50487709,-37.25053406,0.01909381524,-0.03864673525,-0.004890202545,-0.1061906591,0.1626597643,10.02290344 +1.90969094,-6.614612579,26.38240051,-36.24827576,0.04109552875,-0.06041933596,-0.003773062024,-0.1020373926,0.171957165,9.990610123 +1.91969094,-7.17206955,25.34778023,-37.69958496,0.05656939,-0.05699628592,-0.003241203493,-0.09723923355,0.2134001702,9.856469154 +1.92969094,-8.266967773,27.6872654,-37.7109375,0.05981186032,-0.0220338013,-0.0090006385,-0.09895791858,0.1922320724,9.676743507 +1.93969094,-7.080738068,26.73028183,-37.340271,0.04462248087,0.01481336262,-0.01446673647,-0.07513602078,0.1447238624,9.591789246 +1.94969094,-5.692897797,26.34888268,-36.40054321,0.01682722196,0.01619235054,-0.01550634205,-0.04328014329,0.08844234794,9.693587303 +1.95969094,-8.033290863,25.67959595,-35.58467102,-0.004610041622,-0.01159483008,-0.01657947898,-0.06921155751,0.1747955829,9.817505836 +1.96969094,-6.930095673,24.54621887,-38.55877686,-0.01155486237,-0.02273061872,-0.01285894588,-0.1380582452,0.2311763912,9.925704956 +1.97969094,-7.033367157,27.42241287,-37.08439636,-0.007763602771,-0.007799980231,-0.0128642004,-0.08171288669,0.2393242121,9.866889954 +1.98969094,-8.105110168,27.30757713,-36.31077576,0.000226826407,0.007088335231,-0.01391610876,-0.07211033255,0.1989753842,9.786602974 +1.99969094,-8.221088409,26.1794281,-38.51947021,0.007151844911,0.01721406542,-0.0149721168,-0.1541464329,0.2528064251,9.78149128 +2.00969094,-6.014190674,26.30828857,-36.8547821,0.01787129417,0.0273047667,-0.01124131307,-0.2166621983,0.3024373949,9.920562744 +2.01969094,-6.497837067,26.24651718,-37.61238098,0.02909519151,0.02771151997,-0.009713578969,-0.1204545349,0.2341998667,10.05337238 +2.02969094,-7.641288757,25.02638626,-37.06521606,0.03541889042,0.009614607319,-0.01449812017,-0.0005609099171,0.1977932751,9.951425552 +2.03969094,-6.648670197,26.36482239,-37.77308655,0.03854234144,-0.002443780191,-0.02357402071,0.02601853013,0.1741227955,9.71210289 +2.04969094,-6.823036194,25.80330276,-37.12229919,0.03266156837,0.002902421635,-0.03311468288,-0.06614656001,0.2000939846,9.64405632 +2.05969094,-8.481391907,26.83799362,-38.10787964,0.03341981024,0.004892181139,-0.03618502244,-0.1074707657,0.162389487,9.802295685 +2.06969094,-8.481391907,26.83799362,-38.10787964,0.04172888771,-0.004742521793,-0.03478867188,-0.08618438244,0.1765760481,9.902980804 +2.07969094,-9.125282288,26.62259865,-38.70158386,0.04610145465,-0.01111476123,-0.03636684269,-0.1150004193,0.2032267451,9.933595657 +2.08969094,-7.097454071,26.05397224,-36.22384644,0.04376486316,-0.005667108111,-0.03837662563,-0.1557287276,0.1533870399,9.807321548 +2.09969094,-8.169509888,26.60665894,-37.32905579,0.03125278652,-0.001980882138,-0.03598660603,-0.1652607918,0.2026120722,9.855483055 +2.10969094,-7.881961823,25.96215439,-36.52078247,0.0225853771,-0.01276575774,-0.03193943202,-0.1723911315,0.2648062408,9.907776833 +2.11969094,-8.129444122,26.89440918,-36.28132629,0.0193426609,-0.01047625113,-0.02931698225,-0.1844214499,0.2498556525,9.777618408 +2.12969094,-8.090877533,24.98219299,-36.29800415,0.01299724448,0.03212872893,-0.02499904484,-0.1594483554,0.185912326,9.599106789 +2.13969094,-6.583778381,27.46660423,-37.85160828,0.003714879043,0.0774788782,-0.02019474469,-0.1376511902,0.1288460642,9.618491173 +2.14969094,-8.394954681,26.01877022,-38.96549988,-0.004882628098,0.07974929363,-0.01858952455,-0.1264213771,0.2041591108,9.738189697 +2.15969094,-7.352062225,27.65023232,-38.16816711,-0.01172669791,0.06241033226,-0.01871017553,-0.1282311976,0.2556934357,9.854340553 +2.16969094,-6.314064026,27.0799675,-38.58776855,-0.007096226327,0.06087465584,-0.02018002048,-0.140379414,0.2431407869,9.895014763 +2.17969094,-7.603637695,26.11410522,-38.36357117,-0.0009169559926,0.05228283256,-0.02110611834,-0.2296079248,0.2668908536,9.884092331 +2.18969094,-7.875953674,26.23232651,-36.99786377,0.01122065168,0.03513076901,-0.0161176268,-0.2607826889,0.2880160809,9.85874939 +2.19969094,-7.865737915,26.23760033,-36.54040527,0.01991503313,0.01621211134,-0.01369080879,-0.2125987709,0.2349860221,9.828289986 +2.20969094,-7.449401855,25.99755859,-38.05039978,0.02593856305,0.001058021793,-0.01391610876,-0.1139675379,0.1974376589,9.821710587 +2.21969094,-8.971538544,26.10518837,-37.29159546,0.03127063811,-0.00808400847,-0.01858368888,-0.07830224186,0.1740580052,9.894181252 +2.22969094,-8.855258942,25.56844139,-37.55891418,0.04016362131,-0.01468131132,-0.02297156677,-0.08382807672,0.1723902375,9.791069031 +2.23969094,-6.840065002,25.7945137,-37.88470459,0.03510941565,-0.005808715709,-0.02527568862,-0.09655064344,0.2753678262,9.775838852 +2.24969094,-6.344406128,26.39662743,-38.08123779,0.03261594847,-0.001005770639,-0.0267905239,-0.1564678401,0.3032001257,9.954015732 +2.25969094,-6.631149292,26.7744751,-38.10748291,0.03750025481,-0.0004138355143,-0.02738986723,-0.2111902684,0.2499201596,9.906594276 +2.26969094,-5.894012451,26.17416382,-38.06642151,0.03352537006,-0.006534338929,-0.02287421189,-0.185904786,0.256785661,9.880761147 +2.27969094,-7.282344818,26.15469933,-37.90933228,0.02651475742,-0.01083567925,-0.01977552287,-0.1777724028,0.3054589629,9.881853104 +2.28969094,-7.128601074,25.63728905,-36.49934387,0.02215582877,-0.01636308059,-0.01840080321,-0.200542137,0.2789077759,9.947379112 +2.29969094,-8.145980835,27.28648376,-38.14053345,0.0253528133,-0.01774885505,-0.01157522202,-0.2354736328,0.327449441,10.0650425 +2.30969094,-7.559364319,26.13695717,-36.38131714,0.03911292553,-0.01410826668,-0.0002708618995,-0.2110538632,0.3184655607,10.03411579 +2.31969094,-8.043819427,26.34184265,-37.92095947,0.05202747509,0.004895074293,0.007309973706,-0.1512760371,0.3286831975,9.895553589 +2.32969094,-8.00245285,26.76379967,-37.18798828,0.04810490087,0.02173821256,0.008107297122,-0.08198977262,0.3075869679,9.735380173 +2.33969094,-7.408843994,26.68617249,-38.09947205,0.03104375303,0.02506607771,0.008456200361,-0.1293141246,0.3014782965,9.657446861 +2.34969094,-7.115287781,26.31184006,-37.76828003,0.008865046315,0.01515081618,0.01111957058,-0.1489645094,0.3126883209,9.760924339 +2.35969094,-8.885910034,25.55262184,-38.9312439,-0.01044041198,0.005883761216,0.01086863503,-0.1335579604,0.3199432492,9.854743004 +2.36969094,-8.196754456,26.59259605,-38.54891968,-0.01020090934,0.02274373919,0.009521549568,-0.1970662028,0.3293652236,9.818229675 +2.37969094,-8.354896545,26.30652237,-37.91775513,-0.004453454632,0.0365036875,0.01297717914,-0.2366603762,0.3252747357,9.787967682 +2.38969094,-8.15328598,26.88210487,-37.34867859,-0.002847630531,0.03556828201,0.01924502663,-0.2097179592,0.2747732401,9.877931595 +2.39969094,-7.432373047,26.00634766,-37.28799438,0.001555474475,0.03062932193,0.02191158198,-0.1824806482,0.2579643428,9.950833321 +2.40969094,-7.249095917,26.43893433,-37.16656494,0.006780474447,0.03764603287,0.02351267263,-0.1464302391,0.268522054,9.896903038 +2.41969094,-7.10426712,26.050457,-36.52879333,0.01062327344,0.04378296435,0.01857700758,-0.1064542532,0.2074498385,9.702510834 +2.42969094,-7.85161972,26.64549637,-37.02731323,0.002669626847,0.02873747237,0.0129544735,-0.04887576029,0.2210505307,9.567022324 +2.43969094,-7.363460541,24.77747154,-37.81118774,-0.0103612449,0.005181383342,0.009140042588,-0.1335268766,0.2172933221,9.608876228 +2.44969094,-6.644954681,25.69906044,-35.741745,-0.01882448047,-0.004420454614,0.008147323504,-0.09323616326,0.2520536184,9.751316071 +2.45969094,-7.416149139,26.28179359,-37.30761719,-0.02168631367,-0.001290564891,0.004412197508,-0.103556484,0.2825931907,9.850436211 +2.46969094,-8.162696838,26.61017418,-37.02410889,-0.01086263545,0.006069350056,-0.0008704911452,-0.1312154382,0.2413202971,9.851606369 +2.47969094,-7.145629883,25.62849998,-37.26174927,-0.003843489569,0.01436809357,-0.001623692689,-0.1820041239,0.2070912719,9.713765144 +2.48969094,-7.275531769,26.15821457,-37.60437012,-0.009312344715,0.01892914996,-0.001100572641,-0.1220566928,0.1783026159,9.680235863 +2.49969094,-7.507480621,24.89929199,-37.66693115,-0.01717316359,0.02942520566,-0.003816983197,-0.06498123705,0.1999483556,9.725384712 +2.50969094,-7.90920639,25.94809151,-37.74064636,-0.02303904109,0.02805838361,-0.005925999023,-0.1256715059,0.2209081352,9.832509995 +2.51969094,-7.90920639,25.94809151,-37.74064636,-0.02268951386,0.01364582684,-0.003972060047,-0.2051095217,0.225720942,9.763577461 +2.52969094,-6.614120483,26.78326416,-37.34507751,-0.02658636495,0.01437338069,0.002244035713,-0.1648696512,0.2123297453,9.703748703 +2.53969094,-6.614120483,26.78326416,-37.34507751,-0.02037812956,0.02540178038,0.003479044419,-0.1159800962,0.2158698142,9.719527245 +2.54969094,-7.148540497,26.02760506,-38.51106262,-0.0118260067,0.03690432012,0.003315735608,-0.1407853812,0.261910826,9.750429153 +2.55969094,-8.043819427,26.34184265,-37.92095947,-0.006829578429,0.02317268588,0.004384300672,-0.1386529505,0.3286429048,9.795340538 +2.56969094,-8.094593048,25.64795494,-38.32933044,0.003840013407,0.007176350802,0.001100537833,-0.06916922331,0.3458430767,9.878037453 +2.57969094,-6.825942993,26.20240784,-38.37158203,0.008876708336,0.0003457232378,-0.006905544549,-0.08735194057,0.3348065913,9.828860283 +2.58969094,-8.531360626,25.87745094,-37.73423767,0.005340719596,-0.00217997143,-0.01371701993,-0.126806885,0.3439081907,9.871929169 +2.59969094,-6.774856567,26.22877502,-36.08436584,0.0004153251648,-0.01131269149,-0.01645131782,-0.1413100064,0.3308411539,9.938947678 +2.60969094,-7.85161972,26.64549637,-37.02731323,-0.004786434583,-0.003533433191,-0.02084087208,-0.1905422211,0.3367064893,9.995604515 +2.61969094,-6.839752197,25.12699318,-36.00585938,-0.003631051164,0.02111057006,-0.02646948397,-0.1596919447,0.3178349733,9.962801933 +2.62969094,-6.727798462,27.58842468,-37.70732117,-0.002874007449,0.04105066508,-0.02904283628,-0.1408151686,0.3369486332,9.898715019 +2.63969094,-7.459617615,25.99228477,-38.50785828,-0.008197206073,0.04637994617,-0.03437278792,-0.1868165284,0.3002360463,9.948604584 +2.64969094,-7.563079834,26.80271912,-38.41264343,-0.009471255355,0.04070337862,-0.03384329379,-0.1625285,0.3253901303,10.03444958 +2.65969094,-6.765449524,26.50070572,-36.40896606,0.001410008408,0.02944688685,-0.03491182253,-0.1994553506,0.3648633659,9.999612808 +2.66969094,-6.336788177,26.13348579,-36.9942627,0.009185058065,0.03163524717,-0.03544767573,-0.2142654508,0.3202452958,9.846342087 +2.67969094,-7.402030945,26.68968773,-37.79452515,0.01375771593,0.03424095362,-0.03149435297,-0.1759790331,0.2779944837,9.759856224 +2.68969094,-6.60439682,26.38767433,-35.79081726,0.0170276463,0.04406008124,-0.02959464677,-0.1392083019,0.281003803,9.720905304 +2.69969094,-8.084377289,25.65322876,-37.87188721,0.01672596484,0.04845323414,-0.03155862913,-0.1621757448,0.2420970947,9.75135231 +2.70969094,-6.504650116,26.24300194,-37.91734314,0.009563808329,0.04708301276,-0.02664239891,-0.1740728915,0.2218662202,9.769033432 +2.71969094,-7.81186676,27.60076523,-37.8584137,-7.901713252e-05,0.03207566217,-0.02283397503,-0.26397264,0.2458071113,9.813800812 +2.72969094,-7.875461578,26.63319016,-38.09466553,-0.007686767727,0.004682029597,-0.0156044066,-0.2840597332,0.180323258,9.903401375 +2.73969094,-6.792198181,26.88750458,-38.72561646,-0.008024005219,-0.02341856062,-0.004444222432,-0.2712144554,0.2306922525,9.956364632 +2.74969094,-7.576889038,25.72730446,-36.04692078,0.001746371388,-0.02758159675,0.001749235205,-0.2706443071,0.2705234885,9.986332893 +2.75969094,-7.259311676,26.43366051,-37.62400818,0.01849942282,-0.009009528905,0.01289303042,-0.2955932617,0.2673327327,9.963009834 +2.76969094,-7.399120331,26.29058266,-36.54521179,0.03639570996,0.008357273415,0.01962275989,-0.2361007482,0.2875300348,9.913796425 +2.77969094,-7.133922577,27.83374023,-35.73989868,0.04758138955,0.0147917401,0.02016180754,-0.1789459288,0.2186049521,9.904644012 +2.78969094,-7.132316589,26.30305099,-38.53068542,0.05693943799,0.003899033181,0.02071453631,-0.09112010896,0.210464716,9.896371841 +2.79969094,-7.495277405,27.50539398,-37.24186707,0.0550789088,-0.01156693138,0.01165349409,-0.1010814682,0.2988709211,9.751430511 +2.80969094,-7.276340485,26.42487144,-38.38641357,0.04573579505,-0.02328784391,0.003650189843,-0.1644459963,0.2800166309,9.548920631 +2.81969094,-7.063709259,26.73907089,-36.5778656,0.01850998029,-0.01182917133,-0.004340337589,-0.1469102055,0.2510396242,9.466347694 +2.82969094,-6.741111755,26.91387177,-36.43840027,-0.004324021749,-0.01375587657,-0.008865067735,-0.1834320128,0.2701443136,9.711741447 +2.83969094,-6.631641388,26.37361145,-37.01068115,-0.001867462881,-0.02513966337,-0.0110240262,-0.1669787318,0.3215048611,9.896787643 +2.84969094,-7.425559998,26.0098629,-36.98304749,0.01628770307,-0.02488586679,-0.01466791332,-0.2579631507,0.3567141593,9.860242844 +2.85969094,-8.243812561,25.2329464,-36.92596436,0.02688464522,-0.004169604741,-0.01387823559,-0.2582223117,0.2771977484,9.736753464 +2.86969094,-8.193843842,26.19349098,-37.29960632,0.02200842649,0.03450661153,-0.01309495978,-0.272795558,0.2449849695,9.676970482 +2.87969094,-6.823036194,25.80330276,-37.12229919,0.006453162991,0.05085517466,-0.006890017539,-0.3061911166,0.2199545354,9.631800652 +2.88969094,-7.40933609,26.28530884,-37.00267029,-0.01606200263,0.04909879714,0.003005686682,-0.2995644808,0.201525405,9.73643589 +2.89969094,-7.641288757,25.02638626,-37.06521606,-0.02808204293,0.04346767068,0.006027473602,-0.1448681653,0.272339046,9.628669739 +2.90969094,-7.46692276,25.58790588,-37.71600342,-0.04177628458,0.04816025496,0.002225785982,-0.116304405,0.2411215901,9.453680038 +2.91969094,-6.624336243,26.77799034,-37.80253601,-0.05434793979,0.06665814668,-0.006078326143,-0.2214545161,0.2327720374,9.527388573 +2.92969094,-5.72095108,26.60147667,-38.4024353,-0.06135207415,0.06435563415,-0.004860650748,-0.2514836788,0.2456260026,9.681171417 +2.93969094,-7.189098358,25.33899117,-38.46199036,-0.05704882741,0.0275369063,0.0003332057968,-0.2850147784,0.2860251069,9.833475113 +2.94969094,-7.529335022,27.48781586,-38.76667786,-0.04562484473,0.002622469561,0.00259678578,-0.2471337616,0.2965653837,9.949759483 +2.95969094,-8.473773956,26.5748539,-37.02090454,-0.02654643729,0.0005687286612,0.001860674936,-0.2698851526,0.3216832578,10.0168438 +2.96969094,-7.249095917,26.43893433,-37.16656494,-0.006175159011,-6.634369493e-05,0.001427219715,-0.2811920643,0.2962718606,10.01611328 +2.97969094,-7.450206757,26.26421547,-38.83242798,0.006415885873,-0.007441923022,0.002711253706,-0.2567391992,0.20896779,9.862981796 +2.98969094,-7.450206757,26.26421547,-38.83242798,0.0108665498,-0.009281657636,0.00197905954,-0.1455981433,0.2174957097,9.758725166 +2.99969094,-6.447872162,27.20706177,-37.98605347,0.004828017205,0.01278313715,-0.00765129365,-0.09340839088,0.2501848638,9.700984001 +3.00969094,-6.447872162,27.20706177,-37.98605347,-0.003581494559,0.02978005633,-0.01544828713,-0.1543234587,0.2795874476,9.821495056 +3.01969094,-8.49810791,26.16168594,-36.99145508,-0.009767705575,0.02843782678,-0.01857014373,-0.2008584142,0.3034571409,9.921107292 +3.02969094,-8.06734848,25.66201782,-37.10948181,-0.01092633884,0.01583724841,-0.01798436232,-0.2255139798,0.2753494382,9.935453415 +3.03969094,-6.695236206,25.40603638,-37.24693298,-0.01146858186,0.002507640515,-0.01536135934,-0.2470640689,0.2939111292,9.952013969 +3.04969094,-7.316898346,25.73625755,-38.33734131,-0.01291702595,-0.005841163918,-0.01194457337,-0.2390746474,0.2554622889,9.902705193 +3.05969094,-7.374786377,26.70375061,-36.57466125,-0.01152374595,-0.02042293362,-0.009471164085,-0.2179525644,0.2555968761,9.93608284 +3.06969094,-7.333118439,25.46081161,-38.31770325,-0.008825613186,-0.03340864927,-0.007532409392,-0.2380346656,0.3015532494,9.908273697 +3.07969094,-8.468456268,24.37840271,-37.78034973,-0.005647540092,-0.02399409376,-0.006282707676,-0.1862966716,0.2948144078,9.761417389 +3.08969094,-6.474308014,26.92634201,-38.42385864,-0.008505098522,0.01370482426,-0.008589369245,-0.2072754353,0.290933311,9.65144825 +3.09969094,-7.194416046,27.53544235,-37.70251465,-0.01222948264,0.04708583653,-0.009292855859,-0.1612217426,0.2714120448,9.722549438 +3.10969094,-7.488464355,27.50891113,-36.93690491,-0.01520463172,0.05751100183,-0.009822325781,-0.2179809064,0.2705034912,9.771174431 +3.11969094,-7.842208862,26.91742516,-37.35188293,-0.01752957702,0.04657791555,-0.01018739119,-0.20446226,0.3115225434,9.746862411 +3.12969094,-6.607307434,26.7867794,-37.04013062,-0.02082055621,0.03212425858,-0.01252835803,-0.284610033,0.3318831623,9.818239212 +3.13969094,-8.050319672,25.67080688,-36.34707642,-0.02162656561,0.006840092596,-0.01146937907,-0.2710173726,0.335179776,9.85300827 +3.14969094,-6.631641388,26.37361145,-37.01068115,-0.01494333707,-0.003114380408,-0.01216324605,-0.293641299,0.3532663584,9.856822014 +3.15969094,-8.227588654,25.50839233,-36.94558716,-0.00701501593,0.003271889873,-0.01338343509,-0.3079952896,0.2997109592,9.897614479 +3.16969094,-7.115287781,26.31184006,-37.76828003,0.0001519322395,0.005005017854,-0.01125273854,-0.2283413708,0.3019617796,9.842337608 +3.17969094,-7.956577301,25.25596237,-37.996521,0.008300120942,-0.003143170848,-0.01512670144,-0.1571785808,0.2411979139,9.994022369 +3.18969094,-6.794185638,24.2866745,-38.69322205,0.01882113516,-0.01369971037,-0.02005962282,-0.1790753752,0.2787266076,10.03287792 +3.19969094,-7.600730896,25.71500015,-37.11428833,0.0226082243,-0.01807713695,-0.01910396852,-0.2966942191,0.2534326017,9.856265068 +3.20969094,-7.275531769,26.15821457,-37.60437012,0.01845968887,-0.02622262016,-0.01432321966,-0.2260305434,0.2392146587,9.849489212 +3.21969094,-8.507831573,26.55727577,-38.54571533,0.01486025844,-0.02195211686,-0.01205309667,-0.1015152261,0.2238649428,9.757564545 +3.22969094,-8.193843842,26.19349098,-37.29960632,0.007541217841,-0.009552078322,-0.01419021189,-0.1901240647,0.2519818842,9.725886345 +3.23969094,-8.170314789,26.87331581,-38.11108398,-0.002043720335,-0.002106974367,-0.01097225025,-0.3190883398,0.2920241356,9.870922089 +3.24969094,-7.138324738,26.03287888,-38.05360413,-0.002122517675,0.001215161756,-0.002811893821,-0.3439500332,0.3194630742,9.970935822 +3.25969094,-5.583427429,25.80862045,-36.9728241,0.004528208636,0.01517218351,0.0009987638332,-0.246371001,0.294268012,9.926848412 +3.26969094,-6.74079895,26.24635315,-34.55955505,0.01069999952,0.0292138122,0.003129459452,-0.2038469911,0.2858913839,9.858620644 +3.27969094,-7.333118439,25.46081161,-38.31770325,0.01235124376,0.03107888252,0.0003867314663,-0.1960114241,0.3041076958,9.8361063 +3.28969094,-6.695236206,25.40603638,-37.24693298,0.01681809872,0.02263225801,4.35502734e-05,-0.2625229657,0.3344432414,9.877408981 +3.29969094,-8.365112305,26.30124855,-38.37519836,0.01864273101,0.02571816929,-0.001450675773,-0.3248307705,0.3089620173,9.92305851 +3.30969094,-5.703922272,26.61026573,-37.64002991,0.0246123001,0.02688729018,-0.004978229757,-0.1456692666,0.2753237486,9.839014053 +3.31969094,-6.840065002,25.7945137,-37.88470459,0.01809304953,0.03596933186,-0.01544332877,-0.1127016768,0.1825166345,9.618488312 +3.32969094,-7.235782623,27.11348534,-38.43548584,0.0004593646154,0.0279977899,-0.0206976831,-0.2280323654,0.2231822312,9.626499176 +3.33969094,-8.49810791,26.16168594,-36.99145508,-0.01300951093,-0.008212786168,-0.01676802337,-0.2872509062,0.2894925773,9.755972862 +3.34969094,-8.084686279,26.32074928,-39.75073242,-0.01604039222,-0.02611956932,-0.01259132661,-0.3058491349,0.2855690122,9.846001625 +3.35969094,-7.926235199,25.93930244,-38.50305176,-0.01576715335,-0.02967993356,-0.007701023482,-0.2758152187,0.2458155155,9.883844376 +3.36969094,-7.593421936,26.11937904,-37.90612793,-0.0165593382,-0.02559154853,-0.004947154783,-0.2677181363,0.2292198688,9.824416161 +3.37969094,-7.408531189,26.01865196,-36.22064209,-0.01147498749,-0.008676798083,-0.000233884668,-0.288110882,0.2577656209,9.797032356 +3.38969094,-7.148540497,26.02760506,-38.51106262,-0.006633196957,0.01344986074,-0.0008964412846,-0.3024396598,0.3181247711,9.880328178 +3.39969094,-6.470592499,26.26058006,-36.39253235,0.00531923119,0.03014460765,0.0009987638332,-0.2945103049,0.3188195527,9.94841671 +3.40969094,-8.007350922,24.56207466,-38.40490723,0.01756862178,0.03873810172,0.00289370399,-0.2079023421,0.2359249741,9.747755051 +3.41969094,-10.57120132,25.9057312,-39.25784302,0.01010619756,0.05189544708,-0.001664606389,-0.1738738865,0.1913950294,9.680673599 +3.42969094,-7.552864075,26.80799103,-37.9552002,-0.0005048532039,0.03880349547,-0.008658854291,-0.194183588,0.2359473258,9.722563744 +3.43969094,-8.193843842,26.19349098,-37.29960632,-0.01316022221,0.007196725346,-0.008648898453,-0.3240816891,0.297080785,9.751264572 +3.44969094,-8.193843842,26.19349098,-37.29960632,-0.01896319911,0.002648235299,-0.00752402097,-0.348402977,0.307802856,9.702807426 +3.45969094,-7.282344818,26.15469933,-37.90933228,-0.02673238888,0.003566776402,-0.004272747319,-0.3012737632,0.2431114316,9.759227753 +3.46969094,-7.282344818,26.15469933,-37.90933228,-0.02919545397,-0.002234932035,-6.658444181e-05,-0.3082113266,0.2360342592,9.839511871 +3.47969094,-7.601039886,26.38251877,-38.99313354,-0.02647368982,-0.0003828592598,0.004247863777,-0.3241005838,0.2539158165,9.896888733 +3.48969094,-7.70747757,23.79039574,-36.67192078,-0.01391642354,0.005569877103,0.005652846303,-0.3335336745,0.2924467623,9.974705696 +3.49969094,-7.466117859,25.32124901,-36.93397522,-0.002039574087,0.006009228528,0.008374901488,-0.3174402714,0.2626022398,9.936785698 +3.50969094,-6.774856567,26.22877502,-36.08436584,0.005044433288,-0.002327063121,0.009446952492,-0.3287468851,0.2626532614,9.900091171 +3.51969094,-6.638454437,26.37009621,-37.31562805,0.009231860749,-0.0183471106,0.01005422324,-0.2419033647,0.2037884295,9.751329422 +3.52969094,-8.237312317,25.90398216,-38.49984741,-0.0002129487693,-0.0351838246,0.006396608893,-0.2397907376,0.2444081306,9.79660511 +3.53969094,-7.794345856,28.01041794,-38.19281006,-0.001384953968,-0.05765299499,0.004162475467,-0.2784003317,0.2528136969,9.876300812 +3.54969094,-6.01499939,26.57494545,-37.63682556,0.01022412907,-0.04971811175,0.004277116619,-0.2988288105,0.2813760936,9.848402023 +3.55969094,-7.399120331,26.29058266,-36.54521179,0.02244941518,-0.01563763618,0.004172147717,-0.3169019222,0.3055851161,9.799267769 +3.56969094,-7.289653778,25.75032043,-37.11749268,0.0274567008,0.02185425535,0.001589378342,-0.233324185,0.2500683963,9.69851017 +3.57969094,-7.165569305,26.01881599,-39.27346802,0.02480689436,0.02791698091,-0.003749892116,-0.3112936914,0.2143467516,9.725136757 +3.58969094,-6.753314972,24.30776978,-36.86346436,0.01731849089,0.004670565017,-0.003262628801,-0.3077295125,0.2493745536,9.750574112 +3.59969094,-6.712265015,25.39724731,-38.00933838,0.004657322541,-0.01840234734,0.002560213674,-0.3492403328,0.2538228631,9.572223663 +3.60969094,-8.330558777,26.71968842,-37.94718933,-0.01725715771,-0.02274436876,0.00684737647,-0.3248106241,0.2503971457,9.603363991 +3.61969094,-6.785385132,26.89102173,-38.4206543,-0.02854960784,-0.01157763973,0.008985424414,-0.313901335,0.2334174663,9.79224205 +3.62969094,-6.785385132,26.89102173,-38.4206543,-0.03440004215,0.01084966771,0.01005065627,-0.2761927247,0.2305997759,9.864484787 +3.63969094,-7.546051025,26.81150818,-37.65023804,-0.02321261168,0.03533025831,0.0105829034,-0.2884257436,0.258186996,9.792014122 +3.64969094,-7.489959717,25.3089447,-38.00132751,-0.01417162456,0.04542586207,0.01005422324,-0.3501100838,0.2739596069,9.894083023 +3.65969094,-7.131511688,26.03639412,-37.74865723,-0.0024527614,0.04169920832,0.01005422324,-0.3122108281,0.283588022,9.821797371 +3.66969094,-8.243812561,25.2329464,-36.92596436,0.001286050305,0.04755017161,0.006820009556,-0.3645493984,0.2892889977,9.780883789 +3.67969094,-8.11762619,25.36899376,-38.61465454,0.002876439132,0.03516138345,0.006307389122,-0.3239471614,0.2848519981,9.735665321 +3.68969094,-6.440250397,26.94392014,-36.89904785,-0.0002477457747,0.0002992372029,0.007414806169,-0.2544038296,0.3007819951,9.809139252 +3.69969094,-7.536643982,27.08343887,-37.97483826,0.003989266232,-0.05071535707,0.00947406888,-0.2319714129,0.3080909848,9.878863335 +3.70969094,-6.672199249,25.68499756,-36.96160889,0.008822058327,-0.06696711481,0.008434649557,-0.2179051787,0.2917186916,9.934447289 +3.71969094,-7.923324585,25.54019737,-37.2537384,0.0168600902,-0.05562448502,0.00521544693,-0.1962778419,0.2554365098,9.943925858 +3.72969094,-7.080738068,26.73028183,-37.340271,0.02695737779,-0.04445654154,0.00677522039,-0.2058451921,0.2460807711,9.855082512 +3.73969094,-7.258815765,26.83452415,-38.72080994,0.0257118158,-0.03880500048,0.009023677558,-0.2229058892,0.2702342272,9.8239851 +3.74969094,-8.029697418,26.74973679,-38.40783691,0.01767116413,-0.03354083747,0.01173234172,-0.1730276793,0.293876797,9.859820366 +3.75969094,-8.277870178,25.21536827,-38.45077515,0.00750257913,-0.0134898033,0.01595561951,-0.2170909047,0.2744894028,9.763518333 +3.76969094,-7.784622192,27.61482811,-36.6385498,-0.008115497418,0.004707356449,0.01857700758,-0.1322013289,0.2862198949,9.706252098 +3.77969094,-7.408531189,26.01865196,-36.22064209,-0.01740895212,0.02163187601,0.02066392824,-0.200186044,0.2886405289,9.775326729 +3.78969094,-7.276340485,26.42487144,-38.38641357,-0.01349348295,0.02156052925,0.0227907449,-0.05037189275,0.2798060477,10.02560616 +3.79969094,-8.060535431,25.66553307,-36.80451965,-0.006179019809,0.01282461267,0.01740583777,-0.1050582752,0.304669559,9.953327179 +3.80969094,-6.792694092,26.48664284,-37.6288147,-0.008539781906,0.04109317809,0.01307771914,-0.1367159933,0.2985886037,9.855304718 +3.81969094,-6.528675079,25.16231346,-36.00906372,-0.01666204073,0.06206073612,0.009280072525,-0.1876604408,0.2774381042,9.739678383 +3.82969094,-6.840065002,25.7945137,-37.88470459,-0.03033562005,0.04966237396,0.009583637118,-0.1894158125,0.2180558145,9.681078911 +3.83969094,-8.82151413,26.25354004,-37.91294861,-0.0442554988,0.02925441973,0.007262540516,-0.1306591481,0.2705550194,9.748282433 +3.84969094,-7.875461578,26.63319016,-38.09466553,-0.04479436576,0.01652778126,0.002259462141,-0.1970492601,0.2646980286,9.908373833 +3.85969094,-8.313529968,26.72847748,-37.18478394,-0.03184996918,0.02280884422,-0.00311926892,-0.2717116773,0.2997603416,9.914752007 +3.86969094,-6.546016693,25.82104492,-38.65031433,-0.02147505246,0.03477363288,-0.0003719876986,-0.347087115,0.2509671748,9.840457916 +3.87969094,-5.744480133,25.92165184,-37.59095764,-0.01409151033,0.03734870255,0.003584513441,-0.2748141289,0.2386694402,9.767773628 +3.88969094,-7.956577301,25.25596237,-37.996521,-0.005113671999,0.02281517908,0.005340028089,-0.303516686,0.2496834397,9.829120636 +3.89969094,-7.333118439,25.46081161,-38.31770325,0.003993850201,-0.002983287908,0.00775645813,-0.1925490499,0.3337683082,9.893627167 +3.90969094,-7.333118439,25.46081161,-38.31770325,0.01015300211,-0.02382051758,0.0009108139202,-0.2402744293,0.2796029747,9.859054565 +3.91969094,-8.14566803,26.61896324,-36.26170349,0.01105037984,-0.01546274498,-0.003354544286,-0.2564902306,0.254522115,9.739465714 +3.92969094,-8.180530548,26.86804199,-38.56854248,0.005029072054,-0.003389192745,-0.003890158376,-0.2920319736,0.2168327123,9.626190186 +3.93969094,-8.244125366,25.90046692,-38.80479431,-0.0132009536,-0.01026250795,-0.003068285529,-0.2643445432,0.226190418,9.728908539 +3.94969094,-7.898990631,25.95336533,-37.28318787,-0.01736517623,-0.01826028526,0.0005658168811,-0.2710779905,0.2723842859,9.839493752 +3.95969094,-7.899795532,26.2200222,-38.06521606,-0.01492538489,-0.01595095545,0.0008957325481,-0.2685719728,0.2795063257,9.843836784 +3.96969094,-7.566673279,25.73257828,-35.58947754,-0.01235895604,0.003454227,0.001958799548,-0.3106808662,0.2576106787,9.792434692 +3.97969094,-6.982593536,28.11629868,-36.67601013,-0.01192790549,0.006320843473,0.00643603364,-0.2947559953,0.2017954588,9.794699669 +3.98969094,-7.617759705,25.70621109,-37.87669373,-0.01215213537,-0.005099052563,0.00940784812,-0.251095444,0.2411850542,9.778581619 +3.99969094,-6.648178101,26.76568604,-38.86988831,-0.01203843486,-0.008605884388,0.007923526689,-0.2290615439,0.2813150585,9.819487572 +4.00969094,-7.11448288,26.04518318,-36.98625183,-0.006584511138,-0.001146110706,0.008336946368,-0.2567182481,0.3083212674,9.838685036 +4.01969094,-7.385807037,26.96513367,-37.81414795,0.004388485104,0.01485629193,0.002841629088,-0.2343084961,0.2451559454,9.847247124 +4.02969094,-7.097766876,26.72149277,-38.10267639,0.008203179576,0.02133086324,0.00259678578,-0.2072534859,0.2421934903,9.698073387 +4.03969094,-8.01266861,26.75852585,-37.64543152,-0.0003045490012,0.01985875331,-0.003262628801,-0.1555505842,0.1782817245,9.711576462 +4.04969094,-6.823036194,25.80330276,-37.12229919,-0.01336695347,0.005080743693,-0.006058639847,-0.1943382621,0.2094749659,9.714632034 +4.05969094,-8.01997757,26.35414696,-36.85359192,-0.03109871596,-0.01130758598,-0.008986420929,-0.2392482162,0.2315007001,9.763028145 +4.06969094,-7.651504517,25.02111244,-37.5226593,-0.0400146544,-0.004739507101,-0.01046456024,-0.2673299909,0.2497864366,9.792757988 +4.07969094,-7.242595673,27.10997009,-38.740448,-0.03906309232,0.006150948815,-0.01100350544,-0.2492612153,0.2543903887,9.845363617 +4.08969094,-7.074729919,27.00045395,-37.81735229,-0.03663897514,0.004508738406,-0.01430373639,-0.2727160156,0.224393189,9.859981537 +4.09969094,-6.320072174,26.80979538,-38.11068726,-0.02902301773,-0.007368606515,-0.01376769505,-0.2551684082,0.249186188,9.86097908 +4.10969094,-7.587417603,26.38955116,-38.38320923,-0.02162656561,-0.005713349208,-0.0148298014,-0.2201159149,0.2390990704,9.782792091 +4.11969094,-7.859237671,26.90863609,-38.11428833,-0.02336135507,0.003191094613,-0.01513622142,-0.2662590444,0.2411587536,9.972793579 +4.12969094,-7.305873871,25.4748745,-37.09785461,-0.009118622169,-0.002612174489,-0.01077926904,-0.282861501,0.2332253903,9.922781944 +4.13969094,-6.574367523,27.73853493,-38.17617798,-0.005163447931,-0.009760250337,-0.007363054901,-0.211932838,0.212636292,9.728738785 +4.14969094,-8.435512543,25.33015633,-38.91642761,-0.007328464184,0.003674271284,-0.008220901713,-0.20717749,0.1956729591,9.695152283 +4.15969094,-6.055557251,25.88633156,-37.5877533,-0.01590443403,0.01273629628,-0.009684925899,-0.2054186314,0.205473423,9.685699463 +4.16969094,-7.834903717,27.32180405,-38.14373779,-0.02339531854,-0.001536996569,-0.005243080668,-0.2339800149,0.2187104374,9.73903656 +4.17969094,-8.187030792,26.19700623,-36.99465942,-0.02800761536,-0.02326576225,0.001716356725,-0.2859913707,0.2686445117,9.796771049 +4.18969094,-7.242282867,26.44244957,-36.86160278,-0.02908233926,-0.03056327812,0.003839137033,-0.2978691161,0.2407368124,9.87355423 +4.19969094,-7.145629883,25.62849998,-37.26174927,-0.02445448563,-0.04781426489,0.01024218835,-0.2982214987,0.2228002399,9.95925045 +4.20969094,-8.521144867,25.88272476,-37.27677917,-0.01644831337,-0.06270572543,0.01859445311,-0.3007469773,0.2295433432,9.89234066 +4.21969094,-7.385002136,26.69847679,-37.03211975,-0.01185182575,-0.04533778131,0.02409035899,-0.2922000289,0.2343971729,9.830682755 +4.22969094,-7.899482727,25.55250168,-36.18638611,-0.009681108408,-0.02127336524,0.02458944358,-0.1705660373,0.2193717957,9.817026138 +4.23969094,-6.296230316,26.82210159,-37.04333496,-0.008695719764,-0.004451250657,0.01799800247,-0.1621389687,0.2197939306,9.757343292 +4.24969094,-6.509967804,28.43945312,-37.15789795,-0.01495374739,0.002140143886,0.01305411384,-0.2275846601,0.2546940744,9.810265541 +4.25969094,-7.916511536,25.54371262,-36.9487915,-0.02169186994,8.451147005e-05,0.008854851127,-0.2209734768,0.2321222872,9.868536949 +4.26969094,-7.392620087,26.96161842,-38.11909485,-0.02849860862,-0.001703454647,0.006528029684,-0.1220632419,0.1541925073,9.71141243 +4.27969094,-6.614120483,26.78326416,-37.34507751,-0.04428917542,0.01810290664,-0.002197280526,-0.2801389396,0.1237404421,9.710455894 +4.28969094,-7.392307281,26.2940979,-36.24026489,-0.04955516383,0.02923793532,0.0008839904331,-0.2690411806,0.03041800112,9.910918236 +4.29969094,-7.131511688,26.03639412,-37.74865723,-0.03079982474,0.03252982348,-0.002942085499,-0.226893425,0.06968209893,10.19825363 +4.30969094,-8.054035187,26.33656883,-38.37840271,0.008452025242,0.07177698612,0.002925334033,-0.2675082386,0.2150973082,10.1730938 +4.31969094,-7.242595673,27.10997009,-38.740448,0.04162428528,0.1026687622,0.005137396511,-0.3166666329,0.2443601489,10.15234661 +4.32969094,-7.234973907,26.84682846,-37.65344238,0.07101240754,0.09248439223,0.005703500006,-0.2230912894,0.2666797042,9.999241829 +4.33969094,-9.125778198,26.221735,-37.6047821,0.0769181326,0.04123746604,0.003129459452,-0.1260693818,0.2381351441,9.691967964 +4.34969094,-6.775665283,26.4954319,-36.8664093,0.05713566393,-0.01057719067,-0.007675570901,-0.1283408254,0.208796829,9.449485779 +4.35969094,-7.322902679,25.46608543,-37.86026001,0.021940846,-0.03525879979,-0.0164392069,-0.2080651373,0.2594524622,9.481989861 +4.36969094,-7.322902679,25.46608543,-37.86026001,-0.012359038,-0.04465859383,-0.02090466022,-0.33076033,0.2426670343,9.544941902 +4.37969094,-6.873313904,25.5102787,-38.62747192,-0.03849985823,-0.04358688742,-0.01385076717,-0.4688956439,0.2643616199,9.787895203 +4.38969094,-6.873313904,25.5102787,-38.62747192,-0.04186818004,-0.04309883714,-0.0009931600653,-0.4567618966,0.2665080726,9.873948097 +4.39969094,-6.815727234,26.20768166,-37.91413879,-0.03789544106,-0.02908178978,0.01127431542,-0.4167534411,0.3044225276,9.863181114 +4.40969094,-6.782478333,26.49191666,-37.17137146,-0.03473445773,-0.001430485398,0.01775887236,-0.3834793866,0.3153073788,9.803701401 +4.41969094,-8.043819427,26.34184265,-37.92095947,-0.03422413021,0.01866585761,0.01907779463,-0.2802246213,0.277962029,9.730218887 +4.42969094,-7.369091034,27.64144325,-38.93057251,-0.03385256976,0.02039143257,0.01885609329,-0.2379066348,0.2587588131,9.743079185 +4.43969094,-8.297309875,27.00392532,-37.204422,-0.04078369215,0.01425610762,0.01538096368,-0.2097538263,0.237317726,9.7883358 +4.44969094,-6.798698425,26.21647072,-37.1517334,-0.04215364158,0.007632749155,0.0129903052,-0.2162558734,0.2124440819,9.842716217 +4.45969094,-6.679012299,25.68148232,-37.26655579,-0.03894165531,0.002023714595,0.01219128072,-0.21327281,0.2051690519,9.822431564 +4.46969094,-7.881961823,25.96215439,-36.52078247,-0.03920482472,-0.004644265398,0.01271759532,-0.1758301556,0.1789844632,9.79445076 +4.47969094,-9.782485962,25.73265076,-38.02636719,-0.04025738314,-0.007038090378,0.006575869862,-0.1418743432,0.1429290026,9.867178917 +4.48969094,-8.773334503,26.6790123,-36.87501526,-0.03090364486,-0.01158179156,0.001804163679,-0.1442653984,0.1189981252,9.951881409 +4.49969094,-6.671707153,26.08586121,-38.05841064,-0.01513878815,-0.02521918528,-6.658444181e-05,-0.1738433093,0.1544630975,9.960613251 +4.50969094,-6.0623703,25.88281631,-37.89271545,-0.0008522793651,-0.02589283884,0.0004660896957,-0.1996041238,0.1565450281,9.95832634 +4.51969094,-7.868648529,26.6367054,-37.78971863,0.01431652624,-0.004012350924,-0.002729954664,-0.2186203599,0.1420396417,9.912900925 +4.52969094,-7.892982483,26.22353745,-37.76026917,0.018323984,0.02168132737,-0.0004264845047,-0.2232411653,0.1151062325,9.794009209 +4.53969094,-8.274959564,24.81626129,-37.20146179,0.008296172135,0.03482315689,0.0001749894582,-0.1810871363,0.09140643477,9.71138382 +4.54969094,-6.775169373,26.89629364,-37.96321106,-0.0138578536,0.0290421769,0.0007601014804,-0.1903544068,0.133223936,9.736455917 +4.55969094,-6.423221588,26.9527092,-36.13664246,-0.03204216808,0.02157061175,0.001588354819,-0.215617761,0.2027130872,9.723331451 +4.56969094,-8.013477325,27.02518272,-38.42747498,-0.04305369407,0.02441652864,0.00265686959,-0.21561867,0.1740983278,9.768782616 +4.57969094,-8.473773956,26.5748539,-37.02090454,-0.04656537622,0.01722446829,0.005860616919,-0.2287943214,0.1416486502,9.826238632 +4.58969094,-7.249095917,26.43893433,-37.16656494,-0.0395822376,0.01232369244,0.008911250159,-0.2248601615,0.1444966495,9.854561806 +4.59969094,-7.443393707,26.26773071,-38.52748108,-0.03166176751,0.00800199993,0.008988874033,-0.2275328785,0.1459973603,9.916668892 +4.60969094,-8.831729889,26.24826622,-38.37039185,-0.0206013117,0.009386268444,0.008898375556,-0.2268878073,0.1908883601,9.917303085 +4.61969094,-5.772026062,27.57248497,-36.33480835,-0.01100313012,0.01832520589,0.006325504277,-0.2854395807,0.2194937319,9.911237717 +4.62969094,-7.928646088,27.73664856,-36.49429321,-0.003731844481,0.04050150514,0.003878329415,-0.3024586439,0.1961129755,9.810222626 +4.63969094,-6.263290405,27.77385521,-38.17938232,-0.0005319332704,0.05672128499,-0.001344260294,-0.22144261,0.1550287455,9.70290947 +4.64969094,-8.314025879,26.32761574,-36.08798218,-0.002774584573,0.05401900411,-0.006458672695,-0.1730443388,0.1541241705,9.64126873 +4.65969094,-6.654678345,26.09465027,-37.29600525,-0.01445338782,0.03875076026,-0.01371094957,-0.2011986077,0.1363305449,9.544471741 +4.66969094,-7.570388794,26.39834023,-37.62080383,-0.02568543702,0.01708991826,-0.01378848217,-0.29481107,0.1495176256,9.718362808 +4.67969094,-6.825942993,26.20240784,-38.37158203,-0.02655274048,-0.0216173958,-0.01138909534,-0.296559602,0.1946366876,9.950929642 +4.68969094,-7.906295776,25.54898643,-36.49133301,-0.01781709492,-0.04153759778,-0.005925999023,-0.3151355982,0.1484638304,10.02096558 +4.69969094,-7.559364319,26.13695717,-36.38131714,-0.002749661915,-0.03404572606,-0.003847503802,-0.3055528998,0.1347839087,10.00041008 +4.70969094,-8.094593048,25.64795494,-38.32933044,0.00945944991,-0.008453158662,-0.0002174130641,-0.3440961838,0.1186381653,9.896161079 +4.71969094,-6.487621307,26.251791,-37.15493774,0.01406259369,-0.001046618447,0.005578093696,-0.3545964956,0.1277017146,9.871976852 +4.72969094,-7.586608887,26.12289429,-37.60116577,0.007003881969,-0.01737609878,0.01359666139,-0.3624377251,0.1338391006,9.84253788 +4.73969094,-6.487621307,26.251791,-37.15493774,-0.007547943387,-0.01550474204,0.01591363735,-0.2639065683,0.1462768465,9.776966095 +4.74969094,-6.751327515,26.90859985,-36.89584351,-0.02195086703,-0.001110700425,0.01218491793,-0.2222563624,0.1621421576,9.738617897 +4.75969094,-7.249095917,26.43893433,-37.16656494,-0.03246286511,-0.008031003177,0.01146942563,-0.248240903,0.115946807,9.743156433 +4.76969094,-6.457279205,26.93513107,-37.66145325,-0.03635413572,-0.02225174196,0.01254490949,-0.2521977723,0.102270402,9.834392548 +4.77969094,-5.980941772,26.59252357,-36.11201477,-0.02922541276,-0.03147359192,0.01022274233,-0.190122053,0.1274154633,9.831977844 +4.78969094,-7.479057312,27.78084183,-37.26150513,-0.01552046463,-0.0149680879,0.005340931471,-0.2009503394,0.1568839103,9.832667351 +4.79969094,-7.26581192,25.76262474,-36.05012512,-0.0007052477449,0.01664851978,0.001047774218,-0.2484272271,0.1699940115,9.815591812 +4.80969094,-9.155311584,25.27173805,-36.31622314,0.006605156697,0.02571816929,9.230547585e-05,-0.2894585431,0.1414472759,9.803195 +4.81969094,-9.155311584,25.27173805,-36.31622314,0.003320282325,0.007851457223,0.0008433877956,-0.2730394006,0.1397527158,9.857546806 +4.82969094,-7.358562469,26.97919655,-36.59428406,-0.003061691765,-0.01043106336,0.0001573381014,-0.258321166,0.1311504543,9.84120369 +4.83969094,-7.358562469,26.97919655,-36.59428406,-0.004729580134,-0.01538466103,-0.0008347684052,-0.263558358,0.1183589473,9.855064392 +4.84969094,-8.036197662,26.07870102,-36.83395386,-0.004580997862,-0.01142347418,-0.002197280526,-0.2746455669,0.1134366319,9.814276695 +4.85969094,-6.672199249,25.68499756,-36.96160889,-0.007733293343,-0.0003262530081,-0.0008874703199,-0.1970769614,0.08623953164,9.628912926 +4.86969094,-8.81470108,26.25705528,-37.60798645,-0.01761743426,0.01848483458,-0.005533532239,-0.259354353,0.09551355988,9.633827209 +4.87969094,-7.85843277,26.64197922,-37.33226013,-0.02588795871,0.01906336285,-0.0006064993795,-0.2534437478,0.1723184586,9.753379822 +4.88969094,-7.25850296,26.16700363,-36.84196472,-0.03109328821,0.003598091658,0.001400619745,-0.2143057436,0.1092486531,9.908551216 +4.89969094,-7.898990631,25.95336533,-37.28318787,-0.02612610161,-0.01706181467,-0.001536136726,-0.2153220028,0.1003462896,10.02652073 +4.90969094,-6.45356369,26.26936913,-35.63012695,-0.01295110211,-0.02876726352,-0.002070615999,-0.2522668839,0.1105454043,10.04345798 +4.91969094,-9.015007019,25.81567955,-38.49183655,0.002009376884,-0.0287384484,-0.001379664754,-0.2668540478,0.1403126717,9.930277824 +4.92969094,-7.378501892,27.36951256,-38.60600281,0.01198495831,-0.00673567038,-0.003675399348,-0.2398301959,0.1118507236,9.92348671 +4.93969094,-7.471748352,28.18522072,-38.05334473,0.02573502436,0.02284619026,-0.004629021976,-0.224476397,0.1067397967,9.81465435 +4.94969094,-6.574054718,27.0710144,-36.29734802,0.02536131069,0.03054238297,-0.004635522142,-0.2303179353,0.0717818737,9.722005844 +4.95969094,-6.296230316,26.82210159,-37.04333496,0.01556797791,0.004868612625,-0.002214650391,-0.2246153355,0.1043654531,9.742161751 +4.96969094,-7.560173035,26.40361404,-37.1633606,2.473592758e-06,-0.0173169449,-0.0008139780257,-0.2097203881,0.1449587941,9.785895348 +4.97969094,-6.471401215,26.52723694,-37.17457581,-0.008842390031,-0.02489814535,-0.0002747424878,-0.3028791845,0.1854895651,9.777700424 +4.98969094,-7.569580078,26.13168335,-36.83876038,-0.01287231874,-0.02937872149,0.003762538079,-0.271209687,0.129128173,9.908959389 +4.99969094,-6.624336243,26.77799034,-37.80253601,-0.004194155335,-0.04173757136,0.004533623811,-0.1912932694,0.1316759884,10.00805187 +5.00969094,-6.457279205,26.93513107,-37.66145325,0.01475238521,-0.03149639815,0.003035570029,-0.2443001717,0.1331238598,9.971121788 +5.01969094,-8.210559845,25.5171814,-36.18318176,0.03028877825,-0.006620025262,-0.0008588049095,-0.2268170714,0.1420782506,9.910667419 +5.02969094,-7.385002136,26.69847679,-37.03211975,0.04052030668,0.01364735421,-0.004151983652,-0.237571016,0.158927694,9.847308159 +5.03969094,-7.442901611,26.66859436,-39.62428284,0.03705263138,0.0003825714812,-0.006458672695,-0.2139079124,0.1738214344,9.855632782 +5.04969094,-7.299869537,25.74504662,-37.57493591,0.03569614887,-0.0260367766,-0.007442372385,-0.2120704055,0.182754904,9.782229424 +5.05969094,-7.995639801,26.76731491,-36.88302612,0.03453819454,-0.03720206767,-0.00965471752,-0.2041972578,0.1758445352,9.763034821 +5.06969094,-8.423805237,27.53539658,-37.39454651,0.03948170319,-0.04894115031,-0.007673261221,-0.2060015351,0.1358015239,9.880899429 +5.07969094,-7.416149139,26.28179359,-37.30761719,0.04444842041,-0.05113239586,-0.00752402097,-0.2362748682,0.1675562263,9.965304375 +5.08969094,-8.899833679,27.21048546,-37.06517029,0.06157614291,-0.0353282243,-0.003937673289,-0.2722059488,0.193922624,9.950833321 +5.09969094,-7.559364319,26.13695717,-36.38131714,0.07159139216,-0.007121328264,-0.002865193645,-0.2545877993,0.1594867259,9.833031654 +5.10969094,-8.280281067,27.01271439,-36.4420166,0.06986922771,0.0109635517,-0.002792024519,-0.2140764892,0.1569391638,9.850317955 +5.11969094,-8.217372894,25.51366615,-36.48812866,0.06253593415,0.002094432712,-0.007235190365,-0.1947746426,0.2032549828,9.859428406 +5.12969094,-6.286819458,27.09403038,-37.36790466,0.06216876209,-0.008317800239,-0.009709886275,-0.2290931046,0.2211895436,9.904157639 +5.13969094,-8.077068329,26.05760765,-38.66372681,0.06461351365,-0.004909839481,-0.01018739119,-0.2145729065,0.2156002671,9.762316704 +5.14969094,-6.850280762,25.78923988,-38.34214783,0.05910928547,0.001064125914,-0.0116847232,-0.167883575,0.2063707858,9.694931984 +5.15969094,-7.17206955,25.34778023,-37.69958496,0.05363702774,0.01853247173,-0.01631854475,-0.156190142,0.1726144552,9.716248512 +5.16969094,-8.053226471,26.06991196,-37.59635925,0.05348046869,0.02741134167,-0.02060299739,-0.2086635679,0.2122661322,9.849188805 +5.17969094,-7.995639801,26.76731491,-36.88302612,0.053559497,0.02173338085,-0.01817750186,-0.3160272539,0.2369009852,9.909678459 +5.18969094,-7.801651001,27.60603905,-37.4009552,0.06455555558,0.02400907688,-0.01444878429,-0.3371770084,0.280346781,9.874191284 +5.19969094,-8.561702728,25.19411087,-37.22770691,0.07946416736,0.04503597319,-0.01281164959,-0.2494072318,0.2802865803,9.811919212 +5.20969094,-7.282344818,26.15469933,-37.90933228,0.08607848734,0.05437766761,-0.0133135058,-0.2424967289,0.2851865292,9.795174599 +5.21969094,-5.599964142,26.20069504,-38.83203125,0.09813603014,0.05297361314,-0.01501114666,-0.2714679539,0.2680800259,9.75396347 +5.22969094,-8.371612549,25.63021278,-36.80131531,0.09730117023,0.03567400575,-0.0161033757,-0.2618784308,0.2831623554,9.753846169 +5.23969094,-5.85345459,26.86277771,-38.11549377,0.1009470299,0.0124598816,-0.01290932298,-0.2666765749,0.3234401941,9.780581474 +5.24969094,-6.471401215,26.52723694,-37.17457581,0.1109568328,0.01610381715,-0.01498145796,-0.1893724501,0.3073857427,9.646884918 +5.25969094,-7.305873871,25.4748745,-37.09785461,0.09680746496,0.01358146127,-0.01551413164,-0.1401528865,0.2132546306,9.622281075 +5.26969094,-8.179725647,26.60138512,-37.78651428,0.07704415172,-0.02511924133,-0.01607831009,-0.2199209034,0.2536352575,9.718344688 +5.27969094,-7.139129639,26.29953575,-38.83563232,0.06522310525,-0.05784024298,-0.01338343509,-0.2406560779,0.289178431,9.901301384 +5.28969094,-7.139129639,26.29953575,-38.83563232,0.07847820967,-0.06008450687,-0.01284654625,-0.2131624073,0.3604699373,9.955468178 +5.29969094,-5.710235596,27.00761414,-39.04180908,0.09397917241,-0.03338656574,-0.01444436796,-0.2175605744,0.3692127168,9.880940437 +5.30969094,-5.710235596,27.00761414,-39.04180908,0.1072839499,-0.00890467409,-0.0123171173,-0.1554846913,0.338521868,9.983216286 +5.31969094,-8.555202484,25.86514664,-38.80158997,0.1280505955,-0.00145151047,-0.01125509851,-0.1884938926,0.3345931768,9.971286774 +5.32969094,-8.36139679,25.6354866,-36.34387207,0.1440444738,0.001271591522,-0.01606091298,-0.1713260263,0.4138234556,9.948322296 +5.33969094,-7.552864075,26.80799103,-37.9552002,0.1595576257,0.02187692188,-0.02512727119,-0.1057918295,0.3753095269,9.983222008 +5.34969094,-7.186187744,24.93988609,-37.212677,0.1767290533,0.005366987083,-0.0303742215,-0.2102942914,0.3615303338,9.934126854 +5.35969094,-7.898990631,25.95336533,-37.28318787,0.1936744601,0.02417157032,-0.02678085119,-0.1176252067,0.2720018625,10.04296494 +5.36969094,-7.449893951,25.59669495,-36.95359802,0.2405296117,0.06736565381,-0.03421706706,-0.1225322634,0.4234299064,10.36811256 +5.37969094,-8.820705414,25.98688316,-37.13090515,0.3403091133,0.08081629127,-0.03518076614,-0.107458882,0.4686262608,10.39834785 +5.38969094,-7.990322113,24.57086372,-37.64250183,0.4398308396,0.1258682907,-0.0389775075,-0.1128015518,0.5142008662,10.45117283 +5.39969094,-7.16185379,25.35305405,-37.24212646,0.5284068584,0.1784391701,-0.0449366197,-0.2035734504,0.5788798332,10.56637478 +5.40969094,-7.356647491,24.78098679,-37.50622559,0.6149481535,0.1743725836,-0.04687128216,-0.2832054794,0.6244323254,10.58554649 +5.41969094,-7.33052063,25.72922707,-38.94726562,0.7036159635,0.1237051487,-0.0392241925,-0.4429421723,0.6916079521,10.61947918 +5.42969094,-6.578952789,24.8692894,-37.51423645,0.8085047603,0.08715923876,-0.0232714992,-0.5601879358,0.8304725885,10.81384563 +5.43969094,-7.531322479,24.88698578,-38.73428345,0.938151896,0.08443415165,-0.007361578289,-0.6205343008,0.9866380692,10.99567223 +5.44969094,-8.279857635,22.61453819,-38.41836548,1.076931596,0.09234847128,0.01138667203,-0.7176730633,1.141917586,11.07694626 +5.45969094,-6.797901154,24.95243835,-40.7245636,1.21220386,0.142701149,0.01992633007,-0.5940642953,1.237163305,10.96197701 +5.46969094,-6.786567688,24.02353477,-37.60624695,1.331775427,0.2211852372,0.01148944534,-0.5794842839,1.406719089,10.83598232 +5.47969094,-6.683105469,23.21310043,-37.70143127,1.424222708,0.2731072903,0.001702721231,-0.6970831752,1.580493093,10.72273731 +5.48969094,-5.19291687,22.95544434,-39.51779175,1.489971876,0.2497478575,0.005910233129,-0.7620835304,1.679367185,10.78293896 +5.49969094,-7.543029785,22.68174744,-40.25616455,1.567991257,0.1823883057,0.01639992744,-0.7109425664,1.702432513,10.92615986 +5.50969094,-6.331970215,22.53879547,-41.01174927,1.654090762,0.1048861593,0.02590598166,-0.5987703204,1.934044361,10.9778614 +5.51969094,-6.872894287,21.11210251,-40.60383606,1.738529205,0.02646159567,0.03096565418,-0.4815279543,2.036860704,11.03164959 +5.52969094,-6.464668274,20.73433876,-42.10400391,1.831183553,-0.0371132046,0.03252334148,-0.2581321895,2.053145409,11.02364445 +5.53969094,-6.585845947,19.06935883,-43.05360413,1.945584893,-0.0588221848,0.02755689435,-0.03785359487,2.144049168,11.44700241 +5.54969094,-7.916595459,19.74729919,-42.18318176,2.122190714,-0.06661680341,-0.002210137434,-0.2162174582,2.293878794,11.10333824 +5.55969094,-7.169239044,19.15226173,-41.68467712,2.242797375,0.02907058969,0.01810688525,-0.6900045872,2.465847731,10.61191368 +5.56969094,-7.007083893,17.10767746,-42.76051331,2.365107298,0.07657755166,0.04666646942,-0.6271196008,2.857951403,11.23185539 +5.57969094,-7.567134857,15.80464458,-43.58227539,2.533013105,0.009588560089,0.06934992969,-0.6376622319,3.23949194,11.46477699 +5.58969094,-6.512111664,15.24316883,-43.23944092,2.682348967,-0.03812664747,0.1032418609,-1.011867642,3.570411205,11.33907413 +5.59969094,-6.6783638,14.81937218,-42.59846497,2.810805321,-0.02873550914,0.1526294798,-1.132974863,3.754922628,10.92838478 +5.60969094,-6.983627319,12.9884634,-44.45155334,2.896809578,0.02345578931,0.1785736531,-0.7919716239,3.861896276,10.60241222 +5.61969094,-8.335494995,11.18923187,-44.77839661,2.964149952,0.1522061974,0.1822080016,-0.3903231025,3.914002657,10.22689247 +5.62969094,-6.875339508,9.580715179,-44.20939636,3.014033556,0.314350307,0.1683944762,-0.1859156489,4.113828659,9.747583389 +5.63969094,-6.25868988,9.782049179,-44.83552551,3.032514095,0.4121232927,0.1518512666,-0.2616799474,4.384209156,9.284766197 +5.64969094,-5.253143311,8.660894394,-45.21585083,3.02897954,0.3880666196,0.1631702632,-0.4669449329,4.571393967,8.911646843 +5.65969094,-5.458652496,7.682689667,-44.84060669,2.998725176,0.1711447984,0.1893278956,-0.6494381428,4.91509676,8.661911964 +5.66969094,-4.898784637,6.919963837,-45.39804077,2.934651613,-0.1005303711,0.2150487304,-0.6185181737,5.301094055,9.058558464 +5.67969094,-6.250656128,5.120733261,-45.72488403,2.910330772,-0.2587017715,0.2318211645,-0.3392679691,5.375423908,9.396467209 +5.68969094,-5.879581451,4.056110382,-47.02351379,2.922665358,-0.2760679126,0.2243542522,-0.4822314382,5.503026485,9.01198101 +5.69969094,-7.555839539,1.547006607,-46.07824707,2.943333626,-0.2170758992,0.2124996334,-0.4549106658,5.76497364,8.254803658 +5.70969094,-7.756641388,0.7047691345,-45.86528015,2.945270777,-0.1781668216,0.206626907,-0.3903495967,5.746593952,7.337949276 +5.71969094,-7.067371368,-0.9885349274,-44.98329163,2.901698112,-0.208113566,0.1915666908,-0.1816526204,5.731616974,6.997162819 +5.72969094,-6.940868378,-1.520008087,-44.79315186,2.863831282,-0.2202323377,0.1786220968,0.2177357078,5.766057968,6.696406841 +5.73969094,-6.940868378,-1.520008087,-44.79315186,2.802832127,-0.2372323573,0.1332490295,0.4498233199,5.980806351,6.113747597 +5.74969094,-6.167266846,-3.900087357,-45.23603821,2.685050011,-0.3130146265,0.08385125548,0.2968164086,6.279119015,5.801239014 +5.75969094,-6.167266846,-3.900087357,-45.23603821,2.537742376,-0.4306736588,0.05320386589,-0.04167699441,6.515561581,5.803927898 +5.76969094,-6.593517303,-5.330215454,-46.65950012,2.41191411,-0.5239192843,0.04438148439,-0.1330732852,6.619808674,5.855363846 +5.77969094,-8.595588684,-5.950117111,-44.62696838,2.333110571,-0.5195303559,0.03090674616,-0.2117768675,6.696700573,5.642460823 +5.78969094,-8.628837585,-6.234352112,-45.36975098,2.297567368,-0.414770335,0.02381796204,-0.1566102207,6.806136608,5.84990263 +5.79969094,-7.396728516,-8.699171066,-45.80760193,2.302094221,-0.2918973565,0.008515642956,-0.2365027964,7.161421299,5.973455906 +5.80969094,-6.642562866,-9.290693283,-45.00413513,2.342935801,-0.1979716867,-0.003534507239,-0.3914331794,7.542293072,6.224736691 +5.81969094,-7.828483582,-9.00123024,-43.49595642,2.388193846,-0.1110058427,-0.01067978889,-0.2491026968,7.591664314,5.918228626 +5.82969094,-8.313922882,-9.598073959,-42.84197998,2.408067226,-0.01837301813,-0.0253147278,0.03190075234,7.624960423,5.320220947 +5.83969094,-8.342350006,-12.47961998,-43.24739075,2.358142614,0.03511781245,-0.05030852184,-0.1354587525,7.447844982,3.707296848 +5.84969094,-7.461193085,-13.20175362,-43.3506012,2.204473257,0.08235535771,-0.04534388334,-0.007207999472,7.318314552,3.282621861 +5.85969094,-7.363540649,-12.21660042,-41.58953857,2.055655241,0.08076247573,-0.0373577401,0.06377741694,7.392906189,3.514599085 +5.86969094,-7.110240936,-14.94444084,-43.68527222,1.952906013,0.0455320403,-0.03147932887,-0.04667448997,7.750877857,3.611784935 +5.87969094,-7.37494278,-15.08936119,-41.23254395,1.861353755,-0.06208071858,-0.02312056161,-0.1080982238,8.078206062,3.719119072 +5.88969094,-6.948883057,-15.72499657,-41.1882782,1.785762429,-0.1727042198,-0.0151411742,-0.1235443279,8.370152473,4.199866295 +5.89969094,-8.64748764,-16.0464344,-40.24595642,1.783481121,-0.1971811503,-0.02015489712,-0.1364535242,8.50699234,4.808917046 +5.90969094,-8.216724396,-16.54610634,-40.36398315,1.838792682,-0.07802446187,-0.03536086157,-0.06082001328,8.485049248,4.451487541 +5.91969094,-7.526954651,-17.83854485,-40.57881165,1.857732654,0.1300803274,-0.06967652589,-0.01184685621,8.531123161,3.72614336 +5.92969094,-8.303844452,-18.19350243,-39.78877258,1.814050555,0.2553527653,-0.08196844906,-0.06807902455,8.328253746,2.931307793 +5.93969094,-8.504646301,-19.03573799,-39.57580566,1.716382623,0.2538946271,-0.06764163077,0.005141078494,8.346729279,2.556385756 +5.94969094,-6.853099823,-20.07394981,-38.89517212,1.598608613,0.152466163,-0.05751124769,0.003872290719,8.425172806,2.134254694 +5.95969094,-7.664543152,-20.84735298,-38.53311157,1.460810065,0.05854824185,-0.04427852854,-0.007188765798,8.432364464,2.211527348 +5.96969094,-7.158172607,-19.83910179,-39.36903381,1.353563786,0.002514161868,-0.03926705942,0.1740697026,8.347842216,2.068900824 +5.97969094,-6.903877258,-20.76783943,-39.30352783,1.247846365,-0.0405799076,-0.04407382011,0.04491699114,8.569720268,1.811930776 +5.98969094,-7.849121094,-21.41414452,-38.33978271,1.123071313,-0.06583211571,-0.04931728542,0.1047966853,8.705016136,1.846391082 +5.99969094,-6.214603424,-22.4611454,-38.42155457,1.026422024,-0.04438321292,-0.07139761746,0.05338488892,8.847383499,2.146280289 +6.00969094,-8.489295959,-22.29530525,-36.90214539,0.9891057611,0.03876886517,-0.0867260173,-0.1419169158,9.085100174,2.551529169 +6.01969094,-7.745658875,-22.22458076,-38.43496704,0.9823154211,0.1017291918,-0.106370911,-0.1593304873,9.150740623,2.281132936 +6.02969094,-7.793209076,-23.98509407,-35.71522522,0.9616700411,0.1661918014,-0.1270233244,0.0574805215,9.073641777,2.047967672 +6.03969094,-6.944122314,-22.12397194,-37.37562561,0.9382100105,0.1993058175,-0.1547543406,0.01744905487,8.995657921,1.867096186 +6.04969094,-7.567459106,-25.06209755,-36.55479431,0.9021967649,0.1762719601,-0.1718251109,-0.1406121552,9.002772331,1.730701089 +6.05969094,-7.148822784,-23.36882973,-36.21832275,0.8519908786,0.1081412658,-0.1755056828,-0.3042657971,9.020177841,1.228797078 +6.06969094,-6.28729248,-24.36816597,-36.45439148,0.7535880208,0.05081130564,-0.1780438125,-0.07804404944,8.942026138,0.0622860454 +6.07969094,-6.615398407,-24.41227531,-37.21359253,0.618732214,0.1305778027,-0.1905168593,-0.1119370759,8.954662323,0.6950393319 +6.08969094,-6.468467712,-24.93319893,-36.10856628,0.5215619206,0.097046718,-0.2001017928,-0.1604000777,9.188315392,0.8780340552 +6.09969094,-8.278152466,-24.18106651,-36.15803528,0.4290464222,-0.01206605136,-0.1879246235,-0.4243336916,9.332118034,1.622178555 +6.10969094,-6.327850342,-25.05677986,-36.40531921,0.405993849,-0.07996894419,-0.1790645421,-0.552688241,9.564300537,2.605819941 +6.11969094,-7.102638245,-25.54419136,-35.147995,0.4912712276,-0.04473871738,-0.1838391423,-0.6350327134,9.846169472,3.193631172 +6.12969094,-6.550388336,-26.04377174,-36.79244995,0.6150547266,0.02244179323,-0.1928945929,-0.5246245861,10.11047459,2.911791801 +6.13969094,-6.669765472,-26.17630577,-34.79878235,0.6870937347,0.05554625392,-0.2030147314,-0.6148418188,9.993902206,2.150117159 +6.14969094,-5.846191406,-27.59583855,-35.61532593,0.6883915663,0.03711918741,-0.1955397874,-0.5256439447,9.68167305,1.30617404 +6.15969094,-5.604026794,-26.33164406,-35.09532166,0.6299076676,-0.03041126765,-0.1765921712,-0.3788752854,9.399337769,0.6339784861 +6.16969094,-8.064910889,-26.19928551,-34.94665527,0.5348986387,-0.09692849219,-0.1555133909,-0.3197306395,9.323526382,0.3968338072 +6.17969094,-7.614517212,-26.42174721,-34.93183899,0.4100218713,-0.1200562641,-0.1371923983,-0.2656853199,9.336148262,0.2756545842 +6.18969094,-7.253158569,-26.09340477,-33.42985535,0.2929925919,-0.1655155718,-0.119509913,-0.5380512476,9.51198101,1.004795432 +6.19969094,-7.253158569,-26.09340477,-33.42985535,0.2704642713,-0.2962380052,-0.09054698795,-0.5876882672,9.83070755,2.051366806 +6.20969094,-7.109138489,-26.21522713,-33.57411194,0.3218514025,-0.3892082572,-0.07513003796,-0.7501949668,10.15983486,1.972729325 +6.21969094,-7.109138489,-26.21522713,-33.57411194,0.3665290177,-0.3839081526,-0.07275354117,-0.8437544703,10.24689865,1.666616678 +6.22969094,-6.416275024,-26.83838844,-35.51531982,0.395961076,-0.362869978,-0.07692997903,-0.6962200403,10.24797153,1.79286015 +6.23969094,-6.924747467,-27.71418571,-35.14666748,0.4236700237,-0.2611028552,-0.08759007603,-0.5691984892,10.19368839,1.651011705 +6.24969094,-5.731018066,-26.20103264,-34.18865967,0.4270048141,-0.06775772572,-0.1009764075,-0.5052172542,10.05403423,1.090990543 +6.25969094,-5.955661774,-27.05557823,-35.04304504,0.3786482811,0.09604948014,-0.1041087434,-0.4335639477,9.900503159,0.7057359815 +6.26969094,-6.411750793,-27.77080727,-32.70195007,0.3053478301,0.1541505158,-0.09603412449,-0.3152265251,9.683514595,0.2176062912 +6.27969094,-7.398361206,-28.83907127,-32.47116089,0.2221469581,0.1793556064,-0.09244065732,-0.3450230062,9.64139843,0.03580936044 +6.28969094,-6.884685516,-27.42643929,-34.09892273,0.1321454048,0.1524278224,-0.07998012006,-0.4099330306,9.661970139,0.1432226747 +6.29969094,-5.973186493,-27.46523094,-34.70864868,0.06124486029,0.006182244048,-0.05034258217,-0.6426629424,9.841538429,0.8780437112 +6.30969094,-5.79510498,-27.56947136,-33.32810974,0.02109704167,-0.1478120834,-0.01445469074,-0.8502721786,10.12072086,1.450038433 +6.31969094,-6.100177765,-27.33461952,-33.80197144,0.02866151184,-0.1757359207,0.02049909532,-1.059395909,10.29624081,1.798726797 +6.32969094,-5.74773407,-26.87734032,-33.07220459,0.07471268624,-0.1548956931,0.04094931856,-0.6283707023,10.32818985,2.137936592 +6.33969094,-7.230003357,-28.54772377,-32.64486694,0.1274963319,-0.03663658351,0.03674643859,-0.6844533682,10.25029659,1.625554085 +6.34969094,-5.938632965,-27.04678917,-34.28063965,0.1503894925,0.09812860191,0.0308074709,-0.5885108113,10.05522156,1.04837501 +6.35969094,-6.94877243,-28.79487801,-33.2383728,0.141530484,0.1597932577,0.0212403778,-0.4865935445,9.90356636,0.6102195382 +6.36969094,-7.224311829,-27.61003304,-35.0007782,0.09064454585,0.1466042995,0.02438522689,-0.4241021872,9.743000031,0.1843403578 +6.37969094,-7.368019104,-28.15572929,-32.97767639,0.01164131891,0.07730738074,0.03112574108,-0.3331443667,9.599993706,0.2176162302 +6.38969094,-6.503890991,-28.88665199,-33.84329224,-0.05099488795,-0.004641776904,0.03543535247,-0.252383858,9.633149147,0.59193331 +6.39969094,-5.962162018,-27.726614,-33.46916199,-0.08003503084,-0.1150991321,0.04340262339,-0.4005373716,9.868338585,1.204803705 +6.40969094,-6.21163559,-29.39518929,-33.1973114,-0.07899376005,-0.1858911067,0.05741436034,-0.7340079546,10.20933914,1.619800568 +6.41969094,-5.45368576,-26.8508091,-33.83781433,-0.05616622418,-0.1541773379,0.07039058954,-1.079650044,10.29853725,1.836036086 +6.42969094,-5.764762878,-26.88612938,-33.83460999,-0.01166790351,-0.04989422113,0.08693824708,-0.7219535708,10.20710182,1.849469781 +6.43969094,-7.55210495,-28.321661,-33.88114929,0.04423505068,0.07162962109,0.07621535659,-0.5237618089,10.04287529,1.466649294 +6.44969094,-6.139930725,-28.28988838,-32.97085571,0.06462156773,0.1659917086,0.06487458199,-0.2693444788,9.804961205,1.003296137 +6.45969094,-6.900909424,-27.70188332,-34.07929993,0.03726745769,0.1448267251,0.05426616967,-0.3218414485,9.69636631,0.6625964046 +6.46969094,-7.200469971,-27.59772682,-33.9334259,-0.01479796506,0.08245497942,0.05262492597,-0.2694553733,9.55301857,0.2537037134 +6.47969094,-7.517551422,-27.90321922,-33.45314026,-0.08166196942,0.04908277839,0.05063142255,-0.2769313157,9.516850471,0.3263844252 +6.48969094,-6.802764893,-26.31586647,-33.41503906,-0.129857108,-0.02138678916,0.04803046212,-0.2548482716,9.549121857,0.8685886264 +6.49969094,-6.453117371,-28.19276619,-33.43490601,-0.1322575063,-0.1084113792,0.04938947782,-0.3309167624,9.744350433,1.420860767 +6.50969094,-6.421966553,-27.77607918,-33.15940857,-0.1049313694,-0.1264381558,0.046568349,-0.5277194381,9.95321846,1.722908378 +6.51969094,-5.795597076,-27.97033501,-32.23129272,-0.05866263807,-0.1087544486,0.05005582422,-0.7864149809,10.11928844,1.995217443 +6.52969094,-6.756572723,-28.49122429,-32.34472656,-0.003624816425,-0.06324600428,0.05518781021,-0.6161818504,10.06101227,1.923961639 +6.53969094,-7.125358582,-26.49067116,-33.55448914,0.04583896324,0.0206390284,0.04705470428,-0.5417268872,9.938585281,1.376885653 +6.54969094,-7.384243011,-28.43117332,-32.95803833,0.05019680783,0.09256529063,0.04150801897,-0.4252683222,9.765837669,0.8409014344 +6.55969094,-4.91444397,-28.69246864,-32.33450317,0.01591055095,0.1238697395,0.0377532728,-0.2604678869,9.612735748,0.5489522815 +6.56969094,-7.374832153,-28.15924644,-33.28263855,-0.03896613047,0.1192587167,0.03361805156,-0.2545866966,9.425396919,0.2876189351 +6.57969094,-7.552417755,-27.65414238,-35.75997925,-0.115347065,0.05623481423,0.03676060215,-0.2348897308,9.471121788,0.4286818206 +6.58969094,-5.604522705,-26.73250771,-33.9985199,-0.1771036536,-0.04492631555,0.04680872709,-0.2702373564,9.532647133,0.8573217988 +6.59969094,-6.436088562,-28.18397713,-32.67250061,-0.2030239701,-0.1211520582,0.05939222872,-0.3787891567,9.717459679,1.445369482 +6.60969094,-6.883880615,-27.69309425,-33.31689453,-0.1903610229,-0.1436105818,0.06776238978,-0.558552444,9.869405746,1.720998168 +6.61969094,-6.573608398,-27.391119,-34.10212708,-0.1610814184,-0.08932308853,0.07173191011,-0.6367165446,9.939030647,1.911174417 +6.62969094,-7.794269562,-29.58585548,-34.40113831,-0.1116924211,0.006465011742,0.06918103993,-0.5721738338,10.05697632,1.982402325 +6.63969094,-6.814472198,-28.52110863,-34.93688965,-0.06562864035,0.08441458642,0.07163098454,-0.5598268509,10.11370468,1.724575639 +6.64969094,-8.08763504,-27.1457653,-33.35314941,-0.04420860112,0.1142552868,0.07546826452,-0.5372830629,10.04286575,1.392329931 +6.65969094,-6.123214722,-27.6135807,-34.08731079,-0.0459260419,0.1182540208,0.07836209983,-0.4197402894,9.951440811,1.140536189 +6.66969094,-6.123214722,-27.6135807,-34.08731079,-0.05532723293,0.1020412371,0.07906202972,-0.3231778741,9.863641739,0.9318038821 +6.67969094,-6.894096375,-27.69836617,-33.77432251,-0.07831289619,0.07124064118,0.07983452082,-0.3302566707,9.701453209,0.7482259274 +6.68969094,-6.894096375,-27.69836617,-33.77432251,-0.1181097478,0.01705702953,0.08364310861,-0.3872544169,9.722757339,1.051870346 +6.69969094,-7.967948914,-27.68075752,-33.46795654,-0.1227426454,-0.05970519036,0.08897811919,-0.277425617,9.779336929,1.489115953 +6.70969094,-7.40127182,-28.43996239,-33.72044373,-0.0898701027,-0.0761885494,0.08000918478,-0.3281622827,9.89973259,1.708828926 +6.71969094,-7.944419861,-27.00093269,-34.2794342,-0.05175863951,-0.01853408106,0.06382262707,-0.288962692,9.960288048,1.617121935 +6.72969094,-7.350498199,-27.74607658,-33.31208801,-0.01620602794,0.05397556722,0.04388963431,-0.2301783711,9.98184967,1.649990201 +6.73969094,-6.850627899,-27.40886116,-32.57411194,0.02669902518,0.1087433919,0.02283645235,-0.2193958163,9.986762047,1.493821502 +6.74969094,-6.276763916,-25.03041267,-34.11810303,0.05053491145,0.1415589154,0.006427761633,-0.186055243,9.888465881,1.22418046 +6.75969094,-5.76556778,-26.61947441,-34.61665344,0.05468331277,0.1356552541,-0.004396722652,-0.2582173944,9.880655289,0.8945252299 +6.76969094,-6.412559509,-27.5041523,-33.48397827,0.02108596638,0.1019207016,-0.00492911879,-0.1788927615,9.746808052,0.8333583474 +6.77969094,-6.438995361,-27.78486824,-33.92181396,-0.01197361574,0.05054997653,-0.004860650748,-0.214070335,9.705836296,0.8822153211 +6.78969094,-7.23532486,-26.35127068,-31.88542175,-0.04006820545,0.009633807465,-0.007401582785,-0.2310826182,9.749238968,0.9326863885 +6.79969094,-5.922412872,-26.77134514,-34.30026245,-0.05779248476,-0.005850811489,-0.01066414267,-0.3249489665,9.765732765,1.428793669 +6.80969094,-6.624382019,-28.08500481,-34.51049805,-0.03070151433,-0.02131212316,-0.01599514671,-0.3499325216,9.840603828,1.784139752 +6.81969094,-7.216690063,-27.87317085,-33.9138031,0.01399721112,0.04281678796,-0.02650093473,-0.3876112998,10.02012634,1.671920061 +6.82969094,-7.334274292,-27.47063255,-33.33171082,0.03933909908,0.1012582257,-0.03255970404,-0.4020133018,9.993823051,1.556693912 +6.83969094,-6.422775269,-27.50942421,-33.94143677,0.04864115268,0.1026042551,-0.03411246836,-0.3927852213,9.927248001,1.404534221 +6.84969094,-6.099685669,-26.93375587,-34.89878845,0.04725361243,0.08464277536,-0.02633284219,-0.3983494043,9.882896423,1.130617261 +6.85969094,-6.709514618,-27.13157463,-33.96768188,0.03037458286,0.05510923266,-0.02556121349,-0.2641792893,9.814121246,1.064528465 +6.86969094,-8.128997803,-27.56772423,-34.08610535,0.01483618375,0.003363851458,-0.02506784908,-0.187411204,9.8035326,1.080051899 +6.87969094,-7.206474304,-27.86789894,-33.4563446,-0.002668799367,-0.01789128594,-0.02890080027,-0.320317775,9.793665886,1.104911685 +6.88969094,-6.774410248,-28.23335838,-33.88916016,-0.01878705993,-0.02112191916,-0.02776563354,-0.4035662711,9.821204185,1.327342987 +6.89969094,-5.018215179,-27.21451378,-34.1181488,-0.02795302868,-0.03217923641,-0.01834156923,-0.5014348626,9.811349869,1.555790424 +6.90969094,-7.20728302,-27.60124397,-34.2383728,-0.008743250743,-0.01979034208,-0.01190102287,-0.58278054,9.913967133,1.847940207 +6.91969094,-7.614204407,-27.08926582,-33.05297852,0.02492422983,0.0008906451985,-0.00287973159,-0.5595668554,10.02017021,1.774408937 +6.92969094,-7.233718872,-27.88195992,-34.6762085,0.04376917332,0.04712672532,-4.605692811e-05,-0.4242864549,9.926309586,1.59119153 +6.93969094,-6.364692688,-26.41115761,-34.3249054,0.05611082911,0.08474594355,0.001481917687,-0.3794151247,9.889209747,1.309559703 +6.94969094,-6.492179871,-26.68140984,-32.32142639,0.04988059774,0.0568838492,0.004740366712,-0.3320913613,9.857626915,1.161911249 +6.95969094,-6.750072479,-27.82018852,-33.91860962,0.03539325669,0.001818951452,0.01166409627,-0.2974179685,9.810185432,1.090349078 +6.96969094,-7.654762268,-27.77787971,-33.00390625,0.01885271445,-0.02488981374,0.01005619578,-0.2859608829,9.783588409,1.121622562 +6.97969094,-6.388221741,-27.09098244,-33.51342773,0.01086804736,-0.04192594439,0.01164975017,-0.3405780494,9.779755592,1.130196571 +6.98969094,-8.289241791,-27.72134972,-33.92221069,-0.001383066177,-0.04618999362,0.01165413111,-0.3816172481,9.75190258,1.178019404 +6.99969094,-7.193969727,-26.92669106,-35.50730896,-0.0115212854,-0.04669833183,0.01270983368,-0.328725487,9.718396187,1.172267914 +7.00969094,-7.662380219,-27.5147419,-34.09091187,-0.02117509581,-0.01347930171,0.006302303169,-0.3198303878,9.751620293,1.187064886 +7.01969094,-6.630390167,-28.35517693,-34.03343201,-0.03068202361,0.03722409159,-0.002729954664,-0.2822543681,9.780727386,1.300859928 +7.02969094,-5.79510498,-27.56947136,-33.32810974,-0.03604897112,0.08230222762,-0.009162247181,-0.2794805765,9.743423462,1.304435372 +7.03969094,-5.177967072,-26.96727562,-35.05105591,-0.03218114376,0.0754532665,-0.01454768702,-0.2370035499,9.778010368,1.412263632 +7.04969094,-7.679096222,-28.19104958,-32.97447205,-0.01724203117,0.04264059663,-0.01922232471,-0.3165919483,9.784212112,1.443166137 +7.05969094,-6.45602417,-27.7936573,-34.68421936,0.001956798136,0.03951912373,-0.01594966836,-0.4240784943,9.821266174,1.450664759 +7.06969094,-6.479553223,-28.47348213,-33.8727417,0.008233220316,0.02018114552,-0.008499246091,-0.3593118489,9.874123573,1.357286453 +7.07969094,-6.767910004,-27.56232262,-35.46304321,0.009127783589,-0.001021089964,-0.004468719475,-0.3498756886,9.895945549,1.244823337 +7.08969094,-6.692485809,-27.12278557,-33.20527649,-0.0003577759489,0.005629235413,-0.005507835187,-0.3065412641,9.852435112,1.167840958 +7.09969094,-6.924747467,-27.71418571,-35.14666748,-0.01182218362,0.001805877313,-0.01178541407,-0.3422503471,9.787656784,1.151168823 +7.10969094,-7.233718872,-27.88195992,-34.6762085,-0.02720049769,-0.03890308738,-0.006950148847,-0.3069644272,9.728578568,1.21667695 +7.11969094,-7.233718872,-27.88195992,-34.6762085,-0.03472160175,-0.08361277729,-0.002064191969,-0.3741668761,9.828631401,1.381771445 +7.12969094,-6.566795349,-27.38760185,-33.79716492,-0.02443219163,-0.1106951907,0.003613332286,-0.414747417,9.909883499,1.478235364 +7.13969094,-6.566795349,-27.38760185,-33.79716492,-0.009322667494,-0.08561228961,0.001425060909,-0.3364252448,9.950445175,1.520546079 +7.14969094,-6.894901276,-27.4317112,-34.55636597,0.01297305804,-0.02708912455,-0.01052638143,-0.2902875245,9.916857719,1.579128742 +7.15969094,-6.756885529,-27.82370567,-34.22355652,0.03051350266,0.02103862539,-0.02036703378,-0.3130198121,9.968650818,1.473050833 +7.16969094,-5.754547119,-26.88085747,-33.37718201,0.0321297273,0.03274782375,-0.02557382733,-0.2421809435,9.896290779,1.491528034 +7.17969094,-6.583019257,-27.66304588,-33.77752686,0.03285422921,0.03309166431,-0.0193908941,-0.3465489447,10.00986385,1.124078274 +7.18969094,-6.140735626,-28.02323341,-33.75289917,0.006991864182,0.03043413348,-0.01644375175,-0.3370473981,9.83026123,1.150320888 +7.19969094,-6.395530701,-27.49536324,-32.72157288,-0.008524950594,-0.003374490887,-0.008978554048,-0.3312490284,9.773057938,1.049988508 +7.20969094,-6.388221741,-27.09098244,-33.51342773,-0.02062888816,-0.03278618306,-0.003952762112,-0.3668813407,9.767832756,1.011610508 +7.21969094,-7.223503113,-27.876688,-34.21875,-0.04249082133,-0.0584564656,0.0001669055782,-0.3540610373,9.73944664,1.033432245 +7.22969094,-6.600048065,-27.67183495,-34.53993225,-0.05800957978,-0.07085700333,0.003791878931,-0.2489045262,9.71612072,1.233810782 +7.23969094,-5.110660553,-26.6654644,-32.78346252,-0.05563404411,-0.05858933181,-0.002280948451,-0.3057807386,9.793344498,1.323033094 +7.24969094,-7.309940338,-27.05746269,-33.36116028,-0.04836731032,-0.03795132041,-0.005654682405,-0.3357924521,10.0036459,1.809297442 +7.25969094,-6.61416626,-28.07973289,-34.05305481,-0.01423756406,0.006586294156,-0.01188713871,-0.3840130866,10.07378864,1.766835213 +7.26969094,-7.343997955,-27.07504082,-34.88597107,0.01365469862,0.07387618721,-0.01604680531,-0.4060422778,9.988751411,1.480861068 +7.27969094,-7.777240753,-29.57706642,-33.63873291,0.02453222126,0.1045350581,-0.01727712527,-0.4209952354,9.866679192,1.274744511 +7.28969094,-5.620742798,-27.00795174,-33.97889709,0.01804178953,0.06112404913,-0.005017064512,-0.3968960345,9.788542747,1.140993834 +7.29969094,-7.377742767,-27.76013756,-34.53192139,0.0008723521605,0.004055330064,0.007162726019,-0.3800239861,9.755570412,1.057734847 +7.30969094,-7.672595978,-27.52001381,-34.5483551,-0.01781889051,-0.02779907547,0.01464441419,-0.3330850601,9.757546425,1.02295351 +7.31969094,-7.233718872,-27.88195992,-34.6762085,-0.03380134702,-0.05228021741,0.01153825596,-0.3335008323,9.738282204,1.145313025 +7.32969094,-7.320156097,-27.0627346,-33.81858826,-0.04232557118,-0.0432979241,0.008302789181,-0.3039984107,9.838270187,1.414606929 +7.33969094,-7.190254211,-27.59245491,-33.47596741,-0.02935238183,-0.006282945164,-0.002957660705,-0.3414013386,9.905882835,1.408320904 +7.34969094,-7.477802277,-26.94795036,-34.28424072,-0.02123269252,0.01367532462,-0.01296658069,-0.4141909778,9.940719604,1.555158734 +7.35969094,-7.60969162,-29.01906395,-34.59448242,-0.001434227452,0.0439722389,-0.01752397791,-0.3848665655,9.939268112,1.577160716 +7.36969094,-6.877067566,-27.6895771,-33.01191711,0.01578740031,0.06906723976,-0.01977552287,-0.4487940073,9.894701958,1.458930373 +7.37969094,-7.281089783,-28.57409096,-34.93208313,0.01979922131,0.06542313844,-0.01498976164,-0.4290300012,9.838287354,1.364023685 +7.38969094,-8.161937714,-28.51947594,-32.95002747,0.01419570576,0.05275142938,-0.007790469099,-0.4366999269,9.831308365,1.314301133 +7.39969094,-5.692451477,-28.11325264,-34.20532227,0.006470880471,0.05087926984,-0.0004649830516,-0.4752577841,9.859540939,1.219009399 +7.40969094,-7.608882904,-29.28571892,-33.81245422,-0.007653800771,0.03567602485,0.007800285239,-0.4810006022,9.848795891,1.221743464 +7.41969094,-7.368019104,-28.15572929,-32.97767639,-0.01515169907,0.01380138658,0.012325285,-0.4593022466,9.826662064,1.251535416 +7.42969094,-7.377742767,-27.76013756,-34.53192139,-0.01542961784,0.001022662269,0.0169789847,-0.4755968451,9.779748917,1.37317872 +7.43969094,-7.240531921,-27.88547707,-34.9811554,-0.001768577844,-0.009118218906,0.0176607091,-0.4284687936,9.814570427,1.428025246 +7.44969094,-5.634056091,-27.68250465,-32.70996094,0.01253924798,-0.02427603118,0.01522912644,-0.4111814201,9.89552021,1.414922833 +7.45969094,-5.822349548,-27.58353233,-34.54794312,0.016501192,-0.02373738401,0.01423248276,-0.3876463473,9.8126688,1.245267987 +7.46969094,-4.953323364,-26.11273003,-34.19667053,0.01277748775,-0.00871932786,0.006232345942,-0.3258767724,9.745279312,1.207792163 +7.47969094,-8.162742615,-28.25282097,-33.73207092,0.008416143246,0.01032425929,-0.002889809664,-0.3021071851,9.780869484,1.24430275 +7.48969094,-7.350498199,-27.74607658,-33.31208801,0.002876439132,0.02547182329,-0.01436623745,-0.2963513732,9.847862244,1.273426533 +7.49969094,-7.305427551,-28.98726082,-34.90263367,-0.002952933777,0.0436315015,-0.02437200397,-0.324506253,9.83515358,1.275908351 +7.50969094,-6.436088562,-28.18397713,-32.67250061,-0.005305088125,0.0420396477,-0.02915145457,-0.347178787,9.725076675,1.296135187 +7.51969094,-6.781223297,-28.23687553,-34.19410706,-0.002636422403,0.03410620987,-0.03221314773,-0.3757326901,9.717552185,1.36000824 +7.52969094,-5.242053986,-28.33571434,-34.19050598,0.0007457425818,-0.01242156141,-0.02688090876,-0.4411390722,9.801218033,1.417131782 +7.53969094,-6.767910004,-27.56232262,-35.46304321,-0.001384953968,-0.05899711698,-0.01799323969,-0.4971121252,9.86701107,1.508045077 +7.54969094,-6.147857666,-27.35922813,-35.93670654,0.005357009359,-0.04358646274,-0.01161833853,-0.4639644921,9.821174622,1.404155374 +7.55969094,-4.976852417,-26.79255486,-33.38519287,0.0151279429,0.0234514568,-0.01500775106,-0.3858245611,9.831782341,1.389816284 +7.56969094,-7.960639954,-27.27637672,-34.2598114,0.0150560895,0.07433521748,-0.02114808559,-0.4165942073,9.863568306,1.26608634 +7.57969094,-7.960639954,-27.27637672,-34.2598114,-0.00113414228,0.0747378394,-0.01512921415,-0.4706613719,9.822451591,1.127860188 +7.58969094,-6.924438477,-28.38170815,-33.26782227,-0.01949587092,0.04672278464,-0.004416399635,-0.445856601,9.810827255,1.171207547 +7.59969094,-6.924438477,-28.38170815,-33.26782227,-0.02355873771,0.02254176326,0.001991390716,-0.4345932901,9.865891457,1.256605029 +7.60969094,-7.360713959,-27.7513485,-33.76951599,-0.01876131445,-0.01338596083,0.01086175442,-0.4302533865,9.892401695,1.399819732 +7.61969094,-6.099372864,-27.60127449,-33.01992798,-0.01351431571,-0.02890297584,0.01611890458,-0.4486133754,9.895947456,1.357090831 +7.62969094,-6.023464203,-27.7582531,-36.21383667,-0.006224627607,-0.03095378913,0.02272274345,-0.393653661,9.873376846,1.453101158 +7.63969094,-4.95381546,-26.51359367,-33.09985352,0.005430291407,-0.03075273894,0.02272888087,-0.3597887158,9.917237282,1.505781412 +7.64969094,-7.662380219,-27.5147419,-34.09091187,0.01662251353,-0.01261894777,0.02528715879,-0.3409255147,9.983776093,1.455637813 +7.65969094,-5.956157684,-27.45644188,-33.94624329,0.02580481023,0.01031739917,0.02337107435,-0.3538760841,9.961380005,1.392374516 +7.66969094,-5.945941925,-27.45116997,-33.48878479,0.02733973414,0.02243147232,0.02270804159,-0.3228459656,9.875902176,1.254824042 +7.67969094,-6.699794769,-27.52716637,-32.41342163,0.01501560863,0.04744583368,0.0235953778,-0.2549906671,9.883605957,1.205357552 +7.68969094,-5.870212555,-28.67652702,-33.70703125,-0.004503405653,0.07281056046,0.02109043114,-0.2139221281,9.83890152,1.22555697 +7.69969094,-6.106185913,-27.60479164,-33.3249054,-0.02125325799,0.07326879352,0.01964235678,-0.3086723089,9.874782562,1.160287142 +7.70969094,-5.586685181,-26.99037361,-32.4540863,-0.03068202361,0.07159326226,0.0173430033,-0.2877425849,9.871712685,1.175171494 +7.71969094,-4.659275055,-26.08619881,-34.96228027,-0.02949244902,0.07909663022,0.009707890451,-0.2861399055,9.862574577,1.186464429 +7.72969094,-6.55688858,-26.71480751,-35.21856689,-0.03317952901,0.09336777031,0.006730064284,-0.2911436856,9.858118057,1.322293639 +7.73969094,-5.501056671,-27.54294014,-34.09371948,-0.02859684452,0.08231816441,0.006812664215,-0.3232996762,9.870845795,1.490914941 +7.74969094,-6.781223297,-28.23687553,-34.19410706,-0.02269191667,0.05310622603,0.01530707069,-0.4537217319,9.846877098,1.393212676 +7.75969094,-6.124019623,-27.34692574,-34.86933899,-0.0262016058,0.02659603208,0.02568925545,-0.4360646009,9.820919991,1.411521673 +7.76969094,-5.696960449,-26.18345451,-32.66384888,-0.01927095279,0.02992433496,0.02766008116,-0.3731211722,9.859240532,1.493628025 +7.77969094,-6.423088074,-26.84190559,-35.82026672,-0.006756332237,0.06467999518,0.02580158599,-0.3479112685,9.877393723,1.410155177 +7.78969094,-6.002719879,-28.41522789,-33.42008972,-0.008050819859,0.1007210612,0.02127013728,-0.3521136343,9.825234413,1.321725845 +7.79969094,-7.713153839,-28.2086277,-34.49928284,-0.008586347103,0.1213297322,0.01726247743,-0.3854017854,9.753717422,1.385715365 +7.80969094,-6.133430481,-27.61885262,-34.54473877,0.006605016999,0.1322527677,0.01431554556,-0.3959612548,9.918157578,1.32136023 +7.81969094,-5.668609619,-28.10094643,-33.13796997,0.01407197956,0.1031682789,0.02043901943,-0.3845593333,9.779145241,1.264665008 +7.82969094,-7.165916443,-27.17928505,-33.50541687,0.01219318714,0.0370747447,0.02418522909,-0.3916911483,9.846901894,1.245322227 +7.83969094,-6.130828857,-27.35043907,-35.17430115,0.009833314456,0.002732901601,0.03058625944,-0.3888401389,9.822085381,1.323127627 +7.84969094,-6.675769806,-26.44647789,-34.32170105,0.01811700687,-0.005176926032,0.03133744374,-0.3480463326,9.801818848,1.435973644 +7.85969094,-6.683078766,-26.85085869,-33.52984619,0.0284155868,-0.004169987515,0.03186465427,-0.3630813062,9.866709709,1.449119687 +7.86969094,-5.778076172,-27.5606823,-32.56570435,0.03512119129,-0.008301069029,0.02844945155,-0.3229779601,9.843298912,1.415961981 +7.87969094,-4.823112488,-27.30996895,-31.97518921,0.0376309827,0.01536973473,0.02377301455,-0.2518242002,9.83313942,1.337736964 +7.88969094,-6.597137451,-28.07094383,-33.29064941,0.03478628397,0.03389872238,0.01552080922,-0.2680309415,9.835711479,1.253291488 +7.89969094,-6.525432587,-26.96564293,-33.06419373,0.0338845104,0.04535847902,0.006745200139,-0.2180101275,9.819023132,1.17667377 +7.90969094,-5.127689362,-26.67425346,-33.54586792,0.03140550107,0.03659430891,-0.001788613503,-0.1975850165,9.744873047,1.114180207 +7.91969094,-5.986499786,-28.13978386,-33.43971252,0.02867707983,0.01808444411,-0.009790910408,-0.2553076744,9.766321182,1.183923244 +7.92969094,-6.099372864,-27.60127449,-33.01992798,0.03164083511,-0.009051376022,-0.01414498687,-0.2725343108,9.800502777,1.266273379 +7.93969094,-7.190254211,-27.59245491,-33.47596741,0.03399694338,-0.03510866314,-0.01080191508,-0.3454096317,9.859103203,1.313657284 +7.94969094,-6.692485809,-27.12278557,-33.20527649,0.03248399124,-0.05040859431,-0.009788624942,-0.3541353345,9.83678627,1.314404488 +7.95969094,-5.701858521,-28.38517952,-33.88075256,0.02853907272,-0.04782148451,-0.009216323495,-0.3199444115,9.823339462,1.327843428 +7.96969094,-7.233718872,-27.88195992,-34.6762085,0.02739499882,-0.0317414254,-0.01145336591,-0.3068087101,9.891061783,1.347099781 +7.97969094,-5.948848724,-27.05206108,-34.73809814,0.02311804891,0.002707405714,-0.01562085561,-0.3483853638,9.85543251,1.401388407 +7.98969094,-6.965305328,-28.40279961,-35.09759521,0.02524874732,0.03096579574,-0.01465842687,-0.391481787,9.854180336,1.335046649 +7.99969094,-6.503890991,-28.88665199,-33.84329224,0.01820404828,0.03465368971,-0.005479752086,-0.3856081367,9.825998306,1.251428127 +8.00969094,-6.600048065,-27.67183495,-34.53993225,0.007291789167,0.007053038105,0.001987132709,-0.3335992694,9.810012817,1.201320887 +8.01969094,-6.163772583,-28.3021946,-34.03823853,-0.004180718213,-0.01456182078,0.01051915064,-0.3299972117,9.774536133,1.128297806 +8.02969094,-6.740352631,-28.21578026,-32.36434937,-0.01397203282,-0.03909219801,0.01338874549,-0.2939029634,9.800624847,1.181418419 +8.03969094,-6.740352631,-28.21578026,-32.36434937,-0.01755905338,-0.06164037436,0.01571976021,-0.3166171908,9.827580452,1.220766783 +8.04969094,-6.462524414,-28.46469307,-33.1103363,-0.02299508452,-0.07050529122,0.0169789847,-0.3287492394,9.846059799,1.265481591 +8.05969094,-6.462524414,-28.46469307,-33.1103363,-0.02086721361,-0.05611816049,0.01313410886,-0.3127599657,9.857430458,1.293400407 +8.06969094,-7.729377747,-28.48407173,-34.47964478,-0.01981415227,-0.02539752796,0.006699037272,-0.2972289622,9.890842438,1.254536748 +8.07969094,-7.350498199,-27.74607658,-33.31208801,-0.01718049124,0.008935067803,-0.003795302706,-0.2668244243,9.924371719,1.326421261 +8.08969094,-5.829654694,-27.98791313,-33.75610352,-0.009907738306,0.0356521979,-0.01852735132,-0.2779456675,9.923431396,1.423228025 +8.09969094,-7.517551422,-27.90321922,-33.45314026,0.006071233191,0.05359268934,-0.02314954251,-0.3708066344,9.927910805,1.405539036 +8.10969094,-6.399246216,-26.82959938,-34.75291443,0.01424688939,0.05802699178,-0.02333005331,-0.3841352463,9.899069786,1.362089515 +8.11969094,-6.709514618,-27.13157463,-33.96768188,0.01599690691,0.02361442149,-0.01315881126,-0.3848026097,9.839741707,1.206048846 +8.12969094,-4.843856812,-26.65299416,-34.76893616,0.002254735678,-0.01309334487,-0.003706273623,-0.3823072016,9.808428764,1.156294227 +8.13969094,-6.504203796,-28.21913338,-35.72212219,-0.0131037822,-0.02041620202,0.002272384241,-0.3847629428,9.844836235,1.307581306 +8.14969094,-6.73985672,-27.81491661,-33.46115112,-0.0191719532,-0.008650093339,0.006325504277,-0.3688550293,9.877473831,1.353151679 +8.15969094,-6.791439056,-28.24214745,-34.65156555,-0.01912099682,0.009797222912,0.006641102489,-0.3635028005,9.896050453,1.36118114 +8.16969094,-5.996219635,-27.74419212,-34.99397278,-0.01667619683,0.02679310925,0.003280967008,-0.3678926826,9.887942314,1.416732907 +8.17969094,-6.496582031,-28.48227119,-34.63514709,-0.004689696245,0.06497299671,0.005499317776,-0.3481041789,9.858240128,1.296657085 +8.18969094,-6.429588318,-27.51294136,-34.24638367,-0.004146645777,0.07617202401,0.001241869759,-0.3353248835,9.83586216,1.141688943 +8.19969094,-5.587493896,-26.72371864,-33.2361145,-0.01000920963,0.02543907054,0.004583748057,-0.3017803431,9.841454506,1.160948873 +8.20969094,-6.705497742,-29.46223259,-34.41235352,-0.01565810107,-0.03551977128,0.01044568978,-0.3462574482,9.866422653,1.207236767 +8.21969094,-6.877872467,-27.42292213,-33.79396057,-0.01856961846,-0.07280281931,0.01563544571,-0.3439301848,9.821113586,1.283706784 +8.22969094,-5.754547119,-26.88085747,-33.37718201,-0.01776750758,-0.05113984644,0.01072100736,-0.3152393103,9.849270821,1.368287325 +8.23969094,-6.790630341,-28.50880241,-33.86953735,-0.007758837193,0.0131536331,0.00110917585,-0.2948393226,9.888804436,1.375244021 +8.24969094,-6.883880615,-27.69309425,-33.31689453,0.0006529949605,0.05003567785,-0.007681441959,-0.3519371152,9.929426193,1.400822401 +8.25969094,-6.733852386,-27.54474449,-33.93823242,0.006397298537,0.0646423623,-0.01796964183,-0.3227308393,9.871684074,1.478527665 +8.26969094,-6.389030457,-26.82432747,-34.29545593,0.01365157124,0.08304973692,-0.02444793843,-0.285761565,9.80264473,1.286781788 +8.27969094,-5.979190826,-27.73540306,-34.23156738,0.01110325288,0.1031337157,-0.02889024839,-0.3004356325,9.809235573,1.281821251 +8.28969094,-7.199661255,-27.86438179,-33.15139771,0.01459526736,0.09770360589,-0.02966837026,-0.3339990079,9.804094315,1.184403181 +8.29969094,-6.924930573,-28.78257179,-32.17100525,0.001203035004,0.04889506102,-0.0212808568,-0.4237344265,9.790711403,1.148083925 +8.30969094,-6.60735321,-28.07621574,-33.74809265,-0.009375064634,0.01187157445,-0.01253232919,-0.3896429539,9.781116486,1.272933602 +8.31969094,-7.638046265,-27.10157204,-34.12036133,-0.00883369986,-0.007211517543,-0.005917307455,-0.4066072404,9.739402771,1.226894975 +8.32969094,-7.719966888,-28.21214485,-34.804245,-0.00851373747,-0.04851602763,0.003152085003,-0.4191360176,9.802324295,1.162682176 +8.33969094,-6.123214722,-27.6135807,-34.08731079,-0.01866781525,-0.08298770338,0.004629021045,-0.3646979332,9.822608948,1.243217111 +8.34969094,-6.573608398,-27.391119,-34.10212708,-0.01661939919,-0.05272687972,-0.001723809517,-0.3448917866,9.821504593,1.550055861 +8.35969094,-5.922412872,-26.77134514,-34.30026245,0.01482299808,0.01570285112,-0.01347478852,-0.4482457042,9.877696037,1.567859054 +8.36969094,-5.51127243,-27.54821205,-34.55114746,0.0364352949,0.05439424515,-0.01533631422,-0.5011907816,9.884627342,1.447217703 +8.37969094,-6.45602417,-27.7936573,-34.68421936,0.03358461335,0.05259715021,-0.007164225448,-0.4079911113,9.869935989,1.386832952 +8.38969094,-5.282611847,-29.02432823,-34.14143372,0.03009070083,0.02654754184,-0.0008480702527,-0.3597754538,9.862734795,1.254638791 +8.39969094,-6.476646423,-28.87259102,-32.62342834,0.02441980317,0.01135490555,0.004194807727,-0.3759404719,9.843818665,1.162875891 +8.40969094,-5.877025604,-28.68004417,-34.01199341,0.01330524031,-0.007301305421,0.01035590097,-0.272282958,9.803807259,1.169050694 +8.41969094,-7.271369934,-28.96968269,-33.37782288,0.004843432456,-0.02468059771,0.0119635351,-0.2605700195,9.832016945,1.192519903 +8.42969094,-6.976016998,-28.80893898,-34.45822144,-0.006202861667,-0.0435533002,0.01424292661,-0.2615356743,9.865633011,1.344079614 +8.43969094,-6.147548676,-28.02675056,-34.05786133,-0.004407898989,-0.04488246143,0.013322182,-0.2936043739,9.890439034,1.490942717 +8.44969094,-6.826293945,-26.9956913,-32.6035614,0.005539809354,-0.004841507412,0.0106570404,-0.2814545333,9.921515465,1.381560922 +8.45969094,-5.945941925,-27.45116997,-33.48878479,0.009740176611,0.0336917229,0.005443233531,-0.2583817542,9.897677422,1.307276845 +8.46969094,-8.233951569,-28.95725441,-35.05532837,0.006820478477,0.03775998205,0.0009987638332,-0.188798517,9.863088608,1.290988088 +8.47969094,-5.508361816,-27.94732094,-33.30186462,0.002396757714,0.02465282194,-0.002144287806,-0.2838674486,9.874014854,1.326351166 +8.48969094,-6.843322754,-27.00448036,-33.3659668,-0.005590040237,0.03597750515,-0.002197280526,-0.3121668994,9.947696686,1.387628078 +8.49969094,-6.843322754,-27.00448036,-33.3659668,6.277114153e-07,0.05783951283,-0.008056694642,-0.286522001,9.934962273,1.414525747 +8.50969094,-6.222164154,-28.73294258,-35.53359985,0.003852193244,0.06198479235,-0.01023218781,-0.3322080374,9.870047569,1.257705212 +8.51969094,-6.222164154,-28.73294258,-35.53359985,-0.007020583376,0.03898721188,-0.007140536793,-0.354721427,9.797861099,1.161445856 +8.52969094,-6.894096375,-27.69836617,-33.77432251,-0.02465378493,0.003852344118,-0.001664606389,-0.3568671644,9.820009232,1.262687206 +8.53969094,-6.123214722,-27.6135807,-34.08731079,-0.0191527456,-0.0115311034,0.00301573053,-0.4283187389,9.78444767,1.281093597 +8.54969094,-5.668922424,-27.43342781,-35.01679993,-0.02239307947,-0.01610159874,0.009372130036,-0.4523630142,9.761321068,1.180357814 +8.55969094,-5.701858521,-28.38517952,-33.88075256,-0.02748598531,-0.02391372249,0.01481722109,-0.3728437424,9.760118484,1.258087873 +8.56969094,-5.822349548,-27.58353233,-34.54794312,-0.02905605733,-0.02079217322,0.01221286692,-0.3384934366,9.839878082,1.264950633 +8.57969094,-7.391365051,-27.76716805,-35.14186096,-0.02644769847,0.004086437169,0.003770391457,-0.3486168683,9.821240425,1.333070636 +8.58969094,-6.013744354,-28.15384483,-34.65957642,-0.01537995227,0.01251769904,0.0009696688503,-0.3741804063,9.827664375,1.279482841 +8.59969094,-8.104663849,-27.15455437,-34.11555481,-0.01356030069,-0.002310473472,-0.002780725248,-0.3859480917,9.867848396,1.242322564 +8.60969094,-6.163772583,-28.3021946,-34.03823853,-0.02265218273,-0.0157774277,-0.00271670986,-0.3495922685,9.865961075,1.364074707 +8.61969094,-7.311927795,-29.65829659,-33.32875061,-0.01418382674,0.0006659564096,-0.005384140648,-0.3531033099,9.862417221,1.514775276 +8.62969094,-4.850357056,-27.32402992,-33.1950531,0.003424083814,0.03531191498,-0.006987604313,-0.4141439497,9.91368103,1.393144369 +8.63969094,-4.049133301,-26.55590248,-34.01454163,0.005545211025,0.02093706839,-0.003268030239,-0.388307482,9.895030975,1.247608185 +8.64969094,-6.623577118,-28.35165977,-33.72845459,-0.0003154044971,-0.02269215137,0.0004618884996,-0.3421718478,9.894777298,1.317503333 +8.65969094,-5.627555847,-27.01146889,-34.28384399,-0.001384953968,-0.04192937911,0.004192750901,-0.3671855927,9.879576683,1.335730672 +8.66969094,-6.40574646,-27.50063515,-33.17903137,0.006623237394,-0.02268284559,0.003680216148,-0.4353603423,9.886388779,1.367411256 +8.67969094,-7.719654083,-28.87966347,-32.92539978,0.01196025219,0.010412395,0.001041296404,-0.3877769411,9.898147583,1.325113058 +8.68969094,-6.09287262,-26.93023872,-34.59381104,0.01560688298,0.03164923191,-0.002251014113,-0.3476009667,9.874887466,1.177082181 +8.69969094,-5.185276031,-27.37165642,-34.25920105,0.007014569826,0.0320574306,-0.006441064179,-0.3255999684,9.796077728,1.179525852 +8.70969094,-5.887241364,-28.68531609,-34.46943665,-0.004665999673,0.02300379425,-0.007558021694,-0.3302562535,9.838270187,1.29654789 +8.71969094,-6.934654236,-28.38698006,-33.72525024,-0.0008134776726,0.01394113898,-0.008628170937,-0.4091928303,9.864145279,1.397688985 +8.72969094,-6.61447525,-27.41221046,-35.93190002,0.01304710936,0.0058097695,-0.01178541407,-0.3761585057,9.922281265,1.419453979 +8.73969094,-7.247840881,-28.28985786,-34.18930054,0.01882565767,-0.01711278409,-0.009561704472,-0.4335117936,9.864617348,1.383633018 +8.74969094,-6.140735626,-28.02323341,-33.75289917,0.01722362638,-0.01998676173,-0.004755612463,-0.3925135732,9.847726822,1.352542996 +8.75969094,-5.692451477,-28.11325264,-34.20532227,0.01413588692,-0.004737165757,-0.003758656094,-0.3975400031,9.840883255,1.424174428 +8.76969094,-6.374908447,-26.41642952,-34.78236389,0.01665059105,0.01422545873,0.001111822668,-0.4121716917,9.825641632,1.308867693 +8.77969094,-6.872554779,-29.61937523,-34.55342102,0.0079627214,0.00683412049,0.008576428518,-0.3998596966,9.780550003,1.15374434 +8.78969094,-8.128192902,-27.8343792,-33.30407715,-0.00830971729,-0.0006057955325,0.0137829408,-0.3708814979,9.795752525,1.336443067 +8.79969094,-6.156959534,-28.29867744,-33.73326111,-0.003319519572,0.03192749619,0.008939841762,-0.3640524149,9.828931808,1.51407516 +8.80969094,-4.970039368,-26.7890377,-33.08021545,0.0151279429,0.08334953338,0.004141870886,-0.40965572,9.86701107,1.33130312 +8.81969094,-6.123214722,-27.6135807,-34.08731079,0.01143238787,0.08572150022,0.005260156002,-0.3747372925,9.777318001,1.236672163 +8.82969094,-5.484832764,-27.26749611,-34.11334229,-0.0006647165865,0.04545711726,0.00868627429,-0.3375897408,9.728914261,1.210252285 +8.83969094,-5.79510498,-27.56947136,-33.32810974,-0.004408819135,0.004282972775,0.01372285746,-0.3406184614,9.721815109,1.191379905 +8.84969094,-6.901714325,-27.43522835,-34.86132812,-0.01009862404,-0.02514038049,0.0141247306,-0.3459747136,9.810904503,1.276666164 +8.85969094,-5.484027863,-27.53415108,-33.33131409,-0.01250343956,-0.009691631421,0.009318539873,-0.3982391655,9.863305092,1.43898356 +8.86969094,-5.594306946,-26.72723579,-33.5410614,-0.005750786047,0.02927107364,0.008384827524,-0.3999786675,9.804018974,1.47284627 +8.87969094,-5.6510849,-27.69129372,-33.47236633,0.001811090857,0.02968214266,0.01073560491,-0.4115684032,9.764409065,1.312410116 +8.88969094,-6.262722015,-29.42155647,-35.48452759,-0.007250172552,0.009495561942,0.01774243638,-0.3444252312,9.76662159,1.203037024 +8.89969094,-5.819438934,-27.98264122,-33.29866028,-0.01225235127,-0.03034259938,0.01879092306,-0.2967675924,9.789928436,1.379716516 +8.90969094,-6.181606293,-28.04432869,-35.58267212,0.003125378862,-0.04652474821,0.0152979847,-0.3290558457,9.828923225,1.316641212 +8.91969094,-7.777553558,-28.90954781,-35.51757812,0.008289773948,-0.05507510155,0.006511804182,-0.3192000985,9.854446411,1.306584358 +8.92969094,-6.726543427,-27.14036369,-34.73008728,0.006072483025,-0.03125399351,-0.001492372714,-0.2490886152,9.842761993,1.329090953 +8.93969094,-6.530639648,-28.49984932,-36.15995789,0.005286928266,0.004717913922,-0.01269114576,-0.2787717581,9.871692657,1.240097642 +8.94969094,-6.156959534,-28.29867744,-33.73326111,0.007722427137,0.00421890663,-0.01890247688,-0.341399461,9.860766411,1.212909102 +8.95969094,-5.195491791,-27.37692833,-34.71665955,-0.0005177315325,-0.02194965258,-0.01977552287,-0.3430656791,9.841578484,1.20852685 +8.96969094,-5.195491791,-27.37692833,-34.71665955,-0.004397911951,-0.01808477193,-0.02489776164,-0.2913993001,9.817040443,1.284796357 +8.97969094,-7.726776123,-28.21565819,-35.10920715,0.002665152773,0.001532091293,-0.02840395272,-0.3493477702,9.777391434,1.309384704 +8.98969094,-7.726776123,-28.21565819,-35.10920715,0.00724685844,-0.006472785026,-0.02936365642,-0.3285253942,9.861621857,1.359132767 +8.99969094,-6.80475235,-28.91670036,-33.38262939,0.007950174622,-0.02342860959,-0.02639209293,-0.4434536099,9.895025253,1.340307117 +9.00969094,-4.424610138,-27.29214287,-35.02961731,0.006259321235,-0.02674228698,-0.02167566307,-0.4321332276,9.925757408,1.370373964 +9.01969094,-5.539627075,-25.63072395,-34.07704163,0.01441753004,-0.003579446115,-0.02427353896,-0.3899079859,9.913691521,1.459052444 +9.02969094,-8.179279327,-27.86074638,-35.59129333,0.02454777062,0.02349377796,-0.02628907003,-0.4051021338,9.94682312,1.378842115 +9.03969094,-4.857170105,-27.32754707,-33.5,0.01992200688,0.02365491912,-0.02259735391,-0.429605186,9.887137413,1.335855365 +9.04969094,-5.644271851,-27.68777657,-33.16741943,0.00781902764,0.003750482807,-0.0154065527,-0.3775611222,9.861557007,1.329430461 +9.05969094,-4.808990479,-26.902071,-32.46209717,0.002876439132,0.002535451669,-0.01032625698,-0.3763277531,9.841564178,1.236484885 +9.06969094,-6.058815002,-26.9126606,-33.06900024,-0.002186387777,0.02572786249,-0.007384793833,-0.3992110789,9.797712326,1.18870461 +9.07969094,-4.641933441,-26.74492836,-32.3210144,-0.01085284352,0.03625140339,7.089274004e-05,-0.3808373511,9.743421555,1.078503132 +9.08969094,-5.76556778,-26.61947441,-34.61665344,-0.03301307559,0.01188983768,0.006888038013,-0.3624552786,9.697451591,1.048014522 +9.09969094,-6.100177765,-27.33461952,-33.80197144,-0.05799226463,-0.02245188132,0.01368184574,-0.3520770371,9.722191811,1.232402325 +9.10969094,-6.076648712,-26.65479469,-34.6134491,-0.05832531676,-0.02201962285,0.01837413386,-0.4136171639,9.800364494,1.344371796 +9.11969094,-7.182945251,-27.18807411,-34.26782227,-0.04887463897,-0.006774947047,0.01895935461,-0.4178379476,9.859547615,1.376471162 +9.12969094,-6.918430328,-28.11153603,-33.74488831,-0.03943052515,-0.01560469344,0.01933539659,-0.4469336271,9.893157959,1.409580708 +9.13969094,-6.422775269,-27.50942421,-33.94143677,-0.03463018686,-0.008655243553,0.01995558664,-0.3561683893,9.847137451,1.53296864 +9.14969094,-5.195491791,-27.37692833,-34.71665955,-0.01544791646,0.02348045073,0.0137829408,-0.3908901513,9.918141365,1.499109507 +9.15969094,-6.600048065,-27.67183495,-34.53993225,0.001441395842,0.03940471262,0.0152179841,-0.4149687886,9.856978416,1.277093053 +9.16969094,-5.989406586,-27.74067497,-34.68902588,-0.006609489676,0.004615615588,0.02280433103,-0.3392545581,9.758050919,1.146429181 +9.17969094,-5.484832764,-27.26749611,-34.11334229,-0.02656558156,-0.03210052848,0.02782632969,-0.2474892884,9.751603127,1.163014531 +9.18969094,-5.819747925,-27.31511879,-35.17750549,-0.02908400446,-0.04421976209,0.02460899949,-0.2521186173,9.782547951,1.296428919 +9.19969094,-5.76556778,-26.61947441,-34.61665344,-0.02535528317,-0.05329878628,0.02000474744,-0.2538339794,9.860228539,1.281494498 +9.20969094,-5.551830292,-28.23682594,-34.5020752,-0.0306779705,-0.04690035433,0.01591363735,-0.259737134,9.867933273,1.315501928 +9.21969094,-6.381721497,-26.41994667,-35.08731079,-0.03282633424,-0.03781130165,0.01075538062,-0.2662158608,9.842382431,1.342554212 +9.22969094,-5.979686737,-28.13626671,-33.13476562,-0.03420222551,-0.0125182569,0.005236934405,-0.2183810771,9.868021965,1.407692909 +9.23969094,-6.100177765,-27.33461952,-33.80197144,-0.02300329134,0.02829145826,-0.00561462203,-0.2840327621,9.901955605,1.485758662 +9.24969094,-4.625217438,-26.06862068,-33.43746948,-0.01911250688,0.04374434054,-0.008972733282,-0.3135461211,9.874196053,1.319430351 +9.25969094,-8.288433075,-27.98800468,-33.1401825,-0.02691411227,0.02197675779,-0.01033035852,-0.299005717,9.850079536,1.237532496 +9.26969094,-6.509208679,-26.6901989,-33.08383179,-0.03162460029,0.001549815293,-0.01018739119,-0.3111849427,9.796510696,1.378709555 +9.27969094,-6.473861694,-27.5357914,-36.22865295,-0.02315361984,-0.002986954525,-0.009323257953,-0.3509652317,9.8478508,1.414524198 +9.28969094,-7.360713959,-27.7513485,-33.76951599,-0.02178758755,-0.01910696179,-0.009859112091,-0.3926713765,9.908628464,1.373457074 +9.29969094,-6.951683044,-28.39576912,-34.48765564,-0.02419984341,-0.01715814695,-0.01178541407,-0.3376707137,9.898147583,1.401946902 +9.30969094,-6.590637207,-27.39990807,-34.86453247,-0.01450248435,0.01003692858,-0.01763336919,-0.3839448094,9.886221886,1.38350749 +9.31969094,-7.36151886,-27.48469353,-34.55155945,-0.01278505847,0.01857505366,-0.01977552287,-0.3988118172,9.814868927,1.286329269 +9.32969094,-6.484874725,-26.27702904,-33.11326599,-0.0293417424,-0.004705166444,-0.01006867364,-0.4420841634,9.828910828,1.158545732 +9.33969094,-6.422775269,-27.50942421,-33.94143677,-0.04848059639,-0.02031212486,-0.001976955682,-0.3750244081,9.772753716,1.376981616 +9.34969094,-6.510704041,-28.89016914,-34.14823914,-0.04332835972,-0.01426587068,-0.00082276389,-0.4337833822,9.7676754,1.373581648 +9.35969094,-7.736190796,-28.48758888,-34.78462219,-0.0376179181,-0.01224911399,0.002211536281,-0.4449275136,9.839629173,1.465065002 +9.36969094,-7.217002869,-27.20565224,-35.79263306,-0.02085287496,-0.03022374399,0.009145841002,-0.4059441388,9.8934021,1.544756889 +9.37969094,-5.001186371,-27.20572472,-33.35574341,-0.01117274538,-0.04885619879,0.01271759532,-0.3361937106,9.898633957,1.443880439 +9.38969094,-6.46333313,-28.1980381,-33.8923645,0.02227536216,-0.04157933593,-0.001544222352,-0.383582741,10.06635571,1.592002034 +9.39969094,-6.45602417,-27.7936573,-34.68421936,0.02985722572,0.008457273245,0.003183291759,-0.4190266728,10.04802799,1.19617939 +9.40969094,-6.100177765,-27.33461952,-33.80197144,0.02161491662,-0.01152697392,0.004680039361,-0.3310981691,9.888607979,1.307484984 +9.41969094,-6.100177765,-27.33461952,-33.80197144,0.02274901047,-0.02205886319,0.003949012142,-0.2644232512,9.895751953,1.281700253 +9.42969094,-8.001693726,-28.36585426,-33.11393738,0.0270957537,0.008429657668,-0.004258564673,-0.3135220706,9.886099815,1.322879076 +9.43969094,-8.001693726,-28.36585426,-33.11393738,0.03116482496,0.03424095362,-0.01175708137,-0.3184842765,9.87495327,1.208269715 +9.44969094,-5.972377777,-27.73188591,-33.92662048,0.0162592046,0.01626186259,-0.0105086565,-0.335937798,9.807515144,1.067642093 +9.45969094,-5.161434174,-27.3593502,-33.19184875,-0.01620616391,-0.01655917056,-0.004570898134,-0.3055139184,9.73563385,1.092912316 +9.46969094,-7.310745239,-26.79080772,-34.14318848,-0.02988763899,-0.03499742597,-0.003262628801,-0.2359486967,9.825713158,1.553684115 +9.47969094,-6.573112488,-26.99025154,-35.19894409,-0.01365076192,-0.01185967028,-0.008056694642,-0.3714031279,9.954780579,1.606928349 +9.48969094,-6.388221741,-27.09098244,-33.51342773,-0.0008522793651,0.03052273393,-0.008053194731,-0.4348410964,9.923695564,1.438187957 +9.49969094,-5.524894714,-27.55524254,-35.16108704,-0.002721569035,0.01259369962,-0.0008508043829,-0.3950788081,9.823553085,1.357761741 +9.50969094,-4.90422821,-28.68719673,-31.87704468,-0.009681989439,-0.01744466461,0.007116397377,-0.3847718537,9.837177277,1.353380203 +9.51969094,-5.419940948,-26.16571236,-34.19186401,-0.01018538047,-0.0034658527,0.009544158354,-0.4182123542,9.833841324,1.346598744 +9.52969094,-7.343997955,-27.07504082,-34.88597107,-0.005646346137,0.01587818377,0.009550519288,-0.3948255181,9.812287331,1.296076536 +9.53969094,-6.710323334,-26.86491966,-34.74971008,-0.006285015494,0.003204530338,0.009770220146,-0.3175331056,9.817604065,1.234001398 +9.54969094,-8.449485779,-27.87497139,-33.75830078,-0.02056121826,-0.02370020375,0.01402841881,-0.310777247,9.741884232,1.292146921 +9.55969094,-5.525390625,-27.95611,-34.06427002,-0.0177536048,-0.04929082096,0.01203878596,-0.3341468275,9.749916077,1.421624541 +9.56969094,-7.351303101,-27.47942162,-34.09411621,-0.005059229676,-0.07262051105,0.01338049769,-0.3519653678,9.822608948,1.402941823 +9.57969094,-6.428779602,-27.77959633,-33.46435547,-0.004816933535,-0.09561718255,0.01041176356,-0.3689287603,9.827497482,1.293786645 +9.58969094,-6.60735321,-28.07621574,-33.74809265,-0.006380548701,-0.09612983465,0.008321847767,-0.2538587749,9.772336006,1.353903651 +9.59969094,-6.934654236,-28.38698006,-33.72525024,0.002941088751,-0.0783181861,-0.002114825416,-0.2645348608,9.775900841,1.470979691 +9.60969094,-7.518360138,-27.63656425,-34.23516846,0.0110929301,-0.0527979359,-0.009588164277,-0.2304200828,9.846688271,1.339077234 +9.61969094,-6.428779602,-27.77959633,-33.46435547,0.00417023059,-0.02648497,-0.01985131018,-0.286404103,9.793714523,1.367927313 +9.62969094,-5.990215302,-27.47402,-35.47105408,0.0009704809636,-0.01529773325,-0.02053293586,-0.3193401098,9.735281944,1.396854877 +9.63969094,-5.915599823,-26.76782799,-33.99531555,0.002660098486,-0.01091999374,-0.02030819841,-0.3742132783,9.726406097,1.408692479 +9.64969094,-5.836467743,-27.99143028,-34.06106567,0.009704994969,0.00570339663,-0.02030819841,-0.396333158,9.782100677,1.37638545 +9.65969094,-5.075801849,-27.91191673,-34.83148193,0.01480308268,0.01089406572,-0.01835904643,-0.383464247,9.801027298,1.287954688 +9.66969094,-7.527767181,-27.90849113,-33.91059875,0.004126793705,-0.01640985347,-0.01433289424,-0.3254973888,9.808070183,1.299425244 +9.67969094,-5.753753662,-28.14489174,-36.94999695,-0.01064739004,-0.03583458811,-0.01477447897,-0.3483581841,9.893923759,1.421264291 +9.68969094,-7.688819885,-27.79545784,-34.52871704,-0.01350821275,-0.01939630695,-0.01784704253,-0.4041688144,9.913567543,1.498895407 +9.69969094,-5.795909882,-27.30281639,-34.11013794,-0.001301977783,0.01890260912,-0.02151327021,-0.4571124613,9.889979362,1.583774805 +9.70969094,-6.037273407,-28.83366966,-33.84809875,0.02016665787,0.03482097015,-0.02001620457,-0.3828421235,9.923582077,1.550175071 +9.71969094,-7.368331909,-27.48821068,-34.85652161,0.02689192817,0.01481494214,-0.01628457569,-0.4274457693,9.955366135,1.418853045 +9.72969094,-5.846683502,-27.99670219,-34.51850891,0.01808453351,0.0007289100904,-0.007230801042,-0.4414436519,9.913903236,1.362712622 +9.73969094,-7.233718872,-27.88195992,-34.6762085,0.006072483025,0.005902021192,-0.00551132299,-0.4905114472,9.913440704,1.355573893 +9.74969094,-7.385047913,-28.16451836,-33.74008179,-0.001548105851,0.03948125988,0.001368286088,-0.3877508342,9.898666382,1.246912837 +9.75969094,-6.509895325,-29.15682411,-33.36621094,-0.01327420678,0.06441155076,-0.002559530549,-0.3439078927,9.951795578,1.185796857 +9.76969094,-7.19997406,-27.19686317,-35.03022766,-0.02978560328,0.05697054416,-0.00752402097,-0.4116650224,9.954465866,1.257131696 +9.77969094,-6.428779602,-27.77959633,-33.46435547,-0.03334539384,0.07115680724,-0.006260839291,-0.5033277869,9.958221436,1.41427815 +9.78969094,-5.668113708,-27.70008278,-34.23477173,-0.02409242094,0.08291181177,-0.003795302706,-0.5041429996,9.987658501,1.488123059 +9.79969094,-5.535114288,-27.56051826,-35.61853027,-0.01179026719,0.08431231976,0.001658534165,-0.5291974545,9.954752922,1.494916201 +9.80969094,-6.589519501,-28.33408165,-32.2036438,0.003343363293,0.07458043098,0.00850411877,-0.4825110435,9.898130417,1.412799358 +9.81969094,-5.997028351,-27.47753716,-35.77600098,0.006553829648,0.05965533108,0.01489961706,-0.4234584868,9.782516479,1.332983136 +9.82969094,-6.059619904,-26.64600563,-33.8510437,0.01331350673,0.04850659519,0.01735352911,-0.3877936006,9.777862549,1.266014695 +9.83969094,-6.503890991,-28.88665199,-33.84329224,0.01824017987,0.03485743701,0.01993710175,-0.3487462103,9.775895119,1.266067386 +9.84969094,-7.19997406,-27.19686317,-35.03022766,0.02160441503,0.02122296393,0.01546534896,-0.311239928,9.760976791,1.272807956 +9.85969094,-6.55657959,-27.38232994,-33.33972168,0.01953481883,0.01390312053,0.01141053811,-0.3209119439,9.78553009,1.278277993 +9.86969094,-5.979190826,-27.73540306,-34.23156738,0.01899946108,0.01229704265,0.006715378258,-0.2684645653,9.816758156,1.335773826 +9.87969094,-7.206474304,-27.86789894,-33.4563446,0.02045468241,0.005114794243,0.002737503033,-0.3115752637,9.813545227,1.378985286 +9.88969094,-7.206474304,-27.86789894,-33.4563446,0.02057313174,-0.009556769393,0.003524060361,-0.2928525209,9.85481739,1.433280349 +9.89969094,-5.709480286,-28.1220417,-34.96772766,0.02471607178,-0.02593976445,-0.000195460394,-0.289963156,9.882286072,1.459529281 +9.90969094,-5.709480286,-28.1220417,-34.96772766,0.0349977091,-0.03940877318,-6.658444181e-05,-0.3231247663,9.801194191,1.448393464 +9.91969094,-6.100177765,-27.33461952,-33.80197144,0.04312978685,-0.04885619879,0.0009987638332,-0.32735309,9.733083725,1.397290349 +9.92969094,-5.444274902,-26.57888222,-34.16241455,0.05012412742,-0.04050931334,-0.001043979544,-0.2132692337,9.657939911,1.322166562 +9.93969094,-6.124019623,-27.34692574,-34.86933899,0.05335898697,-0.02532506175,-0.005103618838,-0.2339841872,9.683965683,1.298429251 +9.94969094,-5.822349548,-27.58353233,-34.54794312,0.04862793908,-0.005474109203,-0.009122043848,-0.2713183165,9.604672432,1.183119655 +9.95969094,-5.028430939,-27.21978569,-34.57559204,0.03629063815,-0.004140705802,-0.01083520055,-0.2865132093,9.572835922,1.108448267 +9.96969094,-6.019748688,-28.42401695,-34.18249512,0.01961249486,0.02772274427,-0.009543137625,-0.2350679785,9.573115349,1.162874699 +9.97969094,-5.610527039,-27.00267982,-33.5214386,0.008852970786,0.05212645978,-0.01561361179,-0.2903158665,9.632687569,1.220809221 +9.98969094,-4.843048096,-26.91964912,-33.98690796,-0.006395697594,0.07004146278,-0.01498145796,-0.3082864285,9.775126457,1.420338035 +9.99969094,-6.510704041,-28.89016914,-34.14823914,-0.01554161683,0.08230477571,-0.01358819567,-0.3808072507,9.88078022,1.6931777 +10.00969094,-5.558643341,-28.24034309,-34.80705261,-0.01750100963,0.09559768438,-0.003126793541,-0.4694359601,9.878305435,1.681256294 +10.01969094,-5.948848724,-27.05206108,-34.73809814,-0.01223013736,0.0947631076,0.01301613264,-0.520768702,9.835087776,1.616311669 +10.02969094,-5.524585724,-28.22276497,-33.28224182,-0.01229349431,0.08211163431,0.03143116459,-0.5171135068,9.89792347,1.515195131 +10.03969094,-5.634864807,-27.41584969,-33.49198914,-0.02304166183,0.07002359629,0.04795950279,-0.4607204795,9.916221619,1.462448239 +10.04969094,-5.86290741,-28.27214622,-34.49887085,-0.03202089667,0.06247267872,0.05862831324,-0.3976040781,9.907452583,1.388126731 +10.05969094,-5.812625885,-27.97912407,-32.99369812,-0.04750698432,0.0630890578,0.06102353334,-0.3217801452,9.843457222,1.314891696 +10.06969094,-6.058815002,-26.9126606,-33.06900024,-0.04895600677,0.05808627605,0.05451076478,-0.3295866549,9.846390724,1.25166893 From 8cc000afa95b60fd34b21767e51982b9764c25f8 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 15 Sep 2024 16:03:16 +0800 Subject: [PATCH 089/213] update i32 fixmul and fixsqrt * dev2. --- .../newton/llvm-ir/c-files/MadgwickAHRS.c | 22 +- applications/newton/llvm-ir/replace.sh | 3 +- applications/newton/llvm-ir/testMadgwick.sh | 6 +- applications/newton/llvm-ir/testOriginal.sh | 4 +- applications/newton/llvm-ir/test_madgwick.c | 41 ++ .../newton-irPass-LLVMIR-quantization.cpp | 399 ++++++++---------- 6 files changed, 237 insertions(+), 238 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index 40d6dc1b3..3e3853379 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -25,12 +25,12 @@ #define sampleFreq 28.0f // sample frequency in Hz #define betaDef 0.1f // 2 * proportional gain -#ifndef lowerBound -#define lowerBound -16 -#endif -#ifndef upperBound -#define upperBound 16 -#endif +//#ifndef lowerBound +//#define lowerBound -16 +//#endif +//#ifndef upperBound +//#define upperBound 16 +//#endif //--------------------------------------------------------------------------------------------------- // Variable definitions @@ -70,11 +70,11 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float float q1 = *q1_ptr; float q2 = *q2_ptr; float q3 = *q3_ptr; -#ifdef ASSUME - __builtin_assume(ax > lowerBound && ax < upperBound); - __builtin_assume(ay > lowerBound && ay < upperBound); - __builtin_assume(az > lowerBound && az < upperBound); -#endif +//#ifdef ASSUME +// __builtin_assume(ax > lowerBound && ax < upperBound); +// __builtin_assume(ay > lowerBound && ay < upperBound); +// __builtin_assume(az > lowerBound && az < upperBound); +//#endif float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; diff --git a/applications/newton/llvm-ir/replace.sh b/applications/newton/llvm-ir/replace.sh index 49ddee761..f148735e8 100755 --- a/applications/newton/llvm-ir/replace.sh +++ b/applications/newton/llvm-ir/replace.sh @@ -18,7 +18,8 @@ sed -i 's/float\*\*/i32**/g' "$file" # Replace float* with i32* sed -i 's/float\*/i32*/g' "$file" - +# Now replace the call to invSqrt with the call to fixrsqrt +sed -i 's/call i32 @invSqrt/call i32 @fixrsqrt/g' "$file" diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 0884fc61f..49dfe8045 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -19,13 +19,13 @@ echo "Step 3: Convert generated bytecode file to LLVM IR file" cd $HOME/CoSense/applications/newton/llvm-ir/&& ./replace.sh $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -#python3 replace.py +python3 replace.py # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" @@ -45,7 +45,7 @@ ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applicat # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index 79e0861b1..97f25f96a 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -6,8 +6,8 @@ clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOM # Step 4: Optimize the generated LLVM IR file -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc @@ -22,7 +22,7 @@ clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applic ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library -clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_Original.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable $HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/test_madgwick.c b/applications/newton/llvm-ir/test_madgwick.c index 808254695..d89549cde 100644 --- a/applications/newton/llvm-ir/test_madgwick.c +++ b/applications/newton/llvm-ir/test_madgwick.c @@ -13,6 +13,7 @@ #include #include #include +#include #include #include @@ -38,6 +39,27 @@ extern volatile float q0, q1, q2, q3; /*************************************** * Timer functions of the test framework ***************************************/ +readticks(unsigned int *result, int enabled) +{ + struct timeval t; + unsigned int cc; + unsigned int val; + if (!enabled) { + // program the performance-counter control-register: + asm volatile("msr pmcr_el0, %0" : : "r" (17)); + //enable all counters + asm volatile("msr PMCNTENSET_EL0, %0" : : "r" (0x8000000f)); + //clear the overflow + asm volatile("msr PMOVSCLR_EL0, %0" : : "r" (0x8000000f)); + enabled = 1; + } + //read the coutner value + asm volatile("mrs %0, PMCCNTR_EL0" : "=r" (cc)); + gettimeofday(&t,(struct timezone *) 0); + result[0] = cc; + result[1] = t.tv_usec; + result[2] = t.tv_sec; +} typedef struct timespec timespec; timespec diff(timespec start, timespec end) @@ -218,6 +240,21 @@ int main() { u_int64_t time_slots[ITERATION]; for (size_t idx = 0; idx < ITERATION; idx++) { + + unsigned int init[3] = {0}; + unsigned int start[3] = {0}; + unsigned int end[3] = {0}; + unsigned int overhead = 0; + + readticks(init, 0); + readticks(start, 1); + readticks(end, 1); + + overhead = end[0] - start[0]; + readticks(init, 0); + readticks(start, 1); + + timespec timer = tic(); for (size_t ts = 0; ts < DATA_SIZE; ts++) { MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], @@ -225,6 +262,10 @@ int main() { mag_x[ts], mag_y[ts], mag_z[ts], &q0[ts], &q1[ts], &q2[ts], &q3[ts]); } + + end[0] = end[0] - start[0] - overhead; + printf("clock cycles= %d\n", end[0]); + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 9ad7009e3..e5f37e851 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1091,9 +1091,9 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->replaceAllUsesWith(callInst); inInstruction->removeFromParent(); } -//TODO : Origianl double fixsqrt -//llvm::Function * -//createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +// TODO : Origianl double fixsqrt +// llvm::Function * +// createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) //{ // // Check if irModule is valid // if (!irModule) @@ -1155,10 +1155,11 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: // llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; // // return func; -//} +// } -//TODO : float version rsqrt -llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +// TODO : float version rsqrt +llvm::Function * +createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { if (!irModule) { @@ -1176,21 +1177,21 @@ llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, st } } - llvm::LLVMContext & context = irModule->getContext(); + llvm::LLVMContext & context = irModule->getContext(); llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); + llvm::IRBuilder<> builder(entryBB); llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value * x = &*args++; + llvm::Value * x = &*args++; // Convert the fixed-point integer to a floating-point number for sqrt computation llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getFloatTy(context)); // Call sqrt on the floating-point value - llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); - llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); // Convert the result back to a fixed-point integer llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); @@ -1203,9 +1204,9 @@ llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, st if (FRAC_Q % 2 != 0) { llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562); - llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); - llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); - finalRes = builder.CreateFPToSI(compensated, quantizedType); + llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); + llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); + finalRes = builder.CreateFPToSI(compensated, quantizedType); } builder.CreateRet(finalRes); @@ -1279,7 +1280,7 @@ llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, st // llvm::Value* squaredBase = builder.CreateMul(currentBase, currentBase); // squaredBase = builder.CreateAShr(squaredBase, FRAC_Q); // -// llvm::Value* newExponent = builder.CreateLShr(currentExponent, 1); +// llvm::Value* newExponent = builder.CreateAShr(currentExponent, 1); // // currentBase->addIncoming(squaredBase, continueBB); // currentExponent->addIncoming(newExponent, continueBB); @@ -1297,9 +1298,9 @@ llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, st // } // Create a fixed-point multiplication function -//TODO :Original version fixmul -//llvm::Function * -//createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +// TODO :Original version fixmul +// llvm::Function * +// createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) //{ // llvm::errs() << "Entering createFixMul\n"; // @@ -1363,8 +1364,7 @@ llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, st // return func; //} - -//TODO: i32 version fixmul +// TODO: i32 version fixmul llvm::Function * createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1401,8 +1401,8 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorarg_begin(); - llvm::Value * arg1 = args++; // Get the first argument - llvm::Value * arg2 = args; // Get the second argument + llvm::Value * arg1 = args++; // Get the first argument + llvm::Value * arg2 = args; // Get the second argument // Perform i32 multiplication llvm::Value * mulInst = builder.CreateMul(arg1, arg2); @@ -1420,8 +1420,6 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1485,8 +1483,8 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) + llvm::Function * + createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { llvm::errs() << "Entering createFixRsqrt\n"; @@ -1520,22 +1518,20 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext())); // Added step: fp_y = fp_y / 1024.0; - //fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); + // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 - fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); - + fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); @@ -1545,10 +1541,14 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE)), quantizedType); // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + // llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + // llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + // llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + // llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, 10)); + llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, 10)); + llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, 10)); // Return the final fixed-point result builder.CreateRet(final_y); @@ -1580,32 +1580,38 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorarg_begin(); // llvm::Value *x = &*args++; +// llvm::Value *x_ext = builder.CreateSExt(x, llvm::Type::getInt64Ty(irModule->getContext())); // Ensure we use i64 for all operations to prevent overflow // -// llvm::Function *fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); -// -// llvm::Value *halfX = builder.CreateLShr(x, 1); +// llvm::Value *halfX = builder.CreateLShr(x_ext, 1); // -// llvm::Value *fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); -// llvm::Value *mulConst = llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625); +// llvm::Value *fpX = builder.CreateSIToFP(x_ext, llvm::Type::getDoubleTy(irModule->getContext())); +// llvm::Value *mulConst = llvm::ConstantFP::get(llvm::Type::getDoubleTy(irModule->getContext()), 0.0009765625); // llvm::Value *approximation = builder.CreateFMul(fpX, mulConst); // -// llvm::Value *intApproximation = builder.CreateBitCast(approximation, llvm::Type::getInt32Ty(irModule->getContext())); -// llvm::Value *shiftedApproximation = builder.CreateLShr(intApproximation, 1); -// llvm::Value *magicNumberSub = builder.CreateSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 1597463007), shiftedApproximation); +// llvm::Value *intApproximation = builder.CreateBitCast(approximation, llvm::Type::getInt64Ty(irModule->getContext())); +// llvm::Value *shiftedApproximation = builder.CreateAShr(intApproximation, 1); +// llvm::Value *magicNumberSub = builder.CreateSub(llvm::ConstantInt::get(llvm::Type::getInt64Ty(irModule->getContext()), 1597463007), shiftedApproximation); // // llvm::Value *scaledApproximation = builder.CreateShl(magicNumberSub, 10); -// llvm::Value *firstMul = builder.CreateCall(fixMulFunc, {halfX, scaledApproximation}); -// llvm::Value *secondMul = builder.CreateCall(fixMulFunc, {firstMul, scaledApproximation}); -// llvm::Value *correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, 1536), secondMul); // -// llvm::Value *finalResult = builder.CreateCall(fixMulFunc, {scaledApproximation, correction}); +// // Inline fixed-point multiplication using mul and ashr +// llvm::Value *firstMul = builder.CreateMul(halfX, scaledApproximation); +// llvm::Value *firstMulShifted = builder.CreateAShr(firstMul, 10, "fixmul_temp"); +// llvm::Value *secondMul = builder.CreateMul(firstMulShifted, scaledApproximation); +// llvm::Value *secondMulShifted = builder.CreateAShr(secondMul, 10, "fixmul_temp"); +// llvm::Value *correction = builder.CreateSub(llvm::ConstantInt::get(llvm::Type::getInt64Ty(irModule->getContext()), 1536), secondMulShifted); // -// builder.CreateRet(finalResult); +// llvm::Value *finalMul = builder.CreateMul(correction, scaledApproximation); +// llvm::Value *finalResult = builder.CreateAShr(finalMul, 10, "fixmul_final"); +// llvm::Value *truncFinalResult = builder.CreateTrunc(finalResult, quantizedType); +// +// builder.CreateRet(truncFinalResult); // functionsToInsert.push_back(func); // // return func; //} + // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -2073,16 +2079,83 @@ convertToQuantizedType(Value * value, Type * quantizedType, IRBuilder<> & Builde } } +bool checkAndSimplifyFMul(ConstantFP *constFP, Value *otherOperand, Instruction *instruction) { + APFloat targetValue(0.28f); // 初始化时使用单精度浮点数 + bool losesInfo; + APFloat::opStatus status = targetValue.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo); // 转换为双精度浮点格式 + + // 检查转换是否成功 + if (status != APFloat::opOK) { + llvm::errs() << "Error: Precision loss or other conversion error occurred.\n"; + } + + // 比较常数值 + if (constFP->getValueAPF().compare(targetValue) == APFloat::cmpEqual) { + llvm::IRBuilder<> Builder(instruction); + // 创建除数常数 (28.0) + Value *divisor = ConstantFP::get(instruction->getType(), 28.0); + // 创建除法指令 + Instruction *divInst = cast(Builder.CreateFDiv(otherOperand, divisor, "divby28")); + + // 替换原始指令 + instruction->replaceAllUsesWith(divInst); + instruction->eraseFromParent(); + return true; + } + return false; +} + void -handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) +simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) +{ + llvm::IRBuilder<> Builder(instruction); + Type * intType = Type::getInt32Ty(instruction->getContext()); + Value * shiftValue = ConstantInt::get(intType, shiftAmount); + Instruction * shiftInst; + + if (isRightShift) + { + shiftInst = BinaryOperator::CreateAShr(operand, shiftValue, "", instruction); + } + else + { + shiftInst = BinaryOperator::CreateShl(operand, shiftValue, "", instruction); + } + + instruction->replaceAllUsesWith(shiftInst); + instruction->eraseFromParent(); +} + +bool +checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruction * instruction) +{ + static const std::map> constantsMap = { + {0.5, {1, true}}, + {2.0, {1, false}}, + {4.0, {2, false}}, + {8.0, {3, false}}}; + double value = constFP->getValueAPF().convertToDouble(); + auto it = constantsMap.find(value); + if (it != constantsMap.end()) + { + int shiftAmount = it->second.first; + bool isRightShift = it->second.second; + simplifyUsingShift(otherOperand, instruction, shiftAmount, isRightShift); + return true; + } + return false; + + // New check for the special multiplication case + return checkAndSimplifyFMul(constFP, otherOperand, instruction); +} + +void +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FMul\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; IRBuilder<> Builder(llvmIrInstruction); - // bool lhsConstantQuantized = false; - // bool rhsConstantQuantized = false; - // Ensure operands are correctly converted to fixed-point integers Value * lhs = llvmIrInstruction->getOperand(0); Value * rhs = llvmIrInstruction->getOperand(1); @@ -2096,145 +2169,17 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix ConstantFP * lhsConst = dyn_cast(lhs); ConstantFP * rhsConst = dyn_cast(rhs); - bool rhsIsDiv28 = false; - bool lhsIsDiv28 = false; - - // 检查是否存在浮点数常数0.5 - bool lhsIsHalf = lhsConst && lhsConst->getValueAPF().convertToDouble() == 0.5; - bool rhsIsHalf = rhsConst && rhsConst->getValueAPF().convertToDouble() == 0.5; - bool lhsIsTwo = lhsConst && lhsConst->getValueAPF().convertToDouble() == 2.0; - bool rhsIsTwo = rhsConst && rhsConst->getValueAPF().convertToDouble() == 2.0; - bool lhsIsFour =lhsConst && lhsConst->getValueAPF().convertToDouble() == 4.0; - bool rhsIsFour = rhsConst && rhsConst->getValueAPF().convertToDouble() == 4.0; - bool lhsIsEight =lhsConst && lhsConst->getValueAPF().convertToDouble() == 8.0; - bool rhsIsEight = rhsConst && rhsConst->getValueAPF().convertToDouble() == 8.0; - + // Check if either operand is a constant that can be simplified if (auto rhsConst = dyn_cast(rhs)) { - // 直接检查常量值是否等于硬编码的特定浮点数 - if (rhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) - { - rhsIsDiv28 = true; - } + if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) + return; } - if (auto lhsConst = dyn_cast(lhs)) { - if (lhsConst->isExactlyValue(APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000LL)))) - { - lhsIsDiv28 = true; - } - } - - // TODO HARDCODE - - if (lhsIsDiv28 || rhsIsDiv28) - { - llvm::errs() << "One operand is a floating-point constant 0x3FA24924A0000000, simplifying using sdiv by 28\n"; - - // 获取另一个操作数 - Value * otherOperand = lhsIsDiv28 ? rhs : lhs; - IRBuilder<> Builder(llvmIrInstruction); - - // 创建浮点型常数 28.0 - Value * twentyEight = ConstantFP::get(llvmIrInstruction->getContext(), APFloat(28.0)); - - // 创建除法指令 - Value * divInstValue = Builder.CreateFDiv(otherOperand, twentyEight, "divFrequency"); - Instruction * divInst = cast(divInstValue); // 将 Value* 转换为 Instruction* - - // 替换原来的乘法指令 - llvmIrInstruction->replaceAllUsesWith(divInst); - llvmIrInstruction->eraseFromParent(); - - return; // 防止后续代码执行 - } - - if (lhsIsHalf || rhsIsHalf) - { - //llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using sdiv by 2\n"; - llvm::errs() << "One operand is a floating-point constant 0.5, simplifying using logical shift right by 1\n"; - - Value * otherOperand = lhsIsHalf ? rhs : lhs; - Type * intType = Type::getInt32Ty(llvmIrInstruction->getContext()); - - // 创建常数1,用于右移 - Value *one = ConstantInt::get(intType, 1); - - // 创建除法指令 - //Instruction * divInst = BinaryOperator::CreateSDiv(otherOperand, two, "divbytwo", llvmIrInstruction); - - // 创建算术右移指令 - Instruction *shrInst = BinaryOperator::CreateAShr(otherOperand, one, "divbytwo", llvmIrInstruction); - // Instruction *divInst = BinaryOperator::CreateSDiv(otherOperand,two); - // 替换原来的乘法指令 - //llvmIrInstruction->replaceAllUsesWith(divInst); - llvmIrInstruction->replaceAllUsesWith(shrInst); - llvmIrInstruction->eraseFromParent(); - - return; // 防止后续代码执行 - } - - if (lhsIsTwo || rhsIsTwo) - { - llvm::errs() << "One operand is a floating-point constant 2.0, simplifying using logical shift left by 1\n"; - - Value *otherOperand = lhsIsTwo ? rhs : lhs; - Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); - - // 创建常数1,用于左移 - Value *one = ConstantInt::get(intType, 1); - - // 创建逻辑左移指令 - Instruction *shlInst = BinaryOperator::CreateShl(otherOperand, one, "mulbytwo", llvmIrInstruction); - - // 替换原来的乘法指令 - llvmIrInstruction->replaceAllUsesWith(shlInst); - llvmIrInstruction->eraseFromParent(); - - return; // 防止后续代码执行 - } - - - if (lhsIsFour || rhsIsFour) - { - llvm::errs() << "One operand is a floating-point constant 4.0, simplifying using logical shift left by 1\n"; - - Value *otherOperand = lhsIsFour ? rhs : lhs; - Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); - - // 创建常数1,用于左移 - Value *one = ConstantInt::get(intType, 2); - - // 创建逻辑左移指令 - Instruction *shlInst = BinaryOperator::CreateShl(otherOperand, one, "mulbyfour", llvmIrInstruction); - - // 替换原来的乘法指令 - llvmIrInstruction->replaceAllUsesWith(shlInst); - llvmIrInstruction->eraseFromParent(); - - return; // 防止后续代码执行 - } - - if (lhsIsEight || rhsIsEight) - { - llvm::errs() << "One operand is a floating-point constant 8.0, simplifying using logical shift left by 1\n"; - - Value *otherOperand = lhsIsEight ? rhs : lhs; - Type *intType = Type::getInt32Ty(llvmIrInstruction->getContext()); - - // 创建常数1,用于左移 - Value *one = ConstantInt::get(intType, 3); - - // 创建逻辑左移指令 - Instruction *shlInst = BinaryOperator::CreateShl(otherOperand, one, "mulbyfour", llvmIrInstruction); - - // 替换原来的乘法指令 - llvmIrInstruction->replaceAllUsesWith(shlInst); - llvmIrInstruction->eraseFromParent(); - - return; // 防止后续代码执行 + if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) + return; } // If either operand is a float constant, convert it to fixed-point using handleConstant @@ -2284,33 +2229,34 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool lhsIsInteger = lhs->getType()->isIntegerTy(); bool rhsIsInteger = rhs->getType()->isIntegerTy(); -// if (lhsIsInteger && rhsIsInteger) -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); -// //callInst->setCallingConv(llvm::CallingConv::Fast); -// //callInst->setTailCall(true); -// llvmIrInstruction->replaceAllUsesWith(callInst); -// llvmIrInstruction->eraseFromParent(); -// } + // if (lhsIsInteger && rhsIsInteger) + // { + // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + // //callInst->setCallingConv(llvm::CallingConv::Fast); + // //callInst->setTailCall(true); + // llvmIrInstruction->replaceAllUsesWith(callInst); + // llvmIrInstruction->eraseFromParent(); + // } -if (lhsIsInteger && rhsIsInteger) { - llvm::errs() << "Both operands are integers, performing inline multiplication and shifting...\n"; + if (lhsIsInteger && rhsIsInteger) + { + llvm::errs() << "Both operands are integers, performing inline multiplication and shifting...\n"; - // Perform multiplication directly - llvm::Value *mulResult = Builder.CreateMul(lhs, rhs); + // Perform multiplication directly + llvm::Value * mulResult = Builder.CreateMul(lhs, rhs,"", true); - // Perform right arithmetic shift - llvm::Value *shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), 10)); + // Perform right arithmetic shift + llvm::Value * shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), 10)); - // Replace all uses of the original instruction with the result of the shift - llvmIrInstruction->replaceAllUsesWith(shiftResult); - llvmIrInstruction->eraseFromParent(); -} + // Replace all uses of the original instruction with the result of the shift + llvmIrInstruction->replaceAllUsesWith(shiftResult); + llvmIrInstruction->eraseFromParent(); + } } void -handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixdiv) +handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FDiv \n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -2352,9 +2298,25 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix else { llvm::errs() << "Both operands are integers, substituting with fixdiv function...\n"; +// { +// Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); +// llvmIrInstruction->replaceAllUsesWith(newInst); +// llvmIrInstruction->eraseFromParent(); +// llvm::errs() << "Finished handling FDiv\n"; +// } { - Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(newInst); + IRBuilder<> Builder(llvmIrInstruction); + // Extend both operands to i64 + Value *lhsExt = Builder.CreateSExt(lhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "lhs.ext"); + Value *rhsExt = Builder.CreateSExt(rhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "rhs.ext"); + // Multiply the lhs extended value by 1024 for scaling + Value *scaledNumerator = Builder.CreateMul(lhsExt, ConstantInt::get(Type::getInt64Ty(llvmIrInstruction->getContext()), 1024), "scaled.numerator"); + // Perform the division + Value *divResult = Builder.CreateSDiv(scaledNumerator, rhsExt, "div.result"); + // Truncate the result back to i32 + Value *truncatedResult = Builder.CreateTrunc(divResult, Type::getInt32Ty(llvmIrInstruction->getContext()), "result.trunc"); + // Replace all uses of the original instruction with this new sequence + llvmIrInstruction->replaceAllUsesWith(truncatedResult); llvmIrInstruction->eraseFromParent(); llvm::errs() << "Finished handling FDiv\n"; } @@ -2558,7 +2520,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64"|| funcName == "llvm.sqrt.f32") + if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64" || funcName == "llvm.sqrt.f32") { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); @@ -3120,11 +3082,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve updateGlobalVariables(module, quantizedType); /* - * generate hardcode function - fixmul and fixdiv + * generate hardcode function * */ - - llvm::Function * fixdiv = createFixDiv(module, quantizedType, functionsToInsert); - llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); @@ -3149,7 +3108,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); + // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt); break; case Instruction::GetElementPtr: @@ -3192,14 +3151,14 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve else { llvm::errs() << "Found FMul instruction.\n"; - handleFMul(llvmIrInstruction, quantizedType, fixmul); + handleFMul(llvmIrInstruction, quantizedType); } break; case Instruction::FDiv: { llvm::errs() << "Found FDiv instruction.\n"; - handleFDiv(llvmIrInstruction, quantizedType, fixdiv); + handleFDiv(llvmIrInstruction, quantizedType); break; } @@ -3298,8 +3257,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FPExt: case Instruction::FPTrunc: case Instruction::BitCast: - // log - llvm::errs() << "handle bitcast\n"; handleBitCast(llvmIrInstruction, quantizedType); break; From d10042da8466e5dc21a11a2cb96c3d21c1071f09 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Thu, 19 Sep 2024 22:29:42 +0100 Subject: [PATCH 090/213] add support for get precisionBits of different sensor * dev2. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 470 +++++++++--------- .../newton-irPass-LLVMIR-quantization.cpp | 154 +++--- 2 files changed, 285 insertions(+), 339 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index d1eca4acc..effa4ff52 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -45,9 +45,8 @@ #include "newton-irPass-LLVMIR-memoryAlignment.h" #include "newton-irPass-LLVMIR-emitAssume.h" - -//added code -//#include "myRangeAnalysis.h" +// added code +// #include "myRangeAnalysis.h" #endif /* __cplusplus */ #include @@ -76,57 +75,17 @@ #include "llvm/Support/FileSystem.h" #include "llvm/IR/Function.h" -using namespace llvm; - - - -void handleSpecialNumber(llvm::Module &M, llvm::LLVMContext &Context) { - llvm::errs() << "Starting special number handling in the module.\n"; - - // 创建一个特定的 NaN 值(双精度浮点数的 IEEE NaN 表示) - uint64_t NaNBits = 0x7FF8000000000000; - llvm::APFloat SpecialNaN(llvm::APFloat::IEEEdouble(), llvm::APInt(64, NaNBits)); - - // 创建一个特殊的整数值作为替换,这里使用 INT_MAX - llvm::Constant *specialValue = llvm::ConstantInt::get(llvm::Type::getInt32Ty(Context), 0x7FFFFFFF); - - // 遍历模块中的所有函数和指令 - for (llvm::Function &F : M) { - for (llvm::BasicBlock &BB : F) { - for (llvm::Instruction &I : BB) { - if (auto *fpInst = llvm::dyn_cast(&I)) { - for (unsigned opIdx = 0; opIdx < fpInst->getNumOperands(); ++opIdx) { - if (auto *cst = llvm::dyn_cast(fpInst->getOperand(opIdx))) { - // 使用 bitcast 检查操作数是否是特定的 NaN 值 - if (cst->getValueAPF().bitcastToAPInt() == SpecialNaN.bitcastToAPInt()) { - // 日志输出 - llvm::errs() << "Replacing special NaN in function " << F.getName() << " at instruction " << *fpInst << "\n"; - - // 替换操作数 - fpInst->setOperand(opIdx, specialValue); - } - } - } - } - } - } - } - - llvm::errs() << "Completed special number handling in the module.\n"; -} - - - - - - +using namespace llvm; // Function to save the IR of a module to a file -void saveModuleIR(llvm::Module &M, const std::string &fileName) { - std::error_code EC; +void +saveModuleIR(llvm::Module & M, const std::string & fileName) +{ + std::error_code EC; llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); - if (EC) { + if (EC) + { llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; return; } @@ -135,27 +94,39 @@ void saveModuleIR(llvm::Module &M, const std::string &fileName) { file.close(); } -void removeQuantizedSuffixInModule(llvm::Module &M) { - for (auto &F : M) { - if (F.hasName()) { +void +removeQuantizedSuffixInModule(llvm::Module & M) +{ + for (auto & F : M) + { + if (F.hasName()) + { std::string FuncName = F.getName().str(); - size_t pos = FuncName.find("_quantized"); - if (pos != std::string::npos) { - FuncName.erase(pos, 10); // Remove "_quantized" + size_t pos = FuncName.find("_quantized"); + if (pos != std::string::npos) + { + FuncName.erase(pos, 10); // Remove "_quantized" F.setName(FuncName); } } } } -void finalCorrectionPass(Module &M, Type *quantizedType) { +void +finalCorrectionPass(Module & M, Type * quantizedType) +{ llvm::errs() << "Entering finalCorrectionPass\n"; - for (Function &F : M) { - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { + for (Function & F : M) + { + for (BasicBlock & BB : F) + { + for (Instruction & I : BB) + { // Correct Load Instructions - if (auto *loadInst = dyn_cast(&I)) { - if (loadInst->getType() != quantizedType) { + if (auto * loadInst = dyn_cast(&I)) + { + if (loadInst->getType() != quantizedType) + { llvm::errs() << "Correcting load instruction: " << *loadInst << "\n"; loadInst->mutateType(quantizedType); @@ -163,12 +134,15 @@ void finalCorrectionPass(Module &M, Type *quantizedType) { } // Correct Store Instructions - if (auto *storeInst = dyn_cast(&I)) { - if (storeInst->getValueOperand()->getType() != quantizedType) { + if (auto * storeInst = dyn_cast(&I)) + { + if (storeInst->getValueOperand()->getType() != quantizedType) + { llvm::errs() << "Correcting store instruction: " << *storeInst << "\n"; storeInst->getValueOperand()->mutateType(quantizedType); } - if (storeInst->getPointerOperand()->getType()->getPointerElementType() != quantizedType) { + if (storeInst->getPointerOperand()->getType()->getPointerElementType() != quantizedType) + { llvm::errs() << "Correcting store pointer operand: " << *storeInst << "\n"; storeInst->getPointerOperand()->mutateType(quantizedType->getPointerTo()); } @@ -179,7 +153,6 @@ void finalCorrectionPass(Module &M, Type *quantizedType) { llvm::errs() << "Exiting finalCorrectionPass\n"; } - void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { @@ -353,6 +326,39 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } + /* + *TODO get sensor info, we only concern the id and precisionBits here + * */ + std::map typePrecisionBits; + if (N->sensorList != NULL) + { + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); + typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); + } + } + + int maxPrecisionBits = 0; + for (auto & typePrecisionBit : typePrecisionBits) + { + if (typePrecisionBit.second > maxPrecisionBits) + { + maxPrecisionBits = typePrecisionBit.second; + } + } + + + flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); + + maxPrecisionBits = 10; + + + + + + /* * get const global variables * */ @@ -435,7 +441,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl for (auto & mi : *Mod) { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert,maxPrecisionBits); } for (auto mi : functionsToInsert) { @@ -444,184 +450,170 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } - - - //Range Analysis - -// for (auto & mi : *Mod) -// { -// rangeAnalysis(mi); -// -// } - - - - - - -// /* -// * analyze the range of all local variables in each function -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// std::map callerMap; -// callerMap.clear(); -// funcBoundInfo.clear(); -// bool useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// shrinkType(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// memoryAlignment(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = true; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// /* -// * simplify the condition of each branch -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// constantSubstitution(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); - + // Range Analysis + + // for (auto & mi : *Mod) + // { + // rangeAnalysis(mi); + // + // } + + // /* + // * analyze the range of all local variables in each function + // * */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // std::map callerMap; + // callerMap.clear(); + // funcBoundInfo.clear(); + // bool useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // shrinkType(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // memoryAlignment(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = true; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + // /* + // * simplify the condition of each branch + // * */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // simplifyControlFlow(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // legacy::PassManager passManager; + // passManager.add(createCFGSimplificationPass()); + // passManager.add(createInstSimplifyLegacyPass()); + // passManager.add(createGlobalDCEPass()); + // passManager.run(*Mod); + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // constantSubstitution(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); // Finally, erase old functions eraseOldFunctions(); eraseOldGlobals(); - - // Perform text replacement to remove "_quantized" suffixes removeQuantizedSuffixInModule(*Mod); - //correct fmul - //correctAllFMulsInModule(Mod.get()); + // correct fmul + // correctAllFMulsInModule(Mod.get()); eraseOldInstructions(); - - - - - // 处理特殊的数值 - //handleSpecialNumber(*Mod, Mod->getContext()); + // handleSpecialNumber(*Mod, Mod->getContext()); - const char* homeDir = getenv("HOME"); - if (!homeDir) { + const char * homeDir = getenv("HOME"); + if (!homeDir) + { llvm::errs() << "Error: HOME environment variable not set.\n"; return; } @@ -629,18 +621,13 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"; saveModuleIR(*Mod, fileName); // Save the optimized IR to a file - //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); - - //finalCorrectionPass(*Mod, quantizedType); - //finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization - //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); - - //替换为$HOMR - - - + // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + // finalCorrectionPass(*Mod, quantizedType); + // finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization + // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + // 替换为$HOMR /* * Dump BC file to a file. @@ -648,4 +635,3 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl dumpIR(N, "output", Mod); llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; } - diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e5f37e851..e63ee7daf 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -33,8 +33,10 @@ POSSIBILITY OF SUCH DAMAGE. #include using namespace llvm; -#define FRAC_Q 10 -#define FRAC_BASE (1 << FRAC_Q) + + +unsigned int FRAC_Q; +#define frac_base (1 << FRAC_Q) #define BIT_WIDTH 32 extern "C" { @@ -46,51 +48,6 @@ isValidFloatingType(Type * type) return type->isFloatTy() || type->isDoubleTy(); } -bool -checkForSpecialFloat(BinaryOperator * MulInst) -{ - // 确保指令是乘法 - if (MulInst->getOpcode() == Instruction::FMul) - { - Value * lhs = MulInst->getOperand(0); - Value * rhs = MulInst->getOperand(1); - - // 检查右侧操作数是否为ConstantFP - if (ConstantFP * rhsConst = dyn_cast(rhs)) - { - // 创建特定的浮点值 - APFloat targetValue = APFloat(APFloat::IEEEdouble(), APInt(64, 0x3FA24924A0000000)); - - // 比较右侧操作数是否等于0x3FA24924A0000000 - if (rhsConst->getValueAPF().bitwiseIsEqual(targetValue)) - { - return true; // 发现目标浮点数 - } - } - } - return false; // 未发现目标浮点数 -} - -bool -isSpecialIEEE754(llvm::Value * value, llvm::IRBuilder<> & builder) -{ - // 创建NaN和Infinity的IEEE表示 - auto * nanValue = llvm::ConstantFP::getNaN(value->getType()); - auto * posInf = llvm::ConstantFP::getInfinity(value->getType(), false); - auto * negInf = llvm::ConstantFP::getInfinity(value->getType(), true); - - // 比较检测 - llvm::Value * isNan = builder.CreateFCmpUNO(value, value); // 检测NaN - llvm::Value * isPosInf = builder.CreateFCmpOEQ(value, posInf); // 检测正无穷 - llvm::Value * isNegInf = builder.CreateFCmpOEQ(value, negInf); // 检测负无穷 - - // 根据需要处理 - if (isNan || isPosInf || isNegInf) - { - return true; // 是特殊值 - } - return false; -} bool isMatrixOperation(Instruction * instr) @@ -292,8 +249,8 @@ shouldSkipFunction(const std::string & functionName) "llvm.dbg.declare", "llvm.dbg.value", "llvm.dbg.label", - "fixmul", - "fixdiv", +// "fixmul", +// "fixdiv", "fixsqrt", "fixrsqrt", "constantMulDiv", @@ -721,7 +678,7 @@ eraseOldInstructions() // if (llvm::Constant *init = globalVar.getInitializer()) { // if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { // double value = constFp->getValueAPF().convertToDouble(); -// int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); +// int64_t quantizedValue = static_cast(round(value * frac_base)); // newInitializer = llvm::ConstantInt::get(quantizedType, quantizedValue); // } // } @@ -769,7 +726,7 @@ updateGlobalVariables(Module * module, Type * quantizedType) if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) { double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(value * frac_base)); globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); } } @@ -835,19 +792,19 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) { // Convert float constant to fixed-point float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; + constValue *= frac_base; int32_t fixedPointValue = round(constValue); // newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / FRAC_BASE); + newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / frac_base); } else if (inValue->getType()->isDoubleTy()) { // Convert double constant to fixed-point and back to double double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; + constValue *= frac_base; int64_t fixedPointValue = round(constValue); // newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / FRAC_BASE); + newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / frac_base); } else { @@ -883,13 +840,13 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // if (inValue->getType()->isFloatTy()) // { // float constValue = constFp->getValueAPF().convertToFloat(); -// constValue *= FRAC_BASE; +// constValue *= frac_base; // newValue = ConstantInt::get(quantizedType, round(constValue), true); // } // else if (inValue->getType()->isDoubleTy()) // { // double constValue = constFp->getValueAPF().convertToDouble(); -// constValue *= FRAC_BASE; +// constValue *= frac_base; // newValue = ConstantInt::get(quantizedType, round(constValue), true); // } // else @@ -1243,9 +1200,9 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext(), "loop", func); // llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(irModule->getContext(), "after", func); @@ -1408,7 +1365,7 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorgetContext()), 1024.0)); + fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), frac_base)); + // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 - fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); + //fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); - // Step 3: int_y = fp_y * FRAC_BASE; - llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + // Step 3: int_y = fp_y * frac_base; + llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), frac_base)), quantizedType); - // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); + // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * frac_base) - (mulfix(mulfix(int_halfx, int_y), int_y)))); // llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); // llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - // llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + // llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * frac_base)), mulfix2); // llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, 10)); - llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, 10)); - llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, 10)); + llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * frac_base)), mulfix2); + llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // Return the final fixed-point result builder.CreateRet(final_y); @@ -1819,7 +1778,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) // if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly - // auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); + // auto quantizedValue = static_cast(round(constValue * frac_base)); // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); // llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; // inInstruction->setOperand(idx, quantizedConst); @@ -1843,7 +1802,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly - // int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + // int64_t quantizedValue = static_cast(round(constValue * frac_base)); // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); // llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; // inInstruction->setOperand(idx, quantizedConst); @@ -1882,7 +1841,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) if (ConstantFP * constFp = dyn_cast(op0)) { float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op0 = ConstantInt::get(quantizedType, quantizedValue); } else @@ -1896,7 +1855,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) if (ConstantFP * constFp = dyn_cast(op1)) { float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op1 = ConstantInt::get(quantizedType, quantizedValue); } else @@ -1910,7 +1869,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) if (ConstantFP * constFp = dyn_cast(op1)) { float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op1 = ConstantInt::get(quantizedType, quantizedValue); } else @@ -1919,20 +1878,20 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } } - // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + // Check if one of the operands is a floating-point constant that needs to be multiplied by frac_base if (ConstantFP * constFp = dyn_cast(op0)) { - // Multiply the constant by FRAC_BASE and convert to integer + // Multiply the constant by frac_base and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op0 = ConstantInt::get(quantizedType, quantizedValue); } if (ConstantFP * constFp = dyn_cast(op1)) { - // Multiply the constant by FRAC_BASE and convert to integer + // Multiply the constant by frac_base and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op1 = ConstantInt::get(quantizedType, quantizedValue); } @@ -1962,7 +1921,7 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) if (ConstantFP * constFp = dyn_cast(op0)) { float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op0 = ConstantInt::get(quantizedType, quantizedValue); } else @@ -1976,7 +1935,7 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) if (ConstantFP * constFp = dyn_cast(op1)) { float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op1 = ConstantInt::get(quantizedType, quantizedValue); } else @@ -1985,20 +1944,20 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } } - // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + // Check if one of the operands is a floating-point constant that needs to be multiplied by frac_base if (ConstantFP * constFp = dyn_cast(op0)) { - // Multiply the constant by FRAC_BASE and convert to integer + // Multiply the constant by frac_base and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op0 = ConstantInt::get(quantizedType, quantizedValue); } if (ConstantFP * constFp = dyn_cast(op1)) { - // Multiply the constant by FRAC_BASE and convert to integer + // Multiply the constant by frac_base and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + int64_t quantizedValue = static_cast(round(constValue * frac_base)); op1 = ConstantInt::get(quantizedType, quantizedValue); } @@ -2063,8 +2022,8 @@ convertToQuantizedType(Value * value, Type * quantizedType, IRBuilder<> & Builde { if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) { - // 如果是浮点类型,首先乘以 FRAC_BASE,然后转换为量化后的整数类型 - Value * scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), FRAC_BASE)); + // 如果是浮点类型,首先乘以 frac_base,然后转换为量化后的整数类型 + Value * scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), frac_base)); return Builder.CreateFPToSI(scaledValue, quantizedType); } else if (value->getType()->isIntegerTy()) @@ -2247,7 +2206,7 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) llvm::Value * mulResult = Builder.CreateMul(lhs, rhs,"", true); // Perform right arithmetic shift - llvm::Value * shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), 10)); + llvm::Value * shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); // Replace all uses of the original instruction with the result of the shift llvmIrInstruction->replaceAllUsesWith(shiftResult); @@ -2624,7 +2583,7 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) // llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; // // // Quantize the value operand -// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); +// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, frac_base)); // quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); // llvmIrStoreInstruction->setOperand(0, quantizedValue); // } @@ -3026,8 +2985,9 @@ quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) // Main function to perform LLVM IR auto quantization void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) { + FRAC_Q = maxPrecisionBits; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; @@ -3202,13 +3162,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; + constValue *= frac_base; newValue = ConstantInt::get(quantizedType, round(constValue), true); } else if (inValue->getType()->isDoubleTy()) { double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; + constValue *= frac_base; newValue = ConstantInt::get(quantizedType, round(constValue), true); } else From 545e000a370b93eb4c2db46cce5f4d3e8a1d81b2 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 25 Sep 2024 11:15:45 +0100 Subject: [PATCH 091/213] add support for quantization and fixrsqrt replacement * dev2. --- .../newton-irPass-LLVMIR-quantization.cpp | 1246 +++++++++-------- 1 file changed, 627 insertions(+), 619 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e63ee7daf..674e51f3e 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -33,10 +33,8 @@ POSSIBILITY OF SUCH DAMAGE. #include using namespace llvm; - - unsigned int FRAC_Q; -#define frac_base (1 << FRAC_Q) +#define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 extern "C" { @@ -48,7 +46,6 @@ isValidFloatingType(Type * type) return type->isFloatTy() || type->isDoubleTy(); } - bool isMatrixOperation(Instruction * instr) { @@ -87,7 +84,6 @@ setQuantizedType(Value * inValue, Type * quantizedType) if (valueType != nullptr) { - // Process pointer types if (valueType->isPointerTy()) { isPointer = true; @@ -223,6 +219,7 @@ replaceFunctionUses(Function & oldFunc, Function * newFunc) CallInst * newCall = CallInst::Create(newFunc, args, "", callInst); newCall->setCallingConv(callInst->getCallingConv()); newCall->setDebugLoc(callInst->getDebugLoc()); + llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; callInst->replaceAllUsesWith(newCall); callInst->eraseFromParent(); } @@ -249,11 +246,8 @@ shouldSkipFunction(const std::string & functionName) "llvm.dbg.declare", "llvm.dbg.value", "llvm.dbg.label", -// "fixmul", -// "fixdiv", "fixsqrt", "fixrsqrt", - "constantMulDiv", "sinf", "llvm.sqrt.f64", "llvm.sqrt.f32", @@ -336,42 +330,17 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) { if (originalType->isArrayTy()) { - // Handle arrays: quantize the element type - Type * elementType = originalType->getArrayElementType(); - Type * newElementType = transformToQuantizedType(elementType, quantizedType); + auto elementType = originalType->getArrayElementType(); + auto newElementType = transformToQuantizedType(elementType, quantizedType); return ArrayType::get(newElementType, originalType->getArrayNumElements()); } - else if (originalType->isPointerTy()) - { - // Handle pointers: quantize the pointee type - Type * pointeeType = originalType->getPointerElementType(); - Type * newPointeeType = transformToQuantizedType(pointeeType, quantizedType); - return PointerType::get(newPointeeType, originalType->getPointerAddressSpace()); - } else if (originalType->isFloatTy() || originalType->isDoubleTy()) { - // Handle floating-point types: replace with quantized type return quantizedType; } // Return original type if no conversion is necessary return originalType; } -// Type * -// transformToQuantizedType(Type * originalType, Type * quantizedType) -//{ -// if (originalType->isArrayTy()) -// { -// auto elementType = originalType->getArrayElementType(); -// auto newElementType = transformToQuantizedType(elementType, quantizedType); -// return ArrayType::get(newElementType, originalType->getArrayNumElements()); -// } -// else if (originalType->isFloatTy() || originalType->isDoubleTy()) -// { -// return quantizedType; -// } -// // Return original type if no conversion is necessary -// return originalType; -// } // llvm::Function *createFixPow(Module *irModule, Type *quantizedType, std::vector &functionsToInsert); @@ -400,6 +369,8 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) // llvmIrCallInstruction->replaceAllUsesWith(fixpowResult); // llvmIrCallInstruction->eraseFromParent(); // } + + void handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) { @@ -432,6 +403,34 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function llvmIrCallInstruction->eraseFromParent(); } +void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) { + llvm::errs() << "Entering handleRsqrtCall\n"; + IRBuilder<> builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + // 检查fixrsqrt是否为空 + if (!fixrsqrt) { + llvm::errs() << "Error: fixrsqrt function is null.\n"; + return; + } + + // 获取调用指令的操作数 + + llvm::errs() << "Operand type before conversion: " << *operand->getType() << "\n"; + + + // 创建fixrsqrt调用替代原始调用 + CallInst * rsqrtResult = builder.CreateCall(fixrsqrt, {operand}); + + // 替换所有使用原始调用的位置为新的结果 + llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); + + // 从其父基本块中删除原始调用 + llvmIrCallInstruction->eraseFromParent(); + llvm::errs() << "Replaced invsqrt with fixrsqrt\n"; +} + + // void // handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) //{ @@ -579,28 +578,7 @@ handleMemCpyCall(Instruction * llvmIrInstruction, Type * quantizedType) } } -void -handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) -{ - IRBuilder<> builder(llvmIrCallInstruction); - - Value * operand = llvmIrCallInstruction->getOperand(0); - - if (operand->getType()->isIntegerTy()) - { - operand = builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, operand); - } - - // Replace the rsqrt call with the custom fixrsqrt function - Value * rsqrtResult = builder.CreateCall(fixrsqrt, {llvmIrCallInstruction->getOperand(0)}); - // Replace all uses of the original rsqrt call with the fixed-point result - llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); - - // Remove the original rsqrt call - llvmIrCallInstruction->eraseFromParent(); -} // A list of global variables to erase after processing std::vector globalsToErase; @@ -641,12 +619,12 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } -std::vector instructionsToErase; +std::vector instructionsToErase; void eraseOldInstructions() { llvm::errs() << "Entering eraseOldInstructions\n"; - for (auto * instr : instructionsToErase) + for (auto *instr : instructionsToErase) { // if (!instr->use_empty()) // { @@ -678,7 +656,7 @@ eraseOldInstructions() // if (llvm::Constant *init = globalVar.getInitializer()) { // if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { // double value = constFp->getValueAPF().convertToDouble(); -// int64_t quantizedValue = static_cast(round(value * frac_base)); +// int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); // newInitializer = llvm::ConstantInt::get(quantizedType, quantizedValue); // } // } @@ -726,7 +704,7 @@ updateGlobalVariables(Module * module, Type * quantizedType) if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) { double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round(value * frac_base)); + int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); } } @@ -792,19 +770,19 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) { // Convert float constant to fixed-point float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= frac_base; + constValue *= FRAC_BASE; int32_t fixedPointValue = round(constValue); // newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / frac_base); + newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / FRAC_BASE); } else if (inValue->getType()->isDoubleTy()) { // Convert double constant to fixed-point and back to double double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= frac_base; + constValue *= FRAC_BASE; int64_t fixedPointValue = round(constValue); // newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / frac_base); + newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / FRAC_BASE); } else { @@ -840,13 +818,13 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // if (inValue->getType()->isFloatTy()) // { // float constValue = constFp->getValueAPF().convertToFloat(); -// constValue *= frac_base; +// constValue *= FRAC_BASE; // newValue = ConstantInt::get(quantizedType, round(constValue), true); // } // else if (inValue->getType()->isDoubleTy()) // { // double constValue = constFp->getValueAPF().convertToDouble(); -// constValue *= frac_base; +// constValue *= FRAC_BASE; // newValue = ConstantInt::get(quantizedType, round(constValue), true); // } // else @@ -900,18 +878,15 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } -void -simplifyConstant(Instruction * inInstruction, Type * quantizedType) -{ +void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Handling constant operand\n"; auto checkDecimal = [](float decimalNum) { int digits = 0; /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) - { + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { decimalNum *= 10; digits++; } @@ -920,82 +895,70 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ float compensateNum = quantizedNum / decimalNum; - Value * constOperand, *nonConstOperand; + Value *constOperand, *nonConstOperand; unsigned constIdx, nonConstIdx; - if (isa(inInstruction->getOperand(0))) - { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); + if (isa(inInstruction->getOperand(0))) { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); nonConstOperand = inInstruction->getOperand(1); - } - else - { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); + } else { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); nonConstOperand = inInstruction->getOperand(0); } auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - IRBuilder<> Builder(inInstruction); + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); + auto instOpCode = inInstruction->getOpcode(); - if (compensateNum == 1) - { + if (compensateNum == 1) { llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; - // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + //newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); inInstruction->setOperand(constIdx, quantizeNumValue); // inInstruction->replaceAllUsesWith(newSecondInst); // //inInstruction->removeFromParent(); // instructionsToErase.push_back(inInstruction); - } - else - { + } else { llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - IRBuilder<> Builder(inInstruction); + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) - { + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) { llvm::errs() << "Handling FMul instruction\n"; - // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newFisrtInst = Builder.CreateNSWMul(nonConstOperand, quantizeNumValue); + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) - { + } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) - { + } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { llvm::errs() << "Handling FDiv instruction with constant numerator\n"; - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } @@ -1004,17 +967,15 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) } }; - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) - { + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { Value * inValue = inInstruction->getOperand(idx); - if (!isa(inValue)) - { + if (!isa(inValue)) { continue; } - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -1048,11 +1009,11 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->replaceAllUsesWith(callInst); inInstruction->removeFromParent(); } -// TODO : Origianl double fixsqrt + // llvm::Function * // createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) //{ -// // Check if irModule is valid +// // check if irModule is valid // if (!irModule) // { // llvm::errs() << "Error: irModule is nullptr\n"; @@ -1069,48 +1030,36 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: // } // } // -// llvm::LLVMContext & context = irModule->getContext(); -// -// // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType // llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixsqrt", irModule); // -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); // llvm::IRBuilder<> builder(entryBB); // // llvm::Function::arg_iterator args = func->arg_begin(); // llvm::Value * x = &*args++; // -// // Convert the fixed-point integer to a floating-point number for sqrt computation -// llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); +// // Initial approximation: x / 2 +// llvm::Value * approx = builder.CreateLShr(x, 1); +// llvm::Value * halfBase = builder.CreateLShr(ConstantInt::get(quantizedType, FRAC_BASE), 1); // -// // Call sqrt on the floating-point value -// llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); -// llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); +// for (int i = 0; i < 5; ++i) +// { // Run the approximation a few times +// llvm::Value * div = builder.CreateSDiv(x, approx); +// llvm::Value * avg = builder.CreateAdd(approx, div); +// approx = builder.CreateLShr(avg, 1); // approx = (approx + x / approx) / 2 +// } // -// // Convert the result back to a fixed-point integer -// llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); +// llvm::Value * result = approx; // Final square root approximation // -// // Perform a left shift to scale the result -// llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); +// // Apply scaling: multiply by FRAC_BASE to maintain fixed-point representation +// result = builder.CreateMul(result, halfBase); // -// // Apply compensation if FRAC_Q is odd -// llvm::Value * finalRes = shlRes; -// if (FRAC_Q % 2 != 0) -// { -// llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); -// llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); -// llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); -// finalRes = builder.CreateFPToSI(compensated, quantizedType); -// } -// -// builder.CreateRet(finalRes); +// builder.CreateRet(result); // -// // Insert the newly created function into the list +// // Add the created function to the vector of functions to be inserted // functionsToInsert.push_back(func); // -// llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; -// // return func; // } @@ -1173,6 +1122,197 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//{ +// // Check if irModule is valid +// if (!irModule) +// { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixSqrtFuncName = "fixsqrt"; +// for (auto & function : *irModule) +// { +// if (function.getName() == fixSqrtFuncName) +// { +// llvm::errs() << "fixsqrt already exists\n"; +// return &function; +// } +// } +// +// llvm::LLVMContext & context = irModule->getContext(); +// +// // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType +// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); +// +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// +// llvm::Function::arg_iterator args = func->arg_begin(); +// llvm::Value * x = &*args++; +// +// // Convert the fixed-point integer to a floating-point number for sqrt computation +// llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); +// +// // Call sqrt on the floating-point value +// llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); +// llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); +// +// // Convert the result back to a fixed-point integer +// llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); +// +// // Perform a left shift to scale the result +// llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); +// +// // Apply compensation if FRAC_Q is odd +// llvm::Value * finalRes = shlRes; +// if (FRAC_Q % 2 != 0) +// { +// llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); +// llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); +// llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); +// finalRes = builder.CreateFPToSI(compensated, quantizedType); +// } +// +// builder.CreateRet(finalRes); +// +// // Insert the newly created function into the list +// functionsToInsert.push_back(func); +// +// llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; +// +// return func; +//} + +// Create a fixed-point reversed square root function +llvm::Function * +createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFixRsqrt\n"; + + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixrsqrtFuncName = "fixrsqrt"; + for (auto & function : *irModule) + { + if (function.getName() == fixrsqrtFuncName) + { + llvm::errs() << "fixrsqrt already exists\n"; + return &function; + } + } + + // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + // Create the function and insert it into the module + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + + // Get the function argument (x) + llvm::Function::arg_iterator args = func->arg_begin(); + llvm::Value * x = &*args++; + + // Create the fixed-point multiplication function + //llvm::Function * fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); + + // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); + // llvm::Value * halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); + llvm::Value * halfBase = builder.CreateLShr(x, ConstantInt::get(quantizedType, 1)); + + // Step 2: Convert x to floating-point and perform the initial approximation + llvm::Value * fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); + + // Added step: fp_y = fp_y / 1024.0; + // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); + + fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + + // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 + //fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); + + llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); + i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + + // Step 3: int_y = fp_y * FRAC_BASE; + llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + + // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); + // llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + // llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + // llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + // llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + + // Return the final fixed-point result + builder.CreateRet(final_y); + functionsToInsert.emplace_back(func); + + return func; +} + +llvm::Function * +createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFloatIntMul\n"; + + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string floatIntMulFuncName = "floatIntMul"; + for (auto & function : *irModule) + { + if (function.getName() == floatIntMulFuncName) + { + llvm::errs() << "floatIntMul already exists\n"; + return &function; + } + } + + llvm::FunctionType * funcType = llvm::FunctionType::get(intType, {floatType, intType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, floatIntMulFuncName, irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + builder.SetInsertPoint(entryBB); + + // Get function arguments + + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * floatArg = &*arg1; + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * intArg = &*arg2; + + // Generate float * int multiplication instruction + llvm::Value * intToFloat = builder.CreateSIToFP(intArg, floatType); + llvm::Value * mulInst = builder.CreateFMul(floatArg, intToFloat); + llvm::Value * floatToInt = builder.CreateFPToSI(mulInst, intType); + + builder.CreateRet(floatToInt); + + functionsToInsert.emplace_back(func); + llvm::errs() << "Created floatIntMul function: " << func->getName() << "\n"; + return func; +} + // llvm::Function* createFixPow(Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { // llvm::errs() << "Entering createFixPow\n"; // @@ -1200,9 +1340,9 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext(), "loop", func); // llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(irModule->getContext(), "after", func); @@ -1237,7 +1377,7 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectoraddIncoming(squaredBase, continueBB); // currentExponent->addIncoming(newExponent, continueBB); @@ -1255,73 +1395,6 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -//{ -// llvm::errs() << "Entering createFixMul\n"; -// -// // Check if irModule is valid -// if (!irModule) -// { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixmulFuncName = "fixmul"; -// for (auto & function : *irModule) -// { -// if (function.getName() == fixmulFuncName) -// { -// llvm::errs() << "fixmul already exists\n"; -// return &function; -// } -// } -// -// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); -// -// // 设置调用约定为 fastcc -// func->setCallingConv(llvm::CallingConv::Fast); -// -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// builder.SetInsertPoint(entryBB); -// -// // Create fixed-point multiplication instruction -// Type * higherQuantizedType; -// switch (BIT_WIDTH) -// { -// case 8: -// higherQuantizedType = Type::getInt16Ty(irModule->getContext()); -// break; -// case 16: -// higherQuantizedType = Type::getInt32Ty(irModule->getContext()); -// break; -// default: -// higherQuantizedType = Type::getInt64Ty(irModule->getContext()); -// break; -// } -// -// llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); -// llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); -// llvm::Function::arg_iterator arg2 = &*(++arg1); -// llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); -// // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); -// // nsw -// llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); -// //llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); -// // lshr -// llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); -// llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); -// builder.CreateRet(truncInst); -// -// functionsToInsert.emplace_back(func); -// llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; -// return func; -//} - -// TODO: i32 version fixmul llvm::Function * createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1344,35 +1417,38 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorsetCallingConv(llvm::CallingConv::Fast); - - // Create entry basic block llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); llvm::IRBuilder<> builder(entryBB); builder.SetInsertPoint(entryBB); - // Get the two arguments of the function - llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value * arg1 = args++; // Get the first argument - llvm::Value * arg2 = args; // Get the second argument - - // Perform i32 multiplication - llvm::Value * mulInst = builder.CreateMul(arg1, arg2); - - // Logical shift right by 10 - llvm::Value * lshrInst = builder.CreateAShr(mulInst, ConstantInt::get(quantizedType, FRAC_Q)); + // Create fixed-point multiplication instruction + Type * higherQuantizedType; + switch (BIT_WIDTH) + { + case 8: + higherQuantizedType = Type::getInt16Ty(irModule->getContext()); + break; + case 16: + higherQuantizedType = Type::getInt32Ty(irModule->getContext()); + break; + default: + higherQuantizedType = Type::getInt64Ty(irModule->getContext()); + break; + } - // Return the result - builder.CreateRet(lshrInst); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); + builder.CreateRet(truncInst); - // Add the function to the list of functions to insert functionsToInsert.emplace_back(func); - llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; return func; } @@ -1414,163 +1490,75 @@ createFixDiv(Module * irModule, Type * quantizedType, std::vectorgetContext()); break; - case 16: - higherQuantizedType = Type::getInt32Ty(irModule->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(irModule->getContext()); - break; - } - - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - - // Multiply numerator by frac_base before division to maintain precision - llvm::Value * mulInst = builder.CreateMul(sext1, ConstantInt::get(higherQuantizedType, frac_base)); - llvm::Value * divInst = builder.CreateSDiv(mulInst, sext2); - llvm::Value * truncInst = builder.CreateTrunc(divInst, quantizedType); - builder.CreateRet(truncInst); - - functionsToInsert.emplace_back(func); - llvm::errs() << "Created fixdiv function: " << func->getName() << "\n"; - return func; -} - -// Create a fixed-point reversed square root function - llvm::Function * - createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -{ - llvm::errs() << "Entering createFixRsqrt\n"; - - // Check if irModule is valid - if (!irModule) - { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } - - std::string fixrsqrtFuncName = "fixrsqrt"; - for (auto & function : *irModule) - { - if (function.getName() == fixrsqrtFuncName) - { - llvm::errs() << "fixrsqrt already exists\n"; - return &function; - } - } - - // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); - // Create the function and insert it into the module - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); - - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - - // Get the function argument (x) - llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value * x = &*args++; - - // Create the fixed-point multiplication function - //llvm::Function * fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); - - // Step 1: int_halfx = mulfix(0.5 * frac_base, x); - // llvm::Value * halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, frac_base / 2), x}); - llvm::Value * halfBase = builder.CreateLShr(x, ConstantInt::get(quantizedType, 1)); - - // Step 2: Convert x to floating-point and perform the initial approximation - llvm::Value * fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); - - // Added step: fp_y = fp_y / 1024.0; - // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); - - fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), frac_base)); - - // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 - //fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); + case 16: + higherQuantizedType = Type::getInt32Ty(irModule->getContext()); + break; + default: + higherQuantizedType = Type::getInt64Ty(irModule->getContext()); + break; + } - llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - // Step 3: int_y = fp_y * frac_base; - llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), frac_base)), quantizedType); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * frac_base) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - // llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - // llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - // llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * frac_base)), mulfix2); - // llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); - llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); - llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * frac_base)), mulfix2); - llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + // Multiply numerator by FRAC_BASE before division to maintain precision + llvm::Value * mulInst = builder.CreateMul(sext1, ConstantInt::get(higherQuantizedType, FRAC_BASE)); + llvm::Value * divInst = builder.CreateSDiv(mulInst, sext2); + llvm::Value * truncInst = builder.CreateTrunc(divInst, quantizedType); + builder.CreateRet(truncInst); - // Return the final fixed-point result - builder.CreateRet(final_y); functionsToInsert.emplace_back(func); - + llvm::errs() << "Created fixdiv function: " << func->getName() << "\n"; return func; } -//llvm::Function *createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vector &functionsToInsert) { -// llvm::errs() << "Entering createFixRsqrt\n"; -// -// if (!irModule) { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } +// Create a fixed-point reversed square root function +// llvm::Function* createFixRsqrt(llvm::Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { // -// std::string fixrsqrtFuncName = "fixrsqrt"; -// for (auto &function : *irModule) { -// if (function.getName() == fixrsqrtFuncName) { -// llvm::errs() << "fixrsqrt already exists\n"; -// return &function; -// } -// } +// // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) +// llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// // Create the function and insert it into the module +// llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); // -// llvm::FunctionType *funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixrsqrtFuncName, irModule); -// llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); // llvm::IRBuilder<> builder(entryBB); // +// // Get the function argument (x) // llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value *x = &*args++; -// llvm::Value *x_ext = builder.CreateSExt(x, llvm::Type::getInt64Ty(irModule->getContext())); // Ensure we use i64 for all operations to prevent overflow +// llvm::Value* x = &*args++; // -// llvm::Value *halfX = builder.CreateLShr(x_ext, 1); +// // Create the fixed-point multiplication function +// llvm::Function* fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); // -// llvm::Value *fpX = builder.CreateSIToFP(x_ext, llvm::Type::getDoubleTy(irModule->getContext())); -// llvm::Value *mulConst = llvm::ConstantFP::get(llvm::Type::getDoubleTy(irModule->getContext()), 0.0009765625); -// llvm::Value *approximation = builder.CreateFMul(fpX, mulConst); +// // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); +// llvm::Value* halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); // -// llvm::Value *intApproximation = builder.CreateBitCast(approximation, llvm::Type::getInt64Ty(irModule->getContext())); -// llvm::Value *shiftedApproximation = builder.CreateAShr(intApproximation, 1); -// llvm::Value *magicNumberSub = builder.CreateSub(llvm::ConstantInt::get(llvm::Type::getInt64Ty(irModule->getContext()), 1597463007), shiftedApproximation); +// // Step 2: Convert x to floating-point and perform the initial approximation +// llvm::Value* fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); +// llvm::Value* i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); +// i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); +// fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // -// llvm::Value *scaledApproximation = builder.CreateShl(magicNumberSub, 10); +// // Step 3: int_y = fp_y * FRAC_BASE; +// llvm::Value* int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); // -// // Inline fixed-point multiplication using mul and ashr -// llvm::Value *firstMul = builder.CreateMul(halfX, scaledApproximation); -// llvm::Value *firstMulShifted = builder.CreateAShr(firstMul, 10, "fixmul_temp"); -// llvm::Value *secondMul = builder.CreateMul(firstMulShifted, scaledApproximation); -// llvm::Value *secondMulShifted = builder.CreateAShr(secondMul, 10, "fixmul_temp"); -// llvm::Value *correction = builder.CreateSub(llvm::ConstantInt::get(llvm::Type::getInt64Ty(irModule->getContext()), 1536), secondMulShifted); +// // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); +// llvm::Value* mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); +// llvm::Value* mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); +// llvm::Value* correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); +// llvm::Value* final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); // -// llvm::Value *finalMul = builder.CreateMul(correction, scaledApproximation); -// llvm::Value *finalResult = builder.CreateAShr(finalMul, 10, "fixmul_final"); -// llvm::Value *truncFinalResult = builder.CreateTrunc(finalResult, quantizedType); // -// builder.CreateRet(truncFinalResult); -// functionsToInsert.push_back(func); +// // Return the final fixed-point result +// builder.CreateRet(final_y); +// functionsToInsert.emplace_back(func); // // return func; //} - // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -1721,15 +1709,13 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, if (instOpCode == Instruction::FMul) { llvm::errs() << "Handling FMul instruction\n"; - // newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newFirstInst = Builder.CreateNSWMul(nonConstOperand, quantizeNumValue); + newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - // newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newFirstInst = Builder.CreateNSWMul(nonConstOperand, compensateNumValue); + newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) @@ -1744,7 +1730,8 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, llvm::errs() << "Replacing old instruction with compensated instruction\n"; inInstruction->replaceAllUsesWith(newSecondInst); inInstruction->eraseFromParent(); - // instructionsToErase.push_back(inInstruction); + //instructionsToErase.push_back(inInstruction); + } else { @@ -1775,10 +1762,10 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) float decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; if (decimalValue == constValue) - // if (fabs(decimalValue - constValue) < 0.001) + //if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly - // auto quantizedValue = static_cast(round(constValue * frac_base)); + // auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); // llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; // inInstruction->setOperand(idx, quantizedConst); @@ -1802,7 +1789,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly - // int64_t quantizedValue = static_cast(round(constValue * frac_base)); + // int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); // llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; // inInstruction->setOperand(idx, quantizedConst); @@ -1824,6 +1811,9 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) } } + + + llvm::errs() << "Exiting handleConstant\n"; } void @@ -1836,68 +1826,55 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) Value * op1 = inInstruction->getOperand(1); // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op0)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op0)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } else { op0 = Builder.CreateFPToSI(op0, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op1)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op1)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } else { op1 = Builder.CreateFPToSI(op1, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op1)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op1)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } else { op1 = Builder.CreateFPToSI(op1, quantizedType); } } - // Check if one of the operands is a floating-point constant that needs to be multiplied by frac_base + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE if (ConstantFP * constFp = dyn_cast(op0)) { - // Multiply the constant by frac_base and convert to integer + // Multiply the constant by FRAC_BASE and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); op0 = ConstantInt::get(quantizedType, quantizedValue); } if (ConstantFP * constFp = dyn_cast(op1)) { - // Multiply the constant by frac_base and convert to integer + // Multiply the constant by FRAC_BASE and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); op1 = ConstantInt::get(quantizedType, quantizedValue); } // Create fixed-point addition - // Value * newInst = Builder.CreateAdd(op0, op1); - Value * newInst = Builder.CreateNSWAdd(op0, op1); + Value * newInst = Builder.CreateAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -1916,54 +1893,45 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) Value * op1 = inInstruction->getOperand(1); // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op0)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op0)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } else { op0 = Builder.CreateFPToSI(op0, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op1)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { + if (ConstantFP *constFp = dyn_cast(op1)) { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } else { op1 = Builder.CreateFPToSI(op1, quantizedType); } } - // Check if one of the operands is a floating-point constant that needs to be multiplied by frac_base + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE if (ConstantFP * constFp = dyn_cast(op0)) { - // Multiply the constant by frac_base and convert to integer + // Multiply the constant by FRAC_BASE and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); op0 = ConstantInt::get(quantizedType, quantizedValue); } if (ConstantFP * constFp = dyn_cast(op1)) { - // Multiply the constant by frac_base and convert to integer + // Multiply the constant by FRAC_BASE and convert to integer float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * frac_base)); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); op1 = ConstantInt::get(quantizedType, quantizedValue); } // Create fixed-point subtraction - // Value * newInst = Builder.CreateSub(op0, op1); - Value * newInst = Builder.CreateNSWSub(op0, op1); + Value * newInst = Builder.CreateSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -2017,25 +1985,25 @@ handleMatrixOperations(Instruction * instr, Type * quantizedType) } } -Value * -convertToQuantizedType(Value * value, Type * quantizedType, IRBuilder<> & Builder) +void +simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) { - if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) - { - // 如果是浮点类型,首先乘以 frac_base,然后转换为量化后的整数类型 - Value * scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), frac_base)); - return Builder.CreateFPToSI(scaledValue, quantizedType); - } - else if (value->getType()->isIntegerTy()) + llvm::IRBuilder<> Builder(instruction); + Type * intType = Type::getInt32Ty(instruction->getContext()); + Value * shiftValue = ConstantInt::get(intType, shiftAmount); + Instruction * shiftInst; + + if (isRightShift) { - // 如果已经是整数类型,则直接返回 - return value; + shiftInst = BinaryOperator::CreateAShr(operand, shiftValue, "", instruction); } else { - llvm::errs() << "Unsupported type for quantization: " << *value->getType() << "\n"; - return nullptr; + shiftInst = BinaryOperator::CreateShl(operand, shiftValue, "", instruction); } + + instruction->replaceAllUsesWith(shiftInst); + instruction->eraseFromParent(); } bool checkAndSimplifyFMul(ConstantFP *constFP, Value *otherOperand, Instruction *instruction) { @@ -2064,27 +2032,26 @@ bool checkAndSimplifyFMul(ConstantFP *constFP, Value *otherOperand, Instruction return false; } -void -simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) +Value * +convertToQuantizedType(Value * value, Type * quantizedType, IRBuilder<> & Builder) { - llvm::IRBuilder<> Builder(instruction); - Type * intType = Type::getInt32Ty(instruction->getContext()); - Value * shiftValue = ConstantInt::get(intType, shiftAmount); - Instruction * shiftInst; - - if (isRightShift) + if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) { - shiftInst = BinaryOperator::CreateAShr(operand, shiftValue, "", instruction); + // 如果是浮点类型,首先乘以 FRAC_BASE,然后转换为量化后的整数类型 + Value * scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), FRAC_BASE)); + return Builder.CreateFPToSI(scaledValue, quantizedType); + } + else if (value->getType()->isIntegerTy()) + { + // 如果已经是整数类型,则直接返回 + return value; } else { - shiftInst = BinaryOperator::CreateShl(operand, shiftValue, "", instruction); + llvm::errs() << "Unsupported type for quantization: " << *value->getType() << "\n"; + return nullptr; } - - instruction->replaceAllUsesWith(shiftInst); - instruction->eraseFromParent(); } - bool checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruction * instruction) { @@ -2107,7 +2074,6 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct // New check for the special multiplication case return checkAndSimplifyFMul(constFP, otherOperand, instruction); } - void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -2214,6 +2180,59 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) } } +// void +// handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) +//{ +// llvm::errs() << "Handling FMul\n"; +// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; +// IRBuilder<> Builder(llvmIrInstruction); +// +// // Ensure operands are correctly converted to fixed-point integers +// Value * lhs = llvmIrInstruction->getOperand(0); +// Value * rhs = llvmIrInstruction->getOperand(1); +// +// llvm::errs() << "LHS: " << *lhs << "\n"; +// llvm::errs() << "RHS: " << *rhs << "\n"; +// +// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); +// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); +// +// // If either operand is a float, convert both to fixed-point +// if (lhsIsFloat) +// { +// lhs = Builder.CreateFPToSI(lhs, quantizedType); +// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; +// } +// if (rhsIsFloat) +// { +// rhs = Builder.CreateFPToSI(rhs, quantizedType); +// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; +// } +// +// // Ensure both operands are now integers +// bool lhsIsInteger = lhs->getType()->isIntegerTy(); +// bool rhsIsInteger = rhs->getType()->isIntegerTy(); +// +// if (lhsIsInteger && rhsIsInteger) +// { +// if (isa(lhs) || isa(rhs)) +// { +// llvm::errs() << "One of the operands is a constant, simplifying...\n"; +// handleConstant(llvmIrInstruction, quantizedType); +// } +// else +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); +// llvmIrInstruction->replaceAllUsesWith(callInst); +// llvmIrInstruction->eraseFromParent(); +// } +// } +// } +// } + void handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -2257,12 +2276,12 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType) else { llvm::errs() << "Both operands are integers, substituting with fixdiv function...\n"; -// { -// Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); -// llvmIrInstruction->replaceAllUsesWith(newInst); -// llvmIrInstruction->eraseFromParent(); -// llvm::errs() << "Finished handling FDiv\n"; -// } + // { + // Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); + // llvmIrInstruction->replaceAllUsesWith(newInst); + // llvmIrInstruction->eraseFromParent(); + // llvm::errs() << "Finished handling FDiv\n"; + // } { IRBuilder<> Builder(llvmIrInstruction); // Extend both operands to i64 @@ -2305,37 +2324,64 @@ handleFRem(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FRem\n"; } -void -handleFNeg(Instruction * inInstruction, Type * quantizedType) -{ + + +//void +//handleFNeg(Instruction * inInstruction, Type * quantizedType) +//{ +// llvm::errs() << "Handling FNeg\n"; +// IRBuilder<> Builder(inInstruction); +// +// // Get the operand for the FNeg operation +// Value * operand = inInstruction->getOperand(0); +// llvm::errs() << "Operand: " << *operand << "\n"; +// +// // Process the operand and quantize constants +// //quantizeConstant(inInstruction, quantizedType); +// +// // If the operand is a floating-point type, quantize it +//// if (operand->getType()->isFloatingPointTy()) +//// { +//// llvm::errs() << "FNEG Quantizing floating-point operand\n"; +//// operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); +//// operand = Builder.CreateFPToSI(operand, quantizedType); +//// } +// +// // Perform the negation in fixed-point arithmetic +// // Fixed-point negation is equivalent to integer negation +// Value * newInst = Builder.CreateNeg(operand); +// //Value * newInst = Builder.CreateNSWNeg(operand); +// +// // Replace the original FNeg instruction with the new fixed-point negation +// inInstruction->replaceAllUsesWith(newInst); +// inInstruction->eraseFromParent(); +// +// llvm::errs() << "Finished handling FNeg\n"; +//} + +void handleFNeg(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Handling FNeg\n"; IRBuilder<> Builder(inInstruction); // Get the operand for the FNeg operation Value * operand = inInstruction->getOperand(0); + llvm::errs() << "Operand: " << *operand << "\n"; - // Process the operand and quantize constants - quantizeConstant(inInstruction, quantizedType); - // If the operand is a floating-point type, quantize it - if (operand->getType()->isFloatingPointTy()) - { - // operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); - operand = Builder.CreateFPToSI(operand, quantizedType); - } + // Create a constant zero of the same integer type as the operand + Value * zero = ConstantInt::get(operand->getType(), 0); - // Perform the negation in fixed-point arithmetic - // Fixed-point negation is equivalent to integer negation - // Value * newInst = Builder.CreateNeg(operand); - Value * newInst = Builder.CreateNSWNeg(operand); + // Perform the integer subtraction: sub 0, operand + Value * newInst = Builder.CreateSub(zero, operand); - // Replace the original FNeg instruction with the new fixed-point negation + // Replace the original FNeg instruction with the new subtraction inInstruction->replaceAllUsesWith(newInst); inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FNeg\n"; + llvm::errs() << "Finished handling FNeg with integer subtraction\n"; } + void handleFCmp(Instruction * inInstruction, Type * quantizedType) { @@ -2370,6 +2416,7 @@ handleFCmp(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FCmp\n"; } } + void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -2377,58 +2424,17 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - // Print the original type for debugging - llvm::errs() << "Original alloca type: " << *allocaType << "\n"; - - // Transform to quantized type considering pointers - Type * newAllocaType = nullptr; - if (allocaType->isPointerTy()) - { - // Get the element type of the pointer - Type * elementType = allocaType->getPointerElementType(); - - // Quantize the element type - Type * quantizedElementType = transformToQuantizedType(elementType, quantizedType); + // 递归转换所有层级的类型为量化类型 + Type * newAllocaType = transformToQuantizedType(allocaType, quantizedType); - // Reconstruct the pointer type with the new quantized element type - newAllocaType = PointerType::get(quantizedElementType, allocaType->getPointerAddressSpace()); - } - else - { - // Quantize non-pointer types directly - newAllocaType = transformToQuantizedType(allocaType, quantizedType); - } + llvm::errs() << "Original alloca type: " << *allocaType << "\n"; + llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; - if (newAllocaType) - { - llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; - llvmIrAllocaInstruction->setAllocatedType(newAllocaType); - } - else - { - llvm::errs() << "Error: Failed to quantize the type.\n"; - } + // Set the new quantized type for the alloca instruction + llvmIrAllocaInstruction->setAllocatedType(newAllocaType); } } -// void -// handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) -// { -// auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); -// -// // 递归转换所有层级的类型为量化类型 -// Type * newAllocaType = transformToQuantizedType(allocaType, quantizedType); -// -// llvm::errs() << "Original alloca type: " << *allocaType << "\n"; -// llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; -// -// // Set the new quantized type for the alloca instruction -// llvmIrAllocaInstruction->setAllocatedType(newAllocaType); -// } -// } - // void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { // if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) { // // 获取原始的分配类型 @@ -2459,12 +2465,25 @@ handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) void // handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) return; + std::string funcName = calledFunction->getName().str(); + + if (funcName == "invSqrt"|| funcName == "invSqrt_quantized") { + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + // 特定处理 invsqrt 调用 + llvm::errs() << "Handling invsqrt call\n"; + handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); + // } + } + if (!calledFunction->getName().startswith("llvm.dbg.value") && !calledFunction->getName().startswith("llvm.dbg.declare") && !calledFunction->getName().startswith("llvm.dbg.label")) @@ -2477,7 +2496,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); + //std::string funcName = calledFunction->getName().str(); if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64" || funcName == "llvm.sqrt.f32") { @@ -2508,6 +2527,8 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetNumOperands() - 1; idx++) - { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); + // for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) + // { + // setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); + // } + // quantizeConstant(llvmIrCallInstruction, quantizedType); // Quantize the return type if necessary if (llvmIrCallInstruction->getType()->isPointerTy()) @@ -2562,43 +2583,33 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); -} + if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) + { + IRBuilder<> Builder(llvmIrStoreInstruction); -// void -// handleStore(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) -// { -// IRBuilder<> Builder(llvmIrStoreInstruction); -// -// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); -// if (valueType->isFloatTy() || valueType->isDoubleTy()) -// { -// llvm::errs() << "Original store value type: " << *valueType << "\n"; -// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; -// -// // Quantize the value operand -// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, frac_base)); -// quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); -// llvmIrStoreInstruction->setOperand(0, quantizedValue); -// } -// -// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); -// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) -// { -// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; -// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; -// -// // Set the new quantized type for the pointer operand -// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); -// } -// } -// } + auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); + if (valueType->isFloatTy() || valueType->isDoubleTy()) + { + llvm::errs() << "Original store value type: " << *valueType << "\n"; + llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; + + // Quantize the value operand + auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); + quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); + llvmIrStoreInstruction->setOperand(0, quantizedValue); + } + + auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + { + llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; + llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; + + // Set the new quantized type for the pointer operand + llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + } + } +} // Function to handle loads involving pointer types void @@ -2810,7 +2821,7 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) { // Erase the unnecessary FPTrunc instruction - llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); + llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(1)); llvmIrInstruction->eraseFromParent(); llvm::errs() << "Removed unnecessary FPTrunc\n"; } @@ -2827,8 +2838,8 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) } } -// void -// handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +//void +//handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) //{ // llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; // IRBuilder<> Builder(llvmIrInstruction); @@ -2845,19 +2856,14 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) // if (operand->getType()->isIntegerTy()) // { // // If it's an integer, convert it to a floating-point value -// llvm::errs() << "Operand is an integer, converting to floating-point type.\n"; // newInst = Builder.CreateSIToFP(operand, llvmIrInstruction->getType()); // } // else // { // // Otherwise, perform the FPTrunc as a floating-point cast -// llvm::errs() << "Operand is a floating-point, performing FPTrunc.\n"; // newInst = Builder.CreateFPCast(operand, llvmIrInstruction->getType()); // } // -// // Log the type of the new instruction created -// llvm::errs() << "New instruction type after conversion: " << *newInst->getType() << "\n"; -// // // Replace all uses of the original FPTrunc with the new instruction // llvmIrInstruction->replaceAllUsesWith(newInst); // @@ -2865,7 +2871,7 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) // llvmIrInstruction->removeFromParent(); // // llvm::errs() << "Finished handling FPTrunc\n"; -// } +//} void handleBitCast(Instruction * llvmIrInstruction, Type * quantizedType) @@ -2921,16 +2927,15 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) switch (llvmIrInstruction->getOpcode()) { - // case Instruction::Alloca: - // handleAlloca(llvmIrInstruction, quantizedType); - // break; - // case Instruction::Store: - // handleStore(llvmIrInstruction, quantizedType); - // break; - // case Instruction::Load: - // handleLoad(llvmIrInstruction, quantizedType); - // break; - + case Instruction::Alloca: + handleAlloca(llvmIrInstruction, quantizedType); + break; + case Instruction::Store: + handleStore(llvmIrInstruction, quantizedType); + break; + case Instruction::Load: + handleLoad(llvmIrInstruction, quantizedType); + break; case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: @@ -2985,7 +2990,7 @@ quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) // Main function to perform LLVM IR auto quantization void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert,int maxPrecisionBits) { FRAC_Q = maxPrecisionBits; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); @@ -3042,16 +3047,17 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve updateGlobalVariables(module, quantizedType); /* - * generate hardcode function + * generate hardcode function - fixmul and fixdiv * */ - llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + + llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); /* * quantize the arguments type * */ - quantizeFunctionArguments(llvmIrFunction, quantizedType); + //quantizeFunctionArguments(llvmIrFunction, quantizedType); // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) @@ -3068,8 +3074,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(llvmIrInstruction, quantizedType); break; case Instruction::Call: - // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); break; case Instruction::GetElementPtr: @@ -3162,13 +3167,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= frac_base; + constValue *= FRAC_BASE; newValue = ConstantInt::get(quantizedType, round(constValue), true); } else if (inValue->getType()->isDoubleTy()) { double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= frac_base; + constValue *= FRAC_BASE; newValue = ConstantInt::get(quantizedType, round(constValue), true); } else @@ -3217,6 +3222,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FPExt: case Instruction::FPTrunc: case Instruction::BitCast: + // log + llvm::errs() << "handle bitcast\n"; handleBitCast(llvmIrInstruction, quantizedType); break; @@ -3256,6 +3263,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } } } + // handleLoadStoreInstructions(llvmIrFunction, quantizedType); adaptTypeCast(llvmIrFunction, quantizedType); return; From 7065d908550d4cb4b411d40aaaf27bd642595550 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Thu, 26 Sep 2024 20:45:02 +0100 Subject: [PATCH 092/213] calucate Mean error and max error for diffferent FRAC_Q * dev2. --- applications/newton/llvm-ir/fracq.py | 59 ++++++++++++++++++++++++++++ 1 file changed, 59 insertions(+) create mode 100644 applications/newton/llvm-ir/fracq.py diff --git a/applications/newton/llvm-ir/fracq.py b/applications/newton/llvm-ir/fracq.py new file mode 100644 index 000000000..c0f13827f --- /dev/null +++ b/applications/newton/llvm-ir/fracq.py @@ -0,0 +1,59 @@ +import numpy as np + +# Function to parse the data from the files after removing the prefix +def parse_data(file_path, prefix_to_remove): + """ + Reads a file and extracts q0, q1, q2, q3 values from each line after removing the specified prefix. + Returns a numpy array of the extracted values. + """ + with open(file_path, 'r') as file: + data = [] + for line in file: + if line.startswith(prefix_to_remove): + # Remove the prefix and strip the line + clean_line = line.replace(prefix_to_remove, "").strip() + values = clean_line.split(", ") + # Extract q0, q1, q2, q3 values and convert them to floats + parsed_values = [float(value.split("=")[1]) for value in values] + data.append(parsed_values) + + # Debug: Print the number of parsed lines and the first few entries + if len(data) == 0: + print(f"No data found in file: {file_path}") + else: + print(f"Parsed {len(data)} lines from {file_path}") + print("First few entries:", data[:3]) + + return np.array(data) + +# Load and clean the data from both files +fp_data = parse_data('fp_result.txt', prefix_to_remove="Original: ") +int_data = parse_data('int_result.txt', prefix_to_remove="FIX: ") + +# Check if data is loaded correctly +if fp_data.size == 0 or int_data.size == 0: + print("Error: One of the datasets is empty. Please check the input files.") +else: + # Calculate the absolute difference between the floating point and integer results + error = np.abs(fp_data - int_data) + + # Calculate the mean and max errors for each q value (q0, q1, q2, q3) + mean_error = np.mean(error, axis=0) + max_error = np.max(error, axis=0) + + # Display the results + print("Mean Error for each q value:") + print(f"q0: {mean_error[0]:.6f}, q1: {mean_error[1]:.6f}, q2: {mean_error[2]:.6f}, q3: {mean_error[3]:.6f}") + + print("\nMax Error for each q value:") + print(f"q0: {max_error[0]:.6f}, q1: {max_error[1]:.6f}, q2: {max_error[2]:.6f}, q3: {max_error[3]:.6f}") + + # Display the summary of errors in a dictionary + error_summary = { + "Mean Error": mean_error, + "Max Error": max_error + } + + # Print the summary dictionary + print("\nError Summary:") + print(error_summary) From 6865fa4b66d77af92c57dbcaa570565ea1256778 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 27 Sep 2024 10:21:15 +0100 Subject: [PATCH 093/213] update gloabal version MadgwickAHRS * dev2. --- ...5e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt | 48 +++++++++ ...0042da8466e5dc21a11a2cb96c3d21c1071f09.txt | 48 +++++++++ .../newton/llvm-ir/c-files/MadgwickAHRS.c | 102 +++++++----------- .../newton/llvm-ir/c-files/MadgwickAHRS.h | 11 +- 4 files changed, 139 insertions(+), 70 deletions(-) create mode 100644 analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt create mode 100644 analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt diff --git a/analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt b/analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt new file mode 100644 index 000000000..af5377cee --- /dev/null +++ b/analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt @@ -0,0 +1,48 @@ + +changeset: 1686:545e000a370b93eb4c2db46cce5f4d3e8a1d81b2 +char kNewtonVersion[] = "0.3-alpha-1686 (545e000a370b93eb4c2db46cce5f4d3e8a1d81b2) (build 09-26-2024-20:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt b/analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt new file mode 100644 index 000000000..9c178a33a --- /dev/null +++ b/analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt @@ -0,0 +1,48 @@ + +changeset: 1685:d10042da8466e5dc21a11a2cb96c3d21c1071f09 +char kNewtonVersion[] = "0.3-alpha-1685 (d10042da8466e5dc21a11a2cb96c3d21c1071f09) (build 09-25-2024-11:15-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index 3e3853379..ce3875775 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -16,6 +16,7 @@ // Header files #include "MadgwickAHRS.h" +#include //--------------------------------------------------------------------------------------------------- // Definitions @@ -25,38 +26,26 @@ #define sampleFreq 28.0f // sample frequency in Hz #define betaDef 0.1f // 2 * proportional gain -//#ifndef lowerBound -//#define lowerBound -16 -//#endif -//#ifndef upperBound -//#define upperBound 16 -//#endif +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; //--------------------------------------------------------------------------------------------------- // Variable definitions volatile float beta = betaDef; // 2 * proportional gain (Kp) -//volatile float q0 = 0.64306622f, q1 = 0.02828862f, q2 = -0.00567953f, q3 = -0.76526684f; // quaternion of sensor frame relative to auxiliary frame +volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame //--------------------------------------------------------------------------------------------------- // Function declarations -//--------------------------------------------------------------------------------------------------- -// Fast inverse square-root -// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root - -// 1/sqrtf(); -float invSqrt(float x) { - float halfx = 0.5f * x; - float y = x; - //#pragma unsupported - long i = *(long*)&y; - i = 0x5f3759df - (i>>1); - y = *(float*)&i; - //#end - y = y * (1.5f - (halfx * y * y)); - return y; -} +float invSqrt(float x); //==================================================================================================== // Functions @@ -64,26 +53,19 @@ float invSqrt(float x) { //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { - float q0 = *q0_ptr; - float q1 = *q1_ptr; - float q2 = *q2_ptr; - float q3 = *q3_ptr; -//#ifdef ASSUME -// __builtin_assume(ax > lowerBound && ax < upperBound); -// __builtin_assume(ay > lowerBound && ay < upperBound); -// __builtin_assume(az > lowerBound && az < upperBound); -//#endif +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz) { float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; float hx, hy; float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + // warpPrint("In MadgwickAHRSupdate!!\n"); // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az); return; } @@ -93,12 +75,17 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); +// printf("qDot1 = %f\n", qDot1); +// printf("qDot2 = %f\n", qDot2); +// printf("qDot3 = %f\n", qDot3); +// printf("qDot4 = %f\n", qDot4); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { // Normalise accelerometer measurement recipNorm = invSqrt(ax * ax + ay * ay + az * az); - // printf("1: %f\n", recipNorm); ax *= recipNorm; ay *= recipNorm; az *= recipNorm; @@ -134,7 +121,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float // Reference direction of Earth's magnetic field hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; - _2bx = sqrt(hx * hx + hy * hy); + _2bx = sqrtf(hx * hx + hy * hy); _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; _4bx = 2.0f * _2bx; _4bz = 2.0f * _2bz; @@ -165,35 +152,16 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); - // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", - // q0, q1, q2, q3, recipNorm); q0 *= recipNorm; q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; - - //q0,q1,q2,q3 -// printf("Final quaternion value: q0 = %d\n", q0); -// printf("Final quaternion value: q1 = %d\n", q1); -// printf("Final quaternion value: q2 = %d\n", q2); -// printf("Final quaternion value: q3 = %d\n", q3); - - // printf("Original: q0 = %f\n", q0); } //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { - float q0 = *q0_ptr; - float q1 = *q1_ptr; - float q2 = *q2_ptr; - float q3 = *q3_ptr; +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) { float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; @@ -259,12 +227,22 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; } //==================================================================================================== // END OF CODE -//==================================================================================================== \ No newline at end of file +//==================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.h b/applications/newton/llvm-ir/c-files/MadgwickAHRS.h index 5e6d5df9e..45336d01b 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.h +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.h @@ -13,22 +13,17 @@ #ifndef MadgwickAHRS_h #define MadgwickAHRS_h -#include -#include - //---------------------------------------------------------------------------------------------------- // Variable declaration // extern volatile float beta; // algorithm gain -// extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame +extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame //--------------------------------------------------------------------------------------------------- // Function declarations -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); #endif //===================================================================================================== From 14d2e5a37fd670198ad313cd3adca507275521ba Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 27 Sep 2024 11:13:33 +0100 Subject: [PATCH 094/213] 9/27 save before change * dev2. --- ...65d908550d4cb4b411d40aaaf27bd642595550.txt | 48 +++++++ .../newton-irPass-LLVMIR-quantization.cpp | 119 +++--------------- 2 files changed, 65 insertions(+), 102 deletions(-) create mode 100644 analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt diff --git a/analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt b/analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt new file mode 100644 index 000000000..3401f3bd5 --- /dev/null +++ b/analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt @@ -0,0 +1,48 @@ + +changeset: 1687:7065d908550d4cb4b411d40aaaf27bd642595550 +char kNewtonVersion[] = "0.3-alpha-1687 (7065d908550d4cb4b411d40aaaf27bd642595550) (build 09-27-2024-10:21-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 674e51f3e..accbe685c 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -342,34 +342,6 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) return originalType; } -// llvm::Function *createFixPow(Module *irModule, Type *quantizedType, std::vector &functionsToInsert); - -// void handlePowCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert) -//{ -// IRBuilder<> Builder(llvmIrCallInstruction); -// auto base = llvmIrCallInstruction->getArgOperand(0); -// auto exponent = llvmIrCallInstruction->getArgOperand(1); -// -// // Convert the operands to the appropriate type for fixpow (which expects quantizedType) -// Value * quantizedBase = Builder.CreateFPToSI(base, quantizedType); -// Value * quantizedExponent = Builder.CreateFPToSI(exponent, quantizedType); -// -// // Get or create the fixpow function -// Function * fixpowFunc = llvmIrCallInstruction->getModule()->getFunction("fixpow"); -// if (!fixpowFunc) -// { -// // If fixpow function doesn't exist, create it -// fixpowFunc = createFixPow(llvmIrCallInstruction->getModule(), quantizedType, functionsToInsert); -// } -// -// // Create call to fixpow with the quantized operands -// Value * fixpowResult = Builder.CreateCall(fixpowFunc, {quantizedBase, quantizedExponent}); -// -// // Replace the original pow call with the fixpow result -// llvmIrCallInstruction->replaceAllUsesWith(fixpowResult); -// llvmIrCallInstruction->eraseFromParent(); -// } - void handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) @@ -430,60 +402,6 @@ void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Fun llvm::errs() << "Replaced invsqrt with fixrsqrt\n"; } - -// void -// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) -//{ -// IRBuilder<> Builder(llvmIrCallInstruction); -// auto operand = llvmIrCallInstruction->getOperand(0); -// -// // Cast the instruction to CallInst to access getCalledFunction method -// CallInst * callInst = dyn_cast(llvmIrCallInstruction); -// if (!callInst) -// { -// llvm::errs() << "Error: Instruction is not a CallInst.\n"; -// return; -// } -// -// // Convert integer operand to float for sqrt computation -// Value * newOperand = Builder.CreateSIToFP(operand, Type::getDoubleTy(llvmIrCallInstruction->getContext())); -// -// // Get the correct sqrt function with double return and argument types -// Function * sqrtFunc = llvmIrCallInstruction->getModule()->getFunction("sqrt"); -// if (!sqrtFunc) -// { -// FunctionType * sqrtType = FunctionType::get(Type::getDoubleTy(llvmIrCallInstruction->getContext()), {Type::getDoubleTy(llvmIrCallInstruction->getContext())}, false); -// sqrtFunc = Function::Create(sqrtType, Function::ExternalLinkage, "sqrt", llvmIrCallInstruction->getModule()); -// } -// -// // Create call to sqrt function with the double operand -// Value * sqrtResult = Builder.CreateCall(sqrtFunc, {newOperand}); -// -// // Convert the result back to integer -// Value * fptosiInst = Builder.CreateFPToSI(sqrtResult, quantizedType); -// // Perform left shift for scaling -// Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); -// Value * resInst = nullptr; -// -// // If FRAC_Q is odd, apply compensation -// if (FRAC_Q % 2) -// { -// Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); -// auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); -// Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); -// resInst = Builder.CreateFPToSI(mulInst, quantizedType); -// llvm::errs() << "Compensation applied\n"; -// } -// else -// { -// resInst = shlInst; -// } -// -// llvmIrCallInstruction->replaceAllUsesWith(resInst); -// llvmIrCallInstruction->eraseFromParent(); -// } -// } - void handleSinCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { @@ -1233,8 +1151,8 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext())); - // Added step: fp_y = fp_y / 1024.0; - // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1024.0)); + // Added step: fp_y = fp_y / FRAC_BASE.0; + // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE.0)); fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); @@ -2287,8 +2205,8 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType) // Extend both operands to i64 Value *lhsExt = Builder.CreateSExt(lhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "lhs.ext"); Value *rhsExt = Builder.CreateSExt(rhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "rhs.ext"); - // Multiply the lhs extended value by 1024 for scaling - Value *scaledNumerator = Builder.CreateMul(lhsExt, ConstantInt::get(Type::getInt64Ty(llvmIrInstruction->getContext()), 1024), "scaled.numerator"); + // Multiply the lhs extended value by FRAC_BASE for scaling + Value *scaledNumerator = Builder.CreateMul(lhsExt, ConstantInt::get(Type::getInt64Ty(llvmIrInstruction->getContext()), FRAC_BASE), "scaled.numerator"); // Perform the division Value *divResult = Builder.CreateSDiv(scaledNumerator, rhsExt, "div.result"); // Truncate the result back to i32 @@ -2467,6 +2385,7 @@ void // handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { + llvm::errs() << "Handling Call\n"; Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) return; @@ -2504,17 +2423,6 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetOperand(0), quantizedType); +// quantizeConstant(llvmIrInstruction, quantizedType); +//} + // Function to handle loads involving pointer types void handlePointerLoad(LoadInst * llvmIrLoadInstruction, IRBuilder<> & Builder, Type * quantizedType) @@ -2821,7 +2739,7 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) { // Erase the unnecessary FPTrunc instruction - llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(1)); + llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); llvmIrInstruction->eraseFromParent(); llvm::errs() << "Removed unnecessary FPTrunc\n"; } @@ -3041,9 +2959,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleFunctionSignature(llvmIrFunction, quantizedType); - // Update global variable type to integer type - - // Update global variables + // Update global variables to integer type updateGlobalVariables(module, quantizedType); /* @@ -3056,7 +2972,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * quantize the arguments type * */ - //quantizeFunctionArguments(llvmIrFunction, quantizedType); // Process each instruction From 9a89e5bca53bce0f88c5325b33ebb7453d53171e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 29 Sep 2024 22:44:43 +0100 Subject: [PATCH 095/213] calucate Mean error and max error for diffferent FRAC_Q * dev2. --- .../newton/llvm-ir/c-files/MadgwickAHRS.c | 102 +-- .../newton/llvm-ir/c-files/MadgwickAHRS.h | 13 +- .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 77 +- applications/newton/llvm-ir/replace.sh | 15 +- applications/newton/llvm-ir/testMadgwick.sh | 22 +- .../newton-irPass-LLVMIR-quantization.cpp | 657 ++++++++++-------- .../newton-irPass-LLVMIR-quantization.h | 2 +- 7 files changed, 521 insertions(+), 367 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index ce3875775..4600adf5f 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -16,7 +16,6 @@ // Header files #include "MadgwickAHRS.h" -#include //--------------------------------------------------------------------------------------------------- // Definitions @@ -26,26 +25,38 @@ #define sampleFreq 28.0f // sample frequency in Hz #define betaDef 0.1f // 2 * proportional gain -typedef float bmx055xAcceleration; -typedef float bmx055yAcceleration; -typedef float bmx055zAcceleration; -typedef float bmx055xAngularRate; -typedef float bmx055yAngularRate; -typedef float bmx055zAngularRate; -typedef float bmx055xMagneto; -typedef float bmx055yMagneto; -typedef float bmx055zMagneto; +#ifndef lowerBound +#define lowerBound -16 +#endif +#ifndef upperBound +#define upperBound 16 +#endif //--------------------------------------------------------------------------------------------------- // Variable definitions volatile float beta = betaDef; // 2 * proportional gain (Kp) -volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame +//volatile float q0 = 0.64306622f, q1 = 0.02828862f, q2 = -0.00567953f, q3 = -0.76526684f; // quaternion of sensor frame relative to auxiliary frame //--------------------------------------------------------------------------------------------------- // Function declarations -float invSqrt(float x); +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +// 1/sqrtf(); +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + //#pragma unsupported + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + //#end + y = y * (1.5f - (halfx * y * y)); + return y; +} //==================================================================================================== // Functions @@ -53,19 +64,26 @@ float invSqrt(float x); //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, - bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, - bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz) { +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; +#ifdef ASSUME + __builtin_assume(ax > lowerBound && ax < upperBound); + __builtin_assume(ay > lowerBound && ay < upperBound); + __builtin_assume(az > lowerBound && az < upperBound); +#endif float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; float hx, hy; float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; - // warpPrint("In MadgwickAHRSupdate!!\n"); // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az); + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); return; } @@ -75,17 +93,12 @@ void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAng qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); -// printf("qDot1 = %f\n", qDot1); -// printf("qDot2 = %f\n", qDot2); -// printf("qDot3 = %f\n", qDot3); -// printf("qDot4 = %f\n", qDot4); - - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { // Normalise accelerometer measurement recipNorm = invSqrt(ax * ax + ay * ay + az * az); + // printf("1: %f\n", recipNorm); ax *= recipNorm; ay *= recipNorm; az *= recipNorm; @@ -121,7 +134,7 @@ void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAng // Reference direction of Earth's magnetic field hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; - _2bx = sqrtf(hx * hx + hy * hy); + _2bx = sqrt(hx * hx + hy * hy); _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; _4bx = 2.0f * _2bx; _4bz = 2.0f * _2bz; @@ -152,16 +165,35 @@ void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAng // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", + // q0, q1, q2, q3, recipNorm); q0 *= recipNorm; q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + + //q0,q1,q2,q3 + // printf("Final quaternion value: q0 = %d\n", q0); + // printf("Final quaternion value: q1 = %d\n", q1); + // printf("Final quaternion value: q2 = %d\n", q2); + // printf("Final quaternion value: q3 = %d\n", q3); + + // printf("Original: q0 = %f\n", q0); } //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) { +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; @@ -227,22 +259,12 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; -} - -//--------------------------------------------------------------------------------------------------- -// Fast inverse square-root -// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root - -float invSqrt(float x) { - float halfx = 0.5f * x; - float y = x; - long i = *(long*)&y; - i = 0x5f3759df - (i>>1); - y = *(float*)&i; - y = y * (1.5f - (halfx * y * y)); - return y; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; } //==================================================================================================== // END OF CODE -//==================================================================================================== +//==================================================================================================== \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.h b/applications/newton/llvm-ir/c-files/MadgwickAHRS.h index 45336d01b..4178f35fb 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.h +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.h @@ -13,19 +13,24 @@ #ifndef MadgwickAHRS_h #define MadgwickAHRS_h +#include +#include + //---------------------------------------------------------------------------------------------------- // Variable declaration // extern volatile float beta; // algorithm gain -extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame +// extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame //--------------------------------------------------------------------------------------------------- // Function declarations -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); #endif //===================================================================================================== // End of file -//===================================================================================================== +//===================================================================================================== \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index e2445c954..b7b585e06 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -2,14 +2,16 @@ #include #include #include +#include #include #include #include #define FRAC_Q 10 +#define FRAC_BASE (1<current = stop - p_tbl->start; -// if (p_tbl->max < p_tbl->current) { -// p_tbl->max = p_tbl->current; -// } -// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { -// p_tbl->min = p_tbl->current; -// } -//} +typedef struct { + uint64_t start; + uint64_t current; + uint64_t min; + uint64_t max; +} ELAPSED_TIME; + +ELAPSED_TIME elapsed_time_tbl[10]; + +static inline uint64_t rdtsc() { + uint32_t lo, hi; + __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); + return ((uint64_t)hi << 32) | lo; +} + +void elapsed_time_start(uint32_t i) { + elapsed_time_tbl[i].start = rdtsc(); +} + +void elapsed_time_stop(uint32_t i) { + uint64_t stop = rdtsc(); + ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; + p_tbl->current = stop - p_tbl->start; + if (p_tbl->max < p_tbl->current) { + p_tbl->max = p_tbl->current; + } + if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { + p_tbl->min = p_tbl->current; + } +} typedef struct timespec timespec; @@ -253,7 +255,7 @@ toc(timespec * start_time, const char * prefix) u_int64_t time_slots[ITERATION]; for (size_t idx = 0; idx < ITERATION; idx++) { - //elapsed_time_start(idx); // 开始计时 + elapsed_time_start(idx); // 开始计时 timespec timer = tic(); for (size_t ts = 0; ts < DATA_SIZE; ts++) { MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], @@ -264,7 +266,7 @@ toc(timespec * start_time, const char * prefix) } - //elapsed_time_stop(idx); // 结束计时 + elapsed_time_stop(idx); // 结束计时 time_slots[idx] = toc(&timer, "computation delay").tv_nsec; } @@ -275,11 +277,14 @@ toc(timespec * start_time, const char * prefix) average_time /= ITERATION; printf("average time = %lu nm\n", average_time); -// // 打印出每次迭代的最大、最小和当前时间 -// for (size_t idx = 0; idx < ITERATION; idx++) { + // 打印出每次迭代的最大、最小和当前时间 + for (size_t idx = 0; idx < ITERATION; idx++) { // printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", // idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); -// } + + + printf("Cycle count for iteration %zu: %lu cycles\n", idx, elapsed_time_tbl[idx].current); + } FILE *fptr = fopen("int_result.txt", "w"); for (size_t ts = 0; ts < DATA_SIZE; ts++) { diff --git a/applications/newton/llvm-ir/replace.sh b/applications/newton/llvm-ir/replace.sh index f148735e8..d40c7ea20 100755 --- a/applications/newton/llvm-ir/replace.sh +++ b/applications/newton/llvm-ir/replace.sh @@ -10,18 +10,23 @@ if [ ! -f "$file" ]; then fi # Use sed to replace the text -sed -i 's/declare dso_local i32 @printf(i8\*)/declare dso_local i32 @printf(i8\*, i32)/g' "$file" +#sed -i 's/declare dso_local i32 @printf(i8\*)/declare dso_local i32 @printf(i8\*, i32)/g' "$file" -# Replace float** with i32** first to avoid conflicting replacements +#Replace float** with i32** first to avoid conflicting replacements sed -i 's/float\*\*/i32**/g' "$file" - -# Replace float* with i32* +# +##Replace float* with i32* sed -i 's/float\*/i32*/g' "$file" # Now replace the call to invSqrt with the call to fixrsqrt sed -i 's/call i32 @invSqrt/call i32 @fixrsqrt/g' "$file" +# Replace the specific call to MadgwickAHRSupdateIMU +#sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(float %0, float %1, float %2, float %3, float %4, float %5, i32* %119, i32* %120, i32* %121, i32* %122)/g' "$file" +#sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(float %0, float %1, float %2, float %3, float %4, float %5)/g' "$file" +#sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(i32 %83, i32 %84, i32 %85, i32 %86, i32 %87, i32 %88, i32* %89, i32* %90, i32* %91, i32* %92)/g' "$file" - +# Remove all occurrences of "_quantized" in the file +sed -i 's/_quantized//g' "$file" echo "Replacement complete." diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 49dfe8045..91d4f1bc2 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -11,6 +11,8 @@ clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOM # Step 2: Use newton for optimization and quantization echo "Step 2: Use newton for optimization and quantization" +#cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/BMX055.nt + cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/test.nt # # Step 3: Convert generated bytecode file to LLVM IR file @@ -19,12 +21,14 @@ echo "Step 3: Convert generated bytecode file to LLVM IR file" cd $HOME/CoSense/applications/newton/llvm-ir/&& ./replace.sh $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -python3 replace.py +#python3 replace.py # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode @@ -45,8 +49,18 @@ ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applicat # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + + +#clang $HOME/CoSense/applications/newton/llvm-ir/c-files/newtest.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/c-files/newtest -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" -$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file +$HOME/CoSense/applications/newton/llvm-ir/main_out + + +#$HOME/CoSense/applications/newton/llvm-ir/c-files/newtest \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index accbe685c..ab6cdebdb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -341,47 +341,78 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) // Return original type if no conversion is necessary return originalType; } - - -void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +void handleSqrtCall(CallInst *llvmIrCallInstruction, Type *quantizedType,Function *fixsqrt) { IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); - - // Cast the instruction to CallInst to access getCalledFunction method - CallInst * callInst = dyn_cast(llvmIrCallInstruction); - if (!callInst) - { - llvm::errs() << "Error: Instruction is not a CallInst.\n"; - return; - } + auto operand = llvmIrCallInstruction->getOperand(0); - // Convert the operand to fixed-point format if necessary + // Ensure operand is in the correct type if (operand->getType()->isFloatingPointTy()) { operand = Builder.CreateFPToSI(operand, quantizedType); } - // Create call to the fixed-point sqrt function - llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - if (auto * call = dyn_cast(sqrtResult)) - { - call->setTailCall(true); - } + // Convert the operand from fixed-point (int32) to float + llvm::Value *operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + + // Call llvm.sqrt.f32 to compute the square root of the float value + llvm::Value *sqrtFloat = Builder.CreateCall( + Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), + {operandAsFloat}); - // No need to apply shl and compensation if it's already done in createFixSqrt - llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); + // Convert the result back to int32 + llvm::Value *sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType ); + + // Apply the shift-left operation (shl i32 %result, 5) + llvm::Value *shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); + + // Replace the original instruction with the new fixed-point sqrt result + llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); llvmIrCallInstruction->eraseFromParent(); } -void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) { +//void +//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto operand = llvmIrCallInstruction->getOperand(0); +// +// // Cast the instruction to CallInst to access getCalledFunction method +// CallInst * callInst = dyn_cast(llvmIrCallInstruction); +// if (!callInst) +// { +// llvm::errs() << "Error: Instruction is not a CallInst.\n"; +// return; +// } +// +// // Convert the operand to fixed-point format if necessary +// if (operand->getType()->isFloatingPointTy()) +// { +// operand = Builder.CreateFPToSI(operand, quantizedType); +// } +// +// // Create call to the fixed-point sqrt function +// llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); +// if (auto * call = dyn_cast(sqrtResult)) +// { +// call->setTailCall(true); +// } +// +// // No need to apply shl and compensation if it's already done in createFixSqrt +// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); +// llvmIrCallInstruction->eraseFromParent(); +//} + +void +handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) +{ llvm::errs() << "Entering handleRsqrtCall\n"; IRBuilder<> builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); // 检查fixrsqrt是否为空 - if (!fixrsqrt) { + if (!fixrsqrt) + { llvm::errs() << "Error: fixrsqrt function is null.\n"; return; } @@ -390,7 +421,6 @@ void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Fun llvm::errs() << "Operand type before conversion: " << *operand->getType() << "\n"; - // 创建fixrsqrt调用替代原始调用 CallInst * rsqrtResult = builder.CreateCall(fixrsqrt, {operand}); @@ -496,8 +526,6 @@ handleMemCpyCall(Instruction * llvmIrInstruction, Type * quantizedType) } } - - // A list of global variables to erase after processing std::vector globalsToErase; @@ -537,12 +565,12 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } -std::vector instructionsToErase; +std::vector instructionsToErase; void eraseOldInstructions() { llvm::errs() << "Entering eraseOldInstructions\n"; - for (auto *instr : instructionsToErase) + for (auto * instr : instructionsToErase) { // if (!instr->use_empty()) // { @@ -796,15 +824,18 @@ quantizeConstant(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FloatIntMul\n"; // } -void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { +void +simplifyConstant(Instruction * inInstruction, Type * quantizedType) +{ llvm::errs() << "Handling constant operand\n"; auto checkDecimal = [](float decimalNum) { int digits = 0; /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) + { decimalNum *= 10; digits++; } @@ -813,70 +844,81 @@ void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ float compensateNum = quantizedNum / decimalNum; - Value *constOperand, *nonConstOperand; + Value * constOperand, *nonConstOperand; unsigned constIdx, nonConstIdx; - if (isa(inInstruction->getOperand(0))) { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); + if (isa(inInstruction->getOperand(0))) + { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); nonConstOperand = inInstruction->getOperand(1); - } else { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); + } + else + { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); nonConstOperand = inInstruction->getOperand(0); } auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - IRBuilder<> Builder(inInstruction); + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); + auto instOpCode = inInstruction->getOpcode(); - if (compensateNum == 1) { + if (compensateNum == 1) + { llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; - //newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); inInstruction->setOperand(constIdx, quantizeNumValue); // inInstruction->replaceAllUsesWith(newSecondInst); // //inInstruction->removeFromParent(); // instructionsToErase.push_back(inInstruction); - } else { + } + else + { llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - IRBuilder<> Builder(inInstruction); + IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) { + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) + { llvm::errs() << "Handling FMul instruction\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) + { llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) + { llvm::errs() << "Handling FDiv instruction with constant numerator\n"; - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } @@ -885,15 +927,17 @@ void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { } }; - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { Value * inValue = inInstruction->getOperand(idx); - if (!isa(inValue)) { + if (!isa(inValue)) + { continue; } - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; if (inValue->getType()->isFloatTy()) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -928,58 +972,7 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->removeFromParent(); } -// llvm::Function * -// createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -//{ -// // check if irModule is valid -// if (!irModule) -// { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixSqrtFuncName = "fixsqrt"; -// for (auto & function : *irModule) -// { -// if (function.getName() == fixSqrtFuncName) -// { -// llvm::errs() << "fixsqrt already exists\n"; -// return &function; -// } -// } -// -// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixsqrt", irModule); -// -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// -// llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value * x = &*args++; -// -// // Initial approximation: x / 2 -// llvm::Value * approx = builder.CreateLShr(x, 1); -// llvm::Value * halfBase = builder.CreateLShr(ConstantInt::get(quantizedType, FRAC_BASE), 1); -// -// for (int i = 0; i < 5; ++i) -// { // Run the approximation a few times -// llvm::Value * div = builder.CreateSDiv(x, approx); -// llvm::Value * avg = builder.CreateAdd(approx, div); -// approx = builder.CreateLShr(avg, 1); // approx = (approx + x / approx) / 2 -// } -// -// llvm::Value * result = approx; // Final square root approximation -// -// // Apply scaling: multiply by FRAC_BASE to maintain fixed-point representation -// result = builder.CreateMul(result, halfBase); -// -// builder.CreateRet(result); -// -// // Add the created function to the vector of functions to be inserted -// functionsToInsert.push_back(func); -// -// return func; -// } + // TODO : float version rsqrt llvm::Function * @@ -1041,9 +1034,11 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//TODO : fix version rsqrt +//llvm::Function *createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vector &functionsToInsert) //{ +// llvm::errs() << "Entering createFixRsqrt\n"; +// // // Check if irModule is valid // if (!irModule) // { @@ -1051,62 +1046,72 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext(); -// -// // Function type: returns quantizedType (e.g., i32), takes one argument of quantizedType -// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); +// // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) +// llvm::FunctionType *funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); +// llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixrsqrtFuncName, irModule); // -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); -// llvm::IRBuilder<> builder(entryBB); +// // Create entry basic block and the IR builder +// llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); // +// // Get the function argument (x) // llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value * x = &*args++; -// -// // Convert the fixed-point integer to a floating-point number for sqrt computation -// llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getDoubleTy(context)); -// -// // Call sqrt on the floating-point value -// llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getDoubleTy(context)); -// llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); -// -// // Convert the result back to a fixed-point integer -// llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); -// -// // Perform a left shift to scale the result -// llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); -// -// // Apply compensation if FRAC_Q is odd -// llvm::Value * finalRes = shlRes; -// if (FRAC_Q % 2 != 0) +// llvm::Value *x = &*args++; +// +// // Find the leading bit position (equivalent of log2(x) computation) +// llvm::Value *m = builder.CreateSub( +// llvm::ConstantInt::get(quantizedType, sizeof(int32_t) * 8 - 1 - FRAC_Q), +// builder.CreateCall(llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::ctlz, {quantizedType}), {x, llvm::ConstantInt::getFalse(irModule->getContext())}), +// "m" +// ); +// +// // Lookup table for initial estimate based on m +// llvm::ArrayType *Y_est_type = llvm::ArrayType::get(quantizedType, 31); +// llvm::Constant *Y_est = llvm::ConstantDataArray::get( +// irModule->getContext(), +// llvm::ArrayRef({0xB50, 0x800, 0x5A8, 0x400, 0x2D4, 0x200, 0x16A, 0x100, 0xB5, 0x80, 0x5A, 0x40, 0x2D, 0x20, 0x16, 0x10, 0xB, 0x8, 0x5, 0x4, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}) +// ); +// llvm::Value *Yi = builder.CreateLoad(Y_est_type, Y_est, builder.CreateAdd(m, llvm::ConstantInt::get(quantizedType, 7))); +// +// // Fixed-point multiplication: xi = x * Yi / FRAC_BASE +// llvm::Value *xi = builder.CreateAShr(builder.CreateMul(x, Yi), llvm::ConstantInt::get(quantizedType, FRAC_Q)); +// llvm::Value *yi = Yi; +// +// // Perform the iterative Goldschmidt's method (3 iterations) +// for (int i = 0; i < 3; ++i) // { -// llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getDoubleTy(context), 1.414213562); -// llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getDoubleTy(context)); -// llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); -// finalRes = builder.CreateFPToSI(compensated, quantizedType); +// llvm::Value *bi = builder.CreateAShr(builder.CreateMul(builder.CreateMul(x, yi), yi), llvm::ConstantInt::get(quantizedType, FRAC_Q * 2)); +// Yi = builder.CreateAShr(builder.CreateSub( +// llvm::ConstantInt::get(quantizedType, 3 * FRAC_BASE), +// bi), +// llvm::ConstantInt::get(quantizedType, 1) +// ); +// +// // Update xi and yi +// xi = builder.CreateAShr(builder.CreateMul(xi, Yi), llvm::ConstantInt::get(quantizedType, FRAC_Q)); +// yi = builder.CreateAShr(builder.CreateMul(yi, Yi), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // } // -// builder.CreateRet(finalRes); -// -// // Insert the newly created function into the list -// functionsToInsert.push_back(func); -// -// llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; +// // Return the final reciprocal square root result +// builder.CreateRet(builder.CreateSelect(builder.CreateICmpSGT(yi, llvm::ConstantInt::get(quantizedType, 0)), yi, llvm::ConstantInt::get(quantizedType, 1))); +// functionsToInsert.emplace_back(func); // // return func; //} -// Create a fixed-point reversed square root function + + +//TODO Original version of fixrsqrt llvm::Function * createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1142,7 +1147,7 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE.0)); - fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + //fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + + fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 - //fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); + // fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; @@ -1648,8 +1655,7 @@ compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, llvm::errs() << "Replacing old instruction with compensated instruction\n"; inInstruction->replaceAllUsesWith(newSecondInst); inInstruction->eraseFromParent(); - //instructionsToErase.push_back(inInstruction); - + // instructionsToErase.push_back(inInstruction); } else { @@ -1680,7 +1686,7 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) float decimalValue = checkDecimal(constValue); llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; if (decimalValue == constValue) - //if (fabs(decimalValue - constValue) < 0.001) + // if (fabs(decimalValue - constValue) < 0.001) { // If the decimal part is already an integer, quantize directly // auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); @@ -1729,9 +1735,6 @@ handleConstant(Instruction * inInstruction, Type * quantizedType) } } - - - llvm::errs() << "Exiting handleConstant\n"; } void @@ -1744,32 +1747,44 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) Value * op1 = inInstruction->getOperand(1); // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op0)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op0)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op0 = Builder.CreateFPToSI(op0, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op1)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op1)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op1 = Builder.CreateFPToSI(op1, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op1)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op1)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op1 = Builder.CreateFPToSI(op1, quantizedType); } } @@ -1792,7 +1807,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point addition - Value * newInst = Builder.CreateAdd(op0, op1); + Value * newInst = Builder.CreateNSWAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -1811,22 +1826,30 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) Value * op1 = inInstruction->getOperand(1); // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op0)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op0)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op0 = Builder.CreateFPToSI(op0, quantizedType); } } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) { - if (ConstantFP *constFp = dyn_cast(op1)) { - float constValue = constFp->getValueAPF().convertToFloat(); + if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) + { + if (ConstantFP * constFp = dyn_cast(op1)) + { + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } else { + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + else + { op1 = Builder.CreateFPToSI(op1, quantizedType); } } @@ -1849,7 +1872,7 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point subtraction - Value * newInst = Builder.CreateSub(op0, op1); + Value * newInst = Builder.CreateNSWSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -1924,23 +1947,27 @@ simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, instruction->eraseFromParent(); } -bool checkAndSimplifyFMul(ConstantFP *constFP, Value *otherOperand, Instruction *instruction) { - APFloat targetValue(0.28f); // 初始化时使用单精度浮点数 - bool losesInfo; +bool +checkAndSimplifyFMul(ConstantFP * constFP, Value * otherOperand, Instruction * instruction) +{ + APFloat targetValue(0.28f); // 初始化时使用单精度浮点数 + bool losesInfo; APFloat::opStatus status = targetValue.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo); // 转换为双精度浮点格式 // 检查转换是否成功 - if (status != APFloat::opOK) { + if (status != APFloat::opOK) + { llvm::errs() << "Error: Precision loss or other conversion error occurred.\n"; } // 比较常数值 - if (constFP->getValueAPF().compare(targetValue) == APFloat::cmpEqual) { + if (constFP->getValueAPF().compare(targetValue) == APFloat::cmpEqual) + { llvm::IRBuilder<> Builder(instruction); // 创建除数常数 (28.0) - Value *divisor = ConstantFP::get(instruction->getType(), 28.0); + Value * divisor = ConstantFP::get(instruction->getType(), 28.0); // 创建除法指令 - Instruction *divInst = cast(Builder.CreateFDiv(otherOperand, divisor, "divby28")); + Instruction * divInst = cast(Builder.CreateFDiv(otherOperand, divisor, "divby28")); // 替换原始指令 instruction->replaceAllUsesWith(divInst); @@ -1992,6 +2019,25 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct // New check for the special multiplication case return checkAndSimplifyFMul(constFP, otherOperand, instruction); } + + +//64bit fixed point multiplication +llvm::Value* performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value* lhs, llvm::Value* rhs, unsigned FRAC_Q) { + // Sign extend the 32-bit operands to 64-bit integers + llvm::Value* lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value* rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + + // Perform 64-bit multiplication + llvm::Value* mulResult64 = Builder.CreateMul(lhs64, rhs64); + + // Right shift the result to simulate fixed-point division by FRAC_Q + llvm::Value* divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + + // Truncate the 64-bit result back to 32-bit integer + llvm::Value* result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + + return result32; +} void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -2012,7 +2058,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) ConstantFP * lhsConst = dyn_cast(lhs); ConstantFP * rhsConst = dyn_cast(rhs); - // Check if either operand is a constant that can be simplified if (auto rhsConst = dyn_cast(rhs)) { @@ -2083,19 +2128,80 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) // } if (lhsIsInteger && rhsIsInteger) + + //缩两边 +// { +// llvm::errs() << "Both operands are integers, applying scaling to prevent overflow...\n"; +// +// // Scale operands down by FRAC_Q before multiplication +// llvm::Value* lhsScaled = Builder.CreateAShr(lhs, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "lhsScaled"); +// llvm::Value* rhsScaled = Builder.CreateAShr(rhs, llvm::ConstantInt::get(rhs->getType(), FRAC_Q / 2), "rhsScaled"); +// +// // Perform 32-bit multiplication +// llvm::Value* mulResult = Builder.CreateMul(lhsScaled, rhsScaled, "mulScaled"); +// +//// // Right shift to maintain precision, compensating for the scaling +//// llvm::Value* finalResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "shiftResult"); +// +// // Replace all uses of the original instruction with the final result +// llvmIrInstruction->replaceAllUsesWith(mulResult); +// llvmIrInstruction->eraseFromParent(); + +// } + + + // 64位 { - llvm::errs() << "Both operands are integers, performing inline multiplication and shifting...\n"; + llvm::errs() << "Both operands are integers, performing 64-bit multiplication and shifting...\n"; - // Perform multiplication directly - llvm::Value * mulResult = Builder.CreateMul(lhs, rhs,"", true); +// // Sign extend the 32-bit operands to 64-bit integers to prevent overflow +// llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); +// llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); +// +// // Perform 64-bit multiplication +// llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); +// +// // Divide the result by FRAC_Q (equivalent to right shift) +// llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q), "div64"); +// +// // Truncate the 64-bit result back to 32-bit integer +// llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); - // Perform right arithmetic shift - llvm::Value * shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); + llvm::Value* result32 = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); - // Replace all uses of the original instruction with the result of the shift - llvmIrInstruction->replaceAllUsesWith(shiftResult); + // Replace all uses of the original instruction with the final result + llvmIrInstruction->replaceAllUsesWith(result32); llvmIrInstruction->eraseFromParent(); } + + // 正常的32位 + // { + // llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; + // + // // Perform multiplication directly + // llvm::Value * mulResult = Builder.CreateMul(lhs, rhs,""); + // + // // Perform right arithmetic shift + // llvm::Value * shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); + // + // // Replace all uses of the original instruction with the result of the shift + // llvmIrInstruction->replaceAllUsesWith(shiftResult); + // llvmIrInstruction->eraseFromParent(); + // } + // { + // // 先移再乘 + // llvm::errs() << "Both operands are integers, performing inline shifting and multiplication...\n"; + // + // // Perform right arithmetic shift on lhs first + // llvm::Value * shiftedLhs = Builder.CreateAShr(lhs, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); + // + // // Perform multiplication with shifted lhs + // llvm::Value * mulResult = Builder.CreateMul(shiftedLhs, rhs); + // + // // Replace all uses of the original instruction with the result of the multiplication + // llvmIrInstruction->replaceAllUsesWith(mulResult); + // llvmIrInstruction->eraseFromParent(); + // } } // void @@ -2203,14 +2309,14 @@ handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType) { IRBuilder<> Builder(llvmIrInstruction); // Extend both operands to i64 - Value *lhsExt = Builder.CreateSExt(lhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "lhs.ext"); - Value *rhsExt = Builder.CreateSExt(rhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "rhs.ext"); + Value * lhsExt = Builder.CreateSExt(lhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "lhs.ext"); + Value * rhsExt = Builder.CreateSExt(rhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "rhs.ext"); // Multiply the lhs extended value by FRAC_BASE for scaling - Value *scaledNumerator = Builder.CreateMul(lhsExt, ConstantInt::get(Type::getInt64Ty(llvmIrInstruction->getContext()), FRAC_BASE), "scaled.numerator"); + Value * scaledNumerator = Builder.CreateMul(lhsExt, ConstantInt::get(Type::getInt64Ty(llvmIrInstruction->getContext()), FRAC_BASE), "scaled.numerator"); // Perform the division - Value *divResult = Builder.CreateSDiv(scaledNumerator, rhsExt, "div.result"); + Value * divResult = Builder.CreateSDiv(scaledNumerator, rhsExt, "div.result"); // Truncate the result back to i32 - Value *truncatedResult = Builder.CreateTrunc(divResult, Type::getInt32Ty(llvmIrInstruction->getContext()), "result.trunc"); + Value * truncatedResult = Builder.CreateTrunc(divResult, Type::getInt32Ty(llvmIrInstruction->getContext()), "result.trunc"); // Replace all uses of the original instruction with this new sequence llvmIrInstruction->replaceAllUsesWith(truncatedResult); llvmIrInstruction->eraseFromParent(); @@ -2242,10 +2348,8 @@ handleFRem(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Finished handling FRem\n"; } - - -//void -//handleFNeg(Instruction * inInstruction, Type * quantizedType) +// void +// handleFNeg(Instruction * inInstruction, Type * quantizedType) //{ // llvm::errs() << "Handling FNeg\n"; // IRBuilder<> Builder(inInstruction); @@ -2277,7 +2381,9 @@ handleFRem(Instruction * inInstruction, Type * quantizedType) // llvm::errs() << "Finished handling FNeg\n"; //} -void handleFNeg(Instruction * inInstruction, Type * quantizedType) { +void +handleFNeg(Instruction * inInstruction, Type * quantizedType) +{ llvm::errs() << "Handling FNeg\n"; IRBuilder<> Builder(inInstruction); @@ -2285,7 +2391,6 @@ void handleFNeg(Instruction * inInstruction, Type * quantizedType) { Value * operand = inInstruction->getOperand(0); llvm::errs() << "Operand: " << *operand << "\n"; - // Create a constant zero of the same integer type as the operand Value * zero = ConstantInt::get(operand->getType(), 0); @@ -2299,7 +2404,6 @@ void handleFNeg(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Finished handling FNeg with integer subtraction\n"; } - void handleFCmp(Instruction * inInstruction, Type * quantizedType) { @@ -2392,7 +2496,8 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "invSqrt"|| funcName == "invSqrt_quantized") { + if (funcName == "invSqrt" || funcName == "invSqrt_quantized") + { IRBuilder<> Builder(llvmIrCallInstruction); Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); @@ -2415,7 +2520,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); + // std::string funcName = calledFunction->getName().str(); if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64" || funcName == "llvm.sqrt.f32") { @@ -2423,7 +2528,6 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector(llvmIrInstruction)) +// { +// IRBuilder<> Builder(llvmIrStoreInstruction); +// +// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); +// if (valueType->isFloatTy() || valueType->isDoubleTy()) +// { +// llvm::errs() << "Original store value type: " << *valueType << "\n"; +// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; +// +// // Quantize the value operand +// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); +// quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); +// llvmIrStoreInstruction->setOperand(0, quantizedValue); +// } +// +// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); +// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) +// { +// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; +// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; +// +// // Set the new quantized type for the pointer operand +// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); +// } +// } +// } + void handleStore(Instruction * llvmIrInstruction, Type * quantizedType) { - if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) - { - IRBuilder<> Builder(llvmIrStoreInstruction); - - auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); - if (valueType->isFloatTy() || valueType->isDoubleTy()) - { - llvm::errs() << "Original store value type: " << *valueType << "\n"; - llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; - - // Quantize the value operand - auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); - quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); - llvmIrStoreInstruction->setOperand(0, quantizedValue); - } - - auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); - if (pointerType->isFloatTy() || pointerType->isDoubleTy()) - { - llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; - llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; - - // Set the new quantized type for the pointer operand - llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); - } - } + /* + * If either of the operands is constant, change it to a int value + * */ + setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + quantizeConstant(llvmIrInstruction, quantizedType); } -//void -//handleStore(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// /* -// * If either of the operands is constant, change it to a int value -// * */ -// setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); -// quantizeConstant(llvmIrInstruction, quantizedType); -//} - // Function to handle loads involving pointer types void handlePointerLoad(LoadInst * llvmIrLoadInstruction, IRBuilder<> & Builder, Type * quantizedType) @@ -2756,8 +2859,8 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) } } -//void -//handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +// void +// handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) //{ // llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; // IRBuilder<> Builder(llvmIrInstruction); @@ -2789,7 +2892,7 @@ handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) // llvmIrInstruction->removeFromParent(); // // llvm::errs() << "Finished handling FPTrunc\n"; -//} +// } void handleBitCast(Instruction * llvmIrInstruction, Type * quantizedType) @@ -2908,7 +3011,7 @@ quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) // Main function to perform LLVM IR auto quantization void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert,int maxPrecisionBits) +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) { FRAC_Q = maxPrecisionBits; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); @@ -2966,13 +3069,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * generate hardcode function - fixmul and fixdiv * */ - llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); /* * quantize the arguments type * */ - //quantizeFunctionArguments(llvmIrFunction, quantizedType); + // quantizeFunctionArguments(llvmIrFunction, quantizedType); // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 7826b6f4e..8cbe12721 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -39,7 +39,7 @@ extern std::vector functionsToErase; extern std::vector globalsToErase; extern std::vector instructionsToErase; void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); extern From 1401b11217ed30b60e34051bffa718f2bbb8c551 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 29 Sep 2024 22:45:55 +0100 Subject: [PATCH 096/213] made some changes and use i64 mul * dev2. --- ...d2e5a37fd670198ad313cd3adca507275521ba.txt | 48 +++++++++++++++++++ ...65fa4b66d77af92c57dbcaa570565ea1256778.txt | 48 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt create mode 100644 analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt diff --git a/analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt b/analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt new file mode 100644 index 000000000..b02a63f08 --- /dev/null +++ b/analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt @@ -0,0 +1,48 @@ + +changeset: 1689:14d2e5a37fd670198ad313cd3adca507275521ba +char kNewtonVersion[] = "0.3-alpha-1689 (14d2e5a37fd670198ad313cd3adca507275521ba) (build 09-29-2024-22:44-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt b/analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt new file mode 100644 index 000000000..95a54b545 --- /dev/null +++ b/analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt @@ -0,0 +1,48 @@ + +changeset: 1688:6865fa4b66d77af92c57dbcaa570565ea1256778 +char kNewtonVersion[] = "0.3-alpha-1688 (6865fa4b66d77af92c57dbcaa570565ea1256778) (build 09-27-2024-11:13-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From d158d0a4397166e63b19ece97c4a95ddb1c51315 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 29 Sep 2024 22:47:32 +0100 Subject: [PATCH 097/213] made some changes and use i64 mul * dev2. --- ...89e5bca53bce0f88c5325b33ebb7453d53171e.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt diff --git a/analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt b/analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt new file mode 100644 index 000000000..44d89999c --- /dev/null +++ b/analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt @@ -0,0 +1,48 @@ + +changeset: 1690:9a89e5bca53bce0f88c5325b33ebb7453d53171e +char kNewtonVersion[] = "0.3-alpha-1690 (9a89e5bca53bce0f88c5325b33ebb7453d53171e) (build 09-29-2024-22:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 3d074aa0fa141b085ce178b6316724d3fcea382f Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 29 Sep 2024 22:50:11 +0100 Subject: [PATCH 098/213] use i64 mul * dev2. --- ...01b11217ed30b60e34051bffa718f2bbb8c551.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt diff --git a/analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt b/analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt new file mode 100644 index 000000000..a9fbb0e45 --- /dev/null +++ b/analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt @@ -0,0 +1,48 @@ + +changeset: 1691:1401b11217ed30b60e34051bffa718f2bbb8c551 +char kNewtonVersion[] = "0.3-alpha-1691 (1401b11217ed30b60e34051bffa718f2bbb8c551) (build 09-29-2024-22:47-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 6771b9a2ea9c18664318972c2e53e9808f432731 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 30 Sep 2024 10:44:22 +0100 Subject: [PATCH 099/213] calucate Mean error and max error for diffferent FRAC_Q * dev2. --- .../newton/llvm-ir/c-files/MadgwickAHRS.c | 3 +- applications/newton/llvm-ir/testMadgwick.sh | 4 +- .../newton-irPass-LLVMIR-quantization.cpp | 119 +++++++++--------- 3 files changed, 62 insertions(+), 64 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index 4600adf5f..90917828e 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -22,7 +22,8 @@ // #define sampleFreq 512.0f // sample frequency in Hz // #define sampleFreq 100.0f // sample frequency in Hz -#define sampleFreq 28.0f // sample frequency in Hz +//#define sampleFreq 28.0f // sample frequency in Hz +#define sampleFreq 128.0f // sample frequency in Hz #define betaDef 0.1f // 2 * proportional gain #ifndef lowerBound diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 91d4f1bc2..76ab4237f 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -26,8 +26,8 @@ cd $HOME/CoSense/applications/newton/llvm-ir/&& # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index ab6cdebdb..0df1e0639 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -341,10 +341,11 @@ transformToQuantizedType(Type * originalType, Type * quantizedType) // Return original type if no conversion is necessary return originalType; } -void handleSqrtCall(CallInst *llvmIrCallInstruction, Type *quantizedType,Function *fixsqrt) +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) { IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); + auto operand = llvmIrCallInstruction->getOperand(0); // Ensure operand is in the correct type if (operand->getType()->isFloatingPointTy()) @@ -353,26 +354,26 @@ void handleSqrtCall(CallInst *llvmIrCallInstruction, Type *quantizedType,Functio } // Convert the operand from fixed-point (int32) to float - llvm::Value *operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); // Call llvm.sqrt.f32 to compute the square root of the float value - llvm::Value *sqrtFloat = Builder.CreateCall( + llvm::Value * sqrtFloat = Builder.CreateCall( Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), {operandAsFloat}); // Convert the result back to int32 - llvm::Value *sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType ); + llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); // Apply the shift-left operation (shl i32 %result, 5) - llvm::Value *shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); + llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); // Replace the original instruction with the new fixed-point sqrt result llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); llvmIrCallInstruction->eraseFromParent(); } -//void -//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +// void +// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) //{ // IRBuilder<> Builder(llvmIrCallInstruction); // auto operand = llvmIrCallInstruction->getOperand(0); @@ -401,7 +402,7 @@ void handleSqrtCall(CallInst *llvmIrCallInstruction, Type *quantizedType,Functio // // No need to apply shl and compensation if it's already done in createFixSqrt // llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); // llvmIrCallInstruction->eraseFromParent(); -//} +// } void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) @@ -972,8 +973,6 @@ substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm:: inInstruction->removeFromParent(); } - - // TODO : float version rsqrt llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1033,9 +1032,8 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector &functionsToInsert) +// TODO : fix version rsqrt +// llvm::Function *createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vector &functionsToInsert) //{ // llvm::errs() << "Entering createFixRsqrt\n"; // @@ -1107,11 +1105,9 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1159,7 +1155,7 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE.0)); - //fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); @@ -2004,7 +2000,8 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct {0.5, {1, true}}, {2.0, {1, false}}, {4.0, {2, false}}, - {8.0, {3, false}}}; + {8.0, {3, false}}, + {1/128.0, {7, true}}}; double value = constFP->getValueAPF().convertToDouble(); auto it = constantsMap.find(value); if (it != constantsMap.end()) @@ -2020,21 +2017,22 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return checkAndSimplifyFMul(constFP, otherOperand, instruction); } - -//64bit fixed point multiplication -llvm::Value* performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value* lhs, llvm::Value* rhs, unsigned FRAC_Q) { +// 64bit fixed point multiplication +llvm::Value * +performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) +{ // Sign extend the 32-bit operands to 64-bit integers - llvm::Value* lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - llvm::Value* rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); // Perform 64-bit multiplication - llvm::Value* mulResult64 = Builder.CreateMul(lhs64, rhs64); + llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value* divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); // Truncate the 64-bit result back to 32-bit integer - llvm::Value* result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); return result32; } @@ -2129,45 +2127,44 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) if (lhsIsInteger && rhsIsInteger) - //缩两边 -// { -// llvm::errs() << "Both operands are integers, applying scaling to prevent overflow...\n"; -// -// // Scale operands down by FRAC_Q before multiplication -// llvm::Value* lhsScaled = Builder.CreateAShr(lhs, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "lhsScaled"); -// llvm::Value* rhsScaled = Builder.CreateAShr(rhs, llvm::ConstantInt::get(rhs->getType(), FRAC_Q / 2), "rhsScaled"); -// -// // Perform 32-bit multiplication -// llvm::Value* mulResult = Builder.CreateMul(lhsScaled, rhsScaled, "mulScaled"); -// -//// // Right shift to maintain precision, compensating for the scaling -//// llvm::Value* finalResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "shiftResult"); -// -// // Replace all uses of the original instruction with the final result -// llvmIrInstruction->replaceAllUsesWith(mulResult); -// llvmIrInstruction->eraseFromParent(); - -// } + // 缩两边 + // { + // llvm::errs() << "Both operands are integers, applying scaling to prevent overflow...\n"; + // + // // Scale operands down by FRAC_Q before multiplication + // llvm::Value* lhsScaled = Builder.CreateAShr(lhs, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "lhsScaled"); + // llvm::Value* rhsScaled = Builder.CreateAShr(rhs, llvm::ConstantInt::get(rhs->getType(), FRAC_Q / 2), "rhsScaled"); + // + // // Perform 32-bit multiplication + // llvm::Value* mulResult = Builder.CreateMul(lhsScaled, rhsScaled, "mulScaled"); + // + //// // Right shift to maintain precision, compensating for the scaling + //// llvm::Value* finalResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "shiftResult"); + // + // // Replace all uses of the original instruction with the final result + // llvmIrInstruction->replaceAllUsesWith(mulResult); + // llvmIrInstruction->eraseFromParent(); + // } // 64位 { llvm::errs() << "Both operands are integers, performing 64-bit multiplication and shifting...\n"; -// // Sign extend the 32-bit operands to 64-bit integers to prevent overflow -// llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); -// llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); -// -// // Perform 64-bit multiplication -// llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); -// -// // Divide the result by FRAC_Q (equivalent to right shift) -// llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q), "div64"); -// -// // Truncate the 64-bit result back to 32-bit integer -// llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); - - llvm::Value* result32 = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); + // // Sign extend the 32-bit operands to 64-bit integers to prevent overflow + // llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + // llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + // + // // Perform 64-bit multiplication + // llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); + // + // // Divide the result by FRAC_Q (equivalent to right shift) + // llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q), "div64"); + // + // // Truncate the 64-bit result back to 32-bit integer + // llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + + llvm::Value * result32 = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); // Replace all uses of the original instruction with the final result llvmIrInstruction->replaceAllUsesWith(result32); From 2b00557e8f76980e0bd2de795d669f5f24606104 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 30 Sep 2024 10:45:25 +0100 Subject: [PATCH 100/213] update frequency constant substitution * dev2. --- ...074aa0fa141b085ce178b6316724d3fcea382f.txt | 48 +++++++++++++++++++ ...58d0a4397166e63b19ece97c4a95ddb1c51315.txt | 48 +++++++++++++++++++ 2 files changed, 96 insertions(+) create mode 100644 analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt create mode 100644 analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt diff --git a/analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt b/analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt new file mode 100644 index 000000000..6f0841dc5 --- /dev/null +++ b/analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt @@ -0,0 +1,48 @@ + +changeset: 1693:3d074aa0fa141b085ce178b6316724d3fcea382f +char kNewtonVersion[] = "0.3-alpha-1693 (3d074aa0fa141b085ce178b6316724d3fcea382f) (build 09-30-2024-10:44-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt b/analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt new file mode 100644 index 000000000..307dd99f8 --- /dev/null +++ b/analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt @@ -0,0 +1,48 @@ + +changeset: 1692:d158d0a4397166e63b19ece97c4a95ddb1c51315 +char kNewtonVersion[] = "0.3-alpha-1692 (d158d0a4397166e63b19ece97c4a95ddb1c51315) (build 09-29-2024-22:50-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 7cdceef32dea94e7110bde8ccc86621a2b98ffe6 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 30 Sep 2024 11:49:18 +0100 Subject: [PATCH 101/213] change back to fixmul * dev2. --- .../newton/llvm-ir/c-files/MadgwickAHRSfix.c | 850 ++++++++++++++---- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 8 +- .../newton-irPass-LLVMIR-quantization.cpp | 193 ++-- 3 files changed, 765 insertions(+), 286 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c index d13a4f45b..e4795a065 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c @@ -1,3 +1,10 @@ +#include +#include +// #include "config.h" + +#include "fsl_misc_utilities.h" + +// #include "warp.h" #include "MadgwickAHRSfix.h" #define sampleFreq 28 // sample frequency in Hz @@ -14,9 +21,10 @@ typedef int32_t bmx055yMagneto; typedef int32_t bmx055zMagneto; volatile bmx055xAcceleration beta = (uint8_t)(betaDef * FRAC_BASE); // 0.1f // 2 * proportional gain (Kp) -// volatile bmx055xAcceleration q0 = 0.64306622f*FRAC_BASE, q1 = 0.02828862f*FRAC_BASE, -// q2 = -0.00567953f*FRAC_BASE, q3 = -0.76526684f*FRAC_BASE; // quaternion of sensor frame relative to auxiliary frame +// volatile bmx055xAcceleration q0 = FRAC_BASE, q1 = 0x0, q2 = 0x0, q3 = 0x0; // quaternion of sensor frame relative to auxiliary frame +volatile bmx055xAcceleration q0 = 0.64306622f * FRAC_BASE, q1 = 0.02828862f * FRAC_BASE, + q2 = -0.00567953f * FRAC_BASE, q3 = -0.76526684f * FRAC_BASE; // m=-7 1/Yest=0.0858 Yest=11.3120 Yest_hex=B50 // m=-6 1/Yest=0.1248 Yest=8.0000 Yest_hex=800 // m=-5 1/Yest=0.1755 Yest=5.6552 Yest_hex=5A8 @@ -48,68 +56,75 @@ volatile bmx055xAcceleration beta = (uint8_t)(betaDef * FRAC_BASE); // 0.1f // m=21 1/Yest=1448.0000 Yest=0.0000 Yest_hex=0 // m=22 1/Yest=2048.0000 Yest=0.0000 Yest_hex=0 -static inline int32_t -fixmul(int32_t x, int32_t y) +int32_t +mulfix(int32_t x, int32_t y) { - // int32_t result; - // int64_t temp; - // temp = (int64_t)x * (int64_t)y; - // temp += K; - // result = round(temp/FRAC_BASE); - // return result; - // return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; - return ((int64_t)x * y) >> FRAC_Q; - //return (x * y) >> FRAC_Q; + return ((int64_t)x * y) / FRAC_BASE; } /* * Compute square root of x and reciprocal with Goldschmidt's method */ -//int32_t -//sqrt_rsqrt(int32_t x, int recip) -//{ -// if (recip) -// { -// int32_t int_halfx = fixmul(0.5 * FRAC_BASE, x); -// float fp_y = (float)x / FRAC_BASE; -// long i = *(long *)&fp_y; -// i = 0x5f3759df - (i >> 1); -// fp_y = *(float *)&i; -// int32_t int_y = fp_y * FRAC_BASE; -// int_y = fixmul(int_y, ((int32_t)(1.5f * FRAC_BASE) - (fixmul(fixmul(int_halfx, int_y), int_y)))); -// return int_y; -// } -// else -// { -// int32_t res = (int32_t)sqrt((double)x) << (FRAC_Q / 2); -// if (FRAC_Q % 2) -// return res * 1.414213562; -// else -// return res; -// } -//} - int32_t -fixsqrt(int32_t x) +sqrt_rsqrt(int32_t x, int recip) { - int32_t res = (int32_t)sqrt((double)x) << (FRAC_Q / 2); - if (FRAC_Q % 2) - return res * 1.414213562; - else - return res; -} - -int32_t -invSqrt(int32_t x) -{ - int32_t int_halfx = fixmul(0.5 * FRAC_BASE, x); - float fp_y = (float)x / FRAC_BASE; - long i = *(long *)&fp_y; - i = 0x5f3759df - (i >> 1); - fp_y = *(float *)&i; - int32_t int_y = fp_y * FRAC_BASE; - int_y = fixmul(int_y, ((int32_t)(1.5f * FRAC_BASE) - (fixmul(fixmul(int_halfx, int_y), int_y)))); - return int_y; + /* assume x>0 */ + // warpPrint("x=%d.%04d\n", DISPLAY_INT(x), DISPLAY_FRAC(x)); + + /* m ranges in -7 -> 23 */ + static int32_t Y_est[31] = + {0xB50, 0x800, 0x5A8, 0x400, 0x2D4, 0x200, 0x16A, 0x100, 0xB5, 0x80, 0x5A, 0x40, 0x2D, 0x20, 0x16, 0x10, 0xB, 0x8, 0x5, 0x4, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; + + /* + * Initial estimate is sqrt(2^(|_ log_2(x) _| - FRAC_BITS))*FRAC_BASE + */ + int32_t m = sizeof(int32_t) * 8 - 1 - __builtin_clz(x) - FRAC_Q; + + /* + * + */ + int32_t bi = x; + int32_t Yi = Y_est[m + 7]; + int32_t xi = mulfix(x, Yi); // x*Yi/FRAC_BASE; + int32_t yi = Yi; + + /* 1st iteration */ + bi = mulfix(mulfix(bi, Yi), Yi); + Yi = (3 * FRAC_BASE - bi) / 2; + xi = mulfix(xi, Yi); + yi = mulfix(yi, Yi); + // bi = bi*Yi*Yi/FRAC_BASE/FRAC_BASE; + // Yi = (3*FRAC_BASE - bi)/2; + // xi = xi*Yi/FRAC_BASE; + // yi = yi*Yi/FRAC_BASE; + + // warpPrint("run fixpoint \n"); + + /* 2nd iteration */ + bi = mulfix(mulfix(bi, Yi), Yi); + Yi = (3 * FRAC_BASE - bi) / 2; + xi = mulfix(xi, Yi); + yi = mulfix(yi, Yi); + + // warpPrint("sqrt(x)_2=%d.%04d\n", DISPLAY_INT(xi), DISPLAY_FRAC(xi)); + + /* 3rd iteration */ + bi = mulfix(mulfix(bi, Yi), Yi); + Yi = (3 * FRAC_BASE - bi) / 2; + xi = mulfix(xi, Yi); + yi = mulfix(yi, Yi); + + // warpPrint("sqrt(x)_3=%d.%04d\n", DISPLAY_INT(xi), DISPLAY_FRAC(xi)); + + /* 4th iteration */ + bi = mulfix(mulfix(bi, Yi), Yi); + Yi = (3 * FRAC_BASE - bi) / 2; + xi = mulfix(xi, Yi); + yi = mulfix(yi, Yi); + + // warpPrint("sqrt(x)_4=%d.%04d\n", DISPLAY_INT(xi), DISPLAY_FRAC(xi)); + + return recip ? (yi > 0 ? yi : 1) : (xi > 0 ? xi : 1); } //==================================================================================================== @@ -122,109 +137,175 @@ void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, - int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr) -{ + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { + int32_t q0 = *q0_ptr; int32_t q1 = *q1_ptr; int32_t q2 = *q2_ptr; int32_t q3 = *q3_ptr; - int32_t recipNorm; - int32_t s0, s1, s2, s3; - int32_t qDot1, qDot2, qDot3, qDot4; - int32_t hx, hy; - int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + int32_t recipNorm; + int32_t s0, s1, s2, s3; + int32_t qDot1, qDot2, qDot3, qDot4; + int32_t hx, hy; + int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) - if ((mx == 0x0) && (my == 0x0) && (mz == 0x0)) - { + if((mx == 0x0) && (my == 0x0) && (mz == 0x0)) { MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); return; } // Rate of change of quaternion from gyroscope - qDot1 = (-fixmul(q1, gx) - fixmul(q2, gy) - fixmul(q3, gz)) / 2; - qDot2 = (fixmul(q0, gx) + fixmul(q2, gz) - fixmul(q3, gy)) / 2; - qDot3 = (fixmul(q0, gy) - fixmul(q1, gz) + fixmul(q3, gx)) / 2; - qDot4 = (fixmul(q0, gz) + fixmul(q1, gy) - fixmul(q2, gx)) / 2; - - + qDot1 = (-mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; + qDot2 = (mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; + qDot3 = (mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; + qDot4 = (mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) - { - + if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { // Normalise accelerometer measurement - recipNorm = invSqrt(fixmul(ax, ax) + fixmul(ay, ay) + fixmul(az, az)); + recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); // printf("1: %f\n", (double)recipNorm/FRAC_BASE); - ax = fixmul(ax, recipNorm); - ay = fixmul(ay, recipNorm); - az = fixmul(az, recipNorm); - + ax = mulfix(ax, recipNorm); + ay = mulfix(ay, recipNorm); + az = mulfix(az, recipNorm); // Normalise magnetometer measurement - recipNorm = invSqrt(fixmul(mx, mx) + fixmul(my, my) + fixmul(mz, mz)); - mx = fixmul(mx, recipNorm); - my = fixmul(my, recipNorm); - mz = fixmul(mz, recipNorm); - + recipNorm = sqrt_rsqrt(mulfix(mx, mx) + mulfix(my, my) + mulfix(mz, mz), true); + mx = mulfix(mx, recipNorm); + my = mulfix(my, recipNorm); + mz = mulfix(mz, recipNorm); // Auxiliary variables to avoid repeated arithmetic - _2q0mx = 2 * fixmul(q0, mx); - _2q0my = 2 * fixmul(q0, my); - _2q0mz = 2 * fixmul(q0, mz); - _2q1mx = 2 * fixmul(q1, mx); - _2q0 = 2 * q0; - _2q1 = 2 * q1; - _2q2 = 2 * q2; - _2q3 = 2 * q3; - _2q0q2 = 2 * fixmul(q0, q2); - _2q2q3 = 2 * fixmul(q2, q3); - q0q0 = fixmul(q0, q0); - q0q1 = fixmul(q0, q1); - q0q2 = fixmul(q0, q2); - q0q3 = fixmul(q0, q3); - q1q1 = fixmul(q1, q1); - q1q2 = fixmul(q1, q2); - q1q3 = fixmul(q1, q3); - q2q2 = fixmul(q2, q2); - q2q3 = fixmul(q2, q3); - q3q3 = fixmul(q3, q3); - + _2q0mx = 2 * mulfix(q0, mx); + _2q0my = 2 * mulfix(q0, my); + _2q0mz = 2 * mulfix(q0, mz); + _2q1mx = 2 * mulfix(q1, mx); + _2q0 = 2*q0; + _2q1 = 2*q1; + _2q2 = 2*q2; + _2q3 = 2*q3; + _2q0q2 = 2 * mulfix(q0, q2); + _2q2q3 = 2 * mulfix(q2, q3); + q0q0 = mulfix(q0, q0); + q0q1 = mulfix(q0, q1); + q0q2 = mulfix(q0, q2); + q0q3 = mulfix(q0, q3); + q1q1 = mulfix(q1, q1); + q1q2 = mulfix(q1, q2); + q1q3 = mulfix(q1, q3); + q2q2 = mulfix(q2, q2); + q2q3 = mulfix(q2, q3); + q3q3 = mulfix(q3, q3); // Reference direction of Earth's magnetic field - hx = fixmul(mx, q0q0) - fixmul(_2q0my, q3) + fixmul(_2q0mz, q2) + fixmul(mx, q1q1) + fixmul(fixmul(_2q1, my), q2) + fixmul(fixmul(_2q1, mz), q3) - fixmul(mx, q2q2) - fixmul(mx, q3q3); - hy = fixmul(_2q0mx, q3) + fixmul(my, q0q0) - fixmul(_2q0mz, q1) + fixmul(_2q1mx, q2) - fixmul(my, q1q1) + fixmul(my, q2q2) + fixmul(fixmul(_2q2, mz), q3) - fixmul(my, q3q3); - _2bx = fixsqrt(fixmul(hx, hx) + fixmul(hy, hy)); - _2bz = -fixmul(_2q0mx, q2) + fixmul(_2q0my, q1) + fixmul(mz, q0q0) + fixmul(_2q1mx, q3) - fixmul(mz, q1q1) + fixmul(fixmul(_2q2, my), q3) - fixmul(mz, q2q2) + fixmul(mz, q3q3); - _4bx = 2 * _2bx; - _4bz = 2 * _2bz; - + hx = mulfix(mx, q0q0) + - mulfix(_2q0my, q3) + + mulfix(_2q0mz, q2) + + mulfix(mx, q1q1) + + mulfix(mulfix(_2q1, my), q2) + + mulfix(mulfix(_2q1, mz), q3) + - mulfix(mx, q2q2) + - mulfix(mx, q3q3); + hy = mulfix(_2q0mx, q3) + + mulfix(my, q0q0) + - mulfix(_2q0mz, q1) + + mulfix(_2q1mx, q2) + - mulfix(my, q1q1) + + mulfix(my, q2q2) + + mulfix(mulfix(_2q2, mz), q3) + - mulfix(my, q3q3); + _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); + _2bz = -mulfix(_2q0mx, q2) + + mulfix(_2q0my, q1) + + mulfix(mz, q0q0) + + mulfix(_2q1mx, q3) + - mulfix(mz, q1q1) + + mulfix(mulfix(_2q2, my), q3) + - mulfix(mz, q2q2) + + mulfix(mz, q3q3); + _4bx = 2*_2bx; + _4bz = 2*_2bz; // Gradient decent algorithm corrective step - s0 = -fixmul(_2q2, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q1, (2 * q0q1 + _2q2q3 - ay)) - fixmul(fixmul(_2bz, q2), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul((-fixmul(_2bx, q3) + fixmul(_2bz, q1)), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul(fixmul(_2bx, q2), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); - - s1 = fixmul(_2q3, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q0, (2 * q0q1 + _2q2q3 - ay)) - 4 * fixmul(q1, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + fixmul(fixmul(_2bz, q3), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul(fixmul(_2bx, q2) + fixmul(_2bz, q0), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul(fixmul(_2bx, q3) - fixmul(_4bz, q1), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); - - - - s2 = -fixmul(_2q0, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q3, (2 * q0q1 + _2q2q3 - ay)) - 4 * fixmul(q2, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + fixmul((-fixmul(_4bx, q2) - fixmul(_2bz, q0)), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul((fixmul(_2bx, q1) + fixmul(_2bz, q3)), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul((fixmul(_2bx, q0) - fixmul(_4bz, q2)), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); - - s3 = fixmul(_2q1, (2 * q1q3 - _2q0q2 - ax)) + fixmul(_2q2, (2 * q0q1 + _2q2q3 - ay)) + fixmul((-fixmul(_4bx, q3) + fixmul(_2bz, q1)), (fixmul(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + fixmul(_2bz, (q1q3 - q0q2)) - mx)) + fixmul((-fixmul(_2bx, q0) + fixmul(_2bz, q2)), (fixmul(_2bx, (q1q2 - q0q3)) + fixmul(_2bz, (q0q1 + q2q3)) - my)) + fixmul(fixmul(_2bx, q1), (fixmul(_2bx, (q0q2 + q1q3)) + fixmul(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - mz)); + s0 = - mulfix(_2q2, (2*q1q3 - _2q0q2 - ax)) + + mulfix(_2q1, (2*q0q1 + _2q2q3 - ay)) + - mulfix(mulfix(_2bz, q2), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix(mulfix(_2bx, q2), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); + s1 = mulfix(_2q3, (2*q1q3 - _2q0q2 - ax)) + + mulfix(_2q0, (2*q0q1 + _2q2q3 - ay)) + - 4 * mulfix(q1, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) + + mulfix(mulfix(_2bz, q3), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); + s2 = - mulfix(_2q0, (2*q1q3 - _2q0q2 - ax)) + + mulfix(_2q3, (2*q0q1 + _2q2q3 - ay)) + - 4 * mulfix(q2, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) + + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); + s3 = mulfix(_2q1, (2*q1q3 - _2q0q2 - ax)) + + mulfix(_2q2, (2*q0q1 + _2q2q3 - ay)) + + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix(mulfix(_2bx, q1), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); + recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude + s0 = mulfix(s0, recipNorm); + s1 = mulfix(s1, recipNorm); + s2 = mulfix(s2, recipNorm); + s3 = mulfix(s3, recipNorm); /* 2nd iter normalizaton */ -// recipNorm = invSqrt(fixmul(s0, s0) + fixmul(s1, s1) + fixmul(s2, s2) + fixmul(s3, s3)); // normalise step magnitude -// s0 = fixmul(s0, recipNorm); -// s1 = fixmul(s1, recipNorm); -// s2 = fixmul(s2, recipNorm); -// s3 = fixmul(s3, recipNorm); + // recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude + // s0 = mulfix(s0, recipNorm); + // s1 = mulfix(s1, recipNorm); + // s2 = mulfix(s2, recipNorm); + // s3 = mulfix(s3, recipNorm); // Apply feedback step - qDot1 -= fixmul(beta, s0); - qDot2 -= fixmul(beta, s1); - qDot3 -= fixmul(beta, s2); - qDot4 -= fixmul(beta, s3); + qDot1 -= mulfix(beta, s0); + qDot2 -= mulfix(beta, s1); + qDot3 -= mulfix(beta, s2); + qDot4 -= mulfix(beta, s3); } // Integrate rate of change of quaternion to yield quaternion @@ -234,18 +315,17 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR q3 += qDot4 / sampleFreq; // Normalise quaternion - recipNorm = invSqrt(fixmul(q0, q0) + fixmul(q1, q1) + fixmul(q2, q2) + fixmul(q3, q3)); + recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", // (double)q0/FRAC_BASE, // (double)q1/FRAC_BASE, // (double)q2/FRAC_BASE, // (double)q3/FRAC_BASE, // (double)recipNorm/FRAC_BASE); - - q0 = fixmul(q0, recipNorm); - q1 = fixmul(q1, recipNorm); - q2 = fixmul(q2, recipNorm); - q3 = fixmul(q3, recipNorm); + q0 = mulfix(q0, recipNorm); + q1 = mulfix(q1, recipNorm); + q2 = mulfix(q2, recipNorm); + q3 = mulfix(q3, recipNorm); *q0_ptr = q0; *q1_ptr = q1; *q2_ptr = q2; @@ -271,63 +351,82 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, - int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr) -{ + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { int32_t q0 = *q0_ptr; int32_t q1 = *q1_ptr; int32_t q2 = *q2_ptr; int32_t q3 = *q3_ptr; - int32_t recipNorm; - int32_t s0, s1, s2, s3; - int32_t qDot1, qDot2, qDot3, qDot4; - int32_t _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2, _8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + int32_t recipNorm; + int32_t s0, s1, s2, s3; + int32_t qDot1, qDot2, qDot3, qDot4; + int32_t _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; // Rate of change of quaternion from gyroscope - qDot1 = (-fixmul(q1, gx) - fixmul(q2, gy) - fixmul(q3, gz)) / 2; - qDot2 = (fixmul(q0, gx) + fixmul(q2, gz) - fixmul(q3, gy)) / 2; - qDot3 = (fixmul(q0, gy) - fixmul(q1, gz) + fixmul(q3, gx)) / 2; - qDot4 = (fixmul(q0, gz) + fixmul(q1, gy) - fixmul(q2, gx)) / 2; + qDot1 = (- mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; + qDot2 = ( mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; + qDot3 = ( mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; + qDot4 = ( mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) - if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) - { + if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { + // Normalise accelerometer measurement - recipNorm = invSqrt(fixmul(ax, ax) + fixmul(ay, ay) + fixmul(az, az)); - ax = fixmul(ax, recipNorm); - ay = fixmul(ay, recipNorm); - az = fixmul(az, recipNorm); + recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); + ax = mulfix(ax, recipNorm); + ay = mulfix(ay, recipNorm); + az = mulfix(az, recipNorm); // Auxiliary variables to avoid repeated arithmetic - _2q0 = 2 * q0; - _2q1 = 2 * q1; - _2q2 = 2 * q2; - _2q3 = 2 * q3; - _4q0 = 4 * q0; - _4q1 = 4 * q1; - _4q2 = 4 * q2; - _8q1 = 8 * q1; - _8q2 = 8 * q2; - q0q0 = fixmul(q0, q0); - q1q1 = fixmul(q1, q1); - q2q2 = fixmul(q2, q2); - q3q3 = fixmul(q3, q3); + _2q0 = 2*q0; + _2q1 = 2*q1; + _2q2 = 2*q2; + _2q3 = 2*q3; + _4q0 = 4*q0; + _4q1 = 4*q1; + _4q2 = 4*q2; + _8q1 = 8*q1; + _8q2 = 8*q2; + q0q0 = mulfix(q0, q0); + q1q1 = mulfix(q1, q1); + q2q2 = mulfix(q2, q2); + q3q3 = mulfix(q3, q3); // Gradient decent algorithm corrective step - s0 = fixmul(_4q0, q2q2) + fixmul(_2q2, ax) + fixmul(_4q0, q1q1) - fixmul(_2q1, ay); - s1 = fixmul(_4q1, q3q3) - fixmul(_2q3, ax) + 4 * fixmul(q0q0, q1) - fixmul(_2q0, ay) - _4q1 + fixmul(_8q1, q1q1) + fixmul(_8q1, q2q2) + fixmul(_4q1, az); - s2 = 4 * fixmul(q0q0, q2) + fixmul(_2q0, ax) + fixmul(_4q2, q3q3) - fixmul(_2q3, ay) - _4q2 + fixmul(_8q2, q1q1) + fixmul(_8q2, q2q2) + fixmul(_4q2, az); - s3 = 4 * fixmul(q1q1, q3) - fixmul(_2q1, ax) + 4 * fixmul(q2q2, q3) - fixmul(_2q2, ay); - recipNorm = invSqrt(fixmul(s0, s0) + fixmul(s1, s1) + fixmul(s2, s2) + fixmul(s3, s3)); // normalise step magnitude - s0 = fixmul(s0, recipNorm); - s1 = fixmul(s1, recipNorm); - s2 = fixmul(s2, recipNorm); - s3 = fixmul(s3, recipNorm); + s0 = mulfix(_4q0, q2q2) + + mulfix(_2q2, ax) + + mulfix(_4q0, q1q1) + - mulfix(_2q1, ay); + s1 = mulfix(_4q1, q3q3) + - mulfix(_2q3, ax) + + 4 * mulfix(q0q0, q1) + - mulfix(_2q0, ay) + - _4q1 + + mulfix(_8q1, q1q1) + + mulfix(_8q1, q2q2) + + mulfix(_4q1, az); + s2 = 4 * mulfix(q0q0, q2) + + mulfix(_2q0, ax) + + mulfix(_4q2, q3q3) + - mulfix(_2q3, ay) + - _4q2 + + mulfix(_8q2, q1q1) + + mulfix(_8q2, q2q2) + + mulfix(_4q2, az); + s3 = 4 * mulfix(q1q1, q3) + - mulfix(_2q1, ax) + + 4 * mulfix(q2q2, q3) + - mulfix(_2q2, ay); + recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude + s0 = mulfix(s0, recipNorm); + s1 = mulfix(s1, recipNorm); + s2 = mulfix(s2, recipNorm); + s3 = mulfix(s3, recipNorm); // Apply feedback step - qDot1 -= fixmul(beta, s0); - qDot2 -= fixmul(beta, s1); - qDot3 -= fixmul(beta, s2); - qDot4 -= fixmul(beta, s3); + qDot1 -= mulfix(beta, s0); + qDot2 -= mulfix(beta, s1); + qDot3 -= mulfix(beta, s2); + qDot4 -= mulfix(beta, s3); } // Integrate rate of change of quaternion to yield quaternion @@ -337,13 +436,390 @@ MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngul q3 += qDot4 / sampleFreq; // Normalise quaternion - recipNorm = invSqrt(fixmul(q0, q0) + fixmul(q1, q1) + fixmul(q2, q2) + fixmul(q3, q3)); - q0 = fixmul(q0, recipNorm); - q1 = fixmul(q1, recipNorm); - q2 = fixmul(q2, recipNorm); - q3 = fixmul(q3, recipNorm); - q0_ptr = &q0; - q1_ptr = &q1; - q2_ptr = &q2; - q3_ptr = &q3; + recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); + q0 = mulfix(q0, recipNorm); + q1 = mulfix(q1, recipNorm); + q2 = mulfix(q2, recipNorm); + q3 = mulfix(q3, recipNorm); + q0_ptr = &q0; + q1_ptr = &q1; + q2_ptr = &q2; + q3_ptr = &q3; } + +//TODO: Original code +//#include "MadgwickAHRSfix.h" +// +//#define sampleFreq 28 // sample frequency in Hz +//#define betaDef 0.1f // 2 * proportional gain +// +//typedef int32_t bmx055xAcceleration; +//typedef int32_t bmx055yAcceleration; +//typedef int32_t bmx055zAcceleration; +//typedef int32_t bmx055xAngularRate; +//typedef int32_t bmx055yAngularRate; +//typedef int32_t bmx055zAngularRate; +//typedef int32_t bmx055xMagneto; +//typedef int32_t bmx055yMagneto; +//typedef int32_t bmx055zMagneto; +// +//volatile bmx055xAcceleration beta = (uint8_t)(betaDef*FRAC_BASE); //0.1f // 2 * proportional gain (Kp) +// +// +//int32_t +//mulfix(int32_t x, int32_t y) +//{ +// // int32_t result; +// // int64_t temp; +// // temp = (int64_t)x * (int64_t)y; +// // temp += K; +// // result = round(temp/FRAC_BASE); +// // return result; +// // return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; +// return ((int64_t)x*y)>>FRAC_Q; +//} +// +///* +// * Compute square root of x and reciprocal with Goldschmidt's method +// */ +//int32_t +//sqrt_rsqrt(int32_t x, int recip) { +// if (recip) { +// int32_t int_halfx = mulfix(0.5*FRAC_BASE, x); +// float fp_y = (float)x/FRAC_BASE; +// long i = *(long*)&fp_y; +// i = 0x5f3759df - (i>>1); +// fp_y = *(float*)&i; +// int32_t int_y = fp_y*FRAC_BASE; +// int_y = mulfix(int_y, ((int32_t)(1.5f*FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); +// return int_y; +// // fp_y = fp_y * (1.5f - (halfx * fp_y * fp_y)); +// // return fp_y*FRAC_BASE; +// } else { +// int32_t res = (int32_t)sqrt((double)x)<<(FRAC_Q/2); +// if (FRAC_Q%2) +// return res*1.414213562; +// else +// return res; +// } +//} +// +////==================================================================================================== +//// Functions +// +////--------------------------------------------------------------------------------------------------- +//// AHRS algorithm update +// +//void +//MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, +// bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, +// bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, +// int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { +// +// int32_t q0 = *q0_ptr; +// int32_t q1 = *q1_ptr; +// int32_t q2 = *q2_ptr; +// int32_t q3 = *q3_ptr; +// +// int32_t recipNorm; +// int32_t s0, s1, s2, s3; +// int32_t qDot1, qDot2, qDot3, qDot4; +// int32_t hx, hy; +// int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; +// +// // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) +// if((mx == 0x0) && (my == 0x0) && (mz == 0x0)) { +// MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); +// return; +// } +// +// // Rate of change of quaternion from gyroscope +// qDot1 = (-mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; +// qDot2 = (mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; +// qDot3 = (mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; +// qDot4 = (mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; +// +// // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) +// if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { +// +// // Normalise accelerometer measurement +// recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); +// // printf("1: %f\n", (double)recipNorm/FRAC_BASE); +// ax = mulfix(ax, recipNorm); +// ay = mulfix(ay, recipNorm); +// az = mulfix(az, recipNorm); +// +// // Normalise magnetometer measurement +// recipNorm = sqrt_rsqrt(mulfix(mx, mx) + mulfix(my, my) + mulfix(mz, mz), true); +// mx = mulfix(mx, recipNorm); +// my = mulfix(my, recipNorm); +// mz = mulfix(mz, recipNorm); +// +// // Auxiliary variables to avoid repeated arithmetic +// _2q0mx = 2 * mulfix(q0, mx); +// _2q0my = 2 * mulfix(q0, my); +// _2q0mz = 2 * mulfix(q0, mz); +// _2q1mx = 2 * mulfix(q1, mx); +// _2q0 = 2*q0; +// _2q1 = 2*q1; +// _2q2 = 2*q2; +// _2q3 = 2*q3; +// _2q0q2 = 2 * mulfix(q0, q2); +// _2q2q3 = 2 * mulfix(q2, q3); +// q0q0 = mulfix(q0, q0); +// q0q1 = mulfix(q0, q1); +// q0q2 = mulfix(q0, q2); +// q0q3 = mulfix(q0, q3); +// q1q1 = mulfix(q1, q1); +// q1q2 = mulfix(q1, q2); +// q1q3 = mulfix(q1, q3); +// q2q2 = mulfix(q2, q2); +// q2q3 = mulfix(q2, q3); +// q3q3 = mulfix(q3, q3); +// +// // Reference direction of Earth's magnetic field +// hx = mulfix(mx, q0q0) +// - mulfix(_2q0my, q3) +// + mulfix(_2q0mz, q2) +// + mulfix(mx, q1q1) +// + mulfix(mulfix(_2q1, my), q2) +// + mulfix(mulfix(_2q1, mz), q3) +// - mulfix(mx, q2q2) +// - mulfix(mx, q3q3); +// hy = mulfix(_2q0mx, q3) +// + mulfix(my, q0q0) +// - mulfix(_2q0mz, q1) +// + mulfix(_2q1mx, q2) +// - mulfix(my, q1q1) +// + mulfix(my, q2q2) +// + mulfix(mulfix(_2q2, mz), q3) +// - mulfix(my, q3q3); +// _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); +// _2bz = -mulfix(_2q0mx, q2) +// + mulfix(_2q0my, q1) +// + mulfix(mz, q0q0) +// + mulfix(_2q1mx, q3) +// - mulfix(mz, q1q1) +// + mulfix(mulfix(_2q2, my), q3) +// - mulfix(mz, q2q2) +// + mulfix(mz, q3q3); +// _4bx = 2*_2bx; +// _4bz = 2*_2bz; +// +// // Gradient decent algorithm corrective step +// s0 = - mulfix(_2q2, (2*q1q3 - _2q0q2 - ax)) +// + mulfix(_2q1, (2*q0q1 + _2q2q3 - ay)) +// - mulfix(mulfix(_2bz, q2), ( +// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) +// + mulfix(_2bz, (q1q3 - q0q2)) +// - mx)) +// + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), ( +// mulfix(_2bx, (q1q2 - q0q3)) +// + mulfix(_2bz, (q0q1 + q2q3)) +// - my)) +// + mulfix(mulfix(_2bx, q2), ( +// mulfix(_2bx, (q0q2 + q1q3)) +// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) +// - mz)); +// s1 = mulfix(_2q3, (2*q1q3 - _2q0q2 - ax)) +// + mulfix(_2q0, (2*q0q1 + _2q2q3 - ay)) +// - 4 * mulfix(q1, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) +// + mulfix(mulfix(_2bz, q3), ( +// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) +// + mulfix(_2bz, (q1q3 - q0q2)) +// - mx)) +// + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), ( +// mulfix(_2bx, (q1q2 - q0q3)) +// + mulfix(_2bz, (q0q1 + q2q3)) +// - my)) +// + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), ( +// mulfix(_2bx, (q0q2 + q1q3)) +// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) +// - mz)); +// s2 = - mulfix(_2q0, (2*q1q3 - _2q0q2 - ax)) +// + mulfix(_2q3, (2*q0q1 + _2q2q3 - ay)) +// - 4 * mulfix(q2, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) +// + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), ( +// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) +// + mulfix(_2bz, (q1q3 - q0q2)) +// - mx)) +// + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), ( +// mulfix(_2bx, (q1q2 - q0q3)) +// + mulfix(_2bz, (q0q1 + q2q3)) +// - my)) +// + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), ( +// mulfix(_2bx, (q0q2 + q1q3)) +// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) +// - mz)); +// s3 = mulfix(_2q1, (2*q1q3 - _2q0q2 - ax)) +// + mulfix(_2q2, (2*q0q1 + _2q2q3 - ay)) +// + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), ( +// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) +// + mulfix(_2bz, (q1q3 - q0q2)) +// - mx)) +// + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), ( +// mulfix(_2bx, (q1q2 - q0q3)) +// + mulfix(_2bz, (q0q1 + q2q3)) +// - my)) +// + mulfix(mulfix(_2bx, q1), ( +// mulfix(_2bx, (q0q2 + q1q3)) +// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) +// - mz)); +// recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude +// s0 = mulfix(s0, recipNorm); +// s1 = mulfix(s1, recipNorm); +// s2 = mulfix(s2, recipNorm); +// s3 = mulfix(s3, recipNorm); +// +// /* 2nd iter normalizaton */ +// // recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude +// // s0 = mulfix(s0, recipNorm); +// // s1 = mulfix(s1, recipNorm); +// // s2 = mulfix(s2, recipNorm); +// // s3 = mulfix(s3, recipNorm); +// +// // Apply feedback step +// qDot1 -= mulfix(beta, s0); +// qDot2 -= mulfix(beta, s1); +// qDot3 -= mulfix(beta, s2); +// qDot4 -= mulfix(beta, s3); +// } +// +// // Integrate rate of change of quaternion to yield quaternion +// q0 += qDot1 / sampleFreq; +// q1 += qDot2 / sampleFreq; +// q2 += qDot3 / sampleFreq; +// q3 += qDot4 / sampleFreq; +// +// // Normalise quaternion +// recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); +// // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", +// // (double)q0/FRAC_BASE, +// // (double)q1/FRAC_BASE, +// // (double)q2/FRAC_BASE, +// // (double)q3/FRAC_BASE, +// // (double)recipNorm/FRAC_BASE); +// q0 = mulfix(q0, recipNorm); +// q1 = mulfix(q1, recipNorm); +// q2 = mulfix(q2, recipNorm); +// q3 = mulfix(q3, recipNorm); +// *q0_ptr = q0; +// *q1_ptr = q1; +// *q2_ptr = q2; +// *q3_ptr = q3; +// +// // printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", +// // DISPLAY_INT(q0), DISPLAY_FRAC(q0), +// // DISPLAY_INT(q1), DISPLAY_FRAC(q1), +// // DISPLAY_INT(q2), DISPLAY_FRAC(q2), +// // DISPLAY_INT(q3), DISPLAY_FRAC(q3)); +// +// // /* 2nd iter normalization */ +// // recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); +// // q0 = mulfix(q0, recipNorm); +// // q1 = mulfix(q1, recipNorm); +// // q2 = mulfix(q2, recipNorm); +// // q3 = mulfix(q3, recipNorm); +//} +// +////--------------------------------------------------------------------------------------------------- +//// IMU algorithm update +// +//void +//MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, +// bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, +// int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { +// int32_t q0 = *q0_ptr; +// int32_t q1 = *q1_ptr; +// int32_t q2 = *q2_ptr; +// int32_t q3 = *q3_ptr; +// int32_t recipNorm; +// int32_t s0, s1, s2, s3; +// int32_t qDot1, qDot2, qDot3, qDot4; +// int32_t _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; +// +// // Rate of change of quaternion from gyroscope +// qDot1 = (- mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; +// qDot2 = ( mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; +// qDot3 = ( mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; +// qDot4 = ( mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; +// +// // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) +// if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { +// +// // Normalise accelerometer measurement +// recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); +// ax = mulfix(ax, recipNorm); +// ay = mulfix(ay, recipNorm); +// az = mulfix(az, recipNorm); +// +// // Auxiliary variables to avoid repeated arithmetic +// _2q0 = 2*q0; +// _2q1 = 2*q1; +// _2q2 = 2*q2; +// _2q3 = 2*q3; +// _4q0 = 4*q0; +// _4q1 = 4*q1; +// _4q2 = 4*q2; +// _8q1 = 8*q1; +// _8q2 = 8*q2; +// q0q0 = mulfix(q0, q0); +// q1q1 = mulfix(q1, q1); +// q2q2 = mulfix(q2, q2); +// q3q3 = mulfix(q3, q3); +// +// // Gradient decent algorithm corrective step +// s0 = mulfix(_4q0, q2q2) +// + mulfix(_2q2, ax) +// + mulfix(_4q0, q1q1) +// - mulfix(_2q1, ay); +// s1 = mulfix(_4q1, q3q3) +// - mulfix(_2q3, ax) +// + 4 * mulfix(q0q0, q1) +// - mulfix(_2q0, ay) +// - _4q1 +// + mulfix(_8q1, q1q1) +// + mulfix(_8q1, q2q2) +// + mulfix(_4q1, az); +// s2 = 4 * mulfix(q0q0, q2) +// + mulfix(_2q0, ax) +// + mulfix(_4q2, q3q3) +// - mulfix(_2q3, ay) +// - _4q2 +// + mulfix(_8q2, q1q1) +// + mulfix(_8q2, q2q2) +// + mulfix(_4q2, az); +// s3 = 4 * mulfix(q1q1, q3) +// - mulfix(_2q1, ax) +// + 4 * mulfix(q2q2, q3) +// - mulfix(_2q2, ay); +// recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude +// s0 = mulfix(s0, recipNorm); +// s1 = mulfix(s1, recipNorm); +// s2 = mulfix(s2, recipNorm); +// s3 = mulfix(s3, recipNorm); +// +// // Apply feedback step +// qDot1 -= mulfix(beta, s0); +// qDot2 -= mulfix(beta, s1); +// qDot3 -= mulfix(beta, s2); +// qDot4 -= mulfix(beta, s3); +// } +// +// // Integrate rate of change of quaternion to yield quaternion +// q0 += qDot1 / sampleFreq; +// q1 += qDot2 / sampleFreq; +// q2 += qDot3 / sampleFreq; +// q3 += qDot4 / sampleFreq; +// +// // Normalise quaternion +// recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); +// q0 = mulfix(q0, recipNorm); +// q1 = mulfix(q1, recipNorm); +// q2 = mulfix(q2, recipNorm); +// q3 = mulfix(q3, recipNorm); +// q0_ptr = &q0; +// q1_ptr = &q1; +// q2_ptr = &q2; +// q3_ptr = &q3; +//} \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index effa4ff52..9f0518a55 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -352,11 +352,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - maxPrecisionBits = 10; - - - - + maxPrecisionBits = 16; /* @@ -625,7 +621,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // finalCorrectionPass(*Mod, quantizedType); // finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization - // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); // 替换为$HOMR diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 0df1e0639..c50504170 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1106,6 +1106,64 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFixMul\n"; + + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixmulFuncName = "fixmul"; + for (auto & function : *irModule) + { + if (function.getName() == fixmulFuncName) + { + llvm::errs() << "fixmul already exists\n"; + return &function; + } + } + + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); + llvm::IRBuilder<> builder(entryBB); + builder.SetInsertPoint(entryBB); + + // Create fixed-point multiplication instruction + Type * higherQuantizedType; + switch (BIT_WIDTH) + { + case 8: + higherQuantizedType = Type::getInt16Ty(irModule->getContext()); + break; + case 16: + higherQuantizedType = Type::getInt32Ty(irModule->getContext()); + break; + default: + higherQuantizedType = Type::getInt64Ty(irModule->getContext()); + break; + } + + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); + builder.CreateRet(truncInst); + + functionsToInsert.emplace_back(func); + llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; + return func; +} // TODO Original version of fixrsqrt llvm::Function * @@ -1143,10 +1201,10 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE)), quantizedType); // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - // llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - // llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - // llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - // llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); - llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); - llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + // llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); +// llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); +// llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); +// llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // Return the final fixed-point result builder.CreateRet(final_y); @@ -1315,64 +1373,7 @@ createFloatIntMul(Module * irModule, Type * intType, Type * floatType, std::vect // return func; // } -// Create a fixed-point multiplication function -llvm::Function * -createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -{ - llvm::errs() << "Entering createFixMul\n"; - - // Check if irModule is valid - if (!irModule) - { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } - - std::string fixmulFuncName = "fixmul"; - for (auto & function : *irModule) - { - if (function.getName() == fixmulFuncName) - { - llvm::errs() << "fixmul already exists\n"; - return &function; - } - } - - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); - - // Create fixed-point multiplication instruction - Type * higherQuantizedType; - switch (BIT_WIDTH) - { - case 8: - higherQuantizedType = Type::getInt16Ty(irModule->getContext()); - break; - case 16: - higherQuantizedType = Type::getInt32Ty(irModule->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(irModule->getContext()); - break; - } - - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value * mulInst = builder.CreateMul(sext1, sext2); - llvm::Value * ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); - builder.CreateRet(truncInst); - functionsToInsert.emplace_back(func); - llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; - return func; -} // Create a fixed-point division function llvm::Function * @@ -2037,7 +2038,7 @@ performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value return result32; } void -handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType,Function * fixmul) { llvm::errs() << "Handling FMul\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -2126,6 +2127,12 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) // } if (lhsIsInteger && rhsIsInteger) + { + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); + } // 缩两边 // { @@ -2148,28 +2155,28 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) // } // 64位 - { - llvm::errs() << "Both operands are integers, performing 64-bit multiplication and shifting...\n"; - - // // Sign extend the 32-bit operands to 64-bit integers to prevent overflow - // llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - // llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); - // - // // Perform 64-bit multiplication - // llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); - // - // // Divide the result by FRAC_Q (equivalent to right shift) - // llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q), "div64"); - // - // // Truncate the 64-bit result back to 32-bit integer - // llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); - - llvm::Value * result32 = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); - - // Replace all uses of the original instruction with the final result - llvmIrInstruction->replaceAllUsesWith(result32); - llvmIrInstruction->eraseFromParent(); - } +// { +// llvm::errs() << "Both operands are integers, performing 64-bit multiplication and shifting...\n"; +// +// // // Sign extend the 32-bit operands to 64-bit integers to prevent overflow +// // llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); +// // llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); +// // +// // // Perform 64-bit multiplication +// // llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); +// // +// // // Divide the result by FRAC_Q (equivalent to right shift) +// // llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q), "div64"); +// // +// // // Truncate the 64-bit result back to 32-bit integer +// // llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); +// +// llvm::Value * result32 = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); +// +// // Replace all uses of the original instruction with the final result +// llvmIrInstruction->replaceAllUsesWith(result32); +// llvmIrInstruction->eraseFromParent(); +// } // 正常的32位 // { @@ -3063,9 +3070,9 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve updateGlobalVariables(module, quantizedType); /* - * generate hardcode function - fixmul and fixdiv + * generate hardcode function * */ - + llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); @@ -3131,7 +3138,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve else { llvm::errs() << "Found FMul instruction.\n"; - handleFMul(llvmIrInstruction, quantizedType); + handleFMul(llvmIrInstruction, quantizedType, fixmul); } break; From 406938a71d4d9f26283a2a7f3a9b4b5011591d18 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 4 Oct 2024 12:54:31 +0100 Subject: [PATCH 102/213] save before change * dev2. --- .../newton/llvm-ir/Include/stm32f303xc.h | 13485 ++++++++++++++++ .../newton/llvm-ir/Include/stm32f3xx.h | 280 + .../newton/llvm-ir/Include/system_stm32f3xx.h | 104 + applications/newton/llvm-ir/MahonyAHRS.sh | 59 + .../newton/llvm-ir/MahonyAHRS_original.sh | 0 .../newton/llvm-ir/c-files/Kalman.cpp | 104 + applications/newton/llvm-ir/c-files/Kalman.h | 59 + .../newton/llvm-ir/c-files/MadgwickAHRSDiff.c | 295 + .../newton/llvm-ir/c-files/MadgwickAHRSDiff.h | 40 + .../newton/llvm-ir/c-files/MadgwickAHRSfix.c | 42 +- .../newton/llvm-ir/c-files/MadgwickAHRSfix.h | 36 +- .../llvm-ir/c-files/MadgwickAHRSfixi8.c | 183 + .../llvm-ir/c-files/MadgwickAHRSfixi8.h | 54 + .../newton/llvm-ir/c-files/Madgwick_global.c | 234 + .../newton/llvm-ir/c-files/Madgwick_global.h | 31 + .../newton/llvm-ir/c-files/MahonyAHRS.c | 230 + .../newton/llvm-ir/c-files/MahonyAHRS.h | 32 + applications/newton/llvm-ir/c-files/fft | Bin 0 -> 16400 bytes applications/newton/llvm-ir/c-files/fft.c | 94 + .../newton/llvm-ir/c-files/fixmul_test.c | 18 + .../newton/llvm-ir/c-files/float_mul_test.c | 16 + .../c-files/floating_point_operations.c | 65 +- .../newton/llvm-ir/c-files/kalman_core.c | 812 + .../newton/llvm-ir/c-files/kalman_core.h | 170 + applications/newton/llvm-ir/c-files/newtest.c | 56 + .../newton/llvm-ir/c-files/test_Kalman.c | 27 + .../llvm-ir/c-files/test_MadgwickAHRS.c | 308 +- .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 157 +- .../newton/llvm-ir/c-files/test_fixmul.c | 91 + applications/newton/llvm-ir/c-files/test_m4.c | 340 + applications/newton/llvm-ir/fft.sh | 28 + applications/newton/llvm-ir/kalman.sh | 28 + applications/newton/llvm-ir/m4_fix.sh | 40 + applications/newton/llvm-ir/m4_original.sh | 69 + .../newton/llvm-ir/original_compile.sh | 27 + applications/newton/llvm-ir/replace.sh | 2 +- applications/newton/llvm-ir/run.sh | 4 +- applications/newton/llvm-ir/stm32f303xc.h | 13485 ++++++++++++++++ applications/newton/llvm-ir/syscalls.c | 60 + .../newton/llvm-ir/system_stm32f3xx.c | 287 + applications/newton/llvm-ir/test.sh | 27 + applications/newton/llvm-ir/testMadgwick.sh | 12 +- .../newton/llvm-ir/testMadgwickfix.sh | 5 +- applications/newton/llvm-ir/testOriginal.sh | 2 +- applications/newton/llvm-ir/test_madgwick.c | 480 +- src/newton/Range.h | 65 + src/newton/SimplePass.cpp | 5 + src/newton/SimplePass.h | 33 + src/newton/myRangeAnalysis.cpp | 921 ++ src/newton/myRangeAnalysis.h | 12 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 57 +- .../newton-irPass-LLVMIR-quantization.cpp | 136 +- src/newton/runPass.cpp | 45 + 53 files changed, 32736 insertions(+), 516 deletions(-) create mode 100644 applications/newton/llvm-ir/Include/stm32f303xc.h create mode 100644 applications/newton/llvm-ir/Include/stm32f3xx.h create mode 100644 applications/newton/llvm-ir/Include/system_stm32f3xx.h create mode 100755 applications/newton/llvm-ir/MahonyAHRS.sh create mode 100644 applications/newton/llvm-ir/MahonyAHRS_original.sh create mode 100644 applications/newton/llvm-ir/c-files/Kalman.cpp create mode 100644 applications/newton/llvm-ir/c-files/Kalman.h create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h create mode 100644 applications/newton/llvm-ir/c-files/Madgwick_global.c create mode 100644 applications/newton/llvm-ir/c-files/Madgwick_global.h create mode 100644 applications/newton/llvm-ir/c-files/MahonyAHRS.c create mode 100644 applications/newton/llvm-ir/c-files/MahonyAHRS.h create mode 100755 applications/newton/llvm-ir/c-files/fft create mode 100644 applications/newton/llvm-ir/c-files/fft.c create mode 100644 applications/newton/llvm-ir/c-files/fixmul_test.c create mode 100644 applications/newton/llvm-ir/c-files/float_mul_test.c create mode 100644 applications/newton/llvm-ir/c-files/kalman_core.c create mode 100644 applications/newton/llvm-ir/c-files/kalman_core.h create mode 100644 applications/newton/llvm-ir/c-files/newtest.c create mode 100644 applications/newton/llvm-ir/c-files/test_Kalman.c create mode 100644 applications/newton/llvm-ir/c-files/test_fixmul.c create mode 100644 applications/newton/llvm-ir/c-files/test_m4.c create mode 100755 applications/newton/llvm-ir/fft.sh create mode 100755 applications/newton/llvm-ir/kalman.sh create mode 100755 applications/newton/llvm-ir/m4_fix.sh create mode 100755 applications/newton/llvm-ir/m4_original.sh create mode 100755 applications/newton/llvm-ir/original_compile.sh create mode 100644 applications/newton/llvm-ir/stm32f303xc.h create mode 100644 applications/newton/llvm-ir/syscalls.c create mode 100644 applications/newton/llvm-ir/system_stm32f3xx.c create mode 100755 applications/newton/llvm-ir/test.sh create mode 100644 src/newton/Range.h create mode 100644 src/newton/SimplePass.cpp create mode 100644 src/newton/SimplePass.h create mode 100644 src/newton/myRangeAnalysis.cpp create mode 100644 src/newton/myRangeAnalysis.h create mode 100644 src/newton/runPass.cpp diff --git a/applications/newton/llvm-ir/Include/stm32f303xc.h b/applications/newton/llvm-ir/Include/stm32f303xc.h new file mode 100644 index 000000000..557420e9a --- /dev/null +++ b/applications/newton/llvm-ir/Include/stm32f303xc.h @@ -0,0 +1,13485 @@ +/** + ****************************************************************************** + * @file stm32f303xc.h + * @author MCD Application Team + * @brief CMSIS STM32F303xC Devices Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - Peripheral's registers declarations and bits definition + * - Macros to access peripheral's registers hardware + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f303xc + * @{ + */ + +#ifndef __STM32F303xC_H +#define __STM32F303xC_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M4 Processor and Core Peripherals + */ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F303xC devices provide an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F303xC devices use 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< STM32F303xC devices provide an FPU */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F303xC devices Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 3 Cortex-M4 Hard Fault Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line 19 */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line 20 */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_TSC_IRQn = 8, /*!< EXTI Line2 Interrupt and Touch Sense Controller Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 Interrupt */ + DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 Interrupt */ + DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 Interrupt */ + DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 Interrupt */ + DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 Interrupt */ + DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 Interrupt */ + DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 Interrupt */ + ADC1_2_IRQn = 18, /*!< ADC1 & ADC2 Interrupts */ + USB_HP_CAN_TX_IRQn = 19, /*!< USB Device High Priority or CAN TX Interrupts */ + USB_LP_CAN_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN RX0 Interrupts */ + CAN_RX1_IRQn = 21, /*!< CAN RX1 Interrupt */ + CAN_SCE_IRQn = 22, /*!< CAN SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt & EXTI Line23 Interrupt (I2C1 wakeup) */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt & EXTI Line24 Interrupt (I2C2 wakeup) */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt & EXTI Line25 Interrupt (USART1 wakeup) */ + USART2_IRQn = 38, /*!< USART2 global Interrupt & EXTI Line26 Interrupt (USART2 wakeup) */ + USART3_IRQn = 39, /*!< USART3 global Interrupt & EXTI Line28 Interrupt (USART3 wakeup) */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line 17 Interrupt */ + USBWakeUp_IRQn = 42, /*!< USB Wakeup Interrupt */ + TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ + TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ + TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt & EXTI Line34 Interrupt (UART4 wakeup) */ + UART5_IRQn = 53, /*!< UART5 global Interrupt & EXTI Line35 Interrupt (UART5 wakeup) */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC underrun error Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_IRQn = 59, /*!< DMA2 Channel 4 global Interrupt */ + DMA2_Channel5_IRQn = 60, /*!< DMA2 Channel 5 global Interrupt */ + ADC4_IRQn = 61, /*!< ADC4 global Interrupt */ + COMP1_2_3_IRQn = 64, /*!< COMP1, COMP2 and COMP3 global Interrupt via EXTI Line21, 22 and 29*/ + COMP4_5_6_IRQn = 65, /*!< COMP4, COMP5 and COMP6 global Interrupt via EXTI Line30, 31 and 32*/ + COMP7_IRQn = 66, /*!< COMP7 global Interrupt via EXTI Line33 */ + USB_HP_IRQn = 74, /*!< USB High Priority global Interrupt */ + USB_LP_IRQn = 75, /*!< USB Low Priority global Interrupt */ + USBWakeUp_RMP_IRQn = 76, /*!< USB Wakeup Interrupt remap */ + FPU_IRQn = 81, /*!< Floating point Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f3xx.h" /* STM32F3xx System Header */ +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */ + uint32_t RESERVED0; /*!< Reserved, 0x010 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, 0x01C */ + __IO uint32_t TR1; /*!< ADC watchdog threshold register 1, Address offset: 0x20 */ + __IO uint32_t TR2; /*!< ADC watchdog threshold register 2, Address offset: 0x24 */ + __IO uint32_t TR3; /*!< ADC watchdog threshold register 3, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x02C */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x044 */ + uint32_t RESERVED4; /*!< Reserved, 0x048 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t DIFSEL; /*!< ADC Differential Mode Selection Register, Address offset: 0xB0 */ + __IO uint32_t CALFACT; /*!< ADC Calibration Factors, Address offset: 0xB4 */ + +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */ + uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1/3 base address + 0x30C */ +} ADC_Common_TypeDef; + +/** + * @brief Controller Area Network TxMailBox + */ +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief Analog Comparators + */ +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, Address offset: 0x00 */ +} COMP_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, used for bits common to several COMP instances, Address offset: 0x00 */ +} COMP_Common_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CMAR; /*!< DMA channel x memory address register */ +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} DMA_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*! + Kalman::Kalman() { + /* We will set the variables like so, these can also be tuned by the user */ + Q_angle = 0.001f; + Q_bias = 0.003f; + R_measure = 0.03f; + + angle = 0.0f; // Reset the angle + bias = 0.0f; // Reset bias + + P[0][0] = 0.0f; // Since we assume that the bias is 0 and we know the starting angle (use setAngle), the error covariance matrix is set like so - see: http://en.wikipedia.org/wiki/Kalman_filter#Example_application.2C_technical + P[0][1] = 0.0f; + P[1][0] = 0.0f; + P[1][1] = 0.0f; +}; + +// The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds +float Kalman::getAngle(float newAngle, float newRate, float dt) { + // KasBot V2 - Kalman filter module - http://www.x-firm.com/?page_id=145 + // Modified by Kristian Lauszus + // See my blog post for more information: http://blog.tkjelectronics.dk/2012/09/a-practical-approach-to-kalman-filter-and-how-to-implement-it + + // Discrete Kalman filter time update equations - Time Update ("Predict") + // Update xhat - Project the state ahead + /* Step 1 */ + rate = newRate - bias; + angle += dt * rate; + + // Update estimation error covariance - Project the error covariance ahead + /* Step 2 */ + P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle); + P[0][1] -= dt * P[1][1]; + P[1][0] -= dt * P[1][1]; + P[1][1] += Q_bias * dt; + + // Discrete Kalman filter measurement update equations - Measurement Update ("Correct") + // Calculate Kalman gain - Compute the Kalman gain + /* Step 4 */ + float S = P[0][0] + R_measure; // Estimate error + /* Step 5 */ + float K[2]; // Kalman gain - This is a 2x1 vector + K[0] = P[0][0] / S; + K[1] = P[1][0] / S; + + // Calculate angle and bias - Update estimate with measurement zk (newAngle) + /* Step 3 */ + float y = newAngle - angle; // Angle difference + /* Step 6 */ + angle += K[0] * y; + bias += K[1] * y; + + // Calculate estimation error covariance - Update the error covariance + /* Step 7 */ + float P00_temp = P[0][0]; + float P01_temp = P[0][1]; + + P[0][0] -= K[0] * P00_temp; + P[0][1] -= K[0] * P01_temp; + P[1][0] -= K[1] * P00_temp; + P[1][1] -= K[1] * P01_temp; + + return angle; +}; + +void Kalman::setAngle(float angle) { this->angle = angle; }; // Used to set angle, this should be set as the starting angle +float Kalman::getRate() { return this->rate; }; // Return the unbiased rate + +/* These are used to tune the Kalman filter */ +void Kalman::setQangle(float Q_angle) { this->Q_angle = Q_angle; }; +void Kalman::setQbias(float Q_bias) { this->Q_bias = Q_bias; }; +void Kalman::setRmeasure(float R_measure) { this->R_measure = R_measure; }; + +float Kalman::getQangle() { return this->Q_angle; }; +float Kalman::getQbias() { return this->Q_bias; }; +float Kalman::getRmeasure() { return this->R_measure; }; + +int main() { + Kalman kalman; + kalman.setAngle(0.0f); // 设置初始角度为0度 + + // 进行一些简单的测试操作 + float angle = kalman.getAngle(1.0f, 0.1f, 0.1f); + std::cout << "Estimated angle: " << angle << std::endl; + + return 0; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/Kalman.h b/applications/newton/llvm-ir/c-files/Kalman.h new file mode 100644 index 000000000..a7387c0a1 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Kalman.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + +This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _Kalman_h_ +#define _Kalman_h_ + + class Kalman { + public: + Kalman(); + + // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds + float getAngle(float newAngle, float newRate, float dt); + + void setAngle(float angle); // Used to set angle, this should be set as the starting angle + float getRate(); // Return the unbiased rate + + /* These are used to tune the Kalman filter */ + void setQangle(float Q_angle); + /** + * setQbias(float Q_bias) + * Default value (0.003f) is in Kalman.cpp. + * Raise this to follow input more closely, + * lower this to smooth result of kalman filter. + */ + void setQbias(float Q_bias); + void setRmeasure(float R_measure); + + float getQangle(); + float getQbias(); + float getRmeasure(); + + private: + /* Kalman filter variables */ + float Q_angle; // Process noise variance for the accelerometer + float Q_bias; // Process noise variance for the gyro bias + float R_measure; // Measurement noise variance - this is actually the variance of the measurement noise + + float angle; // The angle calculated by the Kalman filter - part of the 2x1 state vector + float bias; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector + float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate + + float P[2][2]; // Error covariance matrix - This is a 2x2 matrix +}; + +#endif \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c new file mode 100644 index 000000000..87e5e1bba --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c @@ -0,0 +1,295 @@ +#include +#include +#include "MadgwickAHRSDiff.h" + +#define sampleFreq 28 // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain + +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + + +// Quantize a floating-point value to fixed-point format +int32_t quantize(float value, int32_t frac_base) { + return (int32_t)round(value * frac_base); // Quantize to fixed-point +} + + +// Align a smaller frac_q to the largest frac_q +int32_t align_to_frac_q(int32_t value, int32_t frac_q_value, int32_t target_frac_q) { + if (frac_q_value < target_frac_q) { + return value << (target_frac_q - frac_q_value); // Left shift to align + } + return value; // No shift needed if already at the target_frac_q +} + + + + + + +// Define function to multiply two fixed-point numbers with different precision +int32_t +mulfix(int32_t x, int32_t y, int32_t input_frac_q1, int32_t input_frac_q2, int32_t output_frac_q) +{ + int64_t result = (int64_t)x * y; + int32_t shift = input_frac_q1 + input_frac_q2 - output_frac_q; + if (shift > 0) + { + return result >> shift; // Right shift to reduce value + } + else + { + return result << (-shift); // Left shift to enlarge value + } +} + +// Convert one fixed-point number to another precision +int32_t +convert_frac_q(int32_t value, int32_t input_frac_q, int32_t output_frac_q) +{ + if (input_frac_q > output_frac_q) + { + return value >> (input_frac_q - output_frac_q); // Right shift to reduce value + } + else + { + return value << (output_frac_q - input_frac_q); // Left shift to enlarge value + } +} + +/* + * Compute square root of x and reciprocal with Goldschmidt's method + */ + +int32_t sqrt_rsqrt(int32_t x, int recip, int32_t frac_q) { + int32_t frac_base = (1 << frac_q); // Calculate the base value dynamically from frac_q + + if (recip) { + int32_t int_halfx = mulfix(0.5 * frac_base, x, frac_q, frac_q, frac_q); // half * x + float fp_y = (float)x / frac_base; // Convert to floating-point for approximation + long i = *(long*)&fp_y; + i = 0x5f3759df - (i >> 1); // Initial magic number approximation + fp_y = *(float*)&i; + int32_t int_y = fp_y * frac_base; // Convert back to fixed-point + int_y = mulfix(int_y, + ((int32_t)(1.5f * frac_base) - mulfix(mulfix(int_halfx, int_y, frac_q, frac_q, frac_q), int_y, frac_q, frac_q, frac_q)), + frac_q, frac_q, frac_q); + return int_y; + } else { + int32_t res = (int32_t)sqrt((double)x) << (frac_q / 2); // sqrt(x) in integer, shifted to frac_q/2 + + if (frac_q % 2) { + // If frac_q is odd, scale by sqrt(2) to adjust precision. + return mulfix(res, 1.414213562 * frac_base, frac_q / 2, frac_q, frac_q); + } else { + return res; + } + } +} + + +void +MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr) +{ + // Read the current quaternion values + int32_t gx_fixed = quantize(gx, FRAC_BASE_GYRO_X); + int32_t gy_fixed = quantize(gy, FRAC_BASE_GYRO_Y); + int32_t gz_fixed = quantize(gz, FRAC_BASE_GYRO_Z); + + int32_t ax_fixed = quantize(ax, FRAC_BASE_ACCEL_X); + int32_t ay_fixed = quantize(ay, FRAC_BASE_ACCEL_Y); + int32_t az_fixed = quantize(az, FRAC_BASE_ACCEL_Z); + + int32_t mx_fixed = quantize(mx, FRAC_BASE_MAG_X); + int32_t my_fixed = quantize(my, FRAC_BASE_MAG_Y); + int32_t mz_fixed = quantize(mz, FRAC_BASE_MAG_Z); + + + + // + + + + + + + + + + + + + + + + + + + int32_t q0 = *q0_ptr; + int32_t q1 = *q1_ptr; + int32_t q2 = *q2_ptr; + int32_t q3 = *q3_ptr; + + int32_t recipNorm; + int32_t s0, s1, s2, s3; + int32_t qDot1, qDot2, qDot3, qDot4; + int32_t hx, hy; + int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz; + int32_t _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3; + int32_t q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // Convert gyroscope data to a common precision + gx = convert_frac_q(gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); // Keeping it as FRAC_Q_GYRO_X + gy = convert_frac_q(gy, FRAC_Q_GYRO_Y, FRAC_Q_GYRO_X); // Converting to common precision + gz = convert_frac_q(gz, FRAC_Q_GYRO_Z, FRAC_Q_GYRO_X); // Converting to common precision + + // Use IMU algorithm if magnetometer measurement is invalid (avoids NaN in magnetometer normalization) + if ((mx == 0x0) && (my == 0x0) && (mz == 0x0)) + { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = (-mulfix(q1, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q2, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q3, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + qDot2 = (mulfix(q0, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(q2, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q3, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + qDot3 = (mulfix(q0, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q1, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(q3, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + qDot4 = (mulfix(q0, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(q1, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q2, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + + // Compute feedback only if accelerometer measurement is valid (avoids NaN in accelerometer normalization) + if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) + { + // Normalize accelerometer measurement to common precision + recipNorm = sqrt_rsqrt(mulfix(ax, ax, FRAC_Q_ACCEL_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + + mulfix(ay, ay, FRAC_Q_ACCEL_Y, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) + + mulfix(az, az, FRAC_Q_ACCEL_Z, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X), + true, FRAC_Q_GYRO_X); + + ax = mulfix(ax, recipNorm, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X); + ay = mulfix(ay, recipNorm, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y); + az = mulfix(az, recipNorm, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Z); + + // Normalize magnetometer measurement to common precision + recipNorm = sqrt_rsqrt(mulfix(mx, mx, FRAC_Q_MAG_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + + mulfix(my, my, FRAC_Q_MAG_Y, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + + mulfix(mz, mz, FRAC_Q_MAG_Z, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X), + true, FRAC_Q_GYRO_X); + + mx = mulfix(mx, recipNorm, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_MAG_X); + my = mulfix(my, recipNorm, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y); + mz = mulfix(mz, recipNorm, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_MAG_Z); + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2 * mulfix(q0, mx, FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X); + _2q0my = 2 * mulfix(q0, my, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X); + _2q1mx = 2 * mulfix(q1, mx, FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X); + _2q0 = 2 * q0; + _2q1 = 2 * q1; + _2q2 = 2 * q2; + _2q3 = 2 * q3; + _2q0q2 = 2 * mulfix(q0, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + _2q2q3 = 2 * mulfix(q2, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q0 = mulfix(q0, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q1 = mulfix(q0, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q2 = mulfix(q0, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q3 = mulfix(q0, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1q1 = mulfix(q1, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1q2 = mulfix(q1, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1q3 = mulfix(q1, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q2q2 = mulfix(q2, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q2q3 = mulfix(q2, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q3q3 = mulfix(q3, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + // Reference direction of Earth's magnetic field + hx = mulfix(mx, q0q0, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_2q0my, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q0mz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mx, q1q1, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q1, my, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X), q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q1, mz, FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X), q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mx, q2q2, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mx, q3q3, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + hy = mulfix(_2q0mx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(my, q0q0, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_2q0mz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q1mx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(my, q1q1, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(my, q2q2, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q2, mz, FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X), q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(my, q3q3, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + _2bx = sqrt_rsqrt(mulfix(hx, hx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(hy, hy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), + false, FRAC_Q_GYRO_X); + + _2bz = -mulfix(_2q0mx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q0my, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mz, q0q0, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q1mx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mz, q1q1, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q2, my, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X), q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mz, q2q2, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mz, q3q3, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + _4bx = 2 * _2bx; + _4bz = 2 * _2bz; + + // Gradient descent algorithm corrective step + s0 = -mulfix(_2q2, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + mulfix(_2q1, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) - mulfix(mulfix(_2bz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + mulfix((-mulfix(_2bx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, +mulfix(mulfix(_2bx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); FRAC_Q_GYRO_X); + + s1 = mulfix(_2q3, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + mulfix(_2q0, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) - 4 * mulfix(q1, (FRAC_BASE_GYRO_X - 2 * q1q1 - 2 * q2q2 - az), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bz, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_4bz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); + + s2 = -mulfix(_2q0, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + mulfix(_2q3, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) - 4 * mulfix(q2, (FRAC_BASE_GYRO_X - 2 * q1q1 - 2 * q2q2 - az), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X) + mulfix((-mulfix(_4bx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_2bz, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + mulfix((mulfix(_2bx, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + mulfix((mulfix(_2bx, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_4bz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); + + s3 = mulfix(_2q1, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) ++ mulfix(_2q2, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) ++ mulfix((-mulfix(_4bx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), + (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) ++ mulfix((-mulfix(_2bx, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)),(mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) ++ mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) +- my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bx, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), + (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); + + + // Normalize the step magnitude + recipNorm = sqrt_rsqrt(mulfix(s0, s0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(s1, s1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(s2, s2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(s3, s3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), true, FRAC_Q_GYRO_X); + + // Apply normalization + s0 = mulfix(s0, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + s1 = mulfix(s1, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + s2 = mulfix(s2, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + s3 = mulfix(s3, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + // Apply feedback step + qDot1 -= mulfix(beta, s0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + qDot2 -= mulfix(beta, s1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + qDot3 -= mulfix(beta, s2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + qDot4 -= mulfix(beta, s3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + } + + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 / sampleFreq; + q1 += qDot2 / sampleFreq; + q2 += qDot3 / sampleFreq; + q3 += qDot4 / sampleFreq; + + // Normalize quaternion + recipNorm = sqrt_rsqrt(mulfix(q0, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(q1, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(q2, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(q3, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), true, FRAC_Q_GYRO_X); + + q0 = mulfix(q0, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1 = mulfix(q1, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q2 = mulfix(q2, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q3 = mulfix(q3, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + // Update the output quaternion pointers + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h new file mode 100644 index 000000000..ef2bfd508 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h @@ -0,0 +1,40 @@ + + +// Define the precision of the gyroscope sensors (Gyroscope) data types +#define FRAC_Q_GYRO_X 16 // Precision bits for x-axis gyroscope data +#define FRAC_Q_GYRO_Y 16 // Precision bits for y-axis gyroscope data +#define FRAC_Q_GYRO_Z 16 // Precision bits for z-axis gyroscope data + +#define FRAC_BASE_GYRO_X (1 << FRAC_Q_GYRO_X) // 2^16, base value for x-axis gyroscope +#define FRAC_BASE_GYRO_Y (1 << FRAC_Q_GYRO_Y) // 2^16, base value for y-axis gyroscope +#define FRAC_BASE_GYRO_Z (1 << FRAC_Q_GYRO_Z) // 2^16, base value for z-axis gyroscope + +// Define the precision of the accelerometer sensors (Acceleration) data types +#define FRAC_Q_ACCEL_X 12 // Precision bits for x-axis acceleration data +#define FRAC_Q_ACCEL_Y 12 // Precision bits for y-axis acceleration data +#define FRAC_Q_ACCEL_Z 12 // Precision bits for z-axis acceleration data + +#define FRAC_BASE_ACCEL_X (1 << FRAC_Q_ACCEL_X) // 2^12, base value for x-axis acceleration +#define FRAC_BASE_ACCEL_Y (1 << FRAC_Q_ACCEL_Y) // 2^12, base value for y-axis acceleration +#define FRAC_BASE_ACCEL_Z (1 << FRAC_Q_ACCEL_Z) // 2^12, base value for z-axis acceleration + +// Define the precision of the magnetometer sensors (Magnetometer) data types +#define FRAC_Q_MAG_X 13 // Precision bits for x-axis magnetometer data +#define FRAC_Q_MAG_Y 13 // Precision bits for y-axis magnetometer data +#define FRAC_Q_MAG_Z 15 // Precision bits for z-axis magnetometer data + +#define FRAC_BASE_MAG_X (1 << FRAC_Q_MAG_X) // 2^13, base value for x-axis magnetometer +#define FRAC_BASE_MAG_Y (1 << FRAC_Q_MAG_Y) // 2^13, base value for y-axis magnetometer +#define FRAC_BASE_MAG_Z (1 << FRAC_Q_MAG_Z) // 2^15, base value for z-axis magnetometer + + +//void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, +// int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); + + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c index e4795a065..e4658c016 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c @@ -4,7 +4,7 @@ #include "fsl_misc_utilities.h" -// #include "warp.h" + #include "MadgwickAHRSfix.h" #define sampleFreq 28 // sample frequency in Hz @@ -56,6 +56,46 @@ volatile bmx055xAcceleration q0 = 0.64306622f * FRAC_BASE, q1 = 0.02828862f * FR // m=21 1/Yest=1448.0000 Yest=0.0000 Yest_hex=0 // m=22 1/Yest=2048.0000 Yest=0.0000 Yest_hex=0 + +//void MadgwickAHRSupdate_float(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { +// int mag_x = roundf(gx * FRAC_BASE); +// int mag_y = roundf(gy * FRAC_BASE); +// int mag_z = roundf(gz * FRAC_BASE); +// int gyr_x = roundf(ax * FRAC_BASE); +// int gyr_y = roundf(ay * FRAC_BASE); +// int gyr_z = roundf(az * FRAC_BASE); +// int acc_x = roundf(mx * FRAC_BASE); +// int acc_y = roundf(my * FRAC_BASE); +// int acc_z = roundf(mz * FRAC_BASE); +// // Call to update Madgwick's algorithm +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, acc_x, acc_y, acc_z, mag_x, mag_y, mag_z, (int*)q0_ptr, (int*)q1_ptr, (int*)q2_ptr, (int*)q3_ptr); +// +// //dequantize +// +// float q0_float = (float)(*(int*)q0_ptr) / FRAC_BASE; +// float q1_float = (float)(*(int*)q1_ptr) / FRAC_BASE; +// float q2_float = (float)(*(int*)q2_ptr) / FRAC_BASE; +// float q3_float = (float)(*(int*)q3_ptr) / FRAC_BASE; +//} + +int +frac2dec(int32_t x) { + if (x < 0) { + return -((FRAC_BASE - 1) & (~x + 1)) * (10000 / FRAC_BASE); + } + return ((FRAC_BASE - 1) & x) * (10000 / FRAC_BASE); +} + +float fixed_point_to_float(int q0_value, int FRAC_Q) { + int q0_int_part = q0_value >> FRAC_Q; // 提取整数部分 + int q0_frac_part = q0_value & ((1 << FRAC_Q) - 1); // 提取小数部分 + float result = (float)q0_int_part + (float)q0_frac_part / (1 << FRAC_Q); // 组合整数和小数部分 + return result; +} + + + int32_t mulfix(int32_t x, int32_t y) { diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h index cddd75f92..95a15345a 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h @@ -1,21 +1,22 @@ /* - Authored 2021, Orestis Kaparounakis. +Authored 2021, Orestis Kaparounakis. - All rights reserved. +All rights reserved. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ #include #include @@ -23,7 +24,6 @@ #include #define FRAC_Q 10 -#define K (1 << (FRAC_Q - 1)) #define FRAC_BASE (1<>FRAC_Q) + (((uint32_t)_x)>>(sizeof(int32_t)*FRAC_Q-1))) @@ -31,8 +31,8 @@ #define FORMAT_FIXP "%s%d.%04d" void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); int32_t sqrt_rsqrt(int32_t x, int recip); diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c new file mode 100644 index 000000000..07b340691 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c @@ -0,0 +1,183 @@ +#include "MadgwickAHRSfixi8.h" + +#define sampleFreq 28 // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain +#define FRAC_BASE 16 // 定点数基准 +#define FRAC_Q 4 // 定点数小数部分位数 + +typedef int8_t bmx055xAcceleration; +typedef int8_t bmx055yAcceleration; +typedef int8_t bmx055zAcceleration; +typedef int8_t bmx055xAngularRate; +typedef int8_t bmx055yAngularRate; +typedef int8_t bmx055zAngularRate; +typedef int8_t bmx055xMagneto; +typedef int8_t bmx055yMagneto; +typedef int8_t bmx055zMagneto; + +volatile int8_t beta = (int8_t)(betaDef * FRAC_BASE); // 缩放后的beta + +// 定点乘法运算 +int8_t mulfix(int8_t x, int8_t y) { + return (int16_t)x * (int16_t)y / FRAC_BASE; +} + +// 计算平方根和倒数 +int8_t sqrt_rsqrt(int8_t x, int recip) { + if (recip) { + int8_t int_halfx = mulfix((int8_t)(0.5 * FRAC_BASE), x); + float fp_y = (float)x / FRAC_BASE; + int i = *(int*)&fp_y; + i = 0x5f3759df - (i >> 1); // Fast inverse square root approximation + fp_y = *(float*)&i; + int8_t int_y = (int8_t)(fp_y * FRAC_BASE); + int_y = mulfix(int_y, (int8_t)((1.5f * FRAC_BASE) - mulfix(mulfix(int_halfx, int_y), int_y))); + return int_y; + } else { + int8_t res = (int8_t)sqrt((double)x) << (FRAC_Q / 2); + if (FRAC_Q % 2) { + return mulfix(res, (int8_t)(1.414213562 * FRAC_BASE)); + } else { + return res; + } + } +} + +//==================================================================================================== +// Madgwick算法更新函数 + +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr) { + + int8_t q0 = *q0_ptr; + int8_t q1 = *q1_ptr; + int8_t q2 = *q2_ptr; + int8_t q3 = *q3_ptr; + + int8_t recipNorm; + int8_t s0, s1, s2, s3; + int8_t qDot1, qDot2, qDot3, qDot4; + int8_t hx, hy; + int8_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz; + int8_t _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // 当磁力计无效时,使用IMU算法 + if ((mx == 0x0) && (my == 0x0) && (mz == 0x0)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // 根据陀螺仪的变化更新四元数 + qDot1 = (-mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; + qDot2 = ( mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; + qDot3 = ( mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; + qDot4 = ( mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; + + // 如果加速度计有效,则进行归一化 + if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { + recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); + ax = mulfix(ax, recipNorm); + ay = mulfix(ay, recipNorm); + az = mulfix(az, recipNorm); + + recipNorm = sqrt_rsqrt(mulfix(mx, mx) + mulfix(my, my) + mulfix(mz, mz), true); + mx = mulfix(mx, recipNorm); + my = mulfix(my, recipNorm); + mz = mulfix(mz, recipNorm); + + // 算法修正 + _2q0mx = 2 * mulfix(q0, mx); + _2q0my = 2 * mulfix(q0, my); + _2q0mz = 2 * mulfix(q0, mz); + _2q1mx = 2 * mulfix(q1, mx); + _2q0 = 2 * q0; + _2q1 = 2 * q1; + _2q2 = 2 * q2; + _2q3 = 2 * q3; + _2q0q2 = 2 * mulfix(q0, q2); + _2q2q3 = 2 * mulfix(q2, q3); + q0q0 = mulfix(q0, q0); + q0q1 = mulfix(q0, q1); + q0q2 = mulfix(q0, q2); + q0q3 = mulfix(q0, q3); + q1q1 = mulfix(q1, q1); + q1q2 = mulfix(q1, q2); + q1q3 = mulfix(q1, q3); + q2q2 = mulfix(q2, q2); + q2q3 = mulfix(q2, q3); + q3q3 = mulfix(q3, q3); + + // 计算地磁方向 + hx = mulfix(mx, q0q0) - mulfix(_2q0my, q3) + mulfix(_2q0mz, q2) + mulfix(mx, q1q1) + + mulfix(mulfix(_2q1, my), q2) + mulfix(mulfix(_2q1, mz), q3) - mulfix(mx, q2q2) - mulfix(mx, q3q3); + hy = mulfix(_2q0mx, q3) + mulfix(my, q0q0) - mulfix(_2q0mz, q1) + mulfix(_2q1mx, q2) - + mulfix(my, q1q1) + mulfix(my, q2q2) + mulfix(mulfix(_2q2, mz), q3) - mulfix(my, q3q3); + _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); + _2bz = -mulfix(_2q0mx, q2) + mulfix(_2q0my, q1) + mulfix(mz, q0q0) + mulfix(_2q1mx, q3) - + mulfix(mz, q1q1) + mulfix(mulfix(_2q2, my), q3) - mulfix(mz, q2q2) + mulfix(mz, q3q3); + _4bx = 2 * _2bx; + _4bz = 2 * _2bz; + + // 梯度下降法修正 + s0 = -mulfix(_2q2, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q1, (2 * q0q1 + _2q2q3 - ay)) - + mulfix(mulfix(_2bz, q2), (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), (mulfix(_2bx, (q1q2 - q0q3)) + mulfix(_2bz, (q0q1 + q2q3)) - my)) + + mulfix(mulfix(_2bx, q2), (mulfix(_2bx, (q0q2 + q1q3)) + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + s1 = mulfix(_2q3, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q0, (2 * q0q1 + _2q2q3 - ay)) - + 4 * mulfix(q1, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + mulfix(mulfix(_2bz, q3), + (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), (mulfix(_2bx, (q1q2 - q0q3)) + mulfix(_2bz, (q0q1 + q2q3)) - my)) + + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), (mulfix(_2bx, (q0q2 + q1q3)) + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + s2 = -mulfix(_2q0, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q3, (2 * q0q1 + _2q2q3 - ay)) - + 4 * mulfix(q2, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), + (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), (mulfix(_2bx, (q1q2 - q0q3)) + mulfix(_2bz, (q0q1 + q2q3)) - my)) + + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), (mulfix(_2bx, (q0q2 + q1q3)) + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + s3 = mulfix(_2q1, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q2, (2 * q0q1 + _2q2q3 - ay)) + + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), (mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) - my)) + mulfix(mulfix(_2bx, q1), (mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); + s0 = mulfix(s0, recipNorm); + s1 = mulfix(s1, recipNorm); + s2 = mulfix(s2, recipNorm); + s3 = mulfix(s3, recipNorm); + + // 应用反馈修正 + qDot1 -= mulfix(beta, s0); + qDot2 -= mulfix(beta, s1); + qDot3 -= mulfix(beta, s2); + qDot4 -= mulfix(beta, s3); + } + + // 更新四元数 + q0 += qDot1 / sampleFreq; + q1 += qDot2 / sampleFreq; + q2 += qDot3 / sampleFreq; + q3 += qDot4 / sampleFreq; + + // 归一化四元数 + recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); + q0 = mulfix(q0, recipNorm); + q1 = mulfix(q1, recipNorm); + q2 = mulfix(q2, recipNorm); + q3 = mulfix(q3, recipNorm); + + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} + +//==================================================================================================== +// IMU-only update function + +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr) { + // IMU-only update逻辑可以参照上面的MadgwickAHRSupdate编写。 + // 基本上可以移除磁力计相关的计算步骤。 +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h new file mode 100644 index 000000000..b1b3963b9 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h @@ -0,0 +1,54 @@ +/* +Authored 2021, Orestis Kaparounakis. + +All rights reserved. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MADGWICK_AHRS_FIX_H +#define MADGWICK_AHRS_FIX_H + +#include +#include +#include +#include + +// 定义定点数精度为2位 +#define FRAC_Q 2 +#define K (1 << (FRAC_Q - 1)) +#define FRAC_BASE (1 << FRAC_Q) // FRAC_BASE = 4 +#define DEC2FRAC(_d) ((int8_t)(_d * FRAC_BASE)) // 将小数转换为定点数格式 +#define DISPLAY_INT(_x) ((_x >> FRAC_Q)) // 显示定点数的整数部分 +#define DISPLAY_FRAC(_y) (((FRAC_BASE - 1) & _y) * (10000 / FRAC_BASE)) // 显示定点数的小数部分 +#define FORMAT_FIXP "%s%d.%04d" // 格式化输出定点数 + +// 四元数更新函数 +void MadgwickAHRSupdate(int8_t gx, int8_t gy, int8_t gz, int8_t ax, int8_t ay, int8_t az, int8_t mx, int8_t my, int8_t mz, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr); + +// IMU-only更新函数 +void MadgwickAHRSupdateIMU(int8_t gx, int8_t gy, int8_t gz, int8_t ax, int8_t ay, int8_t az, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr); + +// 计算平方根或倒数平方根 +int8_t sqrt_rsqrt(int8_t x, int recip); + +// 定点乘法函数 (内联优化) +inline int8_t fixmul(int8_t x, int8_t y) { +// 定点乘法后将结果右移以保持精度 +return ((int16_t)x * y) >> FRAC_Q; +} + +#endif /* MADGWICK_AHRS_FIX_H */ diff --git a/applications/newton/llvm-ir/c-files/Madgwick_global.c b/applications/newton/llvm-ir/c-files/Madgwick_global.c new file mode 100644 index 000000000..9c7f7dfbf --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Madgwick_global.c @@ -0,0 +1,234 @@ +//===================================================================================================== +// MadgwickAHRS.c +//===================================================================================================== +// +// Implementation of Madgwick's IMU and AHRS algorithms. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised +// +//===================================================================================================== + +//--------------------------------------------------------------------------------------------------- +// Header files + +#include "MadgwickAHRS_global.h" +#include + +//--------------------------------------------------------------------------------------------------- +// Definitions + +// #define sampleFreq 512.0f // sample frequency in Hz +// #define sampleFreq 100.0f // sample frequency in Hz +//#define sampleFreq 128.0f // sample frequency in Hz +#define sampleFreq 28.0f // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain + +//--------------------------------------------------------------------------------------------------- +// Variable definitions + +volatile float beta = betaDef; // 2 * proportional gain (Kp) +volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +float invSqrt(float x); + +//==================================================================================================== +// Functions + +//--------------------------------------------------------------------------------------------------- +// AHRS algorithm update + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float hx, hy; + float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // warpPrint("In MadgwickAHRSupdate!!\n"); + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2.0f * q0 * mx; + _2q0my = 2.0f * q0 * my; + _2q0mz = 2.0f * q0 * mz; + _2q1mx = 2.0f * q1 * mx; + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _2q0q2 = 2.0f * q0 * q2; + _2q2q3 = 2.0f * q2 * q3; + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; + hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; + _2bx = sqrt(hx * hx + hy * hy); + _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + + + + +//--------------------------------------------------------------------------------------------------- +// IMU algorithm update + +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _4q0 = 4.0f * q0; + _4q1 = 4.0f * q1; + _4q2 = 4.0f * q2; + _8q1 = 8.0f * q1; + _8q2 = 8.0f * q2; + q0q0 = q0 * q0; + q1q1 = q1 * q1; + q2q2 = q2 * q2; + q3q3 = q3 * q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; + s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; + s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; + s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} + +//==================================================================================================== +// END OF CODE +//==================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/Madgwick_global.h b/applications/newton/llvm-ir/c-files/Madgwick_global.h new file mode 100644 index 000000000..fc26b4bec --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Madgwick_global.h @@ -0,0 +1,31 @@ +//===================================================================================================== +// MadgwickAHRS.h +//===================================================================================================== +// +// Implementation of Madgwick's IMU and AHRS algorithms. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== +#ifndef MadgwickAHRS_h +#define MadgwickAHRS_h + +//---------------------------------------------------------------------------------------------------- +// Variable declaration + +// extern volatile float beta; // algorithm gain +// extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); + +#endif +//===================================================================================================== +// End of file +//===================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/MahonyAHRS.c b/applications/newton/llvm-ir/c-files/MahonyAHRS.c new file mode 100644 index 000000000..c4cbeb46b --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MahonyAHRS.c @@ -0,0 +1,230 @@ +//===================================================================================================== +// MadgwickAHRS.c +//===================================================================================================== +// +// Implementation of Madgwick's IMU and AHRS algorithms. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised +// +//===================================================================================================== + +//--------------------------------------------------------------------------------------------------- +// Header files + +#include "MadgwickAHRS.h" +#include + +//--------------------------------------------------------------------------------------------------- +// Definitions + +// #define sampleFreq 512.0f // sample frequency in Hz +// #define sampleFreq 100.0f // sample frequency in Hz +#define sampleFreq 28.0f // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain + +//--------------------------------------------------------------------------------------------------- +// Variable definitions + +volatile float beta = betaDef; // 2 * proportional gain (Kp) +volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +float invSqrt(float x); + +//==================================================================================================== +// Functions + +//--------------------------------------------------------------------------------------------------- +// AHRS algorithm update + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float hx, hy; + float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // warpPrint("In MadgwickAHRSupdate!!\n"); + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2.0f * q0 * mx; + _2q0my = 2.0f * q0 * my; + _2q0mz = 2.0f * q0 * mz; + _2q1mx = 2.0f * q1 * mx; + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _2q0q2 = 2.0f * q0 * q2; + _2q2q3 = 2.0f * q2 * q3; + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; + hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; + _2bx = sqrt(hx * hx + hy * hy); + _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +//--------------------------------------------------------------------------------------------------- +// IMU algorithm update + +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _4q0 = 4.0f * q0; + _4q1 = 4.0f * q1; + _4q2 = 4.0f * q2; + _8q1 = 8.0f * q1; + _8q2 = 8.0f * q2; + q0q0 = q0 * q0; + q1q1 = q1 * q1; + q2q2 = q2 * q2; + q3q3 = q3 * q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; + s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; + s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; + s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} + +//==================================================================================================== +// END OF CODE +//==================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/MahonyAHRS.h b/applications/newton/llvm-ir/c-files/MahonyAHRS.h new file mode 100644 index 000000000..2a288a146 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MahonyAHRS.h @@ -0,0 +1,32 @@ +//===================================================================================================== +// MahonyAHRS.h +//===================================================================================================== +// +// Madgwick's implementation of Mayhony's AHRS algorithm. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== +#ifndef MahonyAHRS_h +#define MahonyAHRS_h + +//---------------------------------------------------------------------------------------------------- +// Variable declaration + +// extern volatile float twoKp; // 2 * proportional gain (Kp) +// extern volatile float twoKi; // 2 * integral gain (Ki) +// extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); + +#endif +//===================================================================================================== +// End of file +//===================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/fft b/applications/newton/llvm-ir/c-files/fft new file mode 100755 index 0000000000000000000000000000000000000000..69604a9204e38f463a61a3d41a00d77bff390d7a GIT binary patch literal 16400 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 +#include +#include + +// 复数结构体 +typedef struct { + double real; + double imag; +} Complex; + +// 复数相加 +Complex complex_add(Complex a, Complex b) { + Complex result; + result.real = a.real + b.real; + result.imag = a.imag + b.imag; + return result; +} + +// 复数相减 +Complex complex_sub(Complex a, Complex b) { + Complex result; + result.real = a.real - b.real; + result.imag = a.imag - b.imag; + return result; +} + +// 复数相乘 +Complex complex_mul(Complex a, Complex b) { + Complex result; + result.real = a.real * b.real - a.imag * b.imag; + result.imag = a.real * b.imag + a.imag * b.real; + return result; +} + +// 快速傅里叶变换 +void fft(Complex *x, int n) { + if (n <= 1) return; + + // 将输入数组分成偶数和奇数部分 + Complex *even = (Complex *)malloc(n / 2 * sizeof(Complex)); + Complex *odd = (Complex *)malloc(n / 2 * sizeof(Complex)); + for (int i = 0; i < n / 2; i++) { + even[i] = x[i * 2]; + odd[i] = x[i * 2 + 1]; + } + + // 递归计算子部分 + fft(even, n / 2); + fft(odd, n / 2); + + // 合并子部分结果 + for (int k = 0; k < n / 2; k++) { + double t = -2 * M_PI * k / n; + Complex w = { cos(t), sin(t) }; + Complex t_mul = complex_mul(w, odd[k]); + x[k] = complex_add(even[k], t_mul); + x[k + n / 2] = complex_sub(even[k], t_mul); + } + + free(even); + free(odd); +} + +// 打印复数数组 +void print_complex_array(Complex *x, int n) { + for (int i = 0; i < n; i++) { + printf("(%f, %f)\n", x[i].real, x[i].imag); + } +} + +int main() { + // 输入序列的长度(必须是2的幂次) + int n = 8; + + // 创建复数数组并初始化为输入信号 + Complex x[n]; + for (int i = 0; i < n; i++) { + x[i].real = i; + x[i].imag = 0.0; + } + + // 打印输入信号 + printf("Input:\n"); + print_complex_array(x, n); + + // 计算FFT + fft(x, n); + + // 打印FFT结果 + printf("Output:\n"); + print_complex_array(x, n); + + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/fixmul_test.c b/applications/newton/llvm-ir/c-files/fixmul_test.c new file mode 100644 index 000000000..adb2a5f7c --- /dev/null +++ b/applications/newton/llvm-ir/c-files/fixmul_test.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#define FRAC_Q 10 // 假设定点小数使用16位 + +int32_t fixmul(int32_t x, int32_t y) { + return ((int64_t)x * y) >> FRAC_Q; +} + +int main() { + int32_t a = 32767; + int32_t b = 32767; + for (long i = 0; i < 100000000; i++) { + fixmul(a, b); + } + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/float_mul_test.c b/applications/newton/llvm-ir/c-files/float_mul_test.c new file mode 100644 index 000000000..08928ad93 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/float_mul_test.c @@ -0,0 +1,16 @@ +#include +#include +#include + +float float_mul(float x, float y) { + return x * y; +} + +int main() { + float a = 32767.0f; // 浮点测试也使用相同的数值范围 + float b = 32767.0f; + for (long i = 0; i < 100000000; i++) { + float_mul(a, b); + } + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.c b/applications/newton/llvm-ir/c-files/floating_point_operations.c index 69369f278..33739338c 100644 --- a/applications/newton/llvm-ir/c-files/floating_point_operations.c +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.c @@ -5,29 +5,12 @@ #include #include -// float invSqrt(float x) { -// float halfx = 0.5f * x; -// float y = x; -// //#pragma unsupported -// long i = *(long*)&y; -// i = 0x5f3759df - (i>>1); -// y = *(float*)&i; -// //#end -// y = y * (1.5f - (halfx * y * y)); -// return y; -// } - -// double -// my_quantize(double a) -//{ -// return a; -// } // Function to perform basic floating point operations // Function to perform addition - float perform_addition(float a, float b) { - - return a + b; - } +// float perform_addition(float a, float b) { +// +// return a + b; +// } // // // @@ -46,13 +29,13 @@ //////// ////////// Function to perform multiplication - float perform_multiplication(float a, float b) { - return a * b; - } +// float perform_multiplication(float a, float b) { +// return a * b; +// } //// // float perform_division(float a, float b) { // return a / b; -// } +// }c // float // perform_constant_multiplication(float a) @@ -376,7 +359,7 @@ // return step0 * norm; // } -typedef uint64_t float64; +// typedef uint64_t float64; // void float64_add(float64 a, float64 b, float64 *result) { // // Extract components of first number @@ -424,7 +407,7 @@ typedef uint64_t float64; // *result = ((uint64_t)sign1 << 63) | ((uint64_t)exponent1 << 52) | (mantissa & 0xFFFFFFFFFFFFF); // } -//float invSqrt(float x) { +// float invSqrt(float x) { // float halfx = 0.5f * x; // float y = x; // //#pragma unsupported @@ -434,18 +417,17 @@ typedef uint64_t float64; // //#end // y = y * (1.5f - (halfx * y * y)); // return y; -//} - +// } -//void -//MadgwickAHRSupdateIMU(float ax, +// void +// MadgwickAHRSupdateIMU(float ax, // float * q0_ptr) //{ // float q0 = *q0_ptr; -//} +// } // -//void -//MadgwickAHRSupdate(float gx, float ax, +// void +// MadgwickAHRSupdate(float gx, float ax, // float * q0_ptr) //{ // float q0 = *q0_ptr; @@ -453,4 +435,17 @@ typedef uint64_t float64; // MadgwickAHRSupdateIMU(ax, q0_ptr); // return; // } -//} +// } + +// void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + +void +MadgwickAHRSupdate(float mx, + float * q0_ptr) +{ + float q0 = *q0_ptr; + float _2q0mx; + + _2q0mx = 2.0f * q0 * mx; +} diff --git a/applications/newton/llvm-ir/c-files/kalman_core.c b/applications/newton/llvm-ir/c-files/kalman_core.c new file mode 100644 index 000000000..cd048607e --- /dev/null +++ b/applications/newton/llvm-ir/c-files/kalman_core.c @@ -0,0 +1,812 @@ +/** +* Authored by Michael Hamer (http://www.mikehamer.info), June 2016 +* Thank you to Mark Mueller (www.mwm.im) for advice during implementation, +* and for derivation of the original filter in the below-cited paper. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================================ +* +* The Kalman filter implemented in this file is based on the papers: +* +* "Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation" +* http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=7139421 +* +* and +* +* "Covariance Correction Step for Kalman Filtering with an Attitude" +* http://arc.aiaa.org/doi/abs/10.2514/1.G000848 +* +* Academic citation would be appreciated. +* +* BIBTEX ENTRIES: + @INPROCEEDINGS{MuellerHamerUWB2015, + author = {Mueller, Mark W and Hamer, Michael and D’Andrea, Raffaello}, + title = {Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation}, + booktitle = {2015 IEEE International Conference on Robotics and Automation (ICRA)}, + year = {2015}, + month = {May}, + pages = {1730-1736}, + doi = {10.1109/ICRA.2015.7139421}, + ISSN = {1050-4729}} + + @ARTICLE{MuellerCovariance2016, + author={Mueller, Mark W and Hehn, Markus and D’Andrea, Raffaello}, + title={Covariance Correction Step for Kalman Filtering with an Attitude}, + journal={Journal of Guidance, Control, and Dynamics}, + pages={1--7}, + year={2016}, + publisher={American Institute of Aeronautics and Astronautics}} +* +* ============================================================================ +* +* MAJOR CHANGELOG: +* 2016.06.28, Mike Hamer: Initial version +* 2019.04.12, Kristoffer Richardsson: Refactored, separated kalman implementation from OS related functionality +*/ + +#include "kalman_core.h" +#include "cfassert.h" +#include "autoconf.h" + +#include "physicalConstants.h" + +#include "math3d.h" +#include "static_mem.h" + + // #define DEBUG_STATE_CHECK + + // the reversion of pitch and roll to zero +#ifdef CONFIG_DECK_LOCO_2D_POSITION +#define ROLLPITCH_ZERO_REVERSION (0.0f) +#else +#define ROLLPITCH_ZERO_REVERSION (0.001f) +#endif + + + /** +* Supporting and utility functions + */ + +#ifdef DEBUG_STATE_CHECK +static void assertStateNotNaN(const kalmanCoreData_t* this) { + if ((isnan(this->S[KC_STATE_X])) || + (isnan(this->S[KC_STATE_Y])) || + (isnan(this->S[KC_STATE_Z])) || + (isnan(this->S[KC_STATE_PX])) || + (isnan(this->S[KC_STATE_PY])) || + (isnan(this->S[KC_STATE_PZ])) || + (isnan(this->S[KC_STATE_D0])) || + (isnan(this->S[KC_STATE_D1])) || + (isnan(this->S[KC_STATE_D2])) || + (isnan(this->q[0])) || + (isnan(this->q[1])) || + (isnan(this->q[2])) || + (isnan(this->q[3]))) + { + ASSERT(false); + } + + for(int i=0; iP[i][j])) + { + ASSERT(false); + } + } + } + } +#else +static void assertStateNotNaN(const kalmanCoreData_t* this) + { + return; + } +#endif + + + // The bounds on the covariance, these shouldn't be hit, but sometimes are... why? +#define MAX_COVARIANCE (100) +#define MIN_COVARIANCE (1e-6f) + + // Small number epsilon, to prevent dividing by zero +#define EPS (1e-6f) + + void kalmanCoreDefaultParams(kalmanCoreParams_t* params) + { + // Initial variances, uncertain of position, but know we're stationary and roughly flat + params->stdDevInitialPosition_xy = 100; + params->stdDevInitialPosition_z = 1; + params->stdDevInitialVelocity = 0.01; + params->stdDevInitialAttitude_rollpitch = 0.01; + params->stdDevInitialAttitude_yaw = 0.01; + + params->procNoiseAcc_xy = 0.5f; + params->procNoiseAcc_z = 1.0f; + params->procNoiseVel = 0; + params->procNoisePos = 0; + params->procNoiseAtt = 0; + params->measNoiseBaro = 2.0f; // meters + params->measNoiseGyro_rollpitch = 0.1f; // radians per second + params->measNoiseGyro_yaw = 0.1f; // radians per second + + params->initialX = 0.0; + params->initialY = 0.0; + params->initialZ = 0.0; + + // Initial yaw of the Crazyflie in radians. + // 0 --- facing positive X + // PI / 2 --- facing positive Y + // PI --- facing negative X + // 3 * PI / 2 --- facing negative Y + params->initialYaw = 0.0; + } + + void kalmanCoreInit(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs) + { + // Reset all data to 0 (like upon system reset) + memset(this, 0, sizeof(kalmanCoreData_t)); + + this->S[KC_STATE_X] = params->initialX; + this->S[KC_STATE_Y] = params->initialY; + this->S[KC_STATE_Z] = params->initialZ; + // this->S[KC_STATE_PX] = 0; + // this->S[KC_STATE_PY] = 0; + // this->S[KC_STATE_PZ] = 0; + // this->S[KC_STATE_D0] = 0; + // this->S[KC_STATE_D1] = 0; + // this->S[KC_STATE_D2] = 0; + + // reset the attitude quaternion + this->initialQuaternion[0] = arm_cos_f32(params->initialYaw / 2); + this->initialQuaternion[1] = 0.0; + this->initialQuaternion[2] = 0.0; + this->initialQuaternion[3] = arm_sin_f32(params->initialYaw / 2); + for (int i = 0; i < 4; i++) { this->q[i] = this->initialQuaternion[i]; } + + // then set the initial rotation matrix to the identity. This only affects + // the first prediction step, since in the finalization, after shifting + // attitude errors into the attitude state, the rotation matrix is updated. + for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { this->R[i][j] = i==j ? 1 : 0; }} + + for (int i=0; i< KC_STATE_DIM; i++) { + for (int j=0; j < KC_STATE_DIM; j++) { + this->P[i][j] = 0; // set covariances to zero (diagonals will be changed from zero in the next section) + } + } + + // initialize state variances + this->P[KC_STATE_X][KC_STATE_X] = powf(params->stdDevInitialPosition_xy, 2); + this->P[KC_STATE_Y][KC_STATE_Y] = powf(params->stdDevInitialPosition_xy, 2); + this->P[KC_STATE_Z][KC_STATE_Z] = powf(params->stdDevInitialPosition_z, 2); + + this->P[KC_STATE_PX][KC_STATE_PX] = powf(params->stdDevInitialVelocity, 2); + this->P[KC_STATE_PY][KC_STATE_PY] = powf(params->stdDevInitialVelocity, 2); + this->P[KC_STATE_PZ][KC_STATE_PZ] = powf(params->stdDevInitialVelocity, 2); + + this->P[KC_STATE_D0][KC_STATE_D0] = powf(params->stdDevInitialAttitude_rollpitch, 2); + this->P[KC_STATE_D1][KC_STATE_D1] = powf(params->stdDevInitialAttitude_rollpitch, 2); + this->P[KC_STATE_D2][KC_STATE_D2] = powf(params->stdDevInitialAttitude_yaw, 2); + + this->Pm.numRows = KC_STATE_DIM; + this->Pm.numCols = KC_STATE_DIM; + this->Pm.pData = (float*)this->P; + + this->baroReferenceHeight = 0.0; + + this->isUpdated = false; + this->lastPredictionMs = nowMs; + this->lastProcessNoiseUpdateMs = nowMs; + } + + void kalmanCoreScalarUpdate(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, float error, float stdMeasNoise) + { + // The Kalman gain as a column vector + NO_DMA_CCM_SAFE_ZERO_INIT static float K[KC_STATE_DIM]; + static arm_matrix_instance_f32 Km = {KC_STATE_DIM, 1, (float *)K}; + + // Temporary matrices for the covariance updates + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float tmpNN1d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN1m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN1d}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float tmpNN2d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN2m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN2d}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float tmpNN3d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN3m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN3d}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float HTd[KC_STATE_DIM * 1]; + static arm_matrix_instance_f32 HTm = {KC_STATE_DIM, 1, HTd}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float PHTd[KC_STATE_DIM * 1]; + static arm_matrix_instance_f32 PHTm = {KC_STATE_DIM, 1, PHTd}; + + ASSERT(Hm->numRows == 1); + ASSERT(Hm->numCols == KC_STATE_DIM); + + // ====== INNOVATION COVARIANCE ====== + + mat_trans(Hm, &HTm); + mat_mult(&this->Pm, &HTm, &PHTm); // PH' + float R = stdMeasNoise*stdMeasNoise; + float HPHR = R; // HPH' + R + for (int i=0; ipData[i]*PHTd[i]; // this obviously only works if the update is scalar (as in this function) + } + ASSERT(!isnan(HPHR)); + + // ====== MEASUREMENT UPDATE ====== + // Calculate the Kalman gain and perform the state update + for (int i=0; iS[i] = this->S[i] + K[i] * error; // state update + } + assertStateNotNaN(this); + + // ====== COVARIANCE UPDATE ====== + mat_mult(&Km, Hm, &tmpNN1m); // KH + for (int i=0; iPm, &tmpNN3m); // (KH - I)*P + mat_mult(&tmpNN3m, &tmpNN2m, &this->Pm); // (KH - I)*P*(KH - I)' + assertStateNotNaN(this); + // add the measurement variance and ensure boundedness and symmetry + // TODO: Why would it hit these bounds? Needs to be investigated. + for (int i=0; iP[i][j] + 0.5f*this->P[j][i] + v; // add measurement noise + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + + assertStateNotNaN(this); + + this->isUpdated = true; + } + + void kalmanCoreUpdateWithPKE(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, arm_matrix_instance_f32 *Km, arm_matrix_instance_f32 *P_w_m, float error) + { + // kalman filter update with weighted covariance matrix P_w_m, kalman gain Km, and innovation error + // Temporary matrices for the covariance updates + static float tmpNN1d[KC_STATE_DIM][KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN1m = {KC_STATE_DIM, KC_STATE_DIM, (float *)tmpNN1d}; + for (int i=0; iS[i] = this->S[i] + Km->pData[i] * error; + } + // ====== COVARIANCE UPDATE ====== // + mat_mult(Km, Hm, &tmpNN1m); // KH, the Kalman Gain and H are the updated Kalman Gain and H + mat_scale(&tmpNN1m, -1.0f, &tmpNN1m); // I-KH + for (int i=0; iP, Ppo, sizeof(this->P)); + + assertStateNotNaN(this); + + for (int i=0; iP[i][j] + 0.5f*this->P[j][i]; + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + assertStateNotNaN(this); + + this->isUpdated = true; + } + + void kalmanCoreUpdateWithBaro(kalmanCoreData_t *this, const kalmanCoreParams_t *params, float baroAsl, bool quadIsFlying) + { + float h[KC_STATE_DIM] = {0}; + arm_matrix_instance_f32 H = {1, KC_STATE_DIM, h}; + + h[KC_STATE_Z] = 1; + + if (!quadIsFlying || this->baroReferenceHeight < 1) { + //TODO: maybe we could track the zero height as a state. Would be especially useful if UWB anchors had barometers. + this->baroReferenceHeight = baroAsl; + } + + float meas = (baroAsl - this->baroReferenceHeight); + kalmanCoreScalarUpdate(this, &H, meas - this->S[KC_STATE_Z], params->measNoiseBaro); + } + + static void predictDt(kalmanCoreData_t* this, Axis3f *acc, Axis3f *gyro, float dt, bool quadIsFlying) + { + /* Here we discretize (euler forward) and linearise the quadrocopter dynamics in order + * to push the covariance forward. + * + * QUADROCOPTER DYNAMICS (see paper): + * + * \dot{x} = R(I + [[d]])p + * \dot{p} = f/m * e3 - [[\omega]]p - g(I - [[d]])R^-1 e3 //drag negligible + * \dot{d} = \omega + * + * where [[.]] is the cross-product matrix of . + * \omega are the gyro measurements + * e3 is the column vector [0 0 1]' + * I is the identity + * R is the current attitude as a rotation matrix + * f/m is the mass-normalized motor force (acceleration in the body's z direction) + * g is gravity + * x, p, d are the quad's states + * note that d (attitude error) is zero at the beginning of each iteration, + * since error information is incorporated into R after each Kalman update. + */ + + // The linearized update matrix + NO_DMA_CCM_SAFE_ZERO_INIT static float A[KC_STATE_DIM][KC_STATE_DIM]; + static __attribute__((aligned(4))) arm_matrix_instance_f32 Am = { KC_STATE_DIM, KC_STATE_DIM, (float *)A}; // linearized dynamics for covariance update; + + // Temporary matrices for the covariance updates + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN1d[KC_STATE_DIM * KC_STATE_DIM]; + static __attribute__((aligned(4))) arm_matrix_instance_f32 tmpNN1m = { KC_STATE_DIM, KC_STATE_DIM, tmpNN1d}; + + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN2d[KC_STATE_DIM * KC_STATE_DIM]; + static __attribute__((aligned(4))) arm_matrix_instance_f32 tmpNN2m = { KC_STATE_DIM, KC_STATE_DIM, tmpNN2d}; + + float dt2 = dt*dt; + + // ====== DYNAMICS LINEARIZATION ====== + // Initialize as the identity + A[KC_STATE_X][KC_STATE_X] = 1; + A[KC_STATE_Y][KC_STATE_Y] = 1; + A[KC_STATE_Z][KC_STATE_Z] = 1; + + A[KC_STATE_PX][KC_STATE_PX] = 1; + A[KC_STATE_PY][KC_STATE_PY] = 1; + A[KC_STATE_PZ][KC_STATE_PZ] = 1; + + A[KC_STATE_D0][KC_STATE_D0] = 1; + A[KC_STATE_D1][KC_STATE_D1] = 1; + A[KC_STATE_D2][KC_STATE_D2] = 1; + + // position from body-frame velocity + A[KC_STATE_X][KC_STATE_PX] = this->R[0][0]*dt; + A[KC_STATE_Y][KC_STATE_PX] = this->R[1][0]*dt; + A[KC_STATE_Z][KC_STATE_PX] = this->R[2][0]*dt; + + A[KC_STATE_X][KC_STATE_PY] = this->R[0][1]*dt; + A[KC_STATE_Y][KC_STATE_PY] = this->R[1][1]*dt; + A[KC_STATE_Z][KC_STATE_PY] = this->R[2][1]*dt; + + A[KC_STATE_X][KC_STATE_PZ] = this->R[0][2]*dt; + A[KC_STATE_Y][KC_STATE_PZ] = this->R[1][2]*dt; + A[KC_STATE_Z][KC_STATE_PZ] = this->R[2][2]*dt; + + // position from attitude error + A[KC_STATE_X][KC_STATE_D0] = (this->S[KC_STATE_PY]*this->R[0][2] - this->S[KC_STATE_PZ]*this->R[0][1])*dt; + A[KC_STATE_Y][KC_STATE_D0] = (this->S[KC_STATE_PY]*this->R[1][2] - this->S[KC_STATE_PZ]*this->R[1][1])*dt; + A[KC_STATE_Z][KC_STATE_D0] = (this->S[KC_STATE_PY]*this->R[2][2] - this->S[KC_STATE_PZ]*this->R[2][1])*dt; + + A[KC_STATE_X][KC_STATE_D1] = (- this->S[KC_STATE_PX]*this->R[0][2] + this->S[KC_STATE_PZ]*this->R[0][0])*dt; + A[KC_STATE_Y][KC_STATE_D1] = (- this->S[KC_STATE_PX]*this->R[1][2] + this->S[KC_STATE_PZ]*this->R[1][0])*dt; + A[KC_STATE_Z][KC_STATE_D1] = (- this->S[KC_STATE_PX]*this->R[2][2] + this->S[KC_STATE_PZ]*this->R[2][0])*dt; + + A[KC_STATE_X][KC_STATE_D2] = (this->S[KC_STATE_PX]*this->R[0][1] - this->S[KC_STATE_PY]*this->R[0][0])*dt; + A[KC_STATE_Y][KC_STATE_D2] = (this->S[KC_STATE_PX]*this->R[1][1] - this->S[KC_STATE_PY]*this->R[1][0])*dt; + A[KC_STATE_Z][KC_STATE_D2] = (this->S[KC_STATE_PX]*this->R[2][1] - this->S[KC_STATE_PY]*this->R[2][0])*dt; + + // body-frame velocity from body-frame velocity + A[KC_STATE_PX][KC_STATE_PX] = 1; //drag negligible + A[KC_STATE_PY][KC_STATE_PX] =-gyro->z*dt; + A[KC_STATE_PZ][KC_STATE_PX] = gyro->y*dt; + + A[KC_STATE_PX][KC_STATE_PY] = gyro->z*dt; + A[KC_STATE_PY][KC_STATE_PY] = 1; //drag negligible + A[KC_STATE_PZ][KC_STATE_PY] =-gyro->x*dt; + + A[KC_STATE_PX][KC_STATE_PZ] =-gyro->y*dt; + A[KC_STATE_PY][KC_STATE_PZ] = gyro->x*dt; + A[KC_STATE_PZ][KC_STATE_PZ] = 1; //drag negligible + + // body-frame velocity from attitude error + A[KC_STATE_PX][KC_STATE_D0] = 0; + A[KC_STATE_PY][KC_STATE_D0] = -GRAVITY_MAGNITUDE*this->R[2][2]*dt; + A[KC_STATE_PZ][KC_STATE_D0] = GRAVITY_MAGNITUDE*this->R[2][1]*dt; + + A[KC_STATE_PX][KC_STATE_D1] = GRAVITY_MAGNITUDE*this->R[2][2]*dt; + A[KC_STATE_PY][KC_STATE_D1] = 0; + A[KC_STATE_PZ][KC_STATE_D1] = -GRAVITY_MAGNITUDE*this->R[2][0]*dt; + + A[KC_STATE_PX][KC_STATE_D2] = -GRAVITY_MAGNITUDE*this->R[2][1]*dt; + A[KC_STATE_PY][KC_STATE_D2] = GRAVITY_MAGNITUDE*this->R[2][0]*dt; + A[KC_STATE_PZ][KC_STATE_D2] = 0; + + // attitude error from attitude error + /** + * At first glance, it may not be clear where the next values come from, since they do not appear directly in the + * dynamics. In this prediction step, we skip the step of first updating attitude-error, and then incorporating the + * new error into the current attitude (which requires a rotation of the attitude-error covariance). Instead, we + * directly update the body attitude, however still need to rotate the covariance, which is what you see below. + * + * This comes from a second order approximation to: + * Sigma_post = exps(-d) Sigma_pre exps(-d)' + * ~ (I + [[-d]] + [[-d]]^2 / 2) Sigma_pre (I + [[-d]] + [[-d]]^2 / 2)' + * where d is the attitude error expressed as Rodriges parameters, ie. d0 = 1/2*gyro.x*dt under the assumption that + * d = [0,0,0] at the beginning of each prediction step and that gyro.x is constant over the sampling period + * + * As derived in "Covariance Correction Step for Kalman Filtering with an Attitude" + * http://arc.aiaa.org/doi/abs/10.2514/1.G000848 + */ + float d0 = gyro->x*dt/2; + float d1 = gyro->y*dt/2; + float d2 = gyro->z*dt/2; + + A[KC_STATE_D0][KC_STATE_D0] = 1 - d1*d1/2 - d2*d2/2; + A[KC_STATE_D0][KC_STATE_D1] = d2 + d0*d1/2; + A[KC_STATE_D0][KC_STATE_D2] = -d1 + d0*d2/2; + + A[KC_STATE_D1][KC_STATE_D0] = -d2 + d0*d1/2; + A[KC_STATE_D1][KC_STATE_D1] = 1 - d0*d0/2 - d2*d2/2; + A[KC_STATE_D1][KC_STATE_D2] = d0 + d1*d2/2; + + A[KC_STATE_D2][KC_STATE_D0] = d1 + d0*d2/2; + A[KC_STATE_D2][KC_STATE_D1] = -d0 + d1*d2/2; + A[KC_STATE_D2][KC_STATE_D2] = 1 - d0*d0/2 - d1*d1/2; + + + // ====== COVARIANCE UPDATE ====== + mat_mult(&Am, &this->Pm, &tmpNN1m); // A P + mat_trans(&Am, &tmpNN2m); // A' + mat_mult(&tmpNN1m, &tmpNN2m, &this->Pm); // A P A' + // Process noise is added after the return from the prediction step + + // ====== PREDICTION STEP ====== + // The prediction depends on whether we're on the ground, or in flight. + // When flying, the accelerometer directly measures thrust (hence is useless to estimate body angle while flying) + + float dx, dy, dz; + float tmpSPX, tmpSPY, tmpSPZ; + float zacc; + + if (quadIsFlying) // only acceleration in z direction + { + // Use accelerometer and not commanded thrust, as this has proper physical units + zacc = acc->z; + + // position updates in the body frame (will be rotated to inertial frame) + dx = this->S[KC_STATE_PX] * dt; + dy = this->S[KC_STATE_PY] * dt; + dz = this->S[KC_STATE_PZ] * dt + zacc * dt2 / 2.0f; // thrust can only be produced in the body's Z direction + + // position update + this->S[KC_STATE_X] += this->R[0][0] * dx + this->R[0][1] * dy + this->R[0][2] * dz; + this->S[KC_STATE_Y] += this->R[1][0] * dx + this->R[1][1] * dy + this->R[1][2] * dz; + this->S[KC_STATE_Z] += this->R[2][0] * dx + this->R[2][1] * dy + this->R[2][2] * dz - GRAVITY_MAGNITUDE * dt2 / 2.0f; + + // keep previous time step's state for the update + tmpSPX = this->S[KC_STATE_PX]; + tmpSPY = this->S[KC_STATE_PY]; + tmpSPZ = this->S[KC_STATE_PZ]; + + // body-velocity update: accelerometers - gyros cross velocity - gravity in body frame + this->S[KC_STATE_PX] += dt * (gyro->z * tmpSPY - gyro->y * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][0]); + this->S[KC_STATE_PY] += dt * (-gyro->z * tmpSPX + gyro->x * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][1]); + this->S[KC_STATE_PZ] += dt * (zacc + gyro->y * tmpSPX - gyro->x * tmpSPY - GRAVITY_MAGNITUDE * this->R[2][2]); + } + else // Acceleration can be in any direction, as measured by the accelerometer. This occurs, eg. in freefall or while being carried. + { + // position updates in the body frame (will be rotated to inertial frame) + dx = this->S[KC_STATE_PX] * dt + acc->x * dt2 / 2.0f; + dy = this->S[KC_STATE_PY] * dt + acc->y * dt2 / 2.0f; + dz = this->S[KC_STATE_PZ] * dt + acc->z * dt2 / 2.0f; // thrust can only be produced in the body's Z direction + + // position update + this->S[KC_STATE_X] += this->R[0][0] * dx + this->R[0][1] * dy + this->R[0][2] * dz; + this->S[KC_STATE_Y] += this->R[1][0] * dx + this->R[1][1] * dy + this->R[1][2] * dz; + this->S[KC_STATE_Z] += this->R[2][0] * dx + this->R[2][1] * dy + this->R[2][2] * dz - GRAVITY_MAGNITUDE * dt2 / 2.0f; + + // keep previous time step's state for the update + tmpSPX = this->S[KC_STATE_PX]; + tmpSPY = this->S[KC_STATE_PY]; + tmpSPZ = this->S[KC_STATE_PZ]; + + // body-velocity update: accelerometers - gyros cross velocity - gravity in body frame + this->S[KC_STATE_PX] += dt * (acc->x + gyro->z * tmpSPY - gyro->y * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][0]); + this->S[KC_STATE_PY] += dt * (acc->y - gyro->z * tmpSPX + gyro->x * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][1]); + this->S[KC_STATE_PZ] += dt * (acc->z + gyro->y * tmpSPX - gyro->x * tmpSPY - GRAVITY_MAGNITUDE * this->R[2][2]); + } + + // attitude update (rotate by gyroscope), we do this in quaternions + // this is the gyroscope angular velocity integrated over the sample period + float dtwx = dt*gyro->x; + float dtwy = dt*gyro->y; + float dtwz = dt*gyro->z; + + // compute the quaternion values in [w,x,y,z] order + float angle = arm_sqrt(dtwx*dtwx + dtwy*dtwy + dtwz*dtwz) + EPS; + float ca = arm_cos_f32(angle/2.0f); + float sa = arm_sin_f32(angle/2.0f); + float dq[4] = {ca , sa*dtwx/angle , sa*dtwy/angle , sa*dtwz/angle}; + + float tmpq0; + float tmpq1; + float tmpq2; + float tmpq3; + + // rotate the quad's attitude by the delta quaternion vector computed above + tmpq0 = dq[0]*this->q[0] - dq[1]*this->q[1] - dq[2]*this->q[2] - dq[3]*this->q[3]; + tmpq1 = dq[1]*this->q[0] + dq[0]*this->q[1] + dq[3]*this->q[2] - dq[2]*this->q[3]; + tmpq2 = dq[2]*this->q[0] - dq[3]*this->q[1] + dq[0]*this->q[2] + dq[1]*this->q[3]; + tmpq3 = dq[3]*this->q[0] + dq[2]*this->q[1] - dq[1]*this->q[2] + dq[0]*this->q[3]; + + if (! quadIsFlying) { + float keep = 1.0f - ROLLPITCH_ZERO_REVERSION; + + tmpq0 = keep * tmpq0 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[0]; + tmpq1 = keep * tmpq1 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[1]; + tmpq2 = keep * tmpq2 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[2]; + tmpq3 = keep * tmpq3 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[3]; + } + + // normalize and store the result + float norm = arm_sqrt(tmpq0*tmpq0 + tmpq1*tmpq1 + tmpq2*tmpq2 + tmpq3*tmpq3) + EPS; + this->q[0] = tmpq0/norm; this->q[1] = tmpq1/norm; this->q[2] = tmpq2/norm; this->q[3] = tmpq3/norm; + assertStateNotNaN(this); + + this->isUpdated = true; + } + + void kalmanCorePredict(kalmanCoreData_t* this, Axis3f *acc, Axis3f *gyro, const uint32_t nowMs, bool quadIsFlying) { + float dt = (nowMs - this->lastPredictionMs) / 1000.0f; + predictDt(this, acc, gyro, dt, quadIsFlying); + this->lastPredictionMs = nowMs; + } + + + static void addProcessNoiseDt(kalmanCoreData_t *this, const kalmanCoreParams_t *params, float dt) + { + this->P[KC_STATE_X][KC_STATE_X] += powf(params->procNoiseAcc_xy*dt*dt + params->procNoiseVel*dt + params->procNoisePos, 2); // add process noise on position + this->P[KC_STATE_Y][KC_STATE_Y] += powf(params->procNoiseAcc_xy*dt*dt + params->procNoiseVel*dt + params->procNoisePos, 2); // add process noise on position + this->P[KC_STATE_Z][KC_STATE_Z] += powf(params->procNoiseAcc_z*dt*dt + params->procNoiseVel*dt + params->procNoisePos, 2); // add process noise on position + + this->P[KC_STATE_PX][KC_STATE_PX] += powf(params->procNoiseAcc_xy*dt + params->procNoiseVel, 2); // add process noise on velocity + this->P[KC_STATE_PY][KC_STATE_PY] += powf(params->procNoiseAcc_xy*dt + params->procNoiseVel, 2); // add process noise on velocity + this->P[KC_STATE_PZ][KC_STATE_PZ] += powf(params->procNoiseAcc_z*dt + params->procNoiseVel, 2); // add process noise on velocity + + this->P[KC_STATE_D0][KC_STATE_D0] += powf(params->measNoiseGyro_rollpitch * dt + params->procNoiseAtt, 2); + this->P[KC_STATE_D1][KC_STATE_D1] += powf(params->measNoiseGyro_rollpitch * dt + params->procNoiseAtt, 2); + this->P[KC_STATE_D2][KC_STATE_D2] += powf(params->measNoiseGyro_yaw * dt + params->procNoiseAtt, 2); + + for (int i=0; iP[i][j] + 0.5f*this->P[j][i]; + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + + assertStateNotNaN(this); + } + + void kalmanCoreAddProcessNoise(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs) { + float dt = (nowMs - this->lastProcessNoiseUpdateMs) / 1000.0f; + if (dt > 0.0f) { + addProcessNoiseDt(this, params, dt); + this->lastProcessNoiseUpdateMs = nowMs; + } + } + + bool kalmanCoreFinalize(kalmanCoreData_t* this) + { + // Only finalize if data is updated + if (! this->isUpdated) { + return false; + } + + + // Matrix to rotate the attitude covariances once updated + NO_DMA_CCM_SAFE_ZERO_INIT static float A[KC_STATE_DIM][KC_STATE_DIM]; + static arm_matrix_instance_f32 Am = {KC_STATE_DIM, KC_STATE_DIM, (float *)A}; + + // Temporary matrices for the covariance updates + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN1d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN1m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN1d}; + + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN2d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN2m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN2d}; + + // Incorporate the attitude error (Kalman filter state) with the attitude + float v0 = this->S[KC_STATE_D0]; + float v1 = this->S[KC_STATE_D1]; + float v2 = this->S[KC_STATE_D2]; + + // Move attitude error into attitude if any of the angle errors are large enough + if ((fabsf(v0) > 0.1e-3f || fabsf(v1) > 0.1e-3f || fabsf(v2) > 0.1e-3f) && (fabsf(v0) < 10 && fabsf(v1) < 10 && fabsf(v2) < 10)) + { + float angle = arm_sqrt(v0*v0 + v1*v1 + v2*v2) + EPS; + float ca = arm_cos_f32(angle / 2.0f); + float sa = arm_sin_f32(angle / 2.0f); + float dq[4] = {ca, sa * v0 / angle, sa * v1 / angle, sa * v2 / angle}; + + // rotate the quad's attitude by the delta quaternion vector computed above + float tmpq0 = dq[0] * this->q[0] - dq[1] * this->q[1] - dq[2] * this->q[2] - dq[3] * this->q[3]; + float tmpq1 = dq[1] * this->q[0] + dq[0] * this->q[1] + dq[3] * this->q[2] - dq[2] * this->q[3]; + float tmpq2 = dq[2] * this->q[0] - dq[3] * this->q[1] + dq[0] * this->q[2] + dq[1] * this->q[3]; + float tmpq3 = dq[3] * this->q[0] + dq[2] * this->q[1] - dq[1] * this->q[2] + dq[0] * this->q[3]; + + // normalize and store the result + float norm = arm_sqrt(tmpq0 * tmpq0 + tmpq1 * tmpq1 + tmpq2 * tmpq2 + tmpq3 * tmpq3) + EPS; + this->q[0] = tmpq0 / norm; + this->q[1] = tmpq1 / norm; + this->q[2] = tmpq2 / norm; + this->q[3] = tmpq3 / norm; + + /** Rotate the covariance, since we've rotated the body + * + * This comes from a second order approximation to: + * Sigma_post = exps(-d) Sigma_pre exps(-d)' + * ~ (I + [[-d]] + [[-d]]^2 / 2) Sigma_pre (I + [[-d]] + [[-d]]^2 / 2)' + * where d is the attitude error expressed as Rodriges parameters, ie. d = tan(|v|/2)*v/|v| + * + * As derived in "Covariance Correction Step for Kalman Filtering with an Attitude" + * http://arc.aiaa.org/doi/abs/10.2514/1.G000848 + */ + + float d0 = v0/2; // the attitude error vector (v0,v1,v2) is small, + float d1 = v1/2; // so we use a first order approximation to d0 = tan(|v0|/2)*v0/|v0| + float d2 = v2/2; + + A[KC_STATE_X][KC_STATE_X] = 1; + A[KC_STATE_Y][KC_STATE_Y] = 1; + A[KC_STATE_Z][KC_STATE_Z] = 1; + + A[KC_STATE_PX][KC_STATE_PX] = 1; + A[KC_STATE_PY][KC_STATE_PY] = 1; + A[KC_STATE_PZ][KC_STATE_PZ] = 1; + + A[KC_STATE_D0][KC_STATE_D0] = 1 - d1*d1/2 - d2*d2/2; + A[KC_STATE_D0][KC_STATE_D1] = d2 + d0*d1/2; + A[KC_STATE_D0][KC_STATE_D2] = -d1 + d0*d2/2; + + A[KC_STATE_D1][KC_STATE_D0] = -d2 + d0*d1/2; + A[KC_STATE_D1][KC_STATE_D1] = 1 - d0*d0/2 - d2*d2/2; + A[KC_STATE_D1][KC_STATE_D2] = d0 + d1*d2/2; + + A[KC_STATE_D2][KC_STATE_D0] = d1 + d0*d2/2; + A[KC_STATE_D2][KC_STATE_D1] = -d0 + d1*d2/2; + A[KC_STATE_D2][KC_STATE_D2] = 1 - d0*d0/2 - d1*d1/2; + + mat_trans(&Am, &tmpNN1m); // A' + mat_mult(&Am, &this->Pm, &tmpNN2m); // AP + mat_mult(&tmpNN2m, &tmpNN1m, &this->Pm); //APA' + } + + // convert the new attitude to a rotation matrix, such that we can rotate body-frame velocity and acc + this->R[0][0] = this->q[0] * this->q[0] + this->q[1] * this->q[1] - this->q[2] * this->q[2] - this->q[3] * this->q[3]; + this->R[0][1] = 2 * this->q[1] * this->q[2] - 2 * this->q[0] * this->q[3]; + this->R[0][2] = 2 * this->q[1] * this->q[3] + 2 * this->q[0] * this->q[2]; + + this->R[1][0] = 2 * this->q[1] * this->q[2] + 2 * this->q[0] * this->q[3]; + this->R[1][1] = this->q[0] * this->q[0] - this->q[1] * this->q[1] + this->q[2] * this->q[2] - this->q[3] * this->q[3]; + this->R[1][2] = 2 * this->q[2] * this->q[3] - 2 * this->q[0] * this->q[1]; + + this->R[2][0] = 2 * this->q[1] * this->q[3] - 2 * this->q[0] * this->q[2]; + this->R[2][1] = 2 * this->q[2] * this->q[3] + 2 * this->q[0] * this->q[1]; + this->R[2][2] = this->q[0] * this->q[0] - this->q[1] * this->q[1] - this->q[2] * this->q[2] + this->q[3] * this->q[3]; + + // reset the attitude error + this->S[KC_STATE_D0] = 0; + this->S[KC_STATE_D1] = 0; + this->S[KC_STATE_D2] = 0; + + // enforce symmetry of the covariance matrix, and ensure the values stay bounded + for (int i=0; iP[i][j] + 0.5f*this->P[j][i]; + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + + assertStateNotNaN(this); + + this->isUpdated = false; + return true; + } + + void kalmanCoreExternalizeState(const kalmanCoreData_t* this, state_t *state, const Axis3f *acc) + { + // position state is already in world frame + state->position = (point_t){ + .x = this->S[KC_STATE_X], + .y = this->S[KC_STATE_Y], + .z = this->S[KC_STATE_Z] + }; + + // velocity is in body frame and needs to be rotated to world frame + state->velocity = (velocity_t){ + .x = this->R[0][0]*this->S[KC_STATE_PX] + this->R[0][1]*this->S[KC_STATE_PY] + this->R[0][2]*this->S[KC_STATE_PZ], + .y = this->R[1][0]*this->S[KC_STATE_PX] + this->R[1][1]*this->S[KC_STATE_PY] + this->R[1][2]*this->S[KC_STATE_PZ], + .z = this->R[2][0]*this->S[KC_STATE_PX] + this->R[2][1]*this->S[KC_STATE_PY] + this->R[2][2]*this->S[KC_STATE_PZ] + }; + + // Accelerometer measurements are in the body frame and need to be rotated to world frame. + // Furthermore, the legacy code requires acc.z to be acceleration without gravity. + // Finally, note that these accelerations are in Gs, and not in m/s^2, hence - 1 for removing gravity + state->acc = (acc_t){ + .x = this->R[0][0]*acc->x + this->R[0][1]*acc->y + this->R[0][2]*acc->z, + .y = this->R[1][0]*acc->x + this->R[1][1]*acc->y + this->R[1][2]*acc->z, + .z = this->R[2][0]*acc->x + this->R[2][1]*acc->y + this->R[2][2]*acc->z - 1 + }; + + // convert the new attitude into Euler YPR + float yaw = atan2f(2*(this->q[1]*this->q[2]+this->q[0]*this->q[3]) , this->q[0]*this->q[0] + this->q[1]*this->q[1] - this->q[2]*this->q[2] - this->q[3]*this->q[3]); + float pitch = asinf(-2*(this->q[1]*this->q[3] - this->q[0]*this->q[2])); + float roll = atan2f(2*(this->q[2]*this->q[3]+this->q[0]*this->q[1]) , this->q[0]*this->q[0] - this->q[1]*this->q[1] - this->q[2]*this->q[2] + this->q[3]*this->q[3]); + + // Save attitude, adjusted for the legacy CF2 body coordinate system + state->attitude = (attitude_t){ + .roll = roll*RAD_TO_DEG, + .pitch = -pitch*RAD_TO_DEG, + .yaw = yaw*RAD_TO_DEG + }; + + // Save quaternion, hopefully one day this could be used in a better controller. + // Note that this is not adjusted for the legacy coordinate system + state->attitudeQuaternion = (quaternion_t){ + .w = this->q[0], + .x = this->q[1], + .y = this->q[2], + .z = this->q[3] + }; + + assertStateNotNaN(this); + } + + // Reset a state to 0 with max covariance + // If called often, this decouples the state to the rest of the filter + static void decoupleState(kalmanCoreData_t* this, kalmanCoreStateIdx_t state) + { + // Set all covariance to 0 + for(int i=0; iP[state][i] = 0; + this->P[i][state] = 0; + } + // Set state variance to maximum + this->P[state][state] = MAX_COVARIANCE; + // set state to zero + this->S[state] = 0; + } + + void kalmanCoreDecoupleXY(kalmanCoreData_t* this) + { + decoupleState(this, KC_STATE_X); + decoupleState(this, KC_STATE_PX); + decoupleState(this, KC_STATE_Y); + decoupleState(this, KC_STATE_PY); + } diff --git a/applications/newton/llvm-ir/c-files/kalman_core.h b/applications/newton/llvm-ir/c-files/kalman_core.h new file mode 100644 index 000000000..d78ea2478 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/kalman_core.h @@ -0,0 +1,170 @@ +/** +* Authored by Michael Hamer (http://www.mikehamer.info), June 2016 +* Thank you to Mark Mueller (www.mwm.im) for advice during implementation, +* and for derivation of the original filter in the below-cited paper. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================================ +* +* The Kalman filter implemented in this file is based on the papers: +* +* "Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation" +* http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=7139421 +* +* and +* +* "Covariance Correction Step for Kalman Filtering with an Attitude" +* http://arc.aiaa.org/doi/abs/10.2514/1.G000848 +* +* Academic citation would be appreciated. +* +* BIBTEX ENTRIES: + @INPROCEEDINGS{MuellerHamerUWB2015, + author = {Mueller, Mark W and Hamer, Michael and D'Andrea, Raffaello}, + title = {Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation}, + booktitle = {2015 IEEE International Conference on Robotics and Automation (ICRA)}, + year = {2015}, + month = {May}, + pages = {1730-1736}, + doi = {10.1109/ICRA.2015.7139421}, + ISSN = {1050-4729}} + +@ARTICLE{MuellerCovariance2016, + author={Mueller, Mark W and Hehn, Markus and D'Andrea, Raffaello}, + title={Covariance Correction Step for Kalman Filtering with an Attitude}, + journal={Journal of Guidance, Control, and Dynamics}, + pages={1--7}, + year={2016}, + publisher={American Institute of Aeronautics and Astronautics}} +* +* ============================================================================ +*/ + +#pragma once + +#include "cf_math.h" +#include "stabilizer_types.h" + +// Indexes to access the quad's state, stored as a column vector +typedef enum +{ + KC_STATE_X, KC_STATE_Y, KC_STATE_Z, KC_STATE_PX, KC_STATE_PY, KC_STATE_PZ, KC_STATE_D0, KC_STATE_D1, KC_STATE_D2, KC_STATE_DIM +} kalmanCoreStateIdx_t; + + + // The data used by the kalman core implementation. + typedef struct { + /** + * Quadrocopter State + * + * The internally-estimated state is: + * - X, Y, Z: the quad's position in the global frame + * - PX, PY, PZ: the quad's velocity in its body frame + * - D0, D1, D2: attitude error + * + * For more information, refer to the paper + */ + float S[KC_STATE_DIM]; + + // The quad's attitude as a quaternion (w,x,y,z) + // We store as a quaternion to allow easy normalization (in comparison to a rotation matrix), + // while also being robust against singularities (in comparison to euler angles) + float q[4]; + + // The quad's attitude as a rotation matrix (used by the prediction, updated by the finalization) + float R[3][3]; + + // The covariance matrix + __attribute__((aligned(4))) float P[KC_STATE_DIM][KC_STATE_DIM]; + arm_matrix_instance_f32 Pm; + + float baroReferenceHeight; + + // Quaternion used for initial orientation [w,x,y,z] + float initialQuaternion[4]; + + // Tracks whether an update to the state has been made, and the state therefore requires finalization + bool isUpdated; + + uint32_t lastPredictionMs; + uint32_t lastProcessNoiseUpdateMs; + } kalmanCoreData_t; + + // The parameters used by the filter + typedef struct { + // Initial variances, uncertain of position, but know we're stationary and roughly flat + float stdDevInitialPosition_xy; + float stdDevInitialPosition_z; + float stdDevInitialVelocity; + float stdDevInitialAttitude_rollpitch; + float stdDevInitialAttitude_yaw; + + float procNoiseAcc_xy; + float procNoiseAcc_z; + float procNoiseVel; + float procNoisePos; + float procNoiseAtt; + float measNoiseBaro; // meters + float measNoiseGyro_rollpitch; // radians per second + float measNoiseGyro_yaw; // radians per second + + float initialX; + float initialY; + float initialZ; + + // Initial yaw of the Crazyflie in radians. + // 0 --- facing positive X + // PI / 2 --- facing positive Y + // PI --- facing negative X + // 3 * PI / 2 --- facing negative Y + float initialYaw; + } kalmanCoreParams_t; + + /* - Load default parameters */ + void kalmanCoreDefaultParams(kalmanCoreParams_t *params); + + /* - Initialize Kalman State */ + void kalmanCoreInit(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs); + + /* - Measurement updates based on sensors */ + + // Barometer + void kalmanCoreUpdateWithBaro(kalmanCoreData_t *this, const kalmanCoreParams_t *params, float baroAsl, bool quadIsFlying); + + /** +* Primary Kalman filter functions +* +* The filter progresses as: +* - Predicting the current state forward */ + void kalmanCorePredict(kalmanCoreData_t *this, Axis3f *acc, Axis3f *gyro, const uint32_t nowMs, bool quadIsFlying); + + void kalmanCoreAddProcessNoise(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs); + + /** +* @brief Finalization to incorporate attitude error into body attitude +* +* @param this Core data +* @return true The state was finalized +* @return false The state was not changed and did not require finalization + */ + bool kalmanCoreFinalize(kalmanCoreData_t* this); + + /* - Externalization to move the filter's internal state into the external state expected by other modules */ + void kalmanCoreExternalizeState(const kalmanCoreData_t* this, state_t *state, const Axis3f *acc); + + void kalmanCoreDecoupleXY(kalmanCoreData_t* this); + + void kalmanCoreScalarUpdate(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, float error, float stdMeasNoise); + + void kalmanCoreUpdateWithPKE(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, arm_matrix_instance_f32 *Km, arm_matrix_instance_f32 *P_w_m, float error); diff --git a/applications/newton/llvm-ir/c-files/newtest.c b/applications/newton/llvm-ir/c-files/newtest.c new file mode 100644 index 000000000..6a8ed2b8e --- /dev/null +++ b/applications/newton/llvm-ir/c-files/newtest.c @@ -0,0 +1,56 @@ +#include +#include "MadgwickAHRS.h" + +#define FRAC_BASE 1024 +//volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; +extern volatile float q0, q1, q2, q3; +// 测试函数 +void testMadgwickUpdate() { + // 初始化四元数 + q0 = 1.0f; + q1 = 0.0f; + q2 = 0.0f; + q3 = 0.0f; + +// q0 = 0.64306622f; +// q1= 0.02828862f; +// q2 = -0.00567953f; +// q3 = -0.76526684f; + + // 模拟的传感器数据 (这些值可以根据实际情况调整) + // 使用提取的数据替换模拟传感器数据 + float gx = -0.1184487343f * 61; // 陀螺仪 x 轴角速度 (乘以 61) + float gy = 0.001258035656f * 61; // 陀螺仪 y 轴角速度 (乘以 61) + float gz = 0.008988874033f * 61; // 陀螺仪 z 轴角速度 (乘以 61) + + float ax = -0.3570917249f * 2; // 加速度计 x 轴加速度 (乘以 2) + float ay = 0.3941296637f * 2; // 加速度计 y 轴加速度 (乘以 2) + float az = 9.963726044f * 2; // 加速度计 z 轴加速度 (乘以 2) + + + float mx = -7.249095917f; // 磁力计 x 轴磁场强度 (单位:µT) + float my = 26.43893433f; // 磁力计 y 轴磁场强度 + float mz = -37.16656494f; // 磁力计 z 轴磁场强度 + + + // 调用 MadgwickAHRSupdate 函数 + MadgwickAHRSupdate(gx, gy, gz, ax, ay, az, mx, my, mz); + + // 打印四元数结果 + q0= (double)q0/FRAC_BASE; + q1= (double)q1/FRAC_BASE; + q2= (double)q2/FRAC_BASE; + q3= (double)q3/FRAC_BASE; + + + printf("q0 = %f\n", q0); + printf("q1 = %f\n", q1); + printf("q2 = %f\n", q2); + printf("q3 = %f\n", q3); +} + +int main() { + // 调用测试函数 + testMadgwickUpdate(); + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/test_Kalman.c b/applications/newton/llvm-ir/c-files/test_Kalman.c new file mode 100644 index 000000000..d8b02e5d1 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_Kalman.c @@ -0,0 +1,27 @@ +#include +#include "Kalman.h" + +int main() { + // 初始化Kalman滤波器实例 + Kalman kalman; + kalman.setAngle(0.0f); // 设置初始角度为0度 + + // 硬编码的数据集,模拟传感器输入 + // newAngle: 角度测量值, newRate: 角速度测量值, dt: 时间间隔 + float newAngle[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 2.5, 2.0, 1.5, 1.0}; + float newRate[] = {0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.25, 0.2, 0.15, 0.1}; + float dt = 0.1f; // 时间间隔 100毫秒 + + int dataSize = sizeof(newAngle) / sizeof(newAngle[0]); + + // 打印表头 + printf("Time(s)\tMeasured Angle\tMeasured Rate\tFiltered Angle\n"); + + // 使用硬编码数据测试Kalman滤波器 + for (int i = 0; i < dataSize; i++) { + float filteredAngle = kalman.getAngle(newAngle[i], newRate[i], dt); + printf("%0.1f\t%f\t%f\t%f\n", i * dt, newAngle[i], newRate[i], filteredAngle); + } + + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c index fa47a7e9f..0804e4075 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -43,228 +43,228 @@ extern volatile float q0, q1, q2, q3; typedef struct timespec timespec; timespec diff(timespec start, timespec end) { - timespec temp; - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - return temp; + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + return temp; } timespec sum(timespec t1, timespec t2) { - timespec temp; - if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } else { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } else { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; } void printTimeSpec(timespec t, const char* prefix) { - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); } timespec tic( ) { - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; } timespec toc( timespec* start_time, const char* prefix ) { - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - timespec time_consump = diff( *start_time, current_time ); - printTimeSpec(time_consump, prefix ); - *start_time = current_time; - return time_consump; + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff( *start_time, current_time ); + printTimeSpec(time_consump, prefix ); + *start_time = current_time; + return time_consump; } int main() { - FILE* fp = fopen("input.csv", "r"); + FILE* fp = fopen("input.csv", "r"); - if (!fp) { - printf("Can't open file\n"); - return -1; - } + if (!fp) { + printf("Can't open file\n"); + return -1; + } - double time[DATA_SIZE]; + double time[DATA_SIZE]; #if defined(INT_DATA_TYPE) - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = (0.64306622f*FRAC_BASE); - q1[i] = (0.02828862f*FRAC_BASE); - q2[i] = (-0.00567953f*FRAC_BASE); - q3[i] = (-0.76526684f*FRAC_BASE); - } + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) { + q0[i] = (0.64306622f*FRAC_BASE); + q1[i] = (0.02828862f*FRAC_BASE); + q2[i] = (-0.00567953f*FRAC_BASE); + q3[i] = (-0.76526684f*FRAC_BASE); + } - int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; #elif defined(FP_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + } - float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; #else #error "Must set data type: FP or INT" #endif - char buffer[1024]; - int row = 0, column = 0; - while (fgets(buffer, 1024, fp)) { - column = 0; - row++; + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) { + column = 0; + row++; - if (row == 1) - continue; + if (row == 1) + continue; - char* value = strtok(buffer, ", "); + char* value = strtok(buffer, ", "); - while (value) { - switch (column) { - case 0: - time[row-2] = atof(value); - break; - case 1: + while (value) { + switch (column) { + case 0: + time[row-2] = atof(value); + break; + case 1: #if defined(INT_DATA_TYPE) - mag_x[row-2] = round(atof(value)*FRAC_BASE); + mag_x[row-2] = round(atof(value)*FRAC_BASE); #elif defined(FP_DATA_TYPE) - mag_x[row-2] = atof(value); + mag_x[row-2] = atof(value); #endif - break; - case 2: + break; + case 2: #if defined(INT_DATA_TYPE) - mag_y[row-2] = round(atof(value)*FRAC_BASE); + mag_y[row-2] = round(atof(value)*FRAC_BASE); #elif defined(FP_DATA_TYPE) - mag_y[row-2] = atof(value); + mag_y[row-2] = atof(value); #endif - break; - case 3: + break; + case 3: #if defined(INT_DATA_TYPE) - mag_z[row-2] = round(atof(value)*FRAC_BASE); + mag_z[row-2] = round(atof(value)*FRAC_BASE); #elif defined(FP_DATA_TYPE) - mag_z[row-2] = atof(value); + mag_z[row-2] = atof(value); #endif - break; - case 4: + break; + case 4: #if defined(INT_DATA_TYPE) - gyr_x[row-2] = round(atof(value)*FRAC_BASE)*61; + gyr_x[row-2] = round(atof(value)*FRAC_BASE)*61; #elif defined(FP_DATA_TYPE) - gyr_x[row-2] = atof(value)*61; + gyr_x[row-2] = atof(value)*61; #endif - break; - case 5: + break; + case 5: #if defined(INT_DATA_TYPE) - gyr_y[row-2] = round(atof(value)*FRAC_BASE)*61; + gyr_y[row-2] = round(atof(value)*FRAC_BASE)*61; #elif defined(FP_DATA_TYPE) - gyr_y[row-2] = atof(value)*61; + gyr_y[row-2] = atof(value)*61; #endif - break; - case 6: + break; + case 6: #if defined(INT_DATA_TYPE) - gyr_z[row-2] = round(atof(value)*FRAC_BASE)*61; + gyr_z[row-2] = round(atof(value)*FRAC_BASE)*61; #elif defined(FP_DATA_TYPE) - gyr_z[row-2] = atof(value)*61; + gyr_z[row-2] = atof(value)*61; #endif - break; - case 7: + break; + case 7: #if defined(INT_DATA_TYPE) - acc_x[row-2] = round(atof(value)*FRAC_BASE)*2; + acc_x[row-2] = round(atof(value)*FRAC_BASE)*2; #elif defined(FP_DATA_TYPE) - acc_x[row-2] = atof(value)*2; + acc_x[row-2] = atof(value)*2; #endif - break; - case 8: + break; + case 8: #if defined(INT_DATA_TYPE) - acc_y[row-2] = round(atof(value)*FRAC_BASE)*2; + acc_y[row-2] = round(atof(value)*FRAC_BASE)*2; #elif defined(FP_DATA_TYPE) - acc_y[row-2] = atof(value)*2; + acc_y[row-2] = atof(value)*2; #endif - break; - case 9: + break; + case 9: #if defined(INT_DATA_TYPE) - acc_z[row-2] = round(atof(value)*FRAC_BASE)*2; + acc_z[row-2] = round(atof(value)*FRAC_BASE)*2; #elif defined(FP_DATA_TYPE) - acc_z[row-2] = atof(value)*2; + acc_z[row-2] = atof(value)*2; #endif - break; - default: - break; - } + break; + default: + break; + } - value = strtok(NULL, ", "); - column++; - } - } + value = strtok(NULL, ", "); + column++; + } + } - fclose(fp); + fclose(fp); - u_int64_t time_slots[ITERATION]; + u_int64_t time_slots[ITERATION]; - for (size_t idx = 0; idx < ITERATION; idx++) { - timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - } - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - } + for (size_t idx = 0; idx < ITERATION; idx++) { + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); #if defined(FP_DATA_TYPE) - FILE *fptr = fopen("fp_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - } - fclose(fptr); + FILE *fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); #elif defined(INT_DATA_TYPE) - FILE *fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, (double)q0[ts]/FRAC_BASE, - // ts, (double)q1[ts]/FRAC_BASE, - // ts, (double)q2[ts]/FRAC_BASE, - // ts, (double)q3[ts]/FRAC_BASE); - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, (double)q0[ts]/FRAC_BASE, - ts, (double)q1[ts]/FRAC_BASE, - ts, (double)q2[ts]/FRAC_BASE, - ts, (double)q3[ts]/FRAC_BASE); - } - fclose(fptr); + FILE *fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) { + // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, (double)q0[ts]/FRAC_BASE, + ts, (double)q1[ts]/FRAC_BASE, + ts, (double)q2[ts]/FRAC_BASE, + ts, (double)q3[ts]/FRAC_BASE); + } + fclose(fptr); // printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", // DISPLAY_INT(q0), DISPLAY_FRAC(q0), // DISPLAY_INT(q1), DISPLAY_FRAC(q1), // DISPLAY_INT(q2), DISPLAY_FRAC(q2), // DISPLAY_INT(q3), DISPLAY_FRAC(q3)); #endif - return 0; -} + return 0; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c index b7b585e06..7a5f61d1f 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -6,14 +6,16 @@ #include #include #include -#define FRAC_Q 10 -#define FRAC_BASE (1<current = stop - p_tbl->start; - if (p_tbl->max < p_tbl->current) { - p_tbl->max = p_tbl->current; - } - if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { - p_tbl->min = p_tbl->current; - } -} - +// static inline uint64_t rdtsc() { +// uint32_t lo, hi; +// __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +// return ((uint64_t)hi << 32) | lo; +// } +// +// void elapsed_time_start(uint32_t i) { +// elapsed_time_tbl[i].start = rdtsc(); +// } +// +// void elapsed_time_stop(uint32_t i) { +// uint64_t stop = rdtsc(); +// ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; +// p_tbl->current = stop - p_tbl->start; +// if (p_tbl->max < p_tbl->current) { +// p_tbl->max = p_tbl->current; +// } +// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { +// p_tbl->min = p_tbl->current; +// } +// } typedef struct timespec timespec; timespec @@ -122,10 +121,10 @@ QuantizedValues quantize4(float value1, float value2, float value3, float value4, int frac_base) { QuantizedValues result; - result.quantized1 = round(value1 * frac_base); - result.quantized2 = round(value2 * frac_base); - result.quantized3 = round(value3 * frac_base); - result.quantized4 = round(value4 * frac_base); + result.quantized1 = roundf(value1 * frac_base); + result.quantized2 = roundf(value2 * frac_base); + result.quantized3 = roundf(value3 * frac_base); + result.quantized4 = roundf(value4 * frac_base); return result; } @@ -133,10 +132,10 @@ DequantizedValues dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) { DequantizedValues result; - result.dequantized1 = (float)(quantized1 >> FRAC_Q); - result.dequantized2 = (float)(quantized2 >> FRAC_Q); - result.dequantized3 = (float)(quantized3 >> FRAC_Q); - result.dequantized4 = (float)(quantized4 >> FRAC_Q); + result.dequantized1 = (float)(quantized1/FRAC_BASE); + result.dequantized2 = (float)(quantized2/FRAC_BASE); + result.dequantized3 = (float)(quantized3/FRAC_BASE); + result.dequantized4 = (float)(quantized4/FRAC_BASE); return result; } @@ -167,8 +166,8 @@ toc(timespec * start_time, const char * prefix) *start_time = current_time; return time_consump; } - int - main() +int +main() { FILE * fp = fopen("input.csv", "r"); @@ -214,32 +213,42 @@ toc(timespec * start_time, const char * prefix) break; case 1: mag_x[row - 2] = round(atof(value) * FRAC_BASE); - //mag_x[row - 2] = quantize(atof(value), FRAC_BASE); + + //mag_x[row - 2] = atof(value); break; case 2: mag_y[row - 2] = round(atof(value) * FRAC_BASE); - //mag_y[row - 2] = quantize(atof(value), FRAC_BASE); + + //mag_y[row - 2] = atof(value) ; break; case 3: mag_z[row - 2] = round(atof(value) * FRAC_BASE); + //mag_z[row - 2] = atof(value); break; case 4: gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_x[row - 2] = atof(value) * 61; + break; case 5: gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_y[row - 2] = atof(value) * 61; break; case 6: gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_z[row - 2] = atof(value) * 61; break; case 7: - acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; break; case 8: acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_y[row - 2] = atof(value) * 2; break; case 9: acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_z[row - 2] = atof(value) * 2; break; default: break; @@ -254,60 +263,80 @@ toc(timespec * start_time, const char * prefix) u_int64_t time_slots[ITERATION]; - for (size_t idx = 0; idx < ITERATION; idx++) { - elapsed_time_start(idx); // 开始计时 + for (size_t idx = 0; idx < ITERATION; idx++) + { + // elapsed_time_start(idx); // 开始计时 timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], acc_x[ts], acc_y[ts], acc_z[ts], mag_x[ts], mag_y[ts], mag_z[ts], &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - + dequantize4(q0[ts], q1[ts], q2[ts], q3[ts]); } - elapsed_time_stop(idx); // 结束计时 + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + // elapsed_time_stop(idx); // 结束计时 } u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) { + for (size_t idx = 0; idx < ITERATION; idx++) + { average_time += time_slots[idx]; } average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); + printf("average time = %lu ns\n", average_time); // 打印出每次迭代的最大、最小和当前时间 - for (size_t idx = 0; idx < ITERATION; idx++) { -// printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", -// idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); + // for (size_t idx = 0; idx < ITERATION; idx++) { + // + // printf("Cycle count for iteration %zu: %lu cycles\n", idx, elapsed_time_tbl[idx].current); + // } - - printf("Cycle count for iteration %zu: %lu cycles\n", idx, elapsed_time_tbl[idx].current); - } - - FILE *fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { + FILE * fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", // ts, (double)q0[ts]/FRAC_BASE, // ts, (double)q1[ts]/FRAC_BASE, // ts, (double)q2[ts]/FRAC_BASE, // ts, (double)q3[ts]/FRAC_BASE); - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, (double)q0[ts]/FRAC_BASE, - ts, (double)q1[ts]/FRAC_BASE, - ts, (double)q2[ts]/FRAC_BASE, - ts, (double)q3[ts]/FRAC_BASE); +// fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", +// ts, (double)q0[ts]/FRAC_BASE, +// ts, (double)q1[ts]/FRAC_BASE, +// ts, (double)q2[ts]/FRAC_BASE, +// ts, (double)q3[ts]/FRAC_BASE); + + + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, (float)q0[ts]/FRAC_BASE, + ts, (float)q1[ts]/FRAC_BASE, + ts, (float)q2[ts]/FRAC_BASE, + ts, (float)q3[ts]/FRAC_BASE); + +// fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", +// ts, (double)(q0[ts] >> FRAC_Q), +// ts, (double)(q1[ts] >> FRAC_Q), +// ts, (double)(q2[ts] >> FRAC_Q), +// ts, (double)(q3[ts] >> FRAC_Q)); } fclose(fptr); return 0; - } +} -//int main () +// int main () //{ // /* // * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) -// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 // */ // // float num1 = -7.249095917; diff --git a/applications/newton/llvm-ir/c-files/test_fixmul.c b/applications/newton/llvm-ir/c-files/test_fixmul.c new file mode 100644 index 000000000..f43a8b0da --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_fixmul.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#define DATA_SIZE 1000 +#define ITERATIONS 10000 +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) + +/*************************************** +* Timer functions of the test framework +***************************************/ +typedef struct timespec timespec; + +timespec diff(timespec start, timespec end) { + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +void printTimeSpec(timespec t, const char* prefix) { + printf("%s: %ld.%09ld seconds\n", prefix, t.tv_sec, t.tv_nsec); +} + +timespec sum(timespec t1, timespec t2) { + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } else { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +/*************************************** +* Multiplication functions +***************************************/ +int32_t fixmul(int32_t x, int32_t y) { + return ((int64_t)x * y) >> FRAC_Q; +} + +float fpmul(float x, float y) { + return x * y; +} + +/*************************************** +* Main function to test performance +***************************************/ +int main() { + srand(time(NULL)); + + int32_t int_a, int_b; + float fp_a, fp_b; + + timespec start_time, end_time, total_time_int, total_time_fp; + total_time_int.tv_sec = total_time_int.tv_nsec = 0; + total_time_fp.tv_sec = total_time_fp.tv_nsec = 0; + + for (int i = 0; i < ITERATIONS; i++) { + int_a = rand() % FRAC_BASE; + int_b = rand() % FRAC_BASE; + fp_a = (float)int_a / FRAC_BASE; + fp_b = (float)int_b / FRAC_BASE; + + // Test fixed-point multiplication + clock_gettime(CLOCK_MONOTONIC, &start_time); + int32_t result_int = fixmul(int_a, int_b); + clock_gettime(CLOCK_MONOTONIC, &end_time); + total_time_int = sum(total_time_int, diff(start_time, end_time)); + + // Test floating-point multiplication + clock_gettime(CLOCK_MONOTONIC, &start_time); + float result_fp = fpmul(fp_a, fp_b); + clock_gettime(CLOCK_MONOTONIC, &end_time); + total_time_fp = sum(total_time_fp, diff(start_time, end_time)); + } + + printTimeSpec(total_time_int, "Total time for fixed-point multiplication"); + printTimeSpec(total_time_fp, "Total time for floating-point multiplication"); + + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/test_m4.c b/applications/newton/llvm-ir/c-files/test_m4.c new file mode 100644 index 000000000..3521c8945 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_m4.c @@ -0,0 +1,340 @@ +/* + * Madgwick test case (run locally) + * Compilation command in applications/newton/llvm-ir/performance_test/Makefile + * + * How to compile and run? + * 1. `make perf_madgwick` FP hardware (by default) + * 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) + * 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) + * 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format + * */ +#include "../Include/stm32f3xx.h" +#include +#include +#include +#include +#include +#include + +#if defined(INT_DATA_TYPE) +extern volatile int32_t q0, q1, q2, q3; +#elif defined(FP_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#if defined(SOFT_FLOAT_LIB) +#include "MadgwickAHRS_softfloat.h" +#else +#include "MadgwickAHRS.h" +#endif + +#else +#error "Must set data type: FP or INT" +#endif + +#define DATA_SIZE 10 +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) +#define ITERATION 10 + +//#define SYSTICK_FREQUENCY_HZ 1000 // 假设 SysTick 配置为 1ms (1000Hz) + +static volatile uint32_t system_ticks = 0; // 用于记录系统启动后的总滴答数 +// 声明并定义全局变量 elapsed_time_tbl,用于存储计时信息 + +// 定义 ARM_CM_DWT_CYCCNT 宏 +#define ARM_CM_DWT_CYCCNT (DWT->CYCCNT) + +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct { + uint32_t start; + uint32_t current; + uint32_t max; + uint32_t min; +} ELAPSED_TIME; + +ELAPSED_TIME elapsed_time_tbl[ITERATION]; + +void +elapsed_time_start(uint32_t i) +{ + elapsed_time_tbl[i].start = ARM_CM_DWT_CYCCNT; +} + +void +elapsed_time_stop(uint32_t i) +{ + uint32_t stop = ARM_CM_DWT_CYCCNT; + ELAPSED_TIME * p_tbl = &elapsed_time_tbl[i]; + p_tbl->current = stop - p_tbl->start; + if (p_tbl->max < p_tbl->current) + { + p_tbl->max = p_tbl->current; + } + if (p_tbl->min > p_tbl->current) + { + p_tbl->min = p_tbl->current; + } +} + +// 启动 DWT 计数器 +void +init_dwt_counter(void) +{ + CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable DWT + DWT->CYCCNT = 0; // Clear DWT cycle counter + DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // Enable DWT cycle counter +} + +// SysTick 中断处理程序,用于增加系统滴答计数 +//void +//SysTick_Handler(void) +//{ +// system_ticks++; +//} +// +//typedef struct { +// uint32_t tv_sec; // 秒 +// uint32_t tv_nsec; // 纳秒 +//} timespec; +// +//void +//init_systick(void) +//{ +// // 配置 SysTick 以产生 1ms 的中断 +// if (SysTick_Config(SystemCoreClock / SYSTICK_FREQUENCY_HZ)) +// { +// // 如果 SysTick 配置失败,处理错误 +// while (1) +// ; +// } +//} +// +//timespec +//get_current_time(void) +//{ +// timespec current_time; +// uint32_t ticks = system_ticks; +// uint32_t systick_val = SysTick->VAL; +// +// current_time.tv_sec = ticks / SYSTICK_FREQUENCY_HZ; +// current_time.tv_nsec = (1000000 - (systick_val * 1000 / (SystemCoreClock / SYSTICK_FREQUENCY_HZ))) * 1000; +// +// return current_time; +//} +// +//timespec +//diff(timespec start, timespec end) +//{ +// timespec temp; +// if ((end.tv_nsec - start.tv_nsec) < 0) +// { +// temp.tv_sec = end.tv_sec - start.tv_sec - 1; +// temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; +// } +// else +// { +// temp.tv_sec = end.tv_sec - start.tv_sec; +// temp.tv_nsec = end.tv_nsec - start.tv_nsec; +// } +// return temp; +//} +// +//void +//printTimeSpec(timespec t, const char * prefix) +//{ +// printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +//} +// +//timespec +//tic(void) +//{ +// return get_current_time(); +//} +// +//timespec +//toc(timespec * start_time, const char * prefix) +//{ +// timespec current_time = get_current_time(); +// timespec time_consump = diff(*start_time, current_time); +// printTimeSpec(time_consump, prefix); +// *start_time = current_time; +// return time_consump; +//} + +int +main() +{ +#if defined(INT_DATA_TYPE) + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = (0.64306622f * FRAC_BASE); + q1[i] = (0.02828862f * FRAC_BASE); + q2[i] = (-0.00567953f * FRAC_BASE); + q3[i] = (-0.76526684f * FRAC_BASE); + } + +#elif defined(FP_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + } + +#else +#error "Must set data type: FP or INT" +#endif + +#if defined(FP_DATA_TYPE) + double time[DATA_SIZE] = { + 0.07969094, 0.08969094, 0.09969094, 0.10969094, 0.11969094, + 0.12969094, 0.13969094, 0.14969094, 0.15969094, 0.16969094}; + + float mag_x[DATA_SIZE] = { + -7.249095917, -6.654678345, -7.249095917, -7.548351288, -7.311084747, + -5.958099365, -8.100597382, -6.394374847, -7.610450745, -6.729293823}; + + float mag_y[DATA_SIZE] = { + 26.43893433, 26.09465027, 26.43893433, 24.87819672, 23.94067001, + 24.8057251, 25.37778282, 25.43608284, 26.11058998, 25.38845825}; + + float mag_z[DATA_SIZE] = { + -37.16656494, -37.29600525, -37.16656494, -39.49668884, -40.19360352, + -37.20587158, -37.85224915, -37.70759583, -38.66853333, -38.77174377}; + + float gyr_x[DATA_SIZE] = { + -0.1184487343, -0.1184487343, -0.1184487343, -0.1184487343, -0.1184487343, + -0.1184487343, -0.1184487343, -0.1151283234, -0.08246348053, -0.03473320603}; + + float gyr_y[DATA_SIZE] = { + 0.001258035656, 0.001258035656, 0.001258035656, 0.001258035656, 0.001258035656, + 0.001258035656, 0.001258035656, -0.00251355581, 0.00447106408, 0.04428362101}; + + float gyr_z[DATA_SIZE] = { + 0.008988874033, 0.008988874033, 0.008988874033, 0.008988874033, 0.008988874033, + 0.008988874033, 0.008988874033, 0.01010152325, 0.004323757719, 0.0009580431506}; + + float acc_x[DATA_SIZE] = { + -0.3570917249, -0.3570917249, -0.3570917249, -0.3570917249, -0.3570917249, + -0.3570917249, -0.3570917249, -0.2793728709, -0.2367789149, -0.2995947599}; + + float acc_y[DATA_SIZE] = { + 0.3941296637, 0.3941296637, 0.3941296637, 0.3941296637, 0.3941296637, + 0.3941296637, 0.3941296637, 0.4129949808, 0.4112356901, 0.416141957}; + + float acc_z[DATA_SIZE] = { + 9.963726044, 9.963726044, 9.963726044, 9.963726044, 9.963726044, + 9.963726044, 9.963726044, 10.27848434, 10.43229961, 10.24356842}; + +#elif defined(INT_DATA_TYPE) + int32_t time[DATA_SIZE] = { + round(0.07969094 * FRAC_BASE), round(0.08969094 * FRAC_BASE), + round(0.09969094 * FRAC_BASE), round(0.10969094 * FRAC_BASE), + round(0.11969094 * FRAC_BASE), round(0.12969094 * FRAC_BASE), + round(0.13969094 * FRAC_BASE), round(0.14969094 * FRAC_BASE), + round(0.15969094 * FRAC_BASE), round(0.16969094 * FRAC_BASE)}; + + int32_t mag_x[DATA_SIZE] = { + round(-7.249095917 * FRAC_BASE), round(-6.654678345 * FRAC_BASE), + round(-7.249095917 * FRAC_BASE), round(-7.548351288 * FRAC_BASE), + round(-7.311084747 * FRAC_BASE), round(-5.958099365 * FRAC_BASE), + round(-8.100597382 * FRAC_BASE), round(-6.394374847 * FRAC_BASE), + round(-7.610450745 * FRAC_BASE), round(-6.729293823 * FRAC_BASE)}; + + int32_t mag_y[DATA_SIZE] = { + round(26.43893433 * FRAC_BASE), round(26.09465027 * FRAC_BASE), + round(26.43893433 * FRAC_BASE), round(24.87819672 * FRAC_BASE), + round(23.94067001 * FRAC_BASE), round(24.8057251 * FRAC_BASE), + round(25.37778282 * FRAC_BASE), round(25.43608284 * FRAC_BASE), + round(26.11058998 * FRAC_BASE), round(25.38845825 * FRAC_BASE)}; + + int32_t mag_z[DATA_SIZE] = { + round(-37.16656494 * FRAC_BASE), round(-37.29600525 * FRAC_BASE), + round(-37.16656494 * FRAC_BASE), round(-39.49668884 * FRAC_BASE), + round(-40.19360352 * FRAC_BASE), round(-37.20587158 * FRAC_BASE), + round(-37.85224915 * FRAC_BASE), round(-37.70759583 * FRAC_BASE), + round(-38.66853333 * FRAC_BASE), round(-38.77174377 * FRAC_BASE)}; + + int32_t gyr_x[DATA_SIZE] = { + round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1184487343 * FRAC_BASE) * 61, + round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1184487343 * FRAC_BASE) * 61, + round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1184487343 * FRAC_BASE) * 61, + round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1151283234 * FRAC_BASE) * 61, + round(-0.08246348053 * FRAC_BASE) * 61, round(-0.03473320603 * FRAC_BASE) * 61}; + + int32_t gyr_y[DATA_SIZE] = { + round(0.001258035656 * FRAC_BASE) * 61, round(0.001258035656 * FRAC_BASE) * 61, + round(0.001258035656 * FRAC_BASE) * 61, round(0.001258035656 * FRAC_BASE) * 61, + round(0.001258035656 * FRAC_BASE) * 61, round(0.001258035656 * FRAC_BASE) * 61, + round(0.001258035656 * FRAC_BASE) * 61, round(-0.00251355581 * FRAC_BASE) * 61, + round(0.00447106408 * FRAC_BASE) * 61, round(0.04428362101 * FRAC_BASE) * 61}; + + int32_t gyr_z[DATA_SIZE] = { + round(0.008988874033 * FRAC_BASE) * 61, round(0.008988874033 * FRAC_BASE) * 61, + round(0.008988874033 * FRAC_BASE) * 61, round(0.008988874033 * FRAC_BASE) * 61, + round(0.008988874033 * FRAC_BASE) * 61, round(0.008988874033 * FRAC_BASE) * 61, + round(0.008988874033 * FRAC_BASE) * 61, round(0.01010152325 * FRAC_BASE) * 61, + round(0.004323757719 * FRAC_BASE) * 61, round(0.0009580431506 * FRAC_BASE) * 61}; + + int32_t acc_x[DATA_SIZE] = { + round(-0.3570917249 * FRAC_BASE) * 2, round(-0.3570917249 * FRAC_BASE) * 2, + round(-0.3570917249 * FRAC_BASE) * 2, round(-0.3570917249 * FRAC_BASE) * 2, + round(-0.3570917249 * FRAC_BASE) * 2, round(-0.3570917249 * FRAC_BASE) * 2, + round(-0.3570917249 * FRAC_BASE) * 2, round(-0.2793728709 * FRAC_BASE) * 2, + round(-0.2367789149 * FRAC_BASE) * 2, round(-0.2995947599 * FRAC_BASE) * 2}; + + int32_t acc_y[DATA_SIZE] = { + round(0.3941296637 * FRAC_BASE) * 2, round(0.3941296637 * FRAC_BASE) * 2, + round(0.3941296637 * FRAC_BASE) * 2, round(0.3941296637 * FRAC_BASE) * 2, + round(0.3941296637 * FRAC_BASE) * 2, round(0.3941296637 * FRAC_BASE) * 2, + round(0.3941296637 * FRAC_BASE) * 2, round(0.4129949808 * FRAC_BASE) * 2, + round(0.4112356901 * FRAC_BASE) * 2, round(0.416141957 * FRAC_BASE) * 2}; + + int32_t acc_z[DATA_SIZE] = { + round(9.963726044 * FRAC_BASE) * 2, round(9.963726044 * FRAC_BASE) * 2, + round(9.963726044 * FRAC_BASE) * 2, round(9.963726044 * FRAC_BASE) * 2, + round(9.963726044 * FRAC_BASE) * 2, round(9.963726044 * FRAC_BASE) * 2, + round(9.963726044 * FRAC_BASE) * 2, round(10.27848434 * FRAC_BASE) * 2, + round(10.43229961 * FRAC_BASE) * 2, round(10.24356842 * FRAC_BASE) * 2}; +#endif + + init_dwt_counter(); // 初始化 DWT 计数器 + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + elapsed_time_start(idx); // 开始计时 + // timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + + //time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + + elapsed_time_stop(idx); // 停止计时 + time_slots[idx] = elapsed_time_tbl[idx].current; + printf("Elapsed cycles for iteration %lu: %lu cycles\n", idx, time_slots[idx]); + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); + + return 0; +} diff --git a/applications/newton/llvm-ir/fft.sh b/applications/newton/llvm-ir/fft.sh new file mode 100755 index 000000000..a7f591367 --- /dev/null +++ b/applications/newton/llvm-ir/fft.sh @@ -0,0 +1,28 @@ +USER_HOME=$HOME + + +# Step 1: Generate LLVM IR file +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/fft.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/fft.c + + +# Step 4: Optimize the generated LLVM IR file +opt $HOME/CoSense/applications/newton/llvm-ir/fft.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/fft.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +## Step 9: Compile the test file and link with the static library +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +# +## Step 10: Run the test executable +#$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/kalman.sh b/applications/newton/llvm-ir/kalman.sh new file mode 100755 index 000000000..1109ff2c0 --- /dev/null +++ b/applications/newton/llvm-ir/kalman.sh @@ -0,0 +1,28 @@ +USER_HOME=$HOME + + +# Step 1: Generate LLVM IR file +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/Kalman.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/Kalman.cpp +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/lu.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/lu_openmp.c + +# Step 4: Optimize the generated LLVM IR file +opt $HOME/CoSense/applications/newton/llvm-ir/Kalman.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/Kalman.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +## Step 9: Compile the test file and link with the static library +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +# +## Step 10: Run the test executable +#$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/m4_fix.sh b/applications/newton/llvm-ir/m4_fix.sh new file mode 100755 index 000000000..e0dddb947 --- /dev/null +++ b/applications/newton/llvm-ir/m4_fix.sh @@ -0,0 +1,40 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" + +clang -g0 -O0 -Xclang -disable-O0-optnone -target armv7e-m -mcpu=cortex-m4 -mfloat-abi=soft -mthumb -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd $USER_HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $USER_HOME/CoSense/applications/newton/sensors/test.nt + +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +#llvm-dis $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll +cd $USER_HOME/CoSense/applications/newton/llvm-ir/&& +./replace.sh $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll + +python3 replace.py + +echo "Step 2: Optimize the generated LLVM IR file" + + +opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll + +echo "Step 3: Compile the optimized LLVM IR file to bitcode" + +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +echo "Step 4: Compile the bitcode file to assembly" + +llc -march=arm -mcpu=cortex-m4 -mattr=+thumb,+soft-float $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +echo "Step 5: Compile the assembly file to object file" + +#clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +clang -target armv7e-m -mcpu=cortex-m4 -mfloat-abi=soft -mthumb -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o \ No newline at end of file diff --git a/applications/newton/llvm-ir/m4_original.sh b/applications/newton/llvm-ir/m4_original.sh new file mode 100755 index 000000000..fac19d4f7 --- /dev/null +++ b/applications/newton/llvm-ir/m4_original.sh @@ -0,0 +1,69 @@ +#!/bin/bash + +# 定义目标 CPU 变量 +TARGET_CPU="cortex-m4" + +# 定义交叉编译前缀变量 +CROSS_COMPILE_PREFIX="arm-none-eabi-" + +# 正确使用变量来设置目标 CPU 的 clang 命令 +CLANG_CMD="$CROSS_COMPILE_PREFIX clang" + +# 确保 HOME 变量被正确定义 +if [ -z "$HOME" ]; then + HOME=$HOME +fi + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" + +clang -g0 -O0 -Xclang -disable-O0-optnone -target armv7e-m -mcpu=cortex-m4 + + + -mthumb -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + + +echo "Step 2: Optimize the generated LLVM IR file" + +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +echo "Step 3: Compile the optimized LLVM IR file to bitcode" + +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +echo "Step 4: Compile the bitcode file to assembly" + +llc -march=arm -mcpu=cortex-m4 -mattr=+vfp4,+soft-float $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +echo "Step 5: Compile the assembly file to object file" + + +clang -target armv7e-m -mcpu=cortex-m4 -mfloat-abi=soft -mthumb -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + + +echo "Step 6: Package the object file into a static library" + +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +echo "Step 7: Compile the test file and link with the static library" + + +arm-none-eabi-gcc syscalls.c \ + $HOME/CoSense/applications/newton/llvm-ir/c-files/test_m4.c \ + system_stm32f3xx.c \ + -DSTM32F303xC -DFP_DATA_TYPE -mcpu=cortex-m4 -mfloat-abi=soft -mthumb \ + -L$HOME/CoSense/applications/newton/llvm-ir -lout -lm -O3 -Os -g -fno-builtin \ + -o $HOME/CoSense/applications/newton/llvm-ir/main_out.elf + + + + + +# Step 8: Generate HEX or BIN file for flashing +echo "Step 8: Generate HEX and BIN files" +arm-none-eabi-objcopy -O ihex $HOME/CoSense/applications/newton/llvm-ir/main_out.elf $HOME/CoSense/applications/newton/llvm-ir/main_out.hex +arm-none-eabi-objcopy -O binary $HOME/CoSense/applications/newton/llvm-ir/main_out.elf $HOME/CoSense/applications/newton/llvm-ir/main_out.bin +# +#echo "Step 8: Run the test executable" +# +#$HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/original_compile.sh b/applications/newton/llvm-ir/original_compile.sh new file mode 100755 index 000000000..98fe3a0f5 --- /dev/null +++ b/applications/newton/llvm-ir/original_compile.sh @@ -0,0 +1,27 @@ +# Step 1: Generate LLVM IR file from the C file +echo "Step 1: Generate LLVM IR file from the C file" +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/original.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/original.c + +# Step 2: Convert generated LLVM IR file back to bitcode +echo "Step 2: Convert generated LLVM IR file back to bitcode" +llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/original.ll -o /home/xyf/CoSense/applications/newton/llvm-ir/original.bc + +# Step 3: Optimize the LLVM bitcode file +echo "Step 3: Optimize the LLVM bitcode file" +opt /home/xyf/CoSense/applications/newton/llvm-ir/original.bc --simplifycfg --instsimplify -O3 -Os -o /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.bc + +# Step 4: Convert optimized bitcode back to LLVM IR +echo "Step 4: Convert optimized bitcode back to LLVM IR" +llvm-dis /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.ll + +# Step 5: Compile the optimized LLVM IR file to assembly +echo "Step 5: Compile the optimized LLVM IR file to assembly" +llc /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.ll -o /home/xyf/CoSense/applications/newton/llvm-ir/original.s + +# Step 6: Compile the assembly file to object file +echo "Step 6: Compile the assembly file to object file" +clang -c /home/xyf/CoSense/applications/newton/llvm-ir/original.s -o /home/xyf/CoSense/applications/newton/llvm-ir/original.o + +# Step 7: Package the object file into a static library +echo "Step 7: Package the object file into a static library" +ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/liboriginal.a /home/xyf/CoSense/applications/newton/llvm-ir/original.o diff --git a/applications/newton/llvm-ir/replace.sh b/applications/newton/llvm-ir/replace.sh index d40c7ea20..d036f676a 100755 --- a/applications/newton/llvm-ir/replace.sh +++ b/applications/newton/llvm-ir/replace.sh @@ -19,7 +19,7 @@ sed -i 's/float\*\*/i32**/g' "$file" sed -i 's/float\*/i32*/g' "$file" # Now replace the call to invSqrt with the call to fixrsqrt -sed -i 's/call i32 @invSqrt/call i32 @fixrsqrt/g' "$file" +#sed -i 's/call i32 @invSqrt/call i32 @fixrsqrt/g' "$file" # Replace the specific call to MadgwickAHRSupdateIMU #sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(float %0, float %1, float %2, float %3, float %4, float %5, i32* %119, i32* %120, i32* %121, i32* %122)/g' "$file" diff --git a/applications/newton/llvm-ir/run.sh b/applications/newton/llvm-ir/run.sh index af65bbbc3..b2b7721ef 100755 --- a/applications/newton/llvm-ir/run.sh +++ b/applications/newton/llvm-ir/run.sh @@ -4,7 +4,7 @@ USER_HOME=$HOME #opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" @@ -24,7 +24,7 @@ ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSens # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D INT_DATA_TYPE -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" diff --git a/applications/newton/llvm-ir/stm32f303xc.h b/applications/newton/llvm-ir/stm32f303xc.h new file mode 100644 index 000000000..9a49f5793 --- /dev/null +++ b/applications/newton/llvm-ir/stm32f303xc.h @@ -0,0 +1,13485 @@ +/** +****************************************************************************** +* @file stm32f303xc.h +* @author MCD Application Team +* @brief CMSIS STM32F303xC Devices Peripheral Access Layer Header File. +* +* This file contains: +* - Data structures and the address mapping for all peripherals +* - Peripheral's registers declarations and bits definition +* - Macros to access peripheral's registers hardware +* +****************************************************************************** +* @attention +* +* Copyright (c) 2016 STMicroelectronics. +* All rights reserved. +* +* This software is licensed under terms that can be found in the LICENSE file +* in the root directory of this software component. +* If no LICENSE file comes with this software, it is provided AS-IS. +* +****************************************************************************** +*/ + +/** @addtogroup CMSIS_Device +* @{ +*/ + +/** @addtogroup stm32f303xc +* @{ +*/ + +#ifndef __STM32F303xC_H +#define __STM32F303xC_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS +* @{ +*/ + +/** +* @brief Configuration of the Cortex-M4 Processor and Core Peripherals +*/ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F303xC devices provide an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F303xC devices use 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< STM32F303xC devices provide an FPU */ + +/** +* @} +*/ + +/** @addtogroup Peripheral_interrupt_number_definition +* @{ +*/ + +/** +* @brief STM32F303xC devices Interrupt Number Definition, according to the selected device +* in @ref Library_configuration_section +*/ +typedef enum +{ + /****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 3 Cortex-M4 Hard Fault Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ + /****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line 19 */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line 20 */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_TSC_IRQn = 8, /*!< EXTI Line2 Interrupt and Touch Sense Controller Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 Interrupt */ + DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 Interrupt */ + DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 Interrupt */ + DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 Interrupt */ + DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 Interrupt */ + DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 Interrupt */ + DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 Interrupt */ + ADC1_2_IRQn = 18, /*!< ADC1 & ADC2 Interrupts */ + USB_HP_CAN_TX_IRQn = 19, /*!< USB Device High Priority or CAN TX Interrupts */ + USB_LP_CAN_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN RX0 Interrupts */ + CAN_RX1_IRQn = 21, /*!< CAN RX1 Interrupt */ + CAN_SCE_IRQn = 22, /*!< CAN SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt & EXTI Line23 Interrupt (I2C1 wakeup) */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt & EXTI Line24 Interrupt (I2C2 wakeup) */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt & EXTI Line25 Interrupt (USART1 wakeup) */ + USART2_IRQn = 38, /*!< USART2 global Interrupt & EXTI Line26 Interrupt (USART2 wakeup) */ + USART3_IRQn = 39, /*!< USART3 global Interrupt & EXTI Line28 Interrupt (USART3 wakeup) */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line 17 Interrupt */ + USBWakeUp_IRQn = 42, /*!< USB Wakeup Interrupt */ + TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ + TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ + TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt & EXTI Line34 Interrupt (UART4 wakeup) */ + UART5_IRQn = 53, /*!< UART5 global Interrupt & EXTI Line35 Interrupt (UART5 wakeup) */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC underrun error Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_IRQn = 59, /*!< DMA2 Channel 4 global Interrupt */ + DMA2_Channel5_IRQn = 60, /*!< DMA2 Channel 5 global Interrupt */ + ADC4_IRQn = 61, /*!< ADC4 global Interrupt */ + COMP1_2_3_IRQn = 64, /*!< COMP1, COMP2 and COMP3 global Interrupt via EXTI Line21, 22 and 29*/ + COMP4_5_6_IRQn = 65, /*!< COMP4, COMP5 and COMP6 global Interrupt via EXTI Line30, 31 and 32*/ + COMP7_IRQn = 66, /*!< COMP7 global Interrupt via EXTI Line33 */ + USB_HP_IRQn = 74, /*!< USB High Priority global Interrupt */ + USB_LP_IRQn = 75, /*!< USB Low Priority global Interrupt */ + USBWakeUp_RMP_IRQn = 76, /*!< USB Wakeup Interrupt remap */ + FPU_IRQn = 81, /*!< Floating point Interrupt */ +} IRQn_Type; + +/** +* @} +*/ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f3xx.h" /* STM32F3xx System Header */ +#include + +/** @addtogroup Peripheral_registers_structures +* @{ +*/ + +/** +* @brief Analog to Digital Converter +*/ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */ + uint32_t RESERVED0; /*!< Reserved, 0x010 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, 0x01C */ + __IO uint32_t TR1; /*!< ADC watchdog threshold register 1, Address offset: 0x20 */ + __IO uint32_t TR2; /*!< ADC watchdog threshold register 2, Address offset: 0x24 */ + __IO uint32_t TR3; /*!< ADC watchdog threshold register 3, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x02C */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x044 */ + uint32_t RESERVED4; /*!< Reserved, 0x048 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t DIFSEL; /*!< ADC Differential Mode Selection Register, Address offset: 0xB0 */ + __IO uint32_t CALFACT; /*!< ADC Calibration Factors, Address offset: 0xB4 */ + +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */ + uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1/3 base address + 0x30C */ +} ADC_Common_TypeDef; + +/** +* @brief Controller Area Network TxMailBox +*/ +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** +* @brief Controller Area Network FIFOMailBox +*/ +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** +* @brief Controller Area Network FilterRegister +*/ +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** +* @brief Controller Area Network +*/ +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** +* @brief Analog Comparators +*/ +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, Address offset: 0x00 */ +} COMP_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, used for bits common to several COMP instances, Address offset: 0x00 */ +} COMP_Common_TypeDef; + +/** +* @brief CRC calculation unit +*/ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** +* @brief Digital to Analog Converter +*/ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** +* @brief Debug MCU +*/ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + +/** +* @brief DMA Controller +*/ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CMAR; /*!< DMA channel x memory address register */ +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} DMA_TypeDef; + +/** +* @brief External Interrupt/Event Controller +*/ + +typedef struct +{ + __IO uint32_t IMR; /*! +#include +#include + +extern int _end; + +void _exit(int status) { + while (1) {} +} + +caddr_t _sbrk(int incr) { + static unsigned char *heap = NULL; + unsigned char *prev_heap; + + if (heap == NULL) { + heap = (unsigned char *)&_end; + } + prev_heap = heap; + heap += incr; + + return (caddr_t) prev_heap; +} + +int _write(int file, char *ptr, int len) { + return len; +} + +int _read(int file, char *ptr, int len) { + return 0; +} + +int _close(int file) { + return -1; +} + +int _fstat(int file, struct stat *st) { + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) { + return 1; +} + +int _lseek(int file, int ptr, int dir) { + return 0; +} + +int _open(const char *name, int flags, int mode) { + return -1; +} + +int _kill(int pid, int sig) { + errno = EINVAL; + return -1; +} + +int _getpid(void) { + return 1; +} diff --git a/applications/newton/llvm-ir/system_stm32f3xx.c b/applications/newton/llvm-ir/system_stm32f3xx.c new file mode 100644 index 000000000..f52fba4e6 --- /dev/null +++ b/applications/newton/llvm-ir/system_stm32f3xx.c @@ -0,0 +1,287 @@ +/** + ****************************************************************************** + * @file system_stm32f3xx.c + * @author MCD Application Team + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f3xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (8 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f3xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. This file configures the system clock as follows: + *============================================================================= + * Supported STM32F3xx device + *----------------------------------------------------------------------------- + * System Clock source | HSI + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 8000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 8000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 1 + *----------------------------------------------------------------------------- + * USB Clock | DISABLE + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f3xx_system + * @{ + */ + +/** @addtogroup STM32F3xx_System_Private_Includes + * @{ + */ + +#include "Include/stm32f3xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Defines + * @{ + */ +#if !defined (HSE_VALUE) + #define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSE_VALUE */ + +#if !defined (HSI_VALUE) + #define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSI_VALUE */ + +/* Note: Following vector table addresses must be defined in line with linker + configuration. */ +/*!< Uncomment the following line if you need to relocate the vector table + anywhere in Flash or Sram, else the vector table is kept at the automatic + remap of boot address selected */ +/* #define USER_VECT_TAB_ADDRESS */ + +#if defined(USER_VECT_TAB_ADDRESS) +/*!< Uncomment the following line if you need to relocate your vector Table + in Sram else user remap will be done in Flash. */ +/* #define VECT_TAB_SRAM */ +#if defined(VECT_TAB_SRAM) +#define VECT_TAB_BASE_ADDRESS SRAM_BASE /*!< Vector Table base address field. + This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +#else +#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field. + This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +#endif /* VECT_TAB_SRAM */ +#endif /* USER_VECT_TAB_ADDRESS */ + +/******************************************************************************/ +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Variables + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetHCLKFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock there is no need to + call the 2 first functions listed above, since SystemCoreClock variable is + updated automatically. + */ +uint32_t SystemCoreClock = 8000000; + +const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; +const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * @param None + * @retval None + */ +void SystemInit(void) +{ +/* FPU settings --------------------------------------------------------------*/ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ +#endif + + /* Configure the Vector Table location -------------------------------------*/ +#if defined(USER_VECT_TAB_ADDRESS) + SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#endif /* USER_VECT_TAB_ADDRESS */ +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f3xx_hal.h file (default value + * 8 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f3xx_hal.h file (default value + * 8 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate (void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case RCC_CFGR_SWS_HSI: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + case RCC_CFGR_SWS_HSE: /* HSE used as system clock */ + SystemCoreClock = HSE_VALUE; + break; + case RCC_CFGR_SWS_PLL: /* PLL used as system clock */ + /* Get PLL clock source and multiplication factor ----------------------*/ + pllmull = RCC->CFGR & RCC_CFGR_PLLMUL; + pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; + pllmull = ( pllmull >> 18) + 2; + +#if defined (STM32F302xE) || defined (STM32F303xE) || defined (STM32F398xx) + predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1; + if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV) + { + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / predivfactor) * pllmull; + } + else + { + /* HSI oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSI_VALUE / predivfactor) * pllmull; + } +#else + if (pllsource == RCC_CFGR_PLLSRC_HSI_DIV2) + { + /* HSI oscillator clock divided by 2 selected as PLL clock entry */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } + else + { + predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1; + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / predivfactor) * pllmull; + } +#endif /* STM32F302xE || STM32F303xE || STM32F398xx */ + break; + default: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK clock frequency ----------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK clock frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ diff --git a/applications/newton/llvm-ir/test.sh b/applications/newton/llvm-ir/test.sh new file mode 100755 index 000000000..379a6d082 --- /dev/null +++ b/applications/newton/llvm-ir/test.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +FILE_PATH="$HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.ll" + + #Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/floating_point_operations.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/test.nt +# +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +#llvm-dis $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.bc -o $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.ll +#cd $HOME/CoSense/applications/newton/llvm-ir/&& +#./replace.sh $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.ll + +#python3 replace.py + + +# Step 4: Optimize the generated LLVM IR file +echo "Step 4: Optimize the generated LLVM IR file" +#opt $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 76ab4237f..18ed45d36 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -9,6 +9,8 @@ FILE_PATH="$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" echo "Step 1: Generate LLVM IR file" clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll # Step 2: Use newton for optimization and quantization echo "Step 2: Use newton for optimization and quantization" #cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/BMX055.nt @@ -26,9 +28,9 @@ cd $HOME/CoSense/applications/newton/llvm-ir/&& # Step 4: Optimize the generated LLVM IR file echo "Step 4: Optimize the generated LLVM IR file" -#opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode @@ -51,9 +53,9 @@ ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applicat echo "Step 9: Compile the test file and link with the static library" #clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm -clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +#clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -D -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm -#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm #clang $HOME/CoSense/applications/newton/llvm-ir/c-files/newtest.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/c-files/newtest -lm diff --git a/applications/newton/llvm-ir/testMadgwickfix.sh b/applications/newton/llvm-ir/testMadgwickfix.sh index c2e881bce..19d770ced 100755 --- a/applications/newton/llvm-ir/testMadgwickfix.sh +++ b/applications/newton/llvm-ir/testMadgwickfix.sh @@ -14,7 +14,7 @@ clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOM # Step 4: Optimize the generated LLVM IR file #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc @@ -31,5 +31,8 @@ ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applicat # Step 9: Compile the test file and link with the static library clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + # Step 10: Run the test executable $HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index 97f25f96a..8537f2b19 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -6,7 +6,7 @@ clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOM # Step 4: Optimize the generated LLVM IR file -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll #opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode diff --git a/applications/newton/llvm-ir/test_madgwick.c b/applications/newton/llvm-ir/test_madgwick.c index d89549cde..fda10f215 100644 --- a/applications/newton/llvm-ir/test_madgwick.c +++ b/applications/newton/llvm-ir/test_madgwick.c @@ -18,10 +18,10 @@ #include #if defined(INT_DATA_TYPE) -extern volatile int32_t q0, q1, q2, q3; -#include "c-files/MadgwickAHRSfix.h" +extern volatile int32_t q0, q1, q2, q3; +// #include "c-files/MadgwickAHRSfix.h" #elif defined(FP_DATA_TYPE) -extern volatile float q0, q1, q2, q3; +extern volatile float q0, q1, q2, q3; #if defined(SOFT_FLOAT_LIB) #include "c-files/MadgwickAHRS_softfloat.h" #else @@ -33,278 +33,336 @@ extern volatile float q0, q1, q2, q3; #endif #define DATA_SIZE 1000 - +#define FRAC_Q 14 +#define FRAC_BASE (1 << FRAC_Q) #define ITERATION 10 /*************************************** * Timer functions of the test framework ***************************************/ -readticks(unsigned int *result, int enabled) -{ - struct timeval t; - unsigned int cc; - unsigned int val; - if (!enabled) { - // program the performance-counter control-register: - asm volatile("msr pmcr_el0, %0" : : "r" (17)); - //enable all counters - asm volatile("msr PMCNTENSET_EL0, %0" : : "r" (0x8000000f)); - //clear the overflow - asm volatile("msr PMOVSCLR_EL0, %0" : : "r" (0x8000000f)); - enabled = 1; - } - //read the coutner value - asm volatile("mrs %0, PMCCNTR_EL0" : "=r" (cc)); - gettimeofday(&t,(struct timezone *) 0); - result[0] = cc; - result[1] = t.tv_usec; - result[2] = t.tv_sec; -} +// readticks(unsigned int *result, int enabled) +//{ +// struct timeval t; +// unsigned int cc; +// unsigned int val; +// if (!enabled) { +// // program the performance-counter control-register: +// asm volatile("msr pmcr_el0, %0" : : "r" (17)); +// //enable all counters +// asm volatile("msr PMCNTENSET_EL0, %0" : : "r" (0x8000000f)); +// //clear the overflow +// asm volatile("msr PMOVSCLR_EL0, %0" : : "r" (0x8000000f)); +// enabled = 1; +// } +// //read the coutner value +// asm volatile("mrs %0, PMCCNTR_EL0" : "=r" (cc)); +// gettimeofday(&t,(struct timezone *) 0); +// result[0] = cc; +// result[1] = t.tv_usec; +// result[2] = t.tv_sec; +// } typedef struct timespec timespec; -timespec diff(timespec start, timespec end) +timespec +diff(timespec start, timespec end) { - timespec temp; - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - return temp; + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; } -timespec sum(timespec t1, timespec t2) { - timespec temp; - if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } else { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; } -void printTimeSpec(timespec t, const char* prefix) { - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); } -timespec tic( ) +timespec +tic() { - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; } -timespec toc( timespec* start_time, const char* prefix ) +timespec +toc(timespec * start_time, const char * prefix) { - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - timespec time_consump = diff( *start_time, current_time ); - printTimeSpec(time_consump, prefix ); - *start_time = current_time; - return time_consump; + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; } -int main() { - FILE* fp = fopen("input.csv", "r"); +int +main() +{ + FILE * fp = fopen("input.csv", "r"); - if (!fp) { - printf("Can't open file\n"); - return -1; - } + if (!fp) + { + printf("Can't open file\n"); + return -1; + } - double time[DATA_SIZE]; + double time[DATA_SIZE]; #if defined(INT_DATA_TYPE) - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = (0.64306622f*FRAC_BASE); - q1[i] = (0.02828862f*FRAC_BASE); - q2[i] = (-0.00567953f*FRAC_BASE); - q3[i] = (-0.76526684f*FRAC_BASE); - } - - int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = (0.64306622f * FRAC_BASE); + q1[i] = (0.02828862f * FRAC_BASE); + q2[i] = (-0.00567953f * FRAC_BASE); + q3[i] = (-0.76526684f * FRAC_BASE); + } + + int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; #elif defined(FP_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } - - float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; #else #error "Must set data type: FP or INT" #endif - char buffer[1024]; - int row = 0, column = 0; - while (fgets(buffer, 1024, fp)) { - column = 0; - row++; - - if (row == 1) - continue; - - char* value = strtok(buffer, ", "); - - while (value) { - switch (column) { - case 0: - time[row-2] = atof(value); - break; - case 1: + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: #if defined(INT_DATA_TYPE) - mag_x[row-2] = round(atof(value)*FRAC_BASE); + mag_x[row - 2] = round(atof(value) * FRAC_BASE); #elif defined(FP_DATA_TYPE) - mag_x[row-2] = atof(value); + mag_x[row - 2] = atof(value); #endif - break; - case 2: + break; + case 2: #if defined(INT_DATA_TYPE) - mag_y[row-2] = round(atof(value)*FRAC_BASE); + mag_y[row - 2] = round(atof(value) * FRAC_BASE); #elif defined(FP_DATA_TYPE) - mag_y[row-2] = atof(value); + mag_y[row - 2] = atof(value); #endif - break; - case 3: + break; + case 3: #if defined(INT_DATA_TYPE) - mag_z[row-2] = round(atof(value)*FRAC_BASE); + mag_z[row - 2] = round(atof(value) * FRAC_BASE); #elif defined(FP_DATA_TYPE) - mag_z[row-2] = atof(value); + mag_z[row - 2] = atof(value); #endif - break; - case 4: + break; + case 4: #if defined(INT_DATA_TYPE) - gyr_x[row-2] = round(atof(value)*FRAC_BASE)*61; + gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; #elif defined(FP_DATA_TYPE) - gyr_x[row-2] = atof(value)*61; + gyr_x[row - 2] = atof(value) * 61; #endif - break; - case 5: + break; + case 5: #if defined(INT_DATA_TYPE) - gyr_y[row-2] = round(atof(value)*FRAC_BASE)*61; + gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; #elif defined(FP_DATA_TYPE) - gyr_y[row-2] = atof(value)*61; + gyr_y[row - 2] = atof(value) * 61; #endif - break; - case 6: + break; + case 6: #if defined(INT_DATA_TYPE) - gyr_z[row-2] = round(atof(value)*FRAC_BASE)*61; + gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; #elif defined(FP_DATA_TYPE) - gyr_z[row-2] = atof(value)*61; + gyr_z[row - 2] = atof(value) * 61; #endif - break; - case 7: + break; + case 7: #if defined(INT_DATA_TYPE) - acc_x[row-2] = round(atof(value)*FRAC_BASE)*2; + acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; #elif defined(FP_DATA_TYPE) - acc_x[row-2] = atof(value)*2; + acc_x[row - 2] = atof(value) * 2; #endif - break; - case 8: + break; + case 8: #if defined(INT_DATA_TYPE) - acc_y[row-2] = round(atof(value)*FRAC_BASE)*2; + acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; #elif defined(FP_DATA_TYPE) - acc_y[row-2] = atof(value)*2; + acc_y[row - 2] = atof(value) * 2; #endif - break; - case 9: + break; + case 9: #if defined(INT_DATA_TYPE) - acc_z[row-2] = round(atof(value)*FRAC_BASE)*2; + acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; #elif defined(FP_DATA_TYPE) - acc_z[row-2] = atof(value)*2; + acc_z[row - 2] = atof(value) * 2; #endif - break; - default: - break; - } - - value = strtok(NULL, ", "); - column++; - } - } - - fclose(fp); - - u_int64_t time_slots[ITERATION]; - - for (size_t idx = 0; idx < ITERATION; idx++) { - - unsigned int init[3] = {0}; - unsigned int start[3] = {0}; - unsigned int end[3] = {0}; - unsigned int overhead = 0; - - readticks(init, 0); - readticks(start, 1); - readticks(end, 1); - - overhead = end[0] - start[0]; - readticks(init, 0); - readticks(start, 1); - - - timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - } - - end[0] = end[0] - start[0] - overhead; - printf("clock cycles= %d\n", end[0]); + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - } + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + // unsigned int init[3] = {0}; + // unsigned int start[3] = {0}; + // unsigned int end[3] = {0}; + // unsigned int overhead = 0; + // + // readticks(init, 0); + // readticks(start, 1); + // readticks(end, 1); + // + // overhead = end[0] - start[0]; + // readticks(init, 0); + // readticks(start, 1); + + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + + // MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + // acc_x[ts], acc_y[ts], acc_z[ts], + // mag_x[ts], mag_y[ts], mag_z[ts]); + } + + // end[0] = end[0] - start[0] - overhead; + // printf("clock cycles= %d\n", end[0]); + + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); #if defined(FP_DATA_TYPE) - FILE *fptr = fopen("fp_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { -// printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", -// ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - } - fclose(fptr); + FILE * fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); #elif defined(INT_DATA_TYPE) - FILE *fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { -// printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", -// ts, (double)q0[ts]/FRAC_BASE, -// ts, (double)q1[ts]/FRAC_BASE, -// ts, (double)q2[ts]/FRAC_BASE, -// ts, (double)q3[ts]/FRAC_BASE); - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, (double)q0[ts]/FRAC_BASE, - ts, (double)q1[ts]/FRAC_BASE, - ts, (double)q2[ts]/FRAC_BASE, - ts, (double)q3[ts]/FRAC_BASE); - } - fclose(fptr); + FILE * fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); + // fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); + + // fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (float)q0[ts] / FRAC_BASE, + // ts, (float)q1[ts] / FRAC_BASE, + // ts, (float)q2[ts] / FRAC_BASE, + // ts, (float)q3[ts] / FRAC_BASE); + + // 分离整数部分和小数部分 + int q0_int_part = q0[ts] >> FRAC_Q; // 整数部分 + int q0_frac_part = q0[ts] & ((1 << FRAC_Q) - 1); // 小数部分 + float q0_value = (float)q0_int_part + (float)q0_frac_part / FRAC_BASE; + //float q0_value = (float)q0_int_part + (float)(q0_frac_part >> FRAC_Q); + + int q1_int_part = q1[ts] >> FRAC_Q; + int q1_frac_part = q1[ts] & ((1 << FRAC_Q) - 1); + float q1_value = (float)q1_int_part + (float)q1_frac_part / FRAC_BASE; + + int q2_int_part = q2[ts] >> FRAC_Q; + int q2_frac_part = q2[ts] & ((1 << FRAC_Q) - 1); + float q2_value = (float)q2_int_part + (float)q2_frac_part / FRAC_BASE; + + int q3_int_part = q3[ts] >> FRAC_Q; + int q3_frac_part = q3[ts] & ((1 << FRAC_Q) - 1); + float q3_value = (float)q3_int_part + (float)q3_frac_part / FRAC_BASE; + + // 使用定点数计算的结果来写入文件 + fprintf(fptr, "FIX: q0[%zu]=%f, q1[%zu]=%f, q2[%zu]=%f, q3[%zu]=%f\n", + ts, q0_value, ts, q1_value, ts, q2_value, ts, q3_value); + +} +fclose(fptr); // printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", // DISPLAY_INT(q0), DISPLAY_FRAC(q0), // DISPLAY_INT(q1), DISPLAY_FRAC(q1), // DISPLAY_INT(q2), DISPLAY_FRAC(q2), // DISPLAY_INT(q3), DISPLAY_FRAC(q3)); #endif - return 0; + return 0; } diff --git a/src/newton/Range.h b/src/newton/Range.h new file mode 100644 index 000000000..139b2fc66 --- /dev/null +++ b/src/newton/Range.h @@ -0,0 +1,65 @@ +#ifndef RANGE_H +#define RANGE_H + +#include "llvm/ADT/APInt.h" +using llvm::APInt; + +#include "llvm/Support/raw_ostream.h" +using llvm::raw_ostream; + +enum RangeType { Unknown, Regular, Empty }; + +class Range { + private: + APInt l; // The lower bound of the range. + APInt u; // The upper bound of the range. + RangeType type{Regular}; + + public: + Range(); + Range(const APInt &lb, const APInt &ub, RangeType rType = Regular); + ~Range() = default; + Range(const Range &other) = default; + Range(Range &&) = default; + Range &operator=(const Range &other) = default; + Range &operator=(Range &&) = default; + + const APInt& getLower() const { return l; } + const APInt& getUpper() const { return u; } + void setLower(const APInt &newl) { this->l = newl; } + void setUpper(const APInt &newu) { this->u = newu; } + bool isUnknown() const { return type == Unknown; } + void setUnknown() { type = Unknown; } + bool isRegular() const { return type == Regular; } + void setRegular() { type = Regular; } + bool isEmpty() const { return type == Empty; } + void setEmpty() { type = Empty; } + bool isMaxRange() const; + void print(raw_ostream &OS) const; + Range add(const Range &other) const; + Range sub(const Range &other) const; + Range mul(const Range &other) const; + Range udiv(const Range &other) const; + Range sdiv(const Range &other) const; + Range urem(const Range &other) const; + Range srem(const Range &other) const; + Range shl(const Range &other) const; + Range lshr(const Range &other) const; + Range ashr(const Range &other) const; + Range And(const Range &other) const; + Range And_conservative(const Range &other) const; + Range Or(const Range &other) const; + Range Or_conservative(const Range &other) const; + Range Xor(const Range &other) const; + Range truncate(unsigned bitwidth) const; + // Range signExtend(unsigned bitwidth) const; + // Range zeroExtend(unsigned bitwidth) const; + Range sextOrTrunc(unsigned bitwidth) const; + Range zextOrTrunc(unsigned bitwidth) const; + Range intersectWith(const Range &other) const; + Range unionWith(const Range &other) const; + bool operator==(const Range &other) const; + bool operator!=(const Range &other) const; +}; + +#endif // RANGE_H \ No newline at end of file diff --git a/src/newton/SimplePass.cpp b/src/newton/SimplePass.cpp new file mode 100644 index 000000000..723c0e236 --- /dev/null +++ b/src/newton/SimplePass.cpp @@ -0,0 +1,5 @@ +// +// Created by 13862 on 2024/8/2. +// + +#include "SimplePass.h" diff --git a/src/newton/SimplePass.h b/src/newton/SimplePass.h new file mode 100644 index 000000000..f99acee54 --- /dev/null +++ b/src/newton/SimplePass.h @@ -0,0 +1,33 @@ +#include "llvm/Pass.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/IR/PassManager.h" + +using namespace llvm; + +namespace { +class SimplePass : public PassInfoMixin { + public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) { + errs() << "SimplePass: " << F.getName() << "\n"; + return PreservedAnalyses::all(); + } +}; + +extern "C" ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { + return { + LLVM_PLUGIN_API_VERSION, "SimplePass", LLVM_VERSION_STRING, + [](PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager &FPM, + ArrayRef) { + if (Name == "simple-pass") { + FPM.addPass(SimplePass()); + return true; + } + return false; + }); + } + }; +} +} diff --git a/src/newton/myRangeAnalysis.cpp b/src/newton/myRangeAnalysis.cpp new file mode 100644 index 000000000..0c3a28508 --- /dev/null +++ b/src/newton/myRangeAnalysis.cpp @@ -0,0 +1,921 @@ +#include "llvm/Pass.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/KnownBits.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/IR/PassManager.h" +#include "llvm/ADT/APInt.h" +#include "Range.h" +#include +#include + +using namespace llvm; + +namespace +{ + +// The number of bits needed to store the largest variable of the function +// (APInt). +unsigned MAX_BIT_INT = 1; +// 声明用于表示整数常量的全局变量 +APInt Min; +APInt Max; +APInt Zero; +APInt One; + +// 宏定义 +#define MUL_HELPER(x, y) \ + ((x).eq(Max) ? ((x).slt(Zero) ? Min : ((x).eq(Zero) ? Zero : Max)) \ + : ((x).eq(Min) ? ((x).slt(Zero) ? Max : ((x).eq(Zero) ? Zero : Min)) \ + : ((x) * (y)))) + +#define MUL_OV(x, y, xy) \ + ((x).isStrictlyPositive() == (y).isStrictlyPositive() \ + ? ((xy).isNegative() ? Max : (xy)) \ + : ((xy).isStrictlyPositive() ? Min : (xy))) + + +// Range 类定义 +class Range { + public: + // 构造函数 + Range(); + Range(const APInt &lb, const APInt &ub, RangeType rType = Regular); + + // 成员函数 + bool isMaxRange() const; + Range add(const Range &other) const; + Range sub(const Range &other) const; + Range mul(const Range &other) const; + Range urem(const Range &other) const; + Range srem(const Range &other) const; + Range shl(const Range &other) const; + Range lshr(const Range &other) const; + Range ashr(const Range &other) const; + Range And(const Range &other) const; + Range And_conservative(const Range &other) const; + Range Or(const Range &other) const; + + // 获取范围下界 + const APInt &getLower() const { return l; } + + // 获取范围上界 + const APInt &getUpper() const { return u; } + + // 判断范围是否未知 + bool isUnknown() const { return type == Unknown; } + + // 判断范围是否为空 + bool isEmpty() const { return type == Empty; } + + // 操作符 + bool operator==(const Range &other) const; + + + private: + APInt l, u; + RangeType type; +}; + + + +// Range 类的默认构造函数,初始化为 [Min, Max] +Range::Range() : l(Min), u(Max), type(Regular) {} +// 使用下界和上界以及范围类型初始化 Range +Range::Range(const APInt & lb, const APInt & ub, RangeType rType) + : l(lb), u(ub), type(rType) +{ + if (lb.sgt(ub)) + { + type = Empty; // 如果下界大于上界,则认为范围为空 + } +} + + +Range Range::Or(const Range &other) const { + APInt lower = l | other.l; + APInt upper = u | other.u; + return Range(lower, upper); +} + +// 判断是否为最大范围 [Min, Max] +bool +Range::isMaxRange() const +{ + return this->getLower().eq(Min) && this->getUpper().eq(Max); +} + +// 加法运算 [a, b] + [c, d] = [a + c, b + d] +Range +Range::add(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + APInt l = Min, u = Max; + + if (a.ne(Min) && c.ne(Min)) + { + l = a + c; + + // 溢出处理 + if (a.isNegative() == c.isNegative() && a.isNegative() != l.isNegative()) + { + l = Min; + } + } + + if (b.ne(Max) && d.ne(Max)) + { + u = b + d; + + // 溢出处理 + if (b.isNegative() == d.isNegative() && b.isNegative() != u.isNegative()) + { + u = Max; + } + } + + return Range(l, u); +} + +// 减法运算 [a, b] - [c, d] = [a - d, b - c] +Range +Range::sub(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + APInt l, u; + + // 计算下界 + if (a.eq(Min) || d.eq(Max)) + { + l = Min; + } + else + { + l = a - d; + + // 溢出处理 + if (a.isNegative() != d.isNegative() && d.isNegative() == l.isNegative()) + { + l = Min; + } + } + + // 计算上界 + if (b.eq(Max) || c.eq(Min)) + { + u = Max; + } + else + { + u = b - c; + + // 溢出处理 + if (b.isNegative() != c.isNegative() && c.isNegative() == u.isNegative()) + { + u = Max; + } + } + + return Range(l, u); +} + +// 乘法运算 [a, b] * [c, d] = [min(a*c, a*d, b*c, b*d), max(a*c, a*d, b*c, b*d)] +Range +Range::mul(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + if (this->isMaxRange() || other.isMaxRange()) + { + return Range(Min, Max); // 如果任一范围为最大范围,则结果为最大范围 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + APInt candidates[4]; + candidates[0] = MUL_OV(a, c, MUL_HELPER(a, c)); + candidates[1] = MUL_OV(a, d, MUL_HELPER(a, d)); + candidates[2] = MUL_OV(b, c, MUL_HELPER(b, c)); + candidates[3] = MUL_OV(b, d, MUL_HELPER(b, d)); + + // 找到最小和最大值 + APInt * min = &candidates[0]; + APInt * max = &candidates[0]; + + for (unsigned i = 1; i < 4; ++i) + { + if (candidates[i].sgt(*max)) + { + max = &candidates[i]; + } + else if (candidates[i].slt(*min)) + { + min = &candidates[i]; + } + } + + return Range(*min, *max); +} + +// 无符号取模运算 [a, b] % [c, d] +Range +Range::urem(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + // 处理除以0的情况 + if (c.ule(Zero) && d.uge(Zero)) + { + return Range(Min, Max); + } + + APInt candidates[4]; + candidates[0] = Min; + candidates[1] = Min; + candidates[2] = Max; + candidates[3] = Max; + + if (a.ne(Min) && c.ne(Min)) + { + candidates[0] = a.urem(c); // lower lower + } + + if (a.ne(Min) && d.ne(Max)) + { + candidates[1] = a.urem(d); // lower upper + } + + if (b.ne(Max) && c.ne(Min)) + { + candidates[2] = b.urem(c); // upper lower + } + + if (b.ne(Max) && d.ne(Max)) + { + candidates[3] = b.urem(d); // upper upper + } + + // 找到最小和最大值 + APInt * min = &candidates[0]; + APInt * max = &candidates[0]; + + for (unsigned i = 1; i < 4; ++i) + { + if (candidates[i].sgt(*max)) + { + max = &candidates[i]; + } + else if (candidates[i].slt(*min)) + { + min = &candidates[i]; + } + } + + return Range(*min, *max); +} + +// 有符号取模运算 [a, b] % [c, d] +Range +Range::srem(const Range & other) const +{ + if (other == Range(Zero, Zero) || other == Range(Min, Max, Empty)) + { + return Range(Min, Max, Empty); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + // 处理除以0的情况 + if (c.sle(Zero) && d.sge(Zero)) + { + return Range(Min, Max); + } + + APInt candidates[4]; + candidates[0] = MUL_OV(a, c, MUL_HELPER(a, c)); + candidates[1] = MUL_OV(a, d, MUL_HELPER(a, d)); + candidates[2] = MUL_OV(b, c, MUL_HELPER(b, c)); + candidates[3] = MUL_OV(b, d, MUL_HELPER(b, d)); + + // 找到最小和最大值 + APInt * min = &candidates[0]; + APInt * max = &candidates[0]; + + for (unsigned i = 1; i < 4; ++i) + { + if (candidates[i].sgt(*max)) + { + max = &candidates[i]; + } + else if (candidates[i].slt(*min)) + { + min = &candidates[i]; + } + } + + return Range(*min, *max); +} + +// 左移运算 +Range +Range::shl(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || c.eq(Min) || b.eq(Max) || d.eq(Max)) + { + return Range(Min, Max); + } + + APInt min = a.shl(c); + APInt max = b.shl(d); + + APInt Zeros(MAX_BIT_INT, b.countLeadingZeros()); + if (Zeros.ugt(d)) + { + return Range(min, max); + } + + // 返回最大范围 [-inf, +inf] + return Range(Min, Max); +} + +// 逻辑右移运算 +Range +Range::lshr(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || c.eq(Min) || b.eq(Max) || d.eq(Max)) + { + return Range(Min, Max); + } + + APInt max = b.lshr(c); + APInt min = a.lshr(d); + + return Range(min, max); +} + +// 算术右移运算 +Range +Range::ashr(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || c.eq(Min) || b.eq(Max) || d.eq(Max)) + { + return Range(Min, Max); + } + + APInt max = b.ashr(c); + APInt min = a.ashr(d); + + return Range(min, max); +} + +// 与操作,Hacker's Delight算法 +Range +Range::And(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + const APInt & umin = llvm::APIntOps::umin(getUpper(), other.getUpper()); + if (umin.isAllOnesValue()) + { + return Range(Min, Max); + } + return Range(APInt::getNullValue(MAX_BIT_INT), umin); + } + + APInt a = this->getLower(); + APInt b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || b.eq(Max) || c.eq(Min) || d.eq(Max)) + { + return Range(Min, Max); + } + + // 取反所有位 + APInt negA = APInt(a); + negA.flipAllBits(); + APInt negB = APInt(b); + negB.flipAllBits(); + APInt negC = APInt(c); + negC.flipAllBits(); + APInt negD = APInt(d); + negD.flipAllBits(); + + Range inv1 = Range(negB, negA); + Range inv2 = Range(negD, negC); + + Range invres = inv1.Or(inv2); + + // 取反结果 + APInt invLower = invres.getUpper(); + invLower.flipAllBits(); + + APInt invUpper = invres.getLower(); + invUpper.flipAllBits(); + + return Range(invLower, invUpper); +} + +// 保守的与操作,适用于大于64位的值 +Range +Range::And_conservative(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + const APInt & umin = llvm::APIntOps::umin(other.getUpper(), getUpper()); + if (umin.isAllOnesValue()) + { + return Range(Min, Max); + } + return Range(APInt::getNullValue(MAX_BIT_INT), umin); +} + + + + + +// 范围表示和管理 +std::map valueRanges; + +// 更新常量整数值 +void +updateConstantIntegers(unsigned maxBitWidth) +{ + // 使用给定的最大位宽设置最小值和最大值 + Min = APInt::getSignedMinValue(maxBitWidth); + Max = APInt::getSignedMaxValue(maxBitWidth); + Zero = APInt(maxBitWidth, 0UL, true); // 确保使用maxBitWidth来表示Zero + One = APInt(maxBitWidth, 1UL, true); // 确保使用maxBitWidth来表示One +} + +// 初始化分析 +void +initializeAnalysis(Function & F) +{ + // 清除前一次分析的范围信息 + valueRanges.clear(); + errs() << "Starting range analysis for function: " << F.getName() << "\n"; +} + +// 结束分析 +void +finalizeAnalysis(Function & F) +{ + // 结束分析的逻辑 + errs() << "Finished range analysis for function: " << F.getName() << "\n"; +} + +// 判断指令是否有效 +bool +isValidInstruction(const Instruction * I) +{ + switch (I->getOpcode()) + { + // 算术运算 + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::UDiv: + case Instruction::SDiv: + case Instruction::URem: + case Instruction::SRem: + // 位操作 + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + // 类型转换 + case Instruction::Trunc: + case Instruction::ZExt: + case Instruction::SExt: + // 其他操作 + case Instruction::PHI: + case Instruction::Select: + return true; + default: + return false; + } +} + +// 获取函数中最大位宽 +unsigned +getMaxBitWidth(const Function & F) +{ + unsigned max = 0; + for (const Instruction & I : instructions(F)) + { + if (I.getType()->isIntegerTy()) + { + max = std::max(max, static_cast(I.getType()->getPrimitiveSizeInBits().getFixedSize())); + } + + // 获得指令操作数的最大位宽 + for (const Value * operand : I.operands()) + { + if (operand->getType()->isIntegerTy()) + { + max = std::max(max, static_cast(operand->getType()->getPrimitiveSizeInBits().getFixedSize())); + } + } + } + // 确保位宽最小为1 + return max == 0 ? 1 : max; +} + +void handleBinaryOperator(BinaryOperator * binOp); + +// 分析每个指令 +void +analyzeInstruction(Instruction & I) +{ + if (auto * binOp = dyn_cast(&I)) + { + handleBinaryOperator(binOp); + } +} + +void +insertOverflowCheck(BinaryOperator * binOp, Value * op1, Value * op2, Range & resultRange) +{ + Function * F = binOp->getFunction(); + LLVMContext & Context = F->getContext(); + IRBuilder<> Builder(binOp); + + // 检查 x 和 y 是否都是非负数 + Value * xNonNegative = Builder.CreateICmpSGE(op1, ConstantInt::get(op1->getType(), 0)); + Value * yNonNegative = Builder.CreateICmpSGE(op2, ConstantInt::get(op2->getType(), 0)); + Value * bothNonNegative = Builder.CreateAnd(xNonNegative, yNonNegative); + + // 如果是,则检查结果是否也是非负数 + Value * addNegative = Builder.CreateICmpSLT(binOp, ConstantInt::get(binOp->getType(), 0)); + Value * nonNegativeOverflow = Builder.CreateAnd(bothNonNegative, addNegative); + + // 检查 x 和 y 是否都是负数 + Value * xNegative = Builder.CreateICmpSLT(op1, ConstantInt::get(op1->getType(), 0)); + Value * yNegative = Builder.CreateICmpSLT(op2, ConstantInt::get(op2->getType(), 0)); + Value * bothNegative = Builder.CreateAnd(xNegative, yNegative); + + // 如果是,则检查结果是否也是负数 + Value * addNonNegative = Builder.CreateICmpSGE(binOp, ConstantInt::get(binOp->getType(), 0)); + Value * negativeOverflow = Builder.CreateAnd(bothNegative, addNonNegative); + + // 最终检查是否发生溢出 + Value * overflow = Builder.CreateOr(nonNegativeOverflow, negativeOverflow); + + // 根据溢出情况创建分支 + BasicBlock * CurrentBB = binOp->getParent(); + BasicBlock * OverflowBB = BasicBlock::Create(Context, "overflow", F); + BasicBlock * ContinueBB = BasicBlock::Create(Context, "continue", F); + + Builder.CreateCondBr(overflow, OverflowBB, ContinueBB); + + // 在 OverflowBB 中处理溢出 + Builder.SetInsertPoint(OverflowBB); + // 调用处理溢出的函数 + Function * HandleOverflowFunc = F->getParent()->getFunction("handle_overflow"); + if (HandleOverflowFunc) + { + Builder.CreateCall(HandleOverflowFunc); + } + Builder.CreateBr(ContinueBB); + + // 在 ContinueBB 中继续执行 + Builder.SetInsertPoint(ContinueBB); +} + +// 处理二元操作指令 +void +handleBinaryOperator(BinaryOperator * binOp) +{ + auto op1 = binOp->getOperand(0); + auto op2 = binOp->getOperand(1); + + // 获取操作数的范围 + Range range1 = valueRanges.count(op1) ? valueRanges[op1] : Range(Min, Max); + Range range2 = valueRanges.count(op2) ? valueRanges[op2] : Range(Min, Max); + + // 计算新范围 + Range resultRange; + + if (binOp->getOpcode() == Instruction::Add) + { + resultRange = range1.add(range2); + + // 插入溢出检测逻辑 + insertOverflowCheck(binOp, op1, op2, resultRange); + } + + // switch (binOp->getOpcode()) { + // case Instruction::Add: + // // 调用 Range::add 方法 + // resultRange = range1.add(range2); + // break; + // case Instruction::Sub: + // resultRange = range1.sub(range2); + // break; + // case Instruction::Mul: + // resultRange = range1.mul(range2); + // break; + // case Instruction::UDiv: + // resultRange = range1.udiv(range2); + // break; + // case Instruction::SDiv: + // resultRange = range1.sdiv(range2); + // break; + // case Instruction::URem: + // resultRange = range1.urem(range2); + // break; + // case Instruction::SRem: + // resultRange = range1.srem(range2); + // break; + // // 处理其他运算符,如位移、位操作等 + // default: + // resultRange = Range(Min, Max, Unknown); + // break; + // } + + // 存储结果范围 + valueRanges[binOp] = resultRange; +} + +// 处理函数调用指令 +void +handleCallInst(CallInst * callInst) +{ + // 处理函数调用的范围分析逻辑 +} + +// 获取某个值的范围 +std::pair +getValueRange(Value * val) +{ + if (isa(val)) + { + auto cInt = dyn_cast(val); + double v = cInt->getSExtValue(); + return std::make_pair(v, v); + } + else if (valueRanges.find(val) != valueRanges.end()) + { + Range range = valueRanges[val]; + return std::make_pair(range.getLower().getSExtValue(), range.getUpper().getSExtValue()); + } + else + { + return std::make_pair(-INFINITY, INFINITY); + } +} + +// 加法的范围分析 +std::pair +analyzeAddition(std::pair range1, std::pair range2) +{ + return std::make_pair(range1.first + range2.first, range1.second + range2.second); +} + +// 减法的范围分析 +std::pair +analyzeSubtraction(std::pair range1, std::pair range2) +{ + return std::make_pair(range1.first - range2.second, range1.second - range2.first); +} + +// 乘法的范围分析 +std::pair +analyzeMultiplication(std::pair range1, std::pair range2) +{ + double lz = std::min({range1.first * range2.first, range1.first * range2.second, range1.second * range2.first, range1.second * range2.second}); + double uz = std::max({range1.first * range2.first, range1.first * range2.second, range1.second * range2.first, range1.second * range2.second}); + return std::make_pair(lz, uz); +} + +// 除法的范围分析 +std::pair +analyzeDivision(std::pair range1, std::pair range2) +{ + if (range2.first > 0 || range2.second < 0) + { + double lz = std::min({range1.first / range2.first, range1.first / range2.second, range1.second / range2.first, range1.second / range2.second}); + double uz = std::max({range1.first / range2.first, range1.first / range2.second, range1.second / range2.first, range1.second / range2.second}); + return std::make_pair(lz, uz); + } + // 处理除零的情况 + return std::make_pair(-INFINITY, INFINITY); +} + +// 打印变量名 +// void printVarName(const Value *V, raw_ostream &OS) { +// if (const Argument *A = dyn_cast(V)) { +// OS << A->getParent()->getName() << "." << A->getName(); +// } else if (const Instruction *I = dyn_cast(V)) { +// OS << I->getParent()->getParent()->getName() << "." +// << I->getParent()->getName() << "." << I->getName(); +// } else { +// OS << V->getName(); +// } +//} + +// SymbInterval 打印函数 +// void printSymbInterval(raw_ostream &OS, const SymbInterval &SI) { +// switch (SI.getOperation()) { +// case ICmpInst::ICMP_EQ: // equal +// OS << "[lb("; +// printVarName(SI.getBound(), OS); +// OS << "), ub("; +// printVarName(SI.getBound(), OS); +// OS << ")]"; +// break; +// case ICmpInst::ICMP_SLE: // sign less or equal +// OS << "[-inf, ub("; +// printVarName(SI.getBound(), OS); +// OS << ")]"; +// break; +// case ICmpInst::ICMP_SLT: // sign less than +// OS << "[-inf, ub("; +// printVarName(SI.getBound(), OS); +// OS << ") - 1]"; +// break; +// case ICmpInst::ICMP_SGE: // sign greater or equal +// OS << "[lb("; +// printVarName(SI.getBound(), OS); +// OS << "), +inf]"; +// break; +// case ICmpInst::ICMP_SGT: // sign greater than +// OS << "[lb("; +// printVarName(SI.getBound(), OS); +// OS << " - 1), +inf]"; +// break; +// default: +// OS << "Unknown Instruction.\n"; +// } +//} + +// 主方法:运行范围分析 +void +runRangeAnalysis(Function & F) +{ + initializeAnalysis(F); + + // 获取函数的最大位宽 + unsigned maxBitWidth = getMaxBitWidth(F); + + // 更新常量整数值 + updateConstantIntegers(maxBitWidth); + + for (auto & BB : F) + { + for (auto & I : BB) + { + if (isValidInstruction(&I)) + { + analyzeInstruction(I); + } + } + } + finalizeAnalysis(F); +} + +class RangeAnalysisPass : public PassInfoMixin { + public: + PreservedAnalyses + run(Function & F, FunctionAnalysisManager & FAM) + { + runRangeAnalysis(F); + return PreservedAnalyses::all(); + } +}; + +// 注册新的 Pass 机制 +extern "C" ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() +{ + return {LLVM_PLUGIN_API_VERSION, "RangeAnalysisPass", LLVM_VERSION_STRING, + [](PassBuilder & PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager & FPM, + ArrayRef) { + if (Name == "range-analysis") + { + FPM.addPass(RangeAnalysisPass()); + return true; + } + return false; + }); + }}; +} + +} // namespace + +// end of anonymous namespace diff --git a/src/newton/myRangeAnalysis.h b/src/newton/myRangeAnalysis.h new file mode 100644 index 000000000..66de50819 --- /dev/null +++ b/src/newton/myRangeAnalysis.h @@ -0,0 +1,12 @@ +// +// Created by 13862 on 2024/8/1. +// + +#ifndef COSENSE_MYRANGEANALYSIS_H +#define COSENSE_MYRANGEANALYSIS_H + +#endif // COSENSE_MYRANGEANALYSIS_H + + + +void runRangeAnalysis(llvm::Module &module); diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 9f0518a55..e6656be97 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -78,6 +78,58 @@ using namespace llvm; + +std::set whitelist = { + "MadgwickAHRSupdate" +}; + + +//void processWhitelistedFunctions(Module &module, const std::set &whitelist) { +// for (auto &F : module) { +// // 检查函数是否在白名单中 +// if (whitelist.find(F.getName().str()) != whitelist.end()) { +// // 打印找到的函数名 +// llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; +// // 对函数的参数进行量化处理 +// handleArguments(F); +// // 对返回值进行反量化处理 +// dequantizeResults(F); +// } +// } +//} +// +//void handleArguments(Function &F) { +// IRBuilder<> Builder(&F.getEntryBlock()); +// for (auto &arg : F.args()) { +// if (arg.getType()->isFloatingPointTy()) { +// // 量化参数:将浮点数转换为整型(定点) +// Value *quantizedArg = Builder.CreateFPToSI( +// Builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getType(), scale)), +// IntegerType::get(arg.getContext(), 32) +// ); +// // 替换原来的浮点参数 +// arg.replaceAllUsesWith(quantizedArg); +// } +// } +//} + + + + + + + + + + + + + + + + + + // Function to save the IR of a module to a file void saveModuleIR(llvm::Module & M, const std::string & fileName) @@ -604,8 +656,9 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl eraseOldInstructions(); - // 处理特殊的数值 - // handleSpecialNumber(*Mod, Mod->getContext()); + //processWhitelistedFunctions(*Mod, whitelist); + + const char * homeDir = getenv("HOME"); if (!homeDir) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index c50504170..94a1a4e36 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -243,6 +243,7 @@ shouldSkipFunction(const std::string & functionName) { // List of function names to skip static const std::unordered_set skipFunctions = { + "llvm.round.f32", "llvm.dbg.declare", "llvm.dbg.value", "llvm.dbg.label", @@ -1107,6 +1108,8 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1155,8 +1158,8 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//{ +// llvm::errs() << "Entering createFixMul\n"; +// +// // Check if irModule is valid +// if (!irModule) +// { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixmulFuncName = "fixmul"; +// for (auto & function : *irModule) +// { +// if (function.getName() == fixmulFuncName) +// { +// llvm::errs() << "fixmul already exists\n"; +// return &function; +// } +// } +// +// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); +// +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// builder.SetInsertPoint(entryBB); +// +// // Create fixed-point multiplication instruction +// Type * higherQuantizedType; +// switch (BIT_WIDTH) +// { +// case 8: +// higherQuantizedType = Type::getInt16Ty(irModule->getContext()); +// break; +// case 16: +// higherQuantizedType = Type::getInt32Ty(irModule->getContext()); +// break; +// default: +// higherQuantizedType = Type::getInt64Ty(irModule->getContext()); +// break; +// } +// +// llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); +// llvm::Value * lhs = &*arg1; +// llvm::Function::arg_iterator arg2 = &*(++arg1); +// llvm::Value * rhs = &*arg2; +// llvm::Value * mulResult = builder.CreateMul(lhs, rhs); +// llvm::Value * ashrInst = builder.CreateAShr(mulResult, llvm::ConstantInt::get(arg1->getType(), FRAC_Q)); +// builder.CreateRet(ashrInst); +// +// functionsToInsert.emplace_back(func); +// llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; +// return func; +//} + // TODO Original version of fixrsqrt llvm::Function * createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -2952,15 +3013,15 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) switch (llvmIrInstruction->getOpcode()) { - case Instruction::Alloca: - handleAlloca(llvmIrInstruction, quantizedType); - break; - case Instruction::Store: - handleStore(llvmIrInstruction, quantizedType); - break; - case Instruction::Load: - handleLoad(llvmIrInstruction, quantizedType); - break; +// case Instruction::Alloca: +// handleAlloca(llvmIrInstruction, quantizedType); +// break; +// case Instruction::Store: +// handleStore(llvmIrInstruction, quantizedType); +// break; +// case Instruction::Load: +// handleLoad(llvmIrInstruction, quantizedType); +// break; case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: @@ -3003,15 +3064,48 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) llvm::errs() << "Exiting adaptTypeCast\n"; } -void -quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) -{ - for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) - { - auto paramOp = llvmIrFunction.getArg(idx); - setQuantizedType(paramOp, quantizedType); - } -} + + + + +//void +//quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) +//{ +// for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) +// { +// auto paramOp = llvmIrFunction.getArg(idx); +// setQuantizedType(paramOp, quantizedType); +// } +//} + +//void quantizeFunctionArguments(Function &llvmIrFunction, Type *quantizedType) { +// for (auto &arg : llvmIrFunction.args()) { +// if (arg.getType()->isFloatTy()) { // 检查参数是否为浮点类型 +// // 获取原始浮点值 +// Value *originalFloat = &arg; +// +// IRBuilder<> builder(&llvmIrFunction.getEntryBlock().front()); +// +// // 执行乘法 (假设你有一个 FRAC_BASE 的常数) +// Value *scaledValue = builder.CreateFMul(originalFloat, ConstantFP::get(originalFloat->getType(), FRAC_BASE)); +// +// // 调用 roundf32 (假设这是你的量化逻辑) +// Function *roundF32 = Intrinsic::getDeclaration(llvmIrFunction.getParent(), Intrinsic::round, originalFloat->getType()); +// Value *roundedValue = builder.CreateCall(roundF32, scaledValue); +// +// // 将浮点值转换为整数类型(使用 fptosi) +// Value *quantizedValue = builder.CreateFPToSI(roundedValue, quantizedType); +// +// // 用量化后的值替换原来的参数 +// arg.replaceAllUsesWith(quantizedValue); // 使用 replaceAllUsesWith 进行替换 +// +// // 根据需要设置量化的值或进行替换 +// // 你可以在这里将量化后的值赋给参数或者进行进一步的操作 +// // 例如:将参数替换为量化值 +// // setQuantizedType(paramOp, quantizedType); // 这里可以是你的量化类型设置函数 +// } +// } +//} // Main function to perform LLVM IR auto quantization void @@ -3079,7 +3173,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * quantize the arguments type * */ - // quantizeFunctionArguments(llvmIrFunction, quantizedType); + //quantizeFunctionArguments(llvmIrFunction, quantizedType); // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) diff --git a/src/newton/runPass.cpp b/src/newton/runPass.cpp new file mode 100644 index 000000000..07d9462f3 --- /dev/null +++ b/src/newton/runPass.cpp @@ -0,0 +1,45 @@ + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/IR/LegacyPassManager.h" + +// 包含你的 RangeAnalysisPass 头文件 +#include "myRangeAnalysis.h" + +using namespace llvm; + +int main(int argc, char **argv) { + if (argc < 2) { + errs() << "Usage: " << argv[0] << " \n"; + return 1; + } + + // 解析输入文件 + LLVMContext Context; + SMDiagnostic Err; + std::unique_ptr M = parseIRFile(argv[1], Err, Context); + + if (!M) { + Err.print(argv[0], errs()); + return 1; + } + + // 创建 PassManager + legacy::PassManager PM; + + // 添加自定义 Pass + PM.add(new RangeAnalysisPass()); + + // 运行 Pass + PM.run(*M); + + // 输出结果 + M->print(outs(), nullptr); + + return 0; +} From 9a7e5888fd2234f368f99e40a827f00580284241 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 5 Oct 2024 12:46:16 +0100 Subject: [PATCH 103/213] big update Addresses #1. --- ...00557e8f76980e0bd2de795d669f5f24606104.txt | 48 + ...71b9a2ea9c18664318972c2e53e9808f432731.txt | 48 + ...dceef32dea94e7110bde8ccc86621a2b98ffe6.txt | 48 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 52 +- .../newton-irPass-LLVMIR-quantization.cpp | 3284 +++-------------- 5 files changed, 654 insertions(+), 2826 deletions(-) create mode 100644 analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt create mode 100644 analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt create mode 100644 analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt diff --git a/analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt b/analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt new file mode 100644 index 000000000..ca3a2074b --- /dev/null +++ b/analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt @@ -0,0 +1,48 @@ + +changeset: 1695:2b00557e8f76980e0bd2de795d669f5f24606104 +char kNewtonVersion[] = "0.3-alpha-1695 (2b00557e8f76980e0bd2de795d669f5f24606104) (build 09-30-2024-11:49-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt b/analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt new file mode 100644 index 000000000..150baaf88 --- /dev/null +++ b/analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt @@ -0,0 +1,48 @@ + +changeset: 1694:6771b9a2ea9c18664318972c2e53e9808f432731 +char kNewtonVersion[] = "0.3-alpha-1694 (6771b9a2ea9c18664318972c2e53e9808f432731) (build 09-30-2024-10:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt b/analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt new file mode 100644 index 000000000..fbdc7ab18 --- /dev/null +++ b/analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt @@ -0,0 +1,48 @@ + +changeset: 1696:7cdceef32dea94e7110bde8ccc86621a2b98ffe6 +char kNewtonVersion[] = "0.3-alpha-1696 (7cdceef32dea94e7110bde8ccc86621a2b98ffe6) (build 10-04-2024-12:54-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index e6656be97..a3203fa2c 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -162,47 +162,18 @@ removeQuantizedSuffixInModule(llvm::Module & M) } } } -} -void -finalCorrectionPass(Module & M, Type * quantizedType) -{ - llvm::errs() << "Entering finalCorrectionPass\n"; - for (Function & F : M) - { - for (BasicBlock & BB : F) - { - for (Instruction & I : BB) - { - // Correct Load Instructions - if (auto * loadInst = dyn_cast(&I)) - { - if (loadInst->getType() != quantizedType) - { - llvm::errs() << "Correcting load instruction: " << *loadInst << "\n"; - - loadInst->mutateType(quantizedType); - } - } - - // Correct Store Instructions - if (auto * storeInst = dyn_cast(&I)) - { - if (storeInst->getValueOperand()->getType() != quantizedType) - { - llvm::errs() << "Correcting store instruction: " << *storeInst << "\n"; - storeInst->getValueOperand()->mutateType(quantizedType); - } - if (storeInst->getPointerOperand()->getType()->getPointerElementType() != quantizedType) - { - llvm::errs() << "Correcting store pointer operand: " << *storeInst << "\n"; - storeInst->getPointerOperand()->mutateType(quantizedType->getPointerTo()); - } - } + // Remove suffix from global variables + for (auto &G : M.globals()) { + if (G.hasName()) { + std::string GlobalName = G.getName().str(); + size_t pos = GlobalName.find("_quantized"); + if (pos != std::string::npos) { + GlobalName.erase(pos, 10); // Remove "_quantized" + G.setName(GlobalName); } } } - llvm::errs() << "Exiting finalCorrectionPass\n"; } void @@ -644,17 +615,16 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // overloadFunc(Mod, callerMap); // Finally, erase old functions - eraseOldFunctions(); + //eraseOldFunctions(); eraseOldGlobals(); // Perform text replacement to remove "_quantized" suffixes removeQuantizedSuffixInModule(*Mod); - // correct fmul - // correctAllFMulsInModule(Mod.get()); - eraseOldInstructions(); + + //eraseOldInstructions(); //processWhitelistedFunctions(*Mod, whitelist); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 94a1a4e36..a072ee1a3 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1,979 +1,15 @@ -/* -Authored 2022. Pei Mu. -All rights reserved. -Redistribution and use in source and binary forms, with or without -modification, are permitted provided that the following conditions -are met: -* Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. -* Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. -* Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE -COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, -BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; -LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. -*/ - -#include "newton-irPass-LLVMIR-quantization.h" -#include -using namespace llvm; - -unsigned int FRAC_Q; -#define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 32 - -extern "C" { - -// check if the type is a floating type -bool -isValidFloatingType(Type * type) -{ - return type->isFloatTy() || type->isDoubleTy(); -} - -bool -isMatrixOperation(Instruction * instr) -{ - // 检查指令的操作数是否为多维数组或矩阵类型 - // 这里假设多维数组使用特定的类型或有特定的标记 - for (unsigned i = 0; i < instr->getNumOperands(); ++i) - { - Value * operand = instr->getOperand(i); - if (operand->getType()->isPointerTy()) - { - // 检查指针指向的类型是否为多维数组类型 - Type * elementType = operand->getType()->getPointerElementType(); - if (elementType->isArrayTy()) - { - // 进一步检查数组的维度或特定特征 - return true; // 假设这是一个矩阵操作 - } - } - } - return false; // 如果没有匹配到矩阵操作的特征 -} - -// Set the quantized type for a given value -void -setQuantizedType(Value * inValue, Type * quantizedType) -{ - llvm::errs() << "Entering setQuantizedType\n"; - if (inValue == nullptr) - { - llvm::errs() << "inValue is nullptr\n"; - return; - } - auto valueType = inValue->getType(); - unsigned pointerAddr; - bool isPointer = false; - - if (valueType != nullptr) - { - if (valueType->isPointerTy()) - { - isPointer = true; - // If the value is a pointer, get the address space and element type - pointerAddr = valueType->getPointerAddressSpace(); - valueType = valueType->getPointerElementType(); - } - - // Skip integer and struct types - if (valueType->isIntegerTy() || valueType->isStructTy()) - { - llvm::errs() << "Skipping quantization for type: " << *valueType << "\n"; - return; - } - - if (isValidFloatingType(valueType) || valueType->isArrayTy()) - { - // Print the original and new types for debugging - llvm::errs() << "Original type: " << *valueType << "\n"; - llvm::errs() << "New quantized type: " << *quantizedType << "\n"; - // If the value is a pointer, get the address space and element type - if (isPointer) - { - inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - } - else - { - // Otherwise, directly set the type to the quantized type - inValue->mutateType(quantizedType); - } - } - else - { - llvm::errs() << "Unsupported type for quantization: " << *valueType << "\n"; - } - } - else - { - llvm::errs() << "Value type is nullptr\n"; - } -} -// Quantize constants within an instruction -std::unordered_map quantizedValueCache; - -void -fixLoadStoreTypes(Function & F, Type * quantizedType) -{ - for (BasicBlock & BB : F) - { - for (Instruction & I : BB) - { - if (auto * loadInst = dyn_cast(&I)) - { - IRBuilder<> Builder(loadInst); - - // 确保load指令使用量化类型 - auto * pointerOperand = loadInst->getPointerOperand(); - pointerOperand->mutateType(quantizedType->getPointerTo()); - - // 创建新的load指令,并替换旧的load指令 - auto * newLoad = Builder.CreateLoad(quantizedType, pointerOperand); - loadInst->replaceAllUsesWith(newLoad); - loadInst->eraseFromParent(); - } - else if (auto * storeInst = dyn_cast(&I)) - { - IRBuilder<> Builder(storeInst); - - // 确保store指令使用量化类型 - auto * valueOperand = storeInst->getValueOperand(); - auto * pointerOperand = storeInst->getPointerOperand(); - - // 转换store值为量化类型 - auto * newValue = Builder.CreateFPToSI(valueOperand, quantizedType); - pointerOperand->mutateType(quantizedType->getPointerTo()); - - // 创建新的store指令,并替换旧的store指令 - auto * newStore = Builder.CreateStore(newValue, pointerOperand); - storeInst->replaceAllUsesWith(newStore); - storeInst->eraseFromParent(); - } - } - } -} - -// Create a quantized function with the same signature as the original function -Function * -createQuantizedFunction(Function & llvmIrFunction, Type * quantizedType) -{ - std::vector params; - for (auto & arg : llvmIrFunction.args()) - { - params.push_back(arg.getType()); // 保持原参数类型 - } - - Type * returnType = quantizedType; // 返回量化后的整数类型 - FunctionType * newFuncType = FunctionType::get(returnType, params, false); - Function * newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_quantized", llvmIrFunction.getParent()); - return newFunc; -} - -// Clone the function body from the original function to the new quantized function -void -cloneFunctionBody(Function & oldFunc, Function * newFunc) -{ - ValueToValueMapTy vmap; - Function::arg_iterator newArgIt = newFunc->arg_begin(); - for (auto & oldArg : oldFunc.args()) - { - newArgIt->setName(oldArg.getName()); - vmap[&oldArg] = &*newArgIt++; - } - // Clone the function body into the new function - SmallVector returns; - llvm::CloneFunctionInto(newFunc, &oldFunc, vmap, CloneFunctionChangeType::LocalChangesOnly, returns); -} - -// Replace all uses of the original function with the new quantized function -void -replaceFunctionUses(Function & oldFunc, Function * newFunc) -{ - std::vector users(oldFunc.user_begin(), oldFunc.user_end()); - for (auto * U : users) - { - if (CallInst * callInst = dyn_cast(U)) - { - std::vector args; - for (auto & arg : callInst->args()) - { - args.push_back(arg); - } - // Create a new call instruction to the new function - CallInst * newCall = CallInst::Create(newFunc, args, "", callInst); - newCall->setCallingConv(callInst->getCallingConv()); - newCall->setDebugLoc(callInst->getDebugLoc()); - llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; - callInst->replaceAllUsesWith(newCall); - callInst->eraseFromParent(); - } - } -} - -bool -isQuantizedFunctionName(const std::string & functionName) -{ - return functionName.find("_quantized") != std::string::npos; -} - -// A list of functions to erase after processing -std::vector functionsToErase; - -// Track processed functions to avoid duplicate processing -std::set processedFunctions; - -bool -shouldSkipFunction(const std::string & functionName) -{ - // List of function names to skip - static const std::unordered_set skipFunctions = { - "llvm.round.f32", - "llvm.dbg.declare", - "llvm.dbg.value", - "llvm.dbg.label", - "fixsqrt", - "fixrsqrt", - "sinf", - "llvm.sqrt.f64", - "llvm.sqrt.f32", - "sqrt", - "sqrtf"}; - - return skipFunctions.find(functionName) != skipFunctions.end(); -} -// Handle the function signature change for quantization -void -handleFunctionSignature(Function & llvmIrFunction, Type * quantizedType) -{ - llvm::errs() << "Calling handleFunctionSignature for function: " << llvmIrFunction.getName() << "\n"; - // Skip certain functions - std::string functionName = llvmIrFunction.getName().str(); - if (shouldSkipFunction(functionName)) - { - llvm::errs() << "Skipping function signature handling for: " << functionName << "\n"; - return; - } - - if (processedFunctions.find(functionName) != processedFunctions.end()) - { - llvm::errs() << "Function already processed: " << functionName << "\n"; - return; - } - - if (isQuantizedFunctionName(functionName)) - { - llvm::errs() << "Skipping already quantized function: " << functionName << "\n"; - return; - } - - // Skip if the function returns void - if (llvmIrFunction.getReturnType()->isVoidTy()) - { - llvm::errs() << "Skipping function with void return type: " << functionName << "\n"; - return; - } - - processedFunctions.insert(functionName); - - llvm::errs() << "Original function name: " << llvmIrFunction.getName() << "\n"; - for (auto & arg : llvmIrFunction.args()) - { - llvm::errs() << "Original argument type: " << *arg.getType() << "\n"; - } - - // Keep the original function parameters - std::vector params; - for (auto & arg : llvmIrFunction.args()) - { - params.push_back(arg.getType()); - } - - // Create a new function with the quantized return type - Type * returnType = quantizedType; - FunctionType * newFuncType = FunctionType::get(returnType, params, false); - - Function * newFunc = createQuantizedFunction(llvmIrFunction, quantizedType); - - llvm::errs() << "New function name: " << newFunc->getName() << "\n"; - for (auto & arg : newFunc->args()) - { - llvm::errs() << "New argument type: " << *arg.getType() << "\n"; - } - - cloneFunctionBody(llvmIrFunction, newFunc); - replaceFunctionUses(llvmIrFunction, newFunc); - - // Add the old function to the list of functions to erase - functionsToErase.push_back(&llvmIrFunction); - llvmIrFunction.replaceAllUsesWith(UndefValue::get(llvmIrFunction.getType())); - llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; -} - -// Helper function to recursively transform types -Type * -transformToQuantizedType(Type * originalType, Type * quantizedType) -{ - if (originalType->isArrayTy()) - { - auto elementType = originalType->getArrayElementType(); - auto newElementType = transformToQuantizedType(elementType, quantizedType); - return ArrayType::get(newElementType, originalType->getArrayNumElements()); - } - else if (originalType->isFloatTy() || originalType->isDoubleTy()) - { - return quantizedType; - } - // Return original type if no conversion is necessary - return originalType; -} -void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) -{ - IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); - - // Ensure operand is in the correct type - if (operand->getType()->isFloatingPointTy()) - { - operand = Builder.CreateFPToSI(operand, quantizedType); - } - - // Convert the operand from fixed-point (int32) to float - llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); - - // Call llvm.sqrt.f32 to compute the square root of the float value - llvm::Value * sqrtFloat = Builder.CreateCall( - Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), - {operandAsFloat}); - - // Convert the result back to int32 - llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); - - // Apply the shift-left operation (shl i32 %result, 5) - llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); - - // Replace the original instruction with the new fixed-point sqrt result - llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); - llvmIrCallInstruction->eraseFromParent(); -} - -// void -// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) -//{ -// IRBuilder<> Builder(llvmIrCallInstruction); -// auto operand = llvmIrCallInstruction->getOperand(0); -// -// // Cast the instruction to CallInst to access getCalledFunction method -// CallInst * callInst = dyn_cast(llvmIrCallInstruction); -// if (!callInst) -// { -// llvm::errs() << "Error: Instruction is not a CallInst.\n"; -// return; -// } -// -// // Convert the operand to fixed-point format if necessary -// if (operand->getType()->isFloatingPointTy()) -// { -// operand = Builder.CreateFPToSI(operand, quantizedType); -// } -// -// // Create call to the fixed-point sqrt function -// llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); -// if (auto * call = dyn_cast(sqrtResult)) -// { -// call->setTailCall(true); -// } -// -// // No need to apply shl and compensation if it's already done in createFixSqrt -// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); -// llvmIrCallInstruction->eraseFromParent(); -// } - -void -handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) -{ - llvm::errs() << "Entering handleRsqrtCall\n"; - IRBuilder<> builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); - - // 检查fixrsqrt是否为空 - if (!fixrsqrt) - { - llvm::errs() << "Error: fixrsqrt function is null.\n"; - return; - } - - // 获取调用指令的操作数 - - llvm::errs() << "Operand type before conversion: " << *operand->getType() << "\n"; - - // 创建fixrsqrt调用替代原始调用 - CallInst * rsqrtResult = builder.CreateCall(fixrsqrt, {operand}); - - // 替换所有使用原始调用的位置为新的结果 - llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); - - // 从其父基本块中删除原始调用 - llvmIrCallInstruction->eraseFromParent(); - llvm::errs() << "Replaced invsqrt with fixrsqrt\n"; -} - -void -handleSinCall(CallInst * llvmIrCallInstruction, Type * quantizedType) -{ - // Debugging output - llvm::errs() << "Entering handleSinCall\n"; - /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sin(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sin(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ - IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); - - llvm::errs() << "Operand type: " << *operand->getType() << "\n"; - - if (operand->getType()->isIntegerTy()) - { - Value * newOperand = Builder.CreateSIToFP(operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); - } - - auto cloneInst = llvmIrCallInstruction->clone(); - llvm::errs() << "Cloned instruction: " << *cloneInst << "\n"; - Value * fptosiInst = Builder.CreateFPToSI(cloneInst, quantizedType); - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q / 2); - Value * resInst = nullptr; - - /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ - - if (FRAC_Q % 2) - { - Value * lhsCompensateInst = Builder.CreateSIToFP(shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); - llvm::errs() << "Compensation applied\n"; - } - else - { - resInst = shlInst; - } - - llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); - llvm::errs() << "Exiting handleSinCall\n"; -} - -void -handleMemCpyCall(Instruction * llvmIrInstruction, Type * quantizedType) -{ - if (auto callInst = dyn_cast(llvmIrInstruction)) - { - Function * calledFunction = callInst->getCalledFunction(); - if (calledFunction && calledFunction->getName().startswith("llvm.memcpy")) - { - IRBuilder<> Builder(callInst); - - // 获取原始操作数和它们的类型 - Value * dest = callInst->getArgOperand(0); - Value * src = callInst->getArgOperand(1); - Value * size = callInst->getArgOperand(2); - - // 更新源和目标指针的类型 - Type * newDestType = transformToQuantizedType(dest->getType()->getPointerElementType(), quantizedType)->getPointerTo(); - Type * newSrcType = transformToQuantizedType(src->getType()->getPointerElementType(), quantizedType)->getPointerTo(); - - Value * newDest = Builder.CreateBitCast(dest, newDestType); - Value * newSrc = Builder.CreateBitCast(src, newSrcType); - - // 创建新的量化后的 memcpy 调用 - Function * memcpyFunc = Intrinsic::getDeclaration( - callInst->getModule(), - Intrinsic::memcpy, - {newDestType, newSrcType, Builder.getInt64Ty()}); - Builder.CreateCall(memcpyFunc, {newDest, newSrc, size, callInst->getArgOperand(3)}); - - // 删除旧的调用 - callInst->eraseFromParent(); - } - } -} - -// A list of global variables to erase after processing -std::vector globalsToErase; - -// Function to actually erase global variables after processing -void -eraseOldGlobals() -{ - llvm::errs() << "Entering eraseOldGlobals\n"; - std::set uniqueGlobals(globalsToErase.begin(), globalsToErase.end()); - for (auto * global : uniqueGlobals) - { - if (global) - { - llvm::errs() << "Erasing old global variable: " << global->getName() << "\n"; - global->eraseFromParent(); - } - else - { - llvm::errs() << "Skipping null global variable\n"; - } - } - globalsToErase.clear(); - llvm::errs() << "Exiting eraseOldGlobals\n"; -} - -// Function to actually erase functions after processing -void -eraseOldFunctions() -{ - llvm::errs() << "Entering eraseOldFunctions\n"; - for (auto * func : functionsToErase) - { - llvm::errs() << "Erasing old function: " << func->getName() << "\n"; - func->eraseFromParent(); - } - functionsToErase.clear(); - llvm::errs() << "Exiting eraseOldFunctions\n"; -} - -std::vector instructionsToErase; -void -eraseOldInstructions() -{ - llvm::errs() << "Entering eraseOldInstructions\n"; - for (auto * instr : instructionsToErase) - { - // if (!instr->use_empty()) - // { - // llvm::errs() << "Erasing old instruction: " << *instr << "\n"; - // instr->eraseFromParent(); - // } - // else - // { - // llvm::errs() << "Cannot erase instruction still in use: " << *instr << "\n"; - // } - llvm::errs() << "Erasing old instrunctions " << *instr << "\n"; - instr->eraseFromParent(); - } - instructionsToErase.clear(); - llvm::errs() << "Exiting eraseOldInstructions\n"; -} - -// Function to update global variables from floating-point to integer types -// void updateGlobalVariables(llvm::Module *module, llvm::Type *quantizedType) { -// for (llvm::GlobalVariable &globalVar : module->globals()) { -// if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) { -// llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; -// -// // Create the new integer type pointer -// llvm::Type* newType = quantizedType->getPointerTo(); -// -// // Update the initializer of the global variable -// llvm::Constant *newInitializer = nullptr; -// if (llvm::Constant *init = globalVar.getInitializer()) { -// if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { -// double value = constFp->getValueAPF().convertToDouble(); -// int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); -// newInitializer = llvm::ConstantInt::get(quantizedType, quantizedValue); -// } -// } -// -// // Create a new global variable with the updated type and initializer -// llvm::GlobalVariable *newGlobalVar = new llvm::GlobalVariable( -// *module, -// quantizedType, -// globalVar.isConstant(), -// globalVar.getLinkage(), -// newInitializer, -// globalVar.getName() + "_quantized", -// nullptr, -// globalVar.getThreadLocalMode(), -// globalVar.getType()->getAddressSpace(), -// globalVar.isExternallyInitialized() -// ); -// -// // Replace all uses of the old global variable with the new one -// globalVar.replaceAllUsesWith(newGlobalVar); -// -// // Add the old global variable to the list of globals to erase -// globalsToErase.push_back(&globalVar); -// } -// } -//} - -void -updateGlobalVariables(Module * module, Type * quantizedType) -{ - llvm::errs() << "Updating global variables\n"; - - for (GlobalVariable & globalVar : module->globals()) - { - if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) - { - llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; - - // Create the new integer type pointer - Type * newType = quantizedType->getPointerTo(); - - // Update the initializer of the global variable - if (llvm::Constant * init = globalVar.getInitializer()) - { - if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) - { - double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); - globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); - } - } - - // Check if a quantized version of the global variable already exists - std::string quantizedName = globalVar.getName().str() + "_quantized"; - if (GlobalVariable * existingGlobalVar = module->getNamedGlobal(quantizedName)) - { - // Replace all uses of the old global variable with the existing quantized one - globalVar.replaceAllUsesWith(existingGlobalVar); - } - else - { - // Create a new global variable with the updated type and initializer - GlobalVariable * newGlobalVar = new GlobalVariable( - *module, - quantizedType, - globalVar.isConstant(), - globalVar.getLinkage(), - globalVar.getInitializer(), - quantizedName); - - // Replace all uses of the old global variable with the new one - globalVar.replaceAllUsesWith(newGlobalVar); - } - - // Add the old global variable to the list of globals to erase - globalsToErase.push_back(&globalVar); - } - } -} - -// Quantize constants within an instruction - -void -quantizeConstant(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Entering quantizeConstant\n"; - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) - { - Value * inValue = inInstruction->getOperand(idx); - - // Skip if the operand is not a floating-point constant - if (!isa(inValue)) - { - continue; - } - - auto * constFp = llvm::dyn_cast(inValue); - - // Check the cache for the quantized value - auto cachedValue = quantizedValueCache.find(constFp); - - if (cachedValue != quantizedValueCache.end()) - { - inInstruction->replaceUsesOfWith(inValue, cachedValue->second); - continue; - } - - // Compute the quantized value if not found in cache - Value * newValue = nullptr; - if (inValue->getType()->isFloatTy()) - { - // Convert float constant to fixed-point - float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; - int32_t fixedPointValue = round(constValue); - // newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (float)fixedPointValue / FRAC_BASE); - } - else if (inValue->getType()->isDoubleTy()) - { - // Convert double constant to fixed-point and back to double - double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; - int64_t fixedPointValue = round(constValue); - // newValue = ConstantInt::get(quantizedType, round(constValue), true); - newValue = ConstantFP::get(inValue->getType(), (double)fixedPointValue / FRAC_BASE); - } - else - { - llvm::errs() << "Unknown floating type: " << *inValue->getType() << "\n"; - // assert(false && "unknown floating type"); - continue; - } - - // Cache the quantized value - quantizedValueCache[constFp] = newValue; - - // Replace all uses of the original value with the new quantized value - inInstruction->replaceUsesOfWith(inValue, newValue); - } -} - -// 量化常量: -// void -// quantizeConstant(Instruction * inInstruction, Type * quantizedType) -//{ -// for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) -// { -// Value * inValue = inInstruction->getOperand(idx); -// -// if (!isa(inValue)) -// { -// continue; -// } -// -// ConstantFP * constFp = llvm::dyn_cast(inValue); -// Value * newValue = nullptr; -// -// if (inValue->getType()->isFloatTy()) -// { -// float constValue = constFp->getValueAPF().convertToFloat(); -// constValue *= FRAC_BASE; -// newValue = ConstantInt::get(quantizedType, round(constValue), true); -// } -// else if (inValue->getType()->isDoubleTy()) -// { -// double constValue = constFp->getValueAPF().convertToDouble(); -// constValue *= FRAC_BASE; -// newValue = ConstantInt::get(quantizedType, round(constValue), true); -// } -// else -// { -// assert(false && "unknown floating type"); -// } -// -// inInstruction->replaceUsesOfWith(inValue, newValue); -// } -//} - -// void -// handleFloatIntMul(Instruction * llvmIrInstruction, Type * intType, Type * floatType, Function * floatIntMul) -//{ -// llvm::errs() << "Handling FloatIntMul\n"; -// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; -// IRBuilder<> Builder(llvmIrInstruction); -// -// // 获取操作数 -// Value * lhs = llvmIrInstruction->getOperand(0); -// Value * rhs = llvmIrInstruction->getOperand(1); -// -// llvm::errs() << "LHS: " << *lhs << "\n"; -// llvm::errs() << "RHS: " << *rhs << "\n"; -// -// // 确保左操作数是浮点数,右操作数是整数 -// if (!lhs->getType()->isFloatTy() && !lhs->getType()->isDoubleTy()) -// { -// std::swap(lhs, rhs); -// } -// -// if (lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy()) -// { -// if (rhs->getType()->isIntegerTy()) -// { -// llvm::CallInst * callInst = Builder.CreateCall(floatIntMul, {lhs, rhs}); -// llvmIrInstruction->replaceAllUsesWith(callInst); -// llvmIrInstruction->eraseFromParent(); -// llvm::errs() << "Replaced with call to floatIntMul\n"; -// } -// else -// { -// llvm::errs() << "RHS is not an integer\n"; -// } -// } -// else -// { -// llvm::errs() << "LHS is not a float\n"; -// } -// -// llvm::errs() << "Finished handling FloatIntMul\n"; -// } - -void -simplifyConstant(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling constant operand\n"; - auto checkDecimal = [](float decimalNum) { - int digits = 0; - /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) - { - decimalNum *= 10; - digits++; - } - return decimalNum; - }; - - auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { - /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ - float compensateNum = quantizedNum / decimalNum; - - Value * constOperand, *nonConstOperand; - unsigned constIdx, nonConstIdx; - if (isa(inInstruction->getOperand(0))) - { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); - nonConstOperand = inInstruction->getOperand(1); - } - else - { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); - nonConstOperand = inInstruction->getOperand(0); - } - - auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; - Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - - if (compensateNum == 1) - { - llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; - // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - inInstruction->setOperand(constIdx, quantizeNumValue); - // inInstruction->replaceAllUsesWith(newSecondInst); - // //inInstruction->removeFromParent(); - // instructionsToErase.push_back(inInstruction); - } - else - { - llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; - auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; - Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) - { - llvm::errs() << "Handling FMul instruction\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) - { - llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) - { - llvm::errs() << "Handling FDiv instruction with constant numerator\n"; - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } - - inInstruction->replaceAllUsesWith(newSecondInst); - inInstruction->removeFromParent(); - } - }; - - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) - { - Value * inValue = inInstruction->getOperand(idx); - - if (!isa(inValue)) - { - continue; - } - - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - compensateFP(checkDecimal(constValue), constValue); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - compensateFP(checkDecimal(constValue), constValue); - } - else - { - assert(false && "unknown floating type"); - } - } - llvm::errs() << "Exiting Simplifying Constant\n"; -} - -void -substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) -{ - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - // Value * newInst = nullptr; - - llvm::CallInst * callInst = Builder.CreateCall(func, {inInstruction->getOperand(0), inInstruction->getOperand(1)}); - // InlineFunctionInfo inlineFuncInfo; - // llvm::InlineFunction(*callInst, inlineFuncInfo); +#include "llvm/IR/IRBuilder.h" +#include "newton-irPass-LLVMIR-quantization.h" +#include "llvm/Support/raw_ostream.h" +#include +#include "llvm/IR/Metadata.h" +using namespace llvm; - inInstruction->replaceAllUsesWith(callInst); - inInstruction->removeFromParent(); -} +unsigned int FRAC_Q; +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 +extern "C" { // TODO : float version rsqrt llvm::Function * createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1033,83 +69,7 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector &functionsToInsert) -//{ -// llvm::errs() << "Entering createFixRsqrt\n"; -// -// // Check if irModule is valid -// if (!irModule) -// { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixrsqrtFuncName = "fixrsqrt"; -// for (auto &function : *irModule) -// { -// if (function.getName() == fixrsqrtFuncName) -// { -// llvm::errs() << "fixrsqrt already exists\n"; -// return &function; -// } -// } -// -// // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) -// llvm::FunctionType *funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// llvm::Function *func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixrsqrtFuncName, irModule); -// -// // Create entry basic block and the IR builder -// llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// -// // Get the function argument (x) -// llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value *x = &*args++; -// -// // Find the leading bit position (equivalent of log2(x) computation) -// llvm::Value *m = builder.CreateSub( -// llvm::ConstantInt::get(quantizedType, sizeof(int32_t) * 8 - 1 - FRAC_Q), -// builder.CreateCall(llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::ctlz, {quantizedType}), {x, llvm::ConstantInt::getFalse(irModule->getContext())}), -// "m" -// ); -// -// // Lookup table for initial estimate based on m -// llvm::ArrayType *Y_est_type = llvm::ArrayType::get(quantizedType, 31); -// llvm::Constant *Y_est = llvm::ConstantDataArray::get( -// irModule->getContext(), -// llvm::ArrayRef({0xB50, 0x800, 0x5A8, 0x400, 0x2D4, 0x200, 0x16A, 0x100, 0xB5, 0x80, 0x5A, 0x40, 0x2D, 0x20, 0x16, 0x10, 0xB, 0x8, 0x5, 0x4, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}) -// ); -// llvm::Value *Yi = builder.CreateLoad(Y_est_type, Y_est, builder.CreateAdd(m, llvm::ConstantInt::get(quantizedType, 7))); -// -// // Fixed-point multiplication: xi = x * Yi / FRAC_BASE -// llvm::Value *xi = builder.CreateAShr(builder.CreateMul(x, Yi), llvm::ConstantInt::get(quantizedType, FRAC_Q)); -// llvm::Value *yi = Yi; -// -// // Perform the iterative Goldschmidt's method (3 iterations) -// for (int i = 0; i < 3; ++i) -// { -// llvm::Value *bi = builder.CreateAShr(builder.CreateMul(builder.CreateMul(x, yi), yi), llvm::ConstantInt::get(quantizedType, FRAC_Q * 2)); -// Yi = builder.CreateAShr(builder.CreateSub( -// llvm::ConstantInt::get(quantizedType, 3 * FRAC_BASE), -// bi), -// llvm::ConstantInt::get(quantizedType, 1) -// ); -// -// // Update xi and yi -// xi = builder.CreateAShr(builder.CreateMul(xi, Yi), llvm::ConstantInt::get(quantizedType, FRAC_Q)); -// yi = builder.CreateAShr(builder.CreateMul(yi, Yi), llvm::ConstantInt::get(quantizedType, FRAC_Q)); -// } -// -// // Return the final reciprocal square root result -// builder.CreateRet(builder.CreateSelect(builder.CreateICmpSGT(yi, llvm::ConstantInt::get(quantizedType, 0)), yi, llvm::ConstantInt::get(quantizedType, 1))); -// functionsToInsert.emplace_back(func); -// -// return func; -// } -// Create a fixed-point multiplication function - -//TODO 64 bit version of fixmul +// TODO 64 bit version of fixmul llvm::Function * createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) { @@ -1168,64 +128,6 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -//{ -// llvm::errs() << "Entering createFixMul\n"; -// -// // Check if irModule is valid -// if (!irModule) -// { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixmulFuncName = "fixmul"; -// for (auto & function : *irModule) -// { -// if (function.getName() == fixmulFuncName) -// { -// llvm::errs() << "fixmul already exists\n"; -// return &function; -// } -// } -// -// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); -// -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// builder.SetInsertPoint(entryBB); -// -// // Create fixed-point multiplication instruction -// Type * higherQuantizedType; -// switch (BIT_WIDTH) -// { -// case 8: -// higherQuantizedType = Type::getInt16Ty(irModule->getContext()); -// break; -// case 16: -// higherQuantizedType = Type::getInt32Ty(irModule->getContext()); -// break; -// default: -// higherQuantizedType = Type::getInt64Ty(irModule->getContext()); -// break; -// } -// -// llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); -// llvm::Value * lhs = &*arg1; -// llvm::Function::arg_iterator arg2 = &*(++arg1); -// llvm::Value * rhs = &*arg2; -// llvm::Value * mulResult = builder.CreateMul(lhs, rhs); -// llvm::Value * ashrInst = builder.CreateAShr(mulResult, llvm::ConstantInt::get(arg1->getType(), FRAC_Q)); -// builder.CreateRet(ashrInst); -// -// functionsToInsert.emplace_back(func); -// llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; -// return func; -//} - // TODO Original version of fixrsqrt llvm::Function * createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -1265,7 +167,7 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector(1.5f * FRAC_BASE)), mulfix2); llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); // llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); -// llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); -// llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); -// llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + // llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + // llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + // llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // Return the final fixed-point result builder.CreateRet(final_y); @@ -1305,243 +207,182 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +// A list of global variables to erase after processing +std::vector globalsToErase; +void +eraseOldGlobals() { - llvm::errs() << "Entering createFloatIntMul\n"; - - // Check if irModule is valid - if (!irModule) - { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } - - std::string floatIntMulFuncName = "floatIntMul"; - for (auto & function : *irModule) + std::set uniqueGlobals(globalsToErase.begin(), globalsToErase.end()); + for (auto * global : uniqueGlobals) { - if (function.getName() == floatIntMulFuncName) + if (global) { - llvm::errs() << "floatIntMul already exists\n"; - return &function; + llvm::errs() << "Erasing old global variable: " << global->getName() << "\n"; + global->eraseFromParent(); + } + else + { + llvm::errs() << "Skipping null global variable\n"; } } + globalsToErase.clear(); +} - llvm::FunctionType * funcType = llvm::FunctionType::get(intType, {floatType, intType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, floatIntMulFuncName, irModule); +void +updateGlobalVariables(Module * module, Type * quantizedType) +{ + llvm::errs() << "Updating global variables\n"; - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); + for (GlobalVariable & globalVar : module->globals()) + { + if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) + { + llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; - // Get function arguments + // Ensure the new global is dso_local by specifying correct linkage + GlobalValue::LinkageTypes linkage = GlobalValue::InternalLinkage; - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value * floatArg = &*arg1; - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * intArg = &*arg2; + // Create the new integer type pointer + Type * newType = quantizedType->getPointerTo(); - // Generate float * int multiplication instruction - llvm::Value * intToFloat = builder.CreateSIToFP(intArg, floatType); - llvm::Value * mulInst = builder.CreateFMul(floatArg, intToFloat); - llvm::Value * floatToInt = builder.CreateFPToSI(mulInst, intType); + // Update the initializer of the global variable + if (llvm::Constant * init = globalVar.getInitializer()) + { + if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) + { + double value = constFp->getValueAPF().convertToDouble(); + int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); + globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); + } + } - builder.CreateRet(floatToInt); + // Check if a quantized version of the global variable already exists + std::string quantizedName = globalVar.getName().str() + "_quantized"; + if (GlobalVariable * existingGlobalVar = module->getNamedGlobal(quantizedName)) + { + // Replace all uses of the old global variable with the existing quantized one + globalVar.replaceAllUsesWith(existingGlobalVar); + } + else + { + // Create a new global variable with the updated type and initializer + GlobalVariable * newGlobalVar = new GlobalVariable( + *module, + quantizedType, + globalVar.isConstant(), + globalVar.getLinkage(), + globalVar.getInitializer(), + quantizedName); + newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + newGlobalVar->setDSOLocal(true); - functionsToInsert.emplace_back(func); - llvm::errs() << "Created floatIntMul function: " << func->getName() << "\n"; - return func; + // Replace all uses of the old global variable with the new one + globalVar.replaceAllUsesWith(newGlobalVar); + } + + // Add the old global variable to the list of globals to erase + globalsToErase.push_back(&globalVar); + } + } } -// llvm::Function* createFixPow(Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { -// llvm::errs() << "Entering createFixPow\n"; -// -// if (!irModule) { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixPowFuncName = "fixpow"; -// for (auto& function : *irModule) { -// if (function.getName() == fixPowFuncName) { -// llvm::errs() << "fixpow already exists\n"; -// return &function; -// } -// } -// -// llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); -// llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixPowFuncName, irModule); -// -// llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// builder.SetInsertPoint(entryBB); -// -// llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value* base = &*args++; -// llvm::Value* exponent = &*args; -// -// // Initialize result as FRAC_BASE -// llvm::Value* result = builder.CreateAlloca(quantizedType, nullptr, "result"); -// builder.CreateStore(ConstantInt::get(quantizedType, FRAC_BASE), result); -// -// llvm::BasicBlock* loopBB = llvm::BasicBlock::Create(irModule->getContext(), "loop", func); -// llvm::BasicBlock* afterBB = llvm::BasicBlock::Create(irModule->getContext(), "after", func); -// -// builder.CreateBr(loopBB); -// -// builder.SetInsertPoint(loopBB); -// llvm::PHINode* currentBase = builder.CreatePHI(quantizedType, 2, "currentBase"); -// llvm::PHINode* currentExponent = builder.CreatePHI(quantizedType, 2, "currentExponent"); -// -// currentBase->addIncoming(base, entryBB); -// currentExponent->addIncoming(exponent, entryBB); -// -// // Check if the current exponent is odd -// llvm::Value* isOdd = builder.CreateAnd(currentExponent, ConstantInt::get(quantizedType, 1)); -// llvm::Value* isOddCond = builder.CreateICmpEQ(isOdd, ConstantInt::get(quantizedType, 1)); -// -// // Create block for when exponent is odd -// llvm::BasicBlock* oddBB = llvm::BasicBlock::Create(irModule->getContext(), "odd", func); -// llvm::BasicBlock* continueBB = llvm::BasicBlock::Create(irModule->getContext(), "continue", func); -// -// builder.CreateCondBr(isOddCond, oddBB, continueBB); -// -// builder.SetInsertPoint(oddBB); -// llvm::Value* currentResult = builder.CreateLoad(quantizedType, result); -// llvm::Value* updatedResult = builder.CreateMul(currentResult, currentBase); -// updatedResult = builder.CreateAShr(updatedResult, FRAC_Q); -// builder.CreateStore(updatedResult, result); -// builder.CreateBr(continueBB); -// -// builder.SetInsertPoint(continueBB); -// llvm::Value* squaredBase = builder.CreateMul(currentBase, currentBase); -// squaredBase = builder.CreateAShr(squaredBase, FRAC_Q); -// -// llvm::Value* newExponent = builder.CreateLShr(currentExponent, 1); -// -// currentBase->addIncoming(squaredBase, continueBB); -// currentExponent->addIncoming(newExponent, continueBB); -// -// llvm::Value* endCond = builder.CreateICmpEQ(newExponent, ConstantInt::get(quantizedType, 0)); -// builder.CreateCondBr(endCond, afterBB, loopBB); -// -// builder.SetInsertPoint(afterBB); -// llvm::Value* finalResult = builder.CreateLoad(quantizedType, result); -// builder.CreateRet(finalResult); -// -// functionsToInsert.emplace_back(func); -// llvm::errs() << "Created fixpow function: " << func->getName() << "\n"; -// return func; -// } - - - -// Create a fixed-point division function -llvm::Function * -createFixDiv(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +void +handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) { - llvm::errs() << "Entering createFixDiv\n"; - - // Check if irModule is valid - if (!irModule) + if (auto * loadInst = dyn_cast(llvmIrInstruction)) { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } + IRBuilder<> Builder(loadInst); - std::string fixdivFuncName = "fixdiv"; - for (auto & function : *irModule) - { - if (function.getName() == fixdivFuncName) + // Check if the loaded value is of floating-point type + Type * loadedType = loadInst->getType(); + //Type * pointerType = loadInst->getPointerOperandType(); + + + if (loadedType->isFloatingPointTy()) { - llvm::errs() << "fixdiv already exists\n"; - return &function; + llvm::errs() << "Handling normal load instruction\n"; + // Create a new load instruction with the original pointer operand + LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, loadInst->getPointerOperand(), loadInst->getName() + ".orig"); + + // Replace all uses of the original load instruction with the new converted value + loadInst->replaceAllUsesWith(newLoadInst); + + // Schedule the original load instruction for removal + loadInst->eraseFromParent(); + + llvm::errs() << "Replaced floating-point load with quantized integer load.\n"; } } +} - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixdivFuncName, irModule); +void +handleFAdd(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FAdd\n"; + IRBuilder<> Builder(inInstruction); - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); - // Create fixed-point division instruction - Type * higherQuantizedType; - switch (BIT_WIDTH) + // Check if this instruction has metadata indicating it's quantized + if (inInstruction->getMetadata("quantized")) { - case 8: - higherQuantizedType = Type::getInt16Ty(irModule->getContext()); - break; - case 16: - higherQuantizedType = Type::getInt32Ty(irModule->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(irModule->getContext()); - break; + llvm::errs() << "Skipping quantized instruction\n"; + return; } - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + // Create fixed-point addition + Value * newInst = Builder.CreateNSWAdd(op0, op1); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + // Replace the original FAdd instruction with the new fixed-point addition + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); - // Multiply numerator by FRAC_BASE before division to maintain precision - llvm::Value * mulInst = builder.CreateMul(sext1, ConstantInt::get(higherQuantizedType, FRAC_BASE)); - llvm::Value * divInst = builder.CreateSDiv(mulInst, sext2); - llvm::Value * truncInst = builder.CreateTrunc(divInst, quantizedType); - builder.CreateRet(truncInst); + llvm::errs() << "Finished handling FAdd\n"; +} + +void +handleFSub(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + // Create fixed-point subtraction + Value * newInst = Builder.CreateNSWSub(op0, op1); + + // Replace the original FSub instruction with the new fixed-point subtraction + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FSub\n"; +} + +void +handleFNeg(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FNeg\n"; + IRBuilder<> Builder(inInstruction); + + // Get the operand for the FNeg operation + Value * operand = inInstruction->getOperand(0); + llvm::errs() << "Operand: " << *operand << "\n"; + + // Create a constant zero of the same integer type as the operand + Value * zero = ConstantInt::get(operand->getType(), 0); + + // Perform the integer subtraction: sub 0, operand + Value * newInst = Builder.CreateSub(zero, operand); + + // Replace the original FNeg instruction with the new subtraction + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); - functionsToInsert.emplace_back(func); - llvm::errs() << "Created fixdiv function: " << func->getName() << "\n"; - return func; + llvm::errs() << "Finished handling FNeg with integer subtraction\n"; } -// Create a fixed-point reversed square root function -// llvm::Function* createFixRsqrt(llvm::Module* irModule, Type* quantizedType, std::vector& functionsToInsert) { -// -// // Define the function type for fixrsqrt: int32_t fixrsqrt(int32_t x) -// llvm::FunctionType* funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); -// // Create the function and insert it into the module -// llvm::Function* func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, "fixrsqrt", irModule); -// -// llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// -// // Get the function argument (x) -// llvm::Function::arg_iterator args = func->arg_begin(); -// llvm::Value* x = &*args++; -// -// // Create the fixed-point multiplication function -// llvm::Function* fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); -// -// // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); -// llvm::Value* halfBase = builder.CreateCall(fixMulFunc, {ConstantInt::get(quantizedType, FRAC_BASE / 2), x}); -// -// // Step 2: Convert x to floating-point and perform the initial approximation -// llvm::Value* fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); -// llvm::Value* i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); -// i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); -// fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); -// -// // Step 3: int_y = fp_y * FRAC_BASE; -// llvm::Value* int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); -// -// // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); -// llvm::Value* mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); -// llvm::Value* mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); -// llvm::Value* correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); -// llvm::Value* final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); -// -// -// // Return the final fixed-point result -// builder.CreateRet(final_y); -// functionsToInsert.emplace_back(func); -// -// return func; -//} - // Quantize simple floating-point instructions CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) @@ -1574,416 +415,170 @@ quantizePredict(CmpInst::Predicate predict) } void -dequantizeArgumentsAndQuantizeReturn(CallInst * llvmIrCallInstruction, Type * quantizedType) +handleFCmp(Instruction * inInstruction, Type * quantizedType) { - IRBuilder<> Builder(llvmIrCallInstruction); - llvm::Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - - if (!calledFunction) - { - llvm::errs() << "Error: Called function is null.\n"; - return; - } - - llvm::errs() << "De-quantizing arguments for library function call\n"; - - // Create a vector to store de-quantized arguments - std::vector dequantizedArgs; + IRBuilder<> Builder(inInstruction); - // Iterate through the call instruction's operands (arguments) - for (unsigned i = 0; i < llvmIrCallInstruction->getNumArgOperands(); ++i) + if (auto fcmp_inst = dyn_cast(inInstruction)) { - Value * arg = llvmIrCallInstruction->getArgOperand(i); - - if (arg->getType()->isIntegerTy()) + CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); + if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) { - // Convert integer arguments back to floating-point - Value * dequantizedArg = Builder.CreateSIToFP(arg, llvmIrCallInstruction->getFunctionType()->getParamType(i)); - dequantizedArgs.push_back(dequantizedArg); + Value * newInst = ConstantInt::getTrue(quantizedType); + inInstruction->replaceAllUsesWith(newInst); } - else + else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) { - // If not an integer, keep the original argument - dequantizedArgs.push_back(arg); + Value * newInst = ConstantInt::getFalse(quantizedType); + inInstruction->replaceAllUsesWith(newInst); } - } - - // Create a new call instruction with de-quantized arguments - CallInst * newCall = CallInst::Create(calledFunction, dequantizedArgs, "", llvmIrCallInstruction); - newCall->setCallingConv(llvmIrCallInstruction->getCallingConv()); - newCall->setDebugLoc(llvmIrCallInstruction->getDebugLoc()); - - llvm::errs() << "Quantizing return value of the library function call\n"; - // Quantize the return value of the call instruction - if (newCall->getType()->isFloatingPointTy()) - { - Value * quantizedReturnValue = Builder.CreateFPToSI(newCall, quantizedType); - llvmIrCallInstruction->replaceAllUsesWith(quantizedReturnValue); - } - else - { - llvmIrCallInstruction->replaceAllUsesWith(newCall); - } + else + { + Value * fpOp0 = fcmp_inst->getOperand(0); + Value * fpOp1 = fcmp_inst->getOperand(1); - // Remove the old call instruction - llvmIrCallInstruction->eraseFromParent(); -} + Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); + Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); -float -checkDecimal(float decimalNum) -{ - int digits = 0; - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) - { - decimalNum *= 10; // Scale decimal to avoid precision issues - digits++; + Value * newInst = Builder.CreateICmp(pred, intOp0, intOp1); + inInstruction->replaceAllUsesWith(newInst); + } + inInstruction->eraseFromParent(); } - return decimalNum; } void -compensateFP(Instruction * inInstruction, float quantizedNum, float decimalNum, Type * quantizedType) +simplifyConstant(Instruction * inInstruction, Type * quantizedType) { - float compensateNum = quantizedNum / decimalNum; - Value * constOperand = nullptr; - Value * nonConstOperand = nullptr; - unsigned constIdx = 0; - unsigned nonConstIdx = 0; + llvm::errs() << "Handling constant operand\n"; + auto checkDecimal = [](float decimalNum) { + int digits = 0; + /* + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) + { + decimalNum *= 10; + digits++; + } + return decimalNum; + }; - if (isa(inInstruction->getOperand(0))) - { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); - nonConstOperand = inInstruction->getOperand(1); - } - else - { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); - nonConstOperand = inInstruction->getOperand(0); - } + auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { + /* + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ + float compensateNum = quantizedNum / decimalNum; - // uto quantizeNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); + Value * constOperand, *nonConstOperand; + unsigned constIdx, nonConstIdx; + if (isa(inInstruction->getOperand(0))) + { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); + nonConstOperand = inInstruction->getOperand(1); + } + else + { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); + nonConstOperand = inInstruction->getOperand(0); + } - llvm::errs() << "Compensate Num: " << compensateNum << "\n"; - llvm::errs() << "Quantized Num Value: " << quantizedNum << "\n"; - llvm::errs() << "Instruction Opcode: " << inInstruction->getOpcodeName() << "\n"; + auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - if (compensateNum == 1) - { - llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; - inInstruction->setOperand(constIdx, quantizeNumValue); - } - else - { - llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; - auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); IRBuilder<> Builder(inInstruction); Instruction * insertPoint = inInstruction->getNextNode(); Builder.SetInsertPoint(insertPoint); - - Value * newFirstInst = nullptr; + Value * newFisrtInst = nullptr; Value * newSecondInst = nullptr; auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) - { - llvm::errs() << "Handling FMul instruction\n"; - newFirstInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) - { - llvm::errs() << "Handling FDiv instruction with constant denominator\n"; - newFirstInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newSecondInst = Builder.CreateSDiv(newFirstInst, quantizeNumValue); - } - else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) - { - llvm::errs() << "Handling FDiv instruction with constant numerator\n"; - newFirstInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); - newSecondInst = Builder.CreateSDiv(newFirstInst, compensateNumValue); - } - - if (newSecondInst) + if (compensateNum == 1) { - llvm::errs() << "Replacing old instruction with compensated instruction\n"; - inInstruction->replaceAllUsesWith(newSecondInst); - inInstruction->eraseFromParent(); - // instructionsToErase.push_back(inInstruction); + llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; + // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + inInstruction->setOperand(constIdx, quantizeNumValue); + // inInstruction->replaceAllUsesWith(newSecondInst); + // //inInstruction->removeFromParent(); + // instructionsToErase.push_back(inInstruction); } else { - llvm::errs() << "Failed to create new compensated instruction\n"; - } - } -} - -void -handleConstant(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling constant operand\n"; - - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) - { - Value * inValue = inInstruction->getOperand(idx); - if (!isa(inValue)) - { - continue; - } - - ConstantFP * constFp = dyn_cast(inValue); + llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; + auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - llvm::errs() << "Original float constant: " << constValue << "\n"; - float decimalValue = checkDecimal(constValue); - llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; - if (decimalValue == constValue) - // if (fabs(decimalValue - constValue) < 0.001) - { - // If the decimal part is already an integer, quantize directly - // auto quantizedValue = static_cast(round(constValue * FRAC_BASE)); - // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); - // llvm::errs() << "Quantized float value: " << quantizedValue << "\n"; - // inInstruction->setOperand(idx, quantizedConst); - int64_t integerPart = static_cast(round(constValue)); - auto quantizedConst = ConstantInt::get(quantizedType, integerPart); - llvm::errs() << "Converted float value to integer: " << integerPart << "\n"; - inInstruction->setOperand(idx, quantizedConst); - } - else + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newFisrtInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) { - compensateFP(inInstruction, decimalValue, constValue, quantizedType); + llvm::errs() << "Handling FMul instruction\n"; + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - llvm::errs() << "Original double constant: " << constValue << "\n"; - double decimalValue = checkDecimal(constValue); - llvm::errs() << "Decimal value after checkDecimal: " << decimalValue << "\n"; - // if (decimalValue == constValue) - if (fabs(decimalValue - constValue) < 0.001) + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { - // If the decimal part is already an integer, quantize directly - // int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - // auto quantizedConst = ConstantInt::get(quantizedType, quantizedValue); - // llvm::errs() << "Quantized double value: " << quantizedValue << "\n"; - // inInstruction->setOperand(idx, quantizedConst); - - // If the decimal part is already an integer, just convert it directly - int64_t integerPart = static_cast(round(constValue)); - auto quantizedConst = ConstantInt::get(quantizedType, integerPart); - llvm::errs() << "Converted double value to integer: " << integerPart << "\n"; - inInstruction->setOperand(idx, quantizedConst); + llvm::errs() << "Handling FDiv instruction with constant denominator\n"; + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); } - else + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { - compensateFP(inInstruction, decimalValue, constValue, quantizedType); + llvm::errs() << "Handling FDiv instruction with constant numerator\n"; + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); } - } - else - { - assert(false && "unknown floating type"); - } - } - - llvm::errs() << "Exiting handleConstant\n"; -} -void -handleFAdd(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FAdd\n"; - IRBuilder<> Builder(inInstruction); - - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - - // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op0)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { - op0 = Builder.CreateFPToSI(op0, quantizedType); - } - } - - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op1)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { - op1 = Builder.CreateFPToSI(op1, quantizedType); - } - } - - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op1)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { - op1 = Builder.CreateFPToSI(op1, quantizedType); - } - } - - // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE - if (ConstantFP * constFp = dyn_cast(op0)) - { - // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } - - if (ConstantFP * constFp = dyn_cast(op1)) - { - // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - - // Create fixed-point addition - Value * newInst = Builder.CreateNSWAdd(op0, op1); - - // Replace the original FAdd instruction with the new fixed-point addition - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - - llvm::errs() << "Finished handling FAdd\n"; -} - -void -handleFSub(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FSub\n"; - IRBuilder<> Builder(inInstruction); - - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - - // Ensure both operands are integers - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op0)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { - op0 = Builder.CreateFPToSI(op0, quantizedType); - } - } - - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - if (ConstantFP * constFp = dyn_cast(op1)) - { - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - else - { - op1 = Builder.CreateFPToSI(op1, quantizedType); - } - } - - // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE - if (ConstantFP * constFp = dyn_cast(op0)) - { - // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op0 = ConstantInt::get(quantizedType, quantizedValue); - } - - if (ConstantFP * constFp = dyn_cast(op1)) - { - // Multiply the constant by FRAC_BASE and convert to integer - float constValue = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - op1 = ConstantInt::get(quantizedType, quantizedValue); - } - - // Create fixed-point subtraction - Value * newInst = Builder.CreateNSWSub(op0, op1); - - // Replace the original FSub instruction with the new fixed-point subtraction - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FSub\n"; -} + inInstruction->replaceAllUsesWith(newSecondInst); + inInstruction->removeFromParent(); + } + }; -void -handleMatrixOperations(Instruction * instr, Type * quantizedType) -{ - if (instr->getOpcode() == Instruction::FAdd || instr->getOpcode() == Instruction::FMul) + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { - llvm::errs() << "Handling matrix operation: " << *instr << "\n"; - IRBuilder<> Builder(instr); - - Value * lhs = instr->getOperand(0); - Value * rhs = instr->getOperand(1); + Value * inValue = inInstruction->getOperand(idx); - if (lhs->getType()->isPointerTy() && rhs->getType()->isPointerTy()) + if (!isa(inValue)) { - auto * lhsElemType = lhs->getType()->getPointerElementType(); - auto * rhsElemType = rhs->getType()->getPointerElementType(); - - if ((lhsElemType->isArrayTy() && lhsElemType->getArrayElementType()->isFloatTy()) || - (rhsElemType->isArrayTy() && rhsElemType->getArrayElementType()->isFloatTy())) - { - llvm::errs() << "Quantizing matrix elements\n"; - - // 替换矩阵元素操作为整数操作 - for (unsigned i = 0; i < lhsElemType->getArrayNumElements(); ++i) - { - Value * lhsElem = Builder.CreateLoad(lhsElemType->getArrayElementType(), Builder.CreateGEP(lhsElemType, lhs, {Builder.getInt32(0), Builder.getInt32(i)})); - Value * rhsElem = Builder.CreateLoad(rhsElemType->getArrayElementType(), Builder.CreateGEP(rhsElemType, rhs, {Builder.getInt32(0), Builder.getInt32(i)})); - - if (instr->getOpcode() == Instruction::FAdd) - { - Value * resultElem = Builder.CreateAdd(lhsElem, rhsElem); - Builder.CreateStore(resultElem, Builder.CreateGEP(quantizedType->getArrayElementType(), lhs, {Builder.getInt32(0), Builder.getInt32(i)})); - } - else if (instr->getOpcode() == Instruction::FMul) - { - Value * resultElem = Builder.CreateMul(lhsElem, rhsElem); - Builder.CreateStore(resultElem, Builder.CreateGEP(quantizedType->getArrayElementType(), lhs, {Builder.getInt32(0), Builder.getInt32(i)})); - } - } + continue; + } - instr->eraseFromParent(); - } + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; + if (inValue->getType()->isFloatTy()) + { + float constValue = constFp->getValueAPF().convertToFloat(); + compensateFP(checkDecimal(constValue), constValue); + } + else if (inValue->getType()->isDoubleTy()) + { + double constValue = constFp->getValueAPF().convertToDouble(); + compensateFP(checkDecimal(constValue), constValue); + } + else + { + assert(false && "unknown floating type"); } } + llvm::errs() << "Exiting Simplifying Constant\n"; +} } - void simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) { @@ -2005,56 +600,6 @@ simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, instruction->eraseFromParent(); } -bool -checkAndSimplifyFMul(ConstantFP * constFP, Value * otherOperand, Instruction * instruction) -{ - APFloat targetValue(0.28f); // 初始化时使用单精度浮点数 - bool losesInfo; - APFloat::opStatus status = targetValue.convert(APFloat::IEEEdouble(), APFloat::rmNearestTiesToEven, &losesInfo); // 转换为双精度浮点格式 - - // 检查转换是否成功 - if (status != APFloat::opOK) - { - llvm::errs() << "Error: Precision loss or other conversion error occurred.\n"; - } - - // 比较常数值 - if (constFP->getValueAPF().compare(targetValue) == APFloat::cmpEqual) - { - llvm::IRBuilder<> Builder(instruction); - // 创建除数常数 (28.0) - Value * divisor = ConstantFP::get(instruction->getType(), 28.0); - // 创建除法指令 - Instruction * divInst = cast(Builder.CreateFDiv(otherOperand, divisor, "divby28")); - - // 替换原始指令 - instruction->replaceAllUsesWith(divInst); - instruction->eraseFromParent(); - return true; - } - return false; -} - -Value * -convertToQuantizedType(Value * value, Type * quantizedType, IRBuilder<> & Builder) -{ - if (value->getType()->isFloatTy() || value->getType()->isDoubleTy()) - { - // 如果是浮点类型,首先乘以 FRAC_BASE,然后转换为量化后的整数类型 - Value * scaledValue = Builder.CreateFMul(value, ConstantFP::get(value->getType(), FRAC_BASE)); - return Builder.CreateFPToSI(scaledValue, quantizedType); - } - else if (value->getType()->isIntegerTy()) - { - // 如果已经是整数类型,则直接返回 - return value; - } - else - { - llvm::errs() << "Unsupported type for quantization: " << *value->getType() << "\n"; - return nullptr; - } -} bool checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruction * instruction) { @@ -2063,7 +608,7 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct {2.0, {1, false}}, {4.0, {2, false}}, {8.0, {3, false}}, - {1/128.0, {7, true}}}; + {1 / 128.0, {7, true}}}; double value = constFP->getValueAPF().convertToDouble(); auto it = constantsMap.find(value); if (it != constantsMap.end()) @@ -2074,37 +619,22 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return true; } return false; - - // New check for the special multiplication case - return checkAndSimplifyFMul(constFP, otherOperand, instruction); } -// 64bit fixed point multiplication -llvm::Value * -performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) -{ - // Sign extend the 32-bit operands to 64-bit integers - llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); - - // Perform 64-bit multiplication - llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); - - // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); - - // Truncate the 64-bit result back to 32-bit integer - llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); - - return result32; -} void -handleFMul(Instruction * llvmIrInstruction, Type * quantizedType,Function * fixmul) +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) { llvm::errs() << "Handling FMul\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; IRBuilder<> Builder(llvmIrInstruction); + // Check if this instruction has metadata indicating it's quantized + if (llvmIrInstruction->getMetadata("quantized")) + { + llvm::errs() << "Skipping already quantized instruction.\n"; + return; // Skip processing this instruction + } + // Ensure operands are correctly converted to fixed-point integers Value * lhs = llvmIrInstruction->getOperand(0); Value * rhs = llvmIrInstruction->getOperand(1); @@ -2130,982 +660,262 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType,Function * fixm return; } - // If either operand is a float constant, convert it to fixed-point using handleConstant - if (isa(lhs)) - { - llvm::errs() << "LHS is a floating-point constant, handling it with handleConstant\n"; - // handleConstant(llvmIrInstruction, quantizedType); - simplifyConstant(llvmIrInstruction, quantizedType); - lhs = llvmIrInstruction->getOperand(0); - lhsIsFloat = false; // Update float status as it's now a fixed-point - } - - if (isa(rhs)) - { - llvm::errs() << "RHS is a floating-point constant, handling it with handleConstant\n"; - // handleConstant(llvmIrInstruction, quantizedType); - simplifyConstant(llvmIrInstruction, quantizedType); - rhsIsFloat = false; - rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion - } - - // If either operand is an integer constant, directly use mul - if (isa(lhs) || isa(rhs)) - { - llvm::errs() << "One of the operands is an integer constant, using mul\n"; - // Value * newInst = Builder.CreateMul(lhs, rhs); - Value * newInst = Builder.CreateNSWMul(lhs, rhs); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - return; - } - - // If either operand is a float, convert both to fixed-point - if (lhsIsFloat) - { - lhs = Builder.CreateFPToSI(lhs, quantizedType); - llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; - } - - if (rhsIsFloat) - { - rhs = Builder.CreateFPToSI(rhs, quantizedType); - llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; - } - - // If both operands are integers, but neither is a constant, use fixmul - bool lhsIsInteger = lhs->getType()->isIntegerTy(); - bool rhsIsInteger = rhs->getType()->isIntegerTy(); - - // if (lhsIsInteger && rhsIsInteger) - // { - // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - // //callInst->setCallingConv(llvm::CallingConv::Fast); - // //callInst->setTailCall(true); - // llvmIrInstruction->replaceAllUsesWith(callInst); - // llvmIrInstruction->eraseFromParent(); - // } - - if (lhsIsInteger && rhsIsInteger) - { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - } - - // 缩两边 - // { - // llvm::errs() << "Both operands are integers, applying scaling to prevent overflow...\n"; - // - // // Scale operands down by FRAC_Q before multiplication - // llvm::Value* lhsScaled = Builder.CreateAShr(lhs, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "lhsScaled"); - // llvm::Value* rhsScaled = Builder.CreateAShr(rhs, llvm::ConstantInt::get(rhs->getType(), FRAC_Q / 2), "rhsScaled"); - // - // // Perform 32-bit multiplication - // llvm::Value* mulResult = Builder.CreateMul(lhsScaled, rhsScaled, "mulScaled"); - // - //// // Right shift to maintain precision, compensating for the scaling - //// llvm::Value* finalResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q / 2), "shiftResult"); - // - // // Replace all uses of the original instruction with the final result - // llvmIrInstruction->replaceAllUsesWith(mulResult); - // llvmIrInstruction->eraseFromParent(); - - // } - - // 64位 -// { -// llvm::errs() << "Both operands are integers, performing 64-bit multiplication and shifting...\n"; -// -// // // Sign extend the 32-bit operands to 64-bit integers to prevent overflow -// // llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); -// // llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); -// // -// // // Perform 64-bit multiplication -// // llvm::Value * mulResult64 = Builder.CreateMul(lhs64, rhs64); -// // -// // // Divide the result by FRAC_Q (equivalent to right shift) -// // llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q), "div64"); -// // -// // // Truncate the 64-bit result back to 32-bit integer -// // llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); -// -// llvm::Value * result32 = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); -// -// // Replace all uses of the original instruction with the final result -// llvmIrInstruction->replaceAllUsesWith(result32); -// llvmIrInstruction->eraseFromParent(); -// } - - // 正常的32位 - // { - // llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; - // - // // Perform multiplication directly - // llvm::Value * mulResult = Builder.CreateMul(lhs, rhs,""); - // - // // Perform right arithmetic shift - // llvm::Value * shiftResult = Builder.CreateAShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); - // - // // Replace all uses of the original instruction with the result of the shift - // llvmIrInstruction->replaceAllUsesWith(shiftResult); - // llvmIrInstruction->eraseFromParent(); - // } - // { - // // 先移再乘 - // llvm::errs() << "Both operands are integers, performing inline shifting and multiplication...\n"; - // - // // Perform right arithmetic shift on lhs first - // llvm::Value * shiftedLhs = Builder.CreateAShr(lhs, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); - // - // // Perform multiplication with shifted lhs - // llvm::Value * mulResult = Builder.CreateMul(shiftedLhs, rhs); - // - // // Replace all uses of the original instruction with the result of the multiplication - // llvmIrInstruction->replaceAllUsesWith(mulResult); - // llvmIrInstruction->eraseFromParent(); - // } -} - -// void -// handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) -//{ -// llvm::errs() << "Handling FMul\n"; -// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; -// IRBuilder<> Builder(llvmIrInstruction); -// -// // Ensure operands are correctly converted to fixed-point integers -// Value * lhs = llvmIrInstruction->getOperand(0); -// Value * rhs = llvmIrInstruction->getOperand(1); -// -// llvm::errs() << "LHS: " << *lhs << "\n"; -// llvm::errs() << "RHS: " << *rhs << "\n"; -// -// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); -// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); -// -// // If either operand is a float, convert both to fixed-point -// if (lhsIsFloat) -// { -// lhs = Builder.CreateFPToSI(lhs, quantizedType); -// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; -// } -// if (rhsIsFloat) -// { -// rhs = Builder.CreateFPToSI(rhs, quantizedType); -// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; -// } -// -// // Ensure both operands are now integers -// bool lhsIsInteger = lhs->getType()->isIntegerTy(); -// bool rhsIsInteger = rhs->getType()->isIntegerTy(); -// -// if (lhsIsInteger && rhsIsInteger) -// { -// if (isa(lhs) || isa(rhs)) -// { -// llvm::errs() << "One of the operands is a constant, simplifying...\n"; -// handleConstant(llvmIrInstruction, quantizedType); -// } -// else -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); -// llvmIrInstruction->replaceAllUsesWith(callInst); -// llvmIrInstruction->eraseFromParent(); -// } -// } -// } -// } - -void -handleFDiv(Instruction * llvmIrInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FDiv \n"; - llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; - IRBuilder<> Builder(llvmIrInstruction); - - // Ensure operands are correctly converted to fixed-point integers - Value * lhs = llvmIrInstruction->getOperand(0); - Value * rhs = llvmIrInstruction->getOperand(1); - - llvm::errs() << "LHS: " << *lhs << "\n"; - llvm::errs() << "RHS: " << *rhs << "\n"; - - bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); - bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - - // If either operand is a float, convert both to fixed-point - if (lhsIsFloat) - { - lhs = Builder.CreateFPToSI(lhs, quantizedType); - llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; - } - if (rhsIsFloat) - { - rhs = Builder.CreateFPToSI(rhs, quantizedType); - llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; - } - - // Ensure both operands are now integers - bool lhsIsInteger = lhs->getType()->isIntegerTy(); - bool rhsIsInteger = rhs->getType()->isIntegerTy(); - - if (lhsIsInteger && rhsIsInteger) - { - if (isa(lhs) || isa(rhs)) - { - llvm::errs() << "One of the operands is a constant, simplifying...\n"; - handleConstant(llvmIrInstruction, quantizedType); - } - else - { - llvm::errs() << "Both operands are integers, substituting with fixdiv function...\n"; - // { - // Value * newInst = Builder.CreateCall(fixdiv, {lhs, rhs}); - // llvmIrInstruction->replaceAllUsesWith(newInst); - // llvmIrInstruction->eraseFromParent(); - // llvm::errs() << "Finished handling FDiv\n"; - // } - { - IRBuilder<> Builder(llvmIrInstruction); - // Extend both operands to i64 - Value * lhsExt = Builder.CreateSExt(lhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "lhs.ext"); - Value * rhsExt = Builder.CreateSExt(rhs, Type::getInt64Ty(llvmIrInstruction->getContext()), "rhs.ext"); - // Multiply the lhs extended value by FRAC_BASE for scaling - Value * scaledNumerator = Builder.CreateMul(lhsExt, ConstantInt::get(Type::getInt64Ty(llvmIrInstruction->getContext()), FRAC_BASE), "scaled.numerator"); - // Perform the division - Value * divResult = Builder.CreateSDiv(scaledNumerator, rhsExt, "div.result"); - // Truncate the result back to i32 - Value * truncatedResult = Builder.CreateTrunc(divResult, Type::getInt32Ty(llvmIrInstruction->getContext()), "result.trunc"); - // Replace all uses of the original instruction with this new sequence - llvmIrInstruction->replaceAllUsesWith(truncatedResult); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FDiv\n"; - } - } - } -} - -void -handleFRem(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FRem\n"; - IRBuilder<> Builder(inInstruction); - Value * op0 = inInstruction->getOperand(0); - Value * op1 = inInstruction->getOperand(1); - - if (op0->getType()->isFloatTy() || op0->getType()->isDoubleTy()) - { - op0 = Builder.CreateFPToSI(op0, quantizedType); - } - if (op1->getType()->isFloatTy() || op1->getType()->isDoubleTy()) - { - op1 = Builder.CreateFPToSI(op1, quantizedType); - } - Value * newInst = Builder.CreateSRem(op0, op1); - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FRem\n"; -} - -// void -// handleFNeg(Instruction * inInstruction, Type * quantizedType) -//{ -// llvm::errs() << "Handling FNeg\n"; -// IRBuilder<> Builder(inInstruction); -// -// // Get the operand for the FNeg operation -// Value * operand = inInstruction->getOperand(0); -// llvm::errs() << "Operand: " << *operand << "\n"; -// -// // Process the operand and quantize constants -// //quantizeConstant(inInstruction, quantizedType); -// -// // If the operand is a floating-point type, quantize it -//// if (operand->getType()->isFloatingPointTy()) -//// { -//// llvm::errs() << "FNEG Quantizing floating-point operand\n"; -//// operand = Builder.CreateFMul(operand, ConstantFP::get(operand->getType(), -1.0)); -//// operand = Builder.CreateFPToSI(operand, quantizedType); -//// } -// -// // Perform the negation in fixed-point arithmetic -// // Fixed-point negation is equivalent to integer negation -// Value * newInst = Builder.CreateNeg(operand); -// //Value * newInst = Builder.CreateNSWNeg(operand); -// -// // Replace the original FNeg instruction with the new fixed-point negation -// inInstruction->replaceAllUsesWith(newInst); -// inInstruction->eraseFromParent(); -// -// llvm::errs() << "Finished handling FNeg\n"; -//} - -void -handleFNeg(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FNeg\n"; - IRBuilder<> Builder(inInstruction); - - // Get the operand for the FNeg operation - Value * operand = inInstruction->getOperand(0); - llvm::errs() << "Operand: " << *operand << "\n"; - - // Create a constant zero of the same integer type as the operand - Value * zero = ConstantInt::get(operand->getType(), 0); - - // Perform the integer subtraction: sub 0, operand - Value * newInst = Builder.CreateSub(zero, operand); - - // Replace the original FNeg instruction with the new subtraction - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - - llvm::errs() << "Finished handling FNeg with integer subtraction\n"; -} - -void -handleFCmp(Instruction * inInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FCmp\n"; - IRBuilder<> Builder(inInstruction); - - if (auto fcmp_inst = dyn_cast(inInstruction)) - { - CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); - if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) - { - Value * newInst = ConstantInt::getTrue(quantizedType); - inInstruction->replaceAllUsesWith(newInst); - } - else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) - { - Value * newInst = ConstantInt::getFalse(quantizedType); - inInstruction->replaceAllUsesWith(newInst); - } - else - { - Value * fpOp0 = fcmp_inst->getOperand(0); - Value * fpOp1 = fcmp_inst->getOperand(1); - - Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); - Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); - - Value * newInst = Builder.CreateICmp(pred, intOp0, intOp1); - inInstruction->replaceAllUsesWith(newInst); - } - inInstruction->eraseFromParent(); - llvm::errs() << "Finished handling FCmp\n"; - } -} - -void -handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) -{ - if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) + // If either operand is a float constant, convert it to fixed-point using handleConstant + if (isa(lhs)) { - auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - - // 递归转换所有层级的类型为量化类型 - Type * newAllocaType = transformToQuantizedType(allocaType, quantizedType); - - llvm::errs() << "Original alloca type: " << *allocaType << "\n"; - llvm::errs() << "New quantized alloca type: " << *newAllocaType << "\n"; - - // Set the new quantized type for the alloca instruction - llvmIrAllocaInstruction->setAllocatedType(newAllocaType); + llvm::errs() << "LHS is a floating-point constant, handling it with handleConstant\n"; + // handleConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); + lhs = llvmIrInstruction->getOperand(0); + lhsIsFloat = false; // Update float status as it's now a fixed-point } -} -// void handleAlloca(Instruction * llvmIrInstruction, Type * quantizedType) { -// if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) { -// // 获取原始的分配类型 -// Type * allocaType = llvmIrAllocaInstruction->getAllocatedType(); -// -// // 输出原始类型和新量化类型的调试信息 -// llvm::errs() << "Original alloca type: " << *allocaType << "\n"; -// llvm::errs() << "New quantized alloca type: " << *quantizedType << "\n"; -// -// Type * newAllocaType; -// -// // 递归转换所有层级的类型为量化类型 -// if (allocaType->isFloatTy()) { -// // 如果分配类型是浮点类型,转换为量化的整数类型 -// newAllocaType = Type::getInt32Ty(llvmIrInstruction->getContext()); -// } else if (allocaType->isPointerTy()) { -// // 如果分配类型是指针类型,保持其原有分配 -// newAllocaType = allocaType; -// } else { -// // 其他类型,保持不变 -// newAllocaType = allocaType; -// } -// -// // 设置新的量化类型 -// llvmIrAllocaInstruction->setAllocatedType(newAllocaType); -// } -// } + if (isa(rhs)) + { + llvm::errs() << "RHS is a floating-point constant, handling it with handleConstant\n"; + // handleConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); + rhsIsFloat = false; + rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion + } -void -// handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) -{ - llvm::errs() << "Handling Call\n"; - Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + // If either operand is an integer constant, directly use mul + if (isa(lhs) || isa(rhs)) + { + llvm::errs() << "One of the operands is an integer constant, using mul\n"; + // Value * newInst = Builder.CreateMul(lhs, rhs); + Value * newInst = Builder.CreateNSWMul(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); return; + } - std::string funcName = calledFunction->getName().str(); - - if (funcName == "invSqrt" || funcName == "invSqrt_quantized") + // If either operand is a float, convert both to fixed-point + if (lhsIsFloat) { - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - // 特定处理 invsqrt 调用 - llvm::errs() << "Handling invsqrt call\n"; - handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); - // } + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; } - if (!calledFunction->getName().startswith("llvm.dbg.value") && - !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) + if (rhsIsFloat) { - if (calledFunction->isDeclaration()) - { - // For library functions - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - - // std::string funcName = calledFunction->getName().str(); - - if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64" || funcName == "llvm.sqrt.f32") - { - // For sqrt - handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); - } - - else if (funcName == "sin") - { - // For sin - handleSinCall(llvmIrCallInstruction, quantizedType); - } - - else if (funcName == "llvm.memcpy.p0i8.p0i8.i64" || funcName == "llvm.memcpy.p0i8.p0i8.i32") - { - // For llvm.memcpy - handleMemCpyCall(llvmIrCallInstruction, quantizedType); - } - - else - { - /* - * for other lib functions, de-quantize the arguments and quantize the return value - */ - dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); - } - } - else - { - // For user-defined functions, only handle the arguments - // for (size_t idx = 0; idx < llvmIrCallInstruction->getNumArgOperands(); ++idx) - // { - // Value * arg = llvmIrCallInstruction->getArgOperand(idx); - // if (arg->getType()->isPointerTy()) - // { - // Type * elementType = arg->getType()->getPointerElementType(); - // if (elementType->isFloatTy() || elementType->isDoubleTy()) - // { - // // Do not change pointer type, handle it when dereferenced - // continue; - // } - // } - // // Set quantized type for the argument if it is not a pointer - // setQuantizedType(arg, quantizedType); - // } - - /* - * for user-defined function, quantize the arguments - * */ - // for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - // { - // setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - // } - // quantizeConstant(llvmIrCallInstruction, quantizedType); - - // Quantize the return type if necessary - if (llvmIrCallInstruction->getType()->isPointerTy()) - { - Type * returnElementType = llvmIrCallInstruction->getType()->getPointerElementType(); - if (returnElementType->isFloatTy() || returnElementType->isDoubleTy()) - { - // Keep the pointer type, but track the need for de-quantization - return; - } - } - setQuantizedType(llvmIrCallInstruction, quantizedType); - } + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; } -} - -// void -// handleStore(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) -// { -// IRBuilder<> Builder(llvmIrStoreInstruction); -// -// auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); -// if (valueType->isFloatTy() || valueType->isDoubleTy()) -// { -// llvm::errs() << "Original store value type: " << *valueType << "\n"; -// llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; -// -// // Quantize the value operand -// auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); -// quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); -// llvmIrStoreInstruction->setOperand(0, quantizedValue); -// } -// -// auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); -// if (pointerType->isFloatTy() || pointerType->isDoubleTy()) -// { -// llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; -// llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; -// -// // Set the new quantized type for the pointer operand -// llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); -// } -// } -// } -void -handleStore(Instruction * llvmIrInstruction, Type * quantizedType) -{ - /* - * If either of the operands is constant, change it to a int value - * */ - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); -} + // If both operands are integers, but neither is a constant, use fixmul + bool lhsIsInteger = lhs->getType()->isIntegerTy(); + bool rhsIsInteger = rhs->getType()->isIntegerTy(); -// Function to handle loads involving pointer types -void -handlePointerLoad(LoadInst * llvmIrLoadInstruction, IRBuilder<> & Builder, Type * quantizedType) -{ - auto pointerOperand = llvmIrLoadInstruction->getPointerOperand(); - auto pointerType = pointerOperand->getType()->getPointerElementType(); + // if (lhsIsInteger && rhsIsInteger) + // { + // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + // //callInst->setCallingConv(llvm::CallingConv::Fast); + // //callInst->setTailCall(true); + // llvmIrInstruction->replaceAllUsesWith(callInst); + // llvmIrInstruction->eraseFromParent(); + // } - if (pointerType->isPointerTy()) + if (lhsIsInteger && rhsIsInteger) { - auto pointedType = pointerType->getPointerElementType(); - - if (pointedType->isPointerTy()) - { - auto innerPointedType = pointedType->getPointerElementType(); - if (innerPointedType->isFloatTy() || innerPointedType->isDoubleTy()) - { - // Update pointer type for double** or float** to quantizedType** (e.g., int32_t**) - auto quantizedInnerPointerType = quantizedType->getPointerTo(); - auto quantizedPointerType = PointerType::get(quantizedInnerPointerType, pointerType->getPointerAddressSpace()); - pointerOperand->mutateType(quantizedPointerType); - - // Load the inner pointer value - auto loadedPointerValue = Builder.CreateLoad(quantizedPointerType, pointerOperand); - - // Handle uses of the loaded pointer value - for (auto & use : llvmIrLoadInstruction->uses()) - { - auto user = use.getUser(); - if (auto storeInst = dyn_cast(user)) - { - if (storeInst->getValueOperand() == llvmIrLoadInstruction) - { - // If storing to a float or double, convert the loaded pointer value - auto storeType = storeInst->getPointerOperand()->getType()->getPointerElementType(); - if (storeType->isFloatTy()) - { - auto floatValue = Builder.CreateSIToFP(loadedPointerValue, Type::getFloatTy(llvmIrLoadInstruction->getContext())); - storeInst->setOperand(0, floatValue); - } - else if (storeType->isDoubleTy()) - { - auto doubleValue = Builder.CreateSIToFP(loadedPointerValue, Type::getDoubleTy(llvmIrLoadInstruction->getContext())); - storeInst->setOperand(0, doubleValue); - } - else - { - storeInst->setOperand(0, loadedPointerValue); - } - } - } - } - - // Replace all uses of the original load instruction with the new loaded pointer value - llvmIrLoadInstruction->replaceAllUsesWith(loadedPointerValue); - llvmIrLoadInstruction->eraseFromParent(); - return; - } - } + llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); } } + + void -handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +setQuantizedType(Value * inValue, Type * quantizedType) { - if (auto llvmIrLoadInstruction = dyn_cast(llvmIrInstruction)) + auto valueType = inValue->getType(); + unsigned pointerAddr; + bool isPointer = false; + if (valueType != nullptr) { - IRBuilder<> Builder(llvmIrLoadInstruction); - auto pointerOperand = llvmIrLoadInstruction->getPointerOperand(); - auto pointerType = pointerOperand->getType()->getPointerElementType(); - - // Print debug information - llvm::errs() << "Original load pointer type: " << *pointerType << "\n"; - llvm::errs() << "New quantized load pointer type: " << *quantizedType << "\n"; - - // Handle pointer to pointer (e.g., double**) - if (pointerType->isPointerTy()) - { - handlePointerLoad(llvmIrLoadInstruction, Builder, quantizedType); - return; - } - - // Handle i64 type separately - if (pointerType->isIntegerTy(64)) - { - // For i64* types, do not modify or handle this load instruction - llvm::errs() << "Skipping i64 load instruction: " << *llvmIrLoadInstruction << "\n"; - return; // Skip further processing for i64 types - } - - // Update pointer operand's type if necessary - if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + if (valueType->isPointerTy()) { - pointerOperand->mutateType(quantizedType->getPointerTo()); + llvm::errs() << "Pointer type detected\n"; + isPointer = true; + pointerAddr = valueType->getPointerAddressSpace(); + valueType = valueType->getPointerElementType(); } - - // Load the value using the quantized type - auto loadedValue = Builder.CreateLoad(quantizedType, llvmIrLoadInstruction->getPointerOperand()); - - // Handle uses of the loaded value - for (auto & use : llvmIrLoadInstruction->uses()) + if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) { - auto user = use.getUser(); - if (auto storeInst = dyn_cast(user)) + if (isPointer) { - if (storeInst->getValueOperand() == llvmIrLoadInstruction) - { - // If storing to a float or double, convert the loaded i64 to float/double - auto storeType = storeInst->getPointerOperand()->getType()->getPointerElementType(); - if (storeType->isFloatTy()) - { - auto floatValue = Builder.CreateSIToFP(loadedValue, Type::getFloatTy(llvmIrInstruction->getContext())); - storeInst->setOperand(0, floatValue); - } - else if (storeType->isDoubleTy()) - { - auto doubleValue = Builder.CreateSIToFP(loadedValue, Type::getDoubleTy(llvmIrInstruction->getContext())); - storeInst->setOperand(0, doubleValue); - } - else - { - storeInst->setOperand(0, loadedValue); - } - } + // if (isPointer) + // { + // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + // } } else { - llvm::errs() << "Pointer type " << *pointerType << " is not supported for quantization.\n"; + inValue->mutateType(quantizedType); } } - - // Replace all uses of the original load instruction with the new loaded value - llvmIrLoadInstruction->replaceAllUsesWith(loadedValue); - llvmIrLoadInstruction->eraseFromParent(); } } void -handleGetElementPtr(GetElementPtrInst * gepInst, Type * quantizedType) +handleAlloca(AllocaInst * llvmIrAllocaInstruction, Type * quantizedType) { - llvm::errs() << "Handling GetElementPtr instruction: " << *gepInst << "\n"; - IRBuilder<> Builder(gepInst); + auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); + Type * newType = quantizedType; - auto sourceElementType = gepInst->getSourceElementType(); - auto resultType = gepInst->getResultElementType(); + llvm::errs() << "Handling Alloca for variable: " << llvmIrAllocaInstruction->getName() << "\n"; + llvm::errs() << " - Original type: " << *allocaType << "\n"; - if (sourceElementType->isArrayTy()) + if (allocaType->getTypeID() == Type::ArrayTyID) { - Type * elementType = sourceElementType->getArrayElementType(); - - if (elementType->isFloatTy() || elementType->isDoubleTy()) - { - llvm::errs() << "Quantizing element type: " << *elementType << "\n"; - - // 量化后的元素类型 - Type * newElementType = quantizedType; - - // 更新GEP指令的源和结果类型 - Type * newSourceType = ArrayType::get(newElementType, sourceElementType->getArrayNumElements()); - gepInst->setSourceElementType(newSourceType); - gepInst->setResultElementType(newElementType); - - // 更新索引操作数类型 - for (unsigned i = 0; i < gepInst->getNumIndices(); ++i) - { - Value * index = gepInst->getOperand(i + 1); - if (index->getType()->isIntegerTy() && index->getType()->getIntegerBitWidth() != quantizedType->getIntegerBitWidth()) - { - gepInst->setOperand(i + 1, Builder.CreateIntCast(index, quantizedType, true)); - } - } - } + newType = ArrayType::get(quantizedType, allocaType->getArrayNumElements()); + allocaType = allocaType->getArrayElementType(); + llvm::errs() << " - Detected array type. New quantized array type: " << *newType << "\n"; } -} - -void -handleFPExt(Instruction * llvmIrInstruction, Type * quantizedType) -{ - llvm::errs() << "Handling FPExt: " << *llvmIrInstruction << "\n"; - auto * fpextInst = cast(llvmIrInstruction); - auto srcType = fpextInst->getSrcTy(); - auto destType = fpextInst->getDestTy(); - // If the source type is an integer and the destination type is a floating-point type - if (srcType->isIntegerTy() && (destType->isFloatTy() || destType->isDoubleTy())) + if (allocaType->isDoubleTy() || allocaType->isFloatTy()) { - // Erase the unnecessary FPExt instruction - llvmIrInstruction->replaceAllUsesWith(fpextInst->getOperand(0)); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Removed unnecessary FPExt\n"; + llvmIrAllocaInstruction->setAllocatedType(newType); + llvm::errs() << " - Updated allocation type to quantized type: " << *newType << "\n"; } else { - llvm::errs() << "Unhandled FPExt conversion from " << *srcType << " to " << *destType << "\n"; + llvm::errs() << " - No need to change type for non-floating point allocation.\n"; } } void -handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) +handleStore(Instruction * llvmIrInstruction, Type * quantizedType) { - llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; - auto * fptruncInst = cast(llvmIrInstruction); - auto srcType = fptruncInst->getSrcTy(); - auto destType = fptruncInst->getDestTy(); + if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) + { + IRBuilder<> Builder(llvmIrStoreInstruction); - Value * operand = llvmIrInstruction->getOperand(0); - llvm::errs() << "Operand type before conversion: " << *operand->getType() << "\n"; + auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); + if (valueType->isFloatTy() || valueType->isDoubleTy()) + { + llvm::errs() << "Original store value type: " << *valueType << "\n"; + llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; - if (srcType->isFloatingPointTy() && destType->isFloatingPointTy()) - { - // Erase the unnecessary FPTrunc instruction - llvmIrInstruction->replaceAllUsesWith(fptruncInst->getOperand(0)); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Removed unnecessary FPTrunc\n"; - } + // Quantize the value operand + auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); + quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); + llvmIrStoreInstruction->setOperand(0, quantizedValue); + } - if (operand->getType()->isIntegerTy()) - { - llvmIrInstruction->replaceAllUsesWith(operand); - llvmIrInstruction->eraseFromParent(); - llvm::errs() << "Removed unnecessary FPTrunc for integer operand\n"; - } - else - { - llvm::errs() << "Unhandled FPTrunc conversion from " << *srcType << " to " << *destType << "\n"; + auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + { + llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; + llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; + + // Set the new quantized type for the pointer operand + llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + } } } -// void -// handleFPTrunc(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// llvm::errs() << "Handling FPTrunc: " << *llvmIrInstruction << "\n"; -// IRBuilder<> Builder(llvmIrInstruction); -// -// // Insert point is after the current instruction -// Instruction * insertPoint = llvmIrInstruction->getNextNode(); -// Builder.SetInsertPoint(insertPoint); -// -// // Get the operand of the FPTrunc instruction -// Value * operand = llvmIrInstruction->getOperand(0); -// Value * newInst = nullptr; -// -// // Check if the operand is an integer or a floating-point type -// if (operand->getType()->isIntegerTy()) -// { -// // If it's an integer, convert it to a floating-point value -// newInst = Builder.CreateSIToFP(operand, llvmIrInstruction->getType()); -// } -// else -// { -// // Otherwise, perform the FPTrunc as a floating-point cast -// newInst = Builder.CreateFPCast(operand, llvmIrInstruction->getType()); -// } -// -// // Replace all uses of the original FPTrunc with the new instruction -// llvmIrInstruction->replaceAllUsesWith(newInst); -// -// // Remove the original FPTrunc instruction from the parent -// llvmIrInstruction->removeFromParent(); -// -// llvm::errs() << "Finished handling FPTrunc\n"; -// } - +// handle argument pointer void -handleBitCast(Instruction * llvmIrInstruction, Type * quantizedType) +bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { - if (auto bitcastInst = dyn_cast(llvmIrInstruction)) - { - IRBuilder<> Builder(bitcastInst); + SmallVector, 4> argReplacements; - // 获取源类型和目标类型 - auto srcType = bitcastInst->getSrcTy(); - auto destType = bitcastInst->getDestTy(); + Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); + // Iterate over all function arguments + // 遍历所有函数参数 + for (llvm::Argument &Arg : F.args()) { + // 检查是否为 float* 类型 + if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) { + // 创建正确的 i32* 类型 + llvm::PointerType *i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); - // 转换源类型 - if (srcType->isPointerTy()) - { - auto originalElementType = srcType->getPointerElementType(); - auto newElementType = transformToQuantizedType(originalElementType, quantizedType); - srcType = newElementType->getPointerTo(); - } + // 对 float* 参数执行 bitcast 到 i32* + llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); + llvm::errs() << "Bitcast " << Arg.getName() << " to i32*\n"; - // 如果目标类型是浮点类型的指针,则转换为量化类型 - if (destType->isPointerTy() && destType->getPointerElementType()->isFloatTy()) - { - destType = quantizedType->getPointerTo(); + // 存储原始参数和新的 bitcast 值以备后用 + argReplacements.push_back(std::make_pair(&Arg, newArg)); } + } - // 输出日志信息 - llvm::errs() << "Original srcType: " << *bitcastInst->getSrcTy() << "\n"; - llvm::errs() << "Original destType: " << *bitcastInst->getDestTy() << "\n"; - llvm::errs() << "Transformed srcType: " << *srcType << "\n"; - llvm::errs() << "Transformed destType: " << *destType << "\n"; - - // 创建新的BitCast指令 - Value * newInst = Builder.CreateBitCast(bitcastInst->getOperand(0), destType); - bitcastInst->replaceAllUsesWith(newInst); - bitcastInst->eraseFromParent(); + // Replace all uses of the original arguments with the new bitcast values + for (auto & replacement : argReplacements) + { + Argument * originalArg = replacement.first; + Value * newArg = replacement.second; - llvm::errs() << "Created new BitCast: " << *newInst << "\n"; - llvm::errs() << "Erased original BitCast\n"; + // Replace all occurrences of the original argument with the new bitcast value + originalArg->replaceAllUsesWith(newArg); } } +// Helper function to quantize floating-point parameters void -adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) +quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { - llvm::errs() << "Entering adaptTypeCast\n"; - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + builder.SetInsertPoint(&func.getEntryBlock(), func.getEntryBlock().begin()); + + for (auto & arg : func.args()) { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + if (arg.getType()->isFloatingPointTy()) { - Instruction * llvmIrInstruction = &*itBB++; - llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; - - switch (llvmIrInstruction->getOpcode()) + std::vector usesToReplace; + for (auto & use : arg.uses()) { -// case Instruction::Alloca: -// handleAlloca(llvmIrInstruction, quantizedType); -// break; -// case Instruction::Store: -// handleStore(llvmIrInstruction, quantizedType); -// break; -// case Instruction::Load: -// handleLoad(llvmIrInstruction, quantizedType); -// break; - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::SIToFP: - case Instruction::UIToFP: - { - auto sourceOp = llvmIrInstruction->getOperand(0); - if (sourceOp->getType() == llvmIrInstruction->getType()) - { - llvmIrInstruction->replaceAllUsesWith(sourceOp); - llvmIrInstruction->removeFromParent(); - } - } - break; - case Instruction::FPExt: - { - handleFPExt(llvmIrInstruction, quantizedType); - break; - } + usesToReplace.push_back(&use); + } - case Instruction::FPTrunc: - { - handleFPTrunc(llvmIrInstruction, quantizedType); - break; - } - case Instruction::BitCast: - { - // IRBuilder<> Builder(llvmIrInstruction); - // Instruction * insertPoint = llvmIrInstruction->getNextNode(); - // Builder.SetInsertPoint(insertPoint); - // Value * newInst = Builder.CreateBitCast(llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - // llvmIrInstruction->replaceAllUsesWith(newInst); - // llvmIrInstruction->removeFromParent(); - llvm::errs() << "handle bitcast\n"; - handleBitCast(llvmIrInstruction, quantizedType); - break; - } + // Create multiplication and rounding instructions + llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); + llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".quantized")); + + // Attach metadata to each instruction + llvm::MDNode * metadataNode = llvm::MDNode::get(arg.getContext(), llvm::MDString::get(arg.getContext(), "quantized")); + scaled->setMetadata("quantized", metadataNode); + rounded->setMetadata("quantized", metadataNode); + quantized->setMetadata("quantized", metadataNode); + + // Replace all occurrences of the original argument with the new quantized value + for (Use * use : usesToReplace) + { + Use & u = *use; + u.set(quantized); } + + llvm::errs() << "Quantizing argument: " << arg.getName() << "\n"; + llvm::errs() << " - Scaled value: " << *scaled << "\n"; + llvm::errs() << " - Rounded value: " << *rounded << "\n"; + llvm::errs() << " - Quantized value: " << *quantized << "\n"; } } - llvm::errs() << "Exiting adaptTypeCast\n"; } +bool +shouldSkipFunction(const std::string & functionName) +{ + // List of function names to skip + static const std::unordered_set skipFunctions = { + "llvm.round.f32", + "llvm.dbg.declare", + "llvm.dbg.value", + "llvm.dbg.label", + "invSqrt", + "fixsqrt", + "fixrsqrt", + "fixmul", + "sinf", + "llvm.sqrt.f64", + "llvm.sqrt.f32", + "sqrt", + "sqrtf"}; - - - -//void -//quantizeFunctionArguments(Function & llvmIrFunction, Type * quantizedType) -//{ -// for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) -// { -// auto paramOp = llvmIrFunction.getArg(idx); -// setQuantizedType(paramOp, quantizedType); -// } -//} - -//void quantizeFunctionArguments(Function &llvmIrFunction, Type *quantizedType) { -// for (auto &arg : llvmIrFunction.args()) { -// if (arg.getType()->isFloatTy()) { // 检查参数是否为浮点类型 -// // 获取原始浮点值 -// Value *originalFloat = &arg; -// -// IRBuilder<> builder(&llvmIrFunction.getEntryBlock().front()); -// -// // 执行乘法 (假设你有一个 FRAC_BASE 的常数) -// Value *scaledValue = builder.CreateFMul(originalFloat, ConstantFP::get(originalFloat->getType(), FRAC_BASE)); -// -// // 调用 roundf32 (假设这是你的量化逻辑) -// Function *roundF32 = Intrinsic::getDeclaration(llvmIrFunction.getParent(), Intrinsic::round, originalFloat->getType()); -// Value *roundedValue = builder.CreateCall(roundF32, scaledValue); -// -// // 将浮点值转换为整数类型(使用 fptosi) -// Value *quantizedValue = builder.CreateFPToSI(roundedValue, quantizedType); -// -// // 用量化后的值替换原来的参数 -// arg.replaceAllUsesWith(quantizedValue); // 使用 replaceAllUsesWith 进行替换 -// -// // 根据需要设置量化的值或进行替换 -// // 你可以在这里将量化后的值赋给参数或者进行进一步的操作 -// // 例如:将参数替换为量化值 -// // setQuantizedType(paramOp, quantizedType); // 这里可以是你的量化类型设置函数 -// } -// } -//} + return skipFunctions.find(functionName) != skipFunctions.end(); +} // Main function to perform LLVM IR auto quantization void @@ -3152,13 +962,12 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } - // Ensure types are correctly defined - Type * floatType = Type::getFloatTy(llvmIrFunction.getContext()); - Type * intType = Type::getInt32Ty(llvmIrFunction.getContext()); - - // Deal with function signature + // Iterate over the function's arguments to apply quantization + llvm::IRBuilder<> builder(llvmIrFunction.getContext()); + quantizeFunctionArguments(llvmIrFunction, builder); - handleFunctionSignature(llvmIrFunction, quantizedType); + // Perform bitcasting of float poiter arguments to i32* + bitcastFloatPtrArgs(llvmIrFunction, builder); // Update global variables to integer type updateGlobalVariables(module, quantizedType); @@ -3166,40 +975,32 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * generate hardcode function * */ - llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); - /* - * quantize the arguments type - * */ - //quantizeFunctionArguments(llvmIrFunction, quantizedType); - - // Process each instruction for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { Instruction * llvmIrInstruction = &*itBB++; - llvm::errs() << "Processing instruction in adaptTypeCast: " << *llvmIrInstruction << "\n"; - - IRBuilder<> Builder(llvmIrInstruction); + llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; switch (llvmIrInstruction->getOpcode()) { case Instruction::Alloca: - handleAlloca(llvmIrInstruction, quantizedType); + { + handleAlloca(cast(llvmIrInstruction), quantizedType); break; + } case Instruction::Call: - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); - break; case Instruction::GetElementPtr: - - handleGetElementPtr(cast(llvmIrInstruction), quantizedType); - break; case Instruction::Load: - llvm::errs() << "Handling Load instruction: " << *llvmIrInstruction << "\n"; - handleLoad(llvmIrInstruction, quantizedType); + { + llvm::errs() << "Handling load\n"; + //handleLoad(llvmIrInstruction, quantizedType); break; + } + break; case Instruction::PHI: { setQuantizedType(llvmIrInstruction, quantizedType); @@ -3207,126 +1008,45 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve break; case Instruction::Store: - { /* * If either of the operands is constant, change it to a int value * */ - handleStore(llvmIrInstruction, quantizedType); - } - break; - - /* - * For fmul/fdiv, - * - * if either one of the operands is a constant value, simplify it by multiplying with 10^n, - * then replace the instruction to mul/div; - * - * else substitute this instruction to a pre-implemented function: mulfix/fixdiv. - * */ - case Instruction::FMul: - if (isMatrixOperation(llvmIrInstruction)) - { - llvm::errs() << "Found matrix multiplication operation.\n"; - handleMatrixOperations(llvmIrInstruction, quantizedType); - } - else - { - llvm::errs() << "Found FMul instruction.\n"; - handleFMul(llvmIrInstruction, quantizedType, fixmul); - } + llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; + setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); break; - case Instruction::FDiv: + case Instruction::FMul: { - llvm::errs() << "Found FDiv instruction.\n"; - handleFDiv(llvmIrInstruction, quantizedType); + handleFMul(llvmIrInstruction, quantizedType, fixmul); break; } - /* - * If either one of the operands is a constant value, quantize it, - * then replace the instruction to the int version. - * */ + /* + * If either one of the operands is a constant value, quantize it, + * then replace the instruction to the int version. + * */ + case Instruction::FDiv: + case Instruction::FCmp: handleFCmp(llvmIrInstruction, quantizedType); break; case Instruction::FAdd: - if (isMatrixOperation(llvmIrInstruction)) - { - llvm::errs() << "Found matrix addition operation.\n"; - handleMatrixOperations(llvmIrInstruction, quantizedType); - } - else - { - llvm::errs() << "Found FAdd instruction.\n"; - handleFAdd(llvmIrInstruction, quantizedType); - } + { + handleFAdd(llvmIrInstruction, quantizedType); break; + } case Instruction::FSub: + { handleFSub(llvmIrInstruction, quantizedType); break; + } case Instruction::FRem: - handleFRem(llvmIrInstruction, quantizedType); - break; - { - // quantizeConstant(llvmIrInstruction, quantizedType); - // 量化浮点常量 - for (size_t idx = 0; idx < llvmIrInstruction->getNumOperands(); idx++) - { - Value * inValue = llvmIrInstruction->getOperand(idx); - - if (isa(inValue)) - { - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; - - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); - } - else - { - assert(false && "unknown floating type"); - } - - llvmIrInstruction->setOperand(idx, newValue); - } - } - } case Instruction::FNeg: - { - // quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); handleFNeg(llvmIrInstruction, quantizedType); - break; } - // case Instruction::Add: - // case Instruction::Sub: - // case Instruction::Mul: - // case Instruction::UDiv: - // case Instruction::SDiv: - // case Instruction::URem: - // case Instruction::SRem: - // - // case Instruction::Shl: - // case Instruction::LShr: - // case Instruction::AShr: - // case Instruction::And: - // case Instruction::Or: - // case Instruction::Xor: - // - // case Instruction::ICmp: - case Instruction::FPToUI: case Instruction::FPToSI: case Instruction::SIToFP: @@ -3338,11 +1058,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FPExt: case Instruction::FPTrunc: case Instruction::BitCast: - // log - llvm::errs() << "handle bitcast\n"; - handleBitCast(llvmIrInstruction, quantizedType); - break; - case Instruction::Ret: case Instruction::Switch: case Instruction::Br: @@ -3379,9 +1094,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve } } } - // handleLoadStoreInstructions(llvmIrFunction, quantizedType); - adaptTypeCast(llvmIrFunction, quantizedType); + + // adaptTypeCast(llvmIrFunction, quantizedType); return; } -} \ No newline at end of file From 4d8766310a7770aba17a61bebddf161d7c15081e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 5 Oct 2024 14:17:33 +0100 Subject: [PATCH 104/213] fix bitcast pointer issue Addresses #1. --- .../newton-irPass-LLVMIR-quantization.cpp | 106 +++++++++++++----- 1 file changed, 79 insertions(+), 27 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a072ee1a3..24701a495 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -294,9 +294,8 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) IRBuilder<> Builder(loadInst); // Check if the loaded value is of floating-point type - Type * loadedType = loadInst->getType(); - //Type * pointerType = loadInst->getPointerOperandType(); - + Type * loadedType = loadInst->getType(); + // Type * pointerType = loadInst->getPointerOperandType(); if (loadedType->isFloatingPointTy()) { @@ -726,8 +725,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix } } - - void setQuantizedType(Value * inValue, Type * quantizedType) { @@ -818,41 +815,96 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } -// handle argument pointer void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { SmallVector, 4> argReplacements; - - Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); // Iterate over all function arguments - // 遍历所有函数参数 - for (llvm::Argument &Arg : F.args()) { - // 检查是否为 float* 类型 - if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) { - // 创建正确的 i32* 类型 - llvm::PointerType *i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); - - // 对 float* 参数执行 bitcast 到 i32* - llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); - llvm::errs() << "Bitcast " << Arg.getName() << " to i32*\n"; - - // 存储原始参数和新的 bitcast 值以备后用 - argReplacements.push_back(std::make_pair(&Arg, newArg)); + for (Argument & Arg : F.args()) + { + // Check if the argument is a pointer to float + if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) + { + llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); + // Create a bitcast instruction at the beginning of the function + Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); + Value * newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32"); + // llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); + + // Store the original argument and the bitcast result + argReplacements.push_back({&Arg, newArg}); } } - // Replace all uses of the original arguments with the new bitcast values + // Iterate over the function to replace uses of the original arguments for (auto & replacement : argReplacements) { - Argument * originalArg = replacement.first; - Value * newArg = replacement.second; + Argument * oldArg = replacement.first; + Value * newArg = replacement.second; - // Replace all occurrences of the original argument with the new bitcast value - originalArg->replaceAllUsesWith(newArg); + // Replace all uses of the old argument with the new bitcasted value + // 遍历替换所有使用原始参数的地方 + for (auto & replacement : argReplacements) + { + Argument * oldArg = replacement.first; + Value * newArg = replacement.second; + + // 替换前,移除 bitcast 指令自身的使用 + SmallVector usesToReplace; + for (auto & U : oldArg->uses()) + { + User * user = U.getUser(); + if (user != newArg) + { + usesToReplace.push_back(&U); + } + } + + // 替换收集到的使用 + for (auto * use : usesToReplace) + { + use->set(newArg); + } + } } } +// handle argument pointer +// void +// bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) +//{ +// SmallVector, 4> argReplacements; +// +// Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); +// // Iterate over all function arguments +// // 遍历所有函数参数 +// for (llvm::Argument &Arg : F.args()) { +// // 检查是否为 float* 类型 +// if (Arg.getType()->isPointerTy()) { +// llvm::errs() << "Recognized float pointer argument: " << Arg.getName() << "\n"; +// // 创建正确的 i32* 类型 +// llvm::PointerType *i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); +// +// // 对 float* 参数执行 bitcast 到 i32* +// llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); +// llvm::errs() << "Bitcast " << Arg.getName() << " to i32*\n"; +// +// // 存储原始参数和新的 bitcast 值以备后用 +// argReplacements.push_back(std::make_pair(&Arg, newArg)); +// } +// } +// +// // Replace all uses of the original arguments with the new bitcast values +// for (auto & replacement : argReplacements) +// { +// Argument * originalArg = replacement.first; +// Value * newArg = replacement.second; +// +// // Replace all occurrences of the original argument with the new bitcast value +// originalArg->replaceAllUsesWith(newArg); +// } +//} + // Helper function to quantize floating-point parameters void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) @@ -997,7 +1049,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::Load: { llvm::errs() << "Handling load\n"; - //handleLoad(llvmIrInstruction, quantizedType); + handleLoad(llvmIrInstruction, quantizedType); break; } break; From 8531c4f461edb6b6e796ca8432ee18a0a301b39a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 7 Oct 2024 11:31:29 +0100 Subject: [PATCH 105/213] fix several bugs, save before change Addresses #1. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 149 +++++----- .../newton-irPass-LLVMIR-quantization.cpp | 272 +++++++++++++++--- 2 files changed, 310 insertions(+), 111 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index a3203fa2c..375d2ffb6 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -75,60 +75,87 @@ #include "llvm/Support/FileSystem.h" #include "llvm/IR/Function.h" - using namespace llvm; - std::set whitelist = { - "MadgwickAHRSupdate" -}; - - -//void processWhitelistedFunctions(Module &module, const std::set &whitelist) { -// for (auto &F : module) { -// // 检查函数是否在白名单中 -// if (whitelist.find(F.getName().str()) != whitelist.end()) { -// // 打印找到的函数名 -// llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; -// // 对函数的参数进行量化处理 -// handleArguments(F); -// // 对返回值进行反量化处理 -// dequantizeResults(F); -// } -// } -//} -// -//void handleArguments(Function &F) { -// IRBuilder<> Builder(&F.getEntryBlock()); -// for (auto &arg : F.args()) { -// if (arg.getType()->isFloatingPointTy()) { -// // 量化参数:将浮点数转换为整型(定点) -// Value *quantizedArg = Builder.CreateFPToSI( -// Builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getType(), scale)), -// IntegerType::get(arg.getContext(), 32) -// ); -// // 替换原来的浮点参数 -// arg.replaceAllUsesWith(quantizedArg); -// } -// } -//} - - - - - - - - - - - - + "MadgwickAHRSupdate"}; +// Define the dequantizeResult method +std::vector toRemove; +void +dequantizeResults(StoreInst * storeInst, Function & F) +{ + // IRBuilder<> Builder(storeInst); + IRBuilder<> Builder(storeInst->getNextNode()); + auto * pointerOperand = storeInst->getPointerOperand(); + llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; + if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) + { + llvm::errs() << "Integer pointer type detected, proceeding with dequantization. Operand: " << *pointerOperand << "\n"; + auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + llvm::errs() << "Loaded value from pointer: " << *loadInst << "\n"; + Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); + llvm::errs() << "Converted integer to float: " << *convertedFloat << "\n"; + Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 65536.0)); + llvm::errs() << "Divided value by FRAC_BASE: " << *dividedValue << "\n"; + + if (auto * bitcastInst = dyn_cast(pointerOperand)) + { + // if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) + // { + // auto * originalFloatPtr = bitcastInst->getOperand(0); + // llvm::errs() << "Original float pointer: " << *originalFloatPtr << "\n"; + // llvm::errs() << "Storing dequantized value back to original float pointer.\n"; + // Builder.CreateStore(dividedValue, originalFloatPtr); + // storeInst->eraseFromParent(); // Remove the old store instruction + // llvm::errs() << "Original store instruction removed.\n"; + // } + + if (auto * bitcastInst = dyn_cast(pointerOperand)) + { + if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) + { + auto * originalFloatPtr = bitcastInst->getOperand(0); + Builder.CreateStore(dividedValue, originalFloatPtr); + // 标记原始指令以便删除 + // toRemove.push_back(storeInst); + } + } + } + } + else + { + llvm::errs() << "Non-integer pointer type detected, skipping dequantization.\n"; + } +} +// Process functions that are whitelisted for dequantization +void +processWhitelistedFunctions(Module & module, const std::set & whitelist) +{ + for (auto & F : module) + { + if (whitelist.find(F.getName().str()) != whitelist.end()) + { + llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; + for (auto & B : F) + { + for (auto & I : B) + { + llvm::errs() << "Processing instruction: " << I << "\n"; + if (auto * storeInst = dyn_cast(&I)) + { + llvm::errs() << "Found valid StoreInst.\n"; + dequantizeResults(storeInst, F); + } + } + } + } + } +} // Function to save the IR of a module to a file void @@ -164,11 +191,14 @@ removeQuantizedSuffixInModule(llvm::Module & M) } // Remove suffix from global variables - for (auto &G : M.globals()) { - if (G.hasName()) { + for (auto & G : M.globals()) + { + if (G.hasName()) + { std::string GlobalName = G.getName().str(); - size_t pos = GlobalName.find("_quantized"); - if (pos != std::string::npos) { + size_t pos = GlobalName.find("_quantized"); + if (pos != std::string::npos) + { GlobalName.erase(pos, 10); // Remove "_quantized" G.setName(GlobalName); } @@ -372,12 +402,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } - flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); maxPrecisionBits = 16; - /* * get const global variables * */ @@ -460,7 +488,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl for (auto & mi : *Mod) { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert,maxPrecisionBits); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); } for (auto mi : functionsToInsert) { @@ -615,20 +643,16 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // overloadFunc(Mod, callerMap); // Finally, erase old functions - //eraseOldFunctions(); + // eraseOldFunctions(); eraseOldGlobals(); // Perform text replacement to remove "_quantized" suffixes removeQuantizedSuffixInModule(*Mod); + // eraseOldInstructions(); - - //eraseOldInstructions(); - - //processWhitelistedFunctions(*Mod, whitelist); - - + processWhitelistedFunctions(*Mod, whitelist); const char * homeDir = getenv("HOME"); if (!homeDir) @@ -641,10 +665,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl saveModuleIR(*Mod, fileName); // Save the optimized IR to a file // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); - - // finalCorrectionPass(*Mod, quantizedType); - // finalCorrectionPass(*Mod, Type::getInt32Ty(Context)); // Assuming 32-bit quantization - //saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); // 替换为$HOMR diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 24701a495..9fc64eb44 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -349,6 +349,24 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) Value * op0 = inInstruction->getOperand(0); Value * op1 = inInstruction->getOperand(1); + + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + if (ConstantFP * constFp = dyn_cast(op0)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + + if (ConstantFP * constFp = dyn_cast(op1)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + // Create fixed-point subtraction Value * newInst = Builder.CreateNSWSub(op0, op1); @@ -369,6 +387,8 @@ handleFNeg(Instruction * inInstruction, Type * quantizedType) Value * operand = inInstruction->getOperand(0); llvm::errs() << "Operand: " << *operand << "\n"; + + // Create a constant zero of the same integer type as the operand Value * zero = ConstantInt::get(operand->getType(), 0); @@ -744,10 +764,9 @@ setQuantizedType(Value * inValue, Type * quantizedType) { if (isPointer) { - // if (isPointer) - // { - // inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - // } + llvm::errs() << "Original type: " << *valueType << "\n"; + llvm::errs() << "New quantized type: " << *quantizedType << "\n"; + inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); } else { @@ -818,17 +837,25 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { + // Check if the function is the specific one to be skipped + if (F.getName() == "MadgwickAHRSupdateIMU") + { + llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; + return; // Early exit if it's the function to skip + } + SmallVector, 4> argReplacements; // Iterate over all function arguments for (Argument & Arg : F.args()) { + // Check if the argument is a pointer to float if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) { llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); // Create a bitcast instruction at the beginning of the function Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); - Value * newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32"); + Value * newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); // llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); // Store the original argument and the bitcast result @@ -860,7 +887,6 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) } } - // 替换收集到的使用 for (auto * use : usesToReplace) { use->set(newArg); @@ -869,46 +895,134 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) } } -// handle argument pointer -// void -// bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) -//{ -// SmallVector, 4> argReplacements; -// -// Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); -// // Iterate over all function arguments -// // 遍历所有函数参数 -// for (llvm::Argument &Arg : F.args()) { -// // 检查是否为 float* 类型 -// if (Arg.getType()->isPointerTy()) { -// llvm::errs() << "Recognized float pointer argument: " << Arg.getName() << "\n"; -// // 创建正确的 i32* 类型 -// llvm::PointerType *i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); -// -// // 对 float* 参数执行 bitcast 到 i32* -// llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); -// llvm::errs() << "Bitcast " << Arg.getName() << " to i32*\n"; -// -// // 存储原始参数和新的 bitcast 值以备后用 -// argReplacements.push_back(std::make_pair(&Arg, newArg)); -// } -// } -// -// // Replace all uses of the original arguments with the new bitcast values -// for (auto & replacement : argReplacements) -// { -// Argument * originalArg = replacement.first; -// Value * newArg = replacement.second; -// -// // Replace all occurrences of the original argument with the new bitcast value -// originalArg->replaceAllUsesWith(newArg); -// } -//} + + +void +handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) +{ + IRBuilder<> builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + if (!fixrsqrt) + { + llvm::errs() << "Error: fixrsqrt function is null.\n"; + return; + } + + CallInst * rsqrtResult = builder.CreateCall(fixrsqrt, {operand}); + + llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); + + llvmIrCallInstruction->eraseFromParent(); +} + +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +{ + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + // Ensure operand is in the correct type + if (operand->getType()->isFloatingPointTy()) + { + operand = Builder.CreateFPToSI(operand, quantizedType); + } + + // Convert the operand from fixed-point (int32) to float + llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + + // Call llvm.sqrt.f32 to compute the square root of the float value + llvm::Value * sqrtFloat = Builder.CreateCall( + Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), + {operandAsFloat}); + + // Convert the result back to int32 + llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); + + // Apply the shift-left operation (shl i32 %result, 5) + llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); + + // Replace the original instruction with the new fixed-point sqrt result + llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); + llvmIrCallInstruction->eraseFromParent(); +} + +void +// handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) +{ + llvm::errs() << "Handling Call\n"; + Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); + if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + return; + + std::string funcName = calledFunction->getName().str(); + + if (funcName == "invSqrt" || funcName == "invSqrt_quantized") + { + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + llvm::errs() << "Handling invsqrt call\n"; + handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); + } + + if (!calledFunction->getName().startswith("llvm.dbg.value") && + !calledFunction->getName().startswith("llvm.dbg.declare") && + !calledFunction->getName().startswith("llvm.dbg.label")) + { + if (calledFunction->isDeclaration()) + { + // For library functions + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + + // std::string funcName = calledFunction->getName().str(); + + if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64" || funcName == "llvm.sqrt.f32") + { + // For sqrt + handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); + } + else + { + /* + * for other lib functions, de-quantize the arguments and quantize the return value + */ + // dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); + } + } + else + { + // Quantize the return type if necessary + if (llvmIrCallInstruction->getType()->isPointerTy()) + { + Type * returnElementType = llvmIrCallInstruction->getType()->getPointerElementType(); + if (returnElementType->isFloatTy() || returnElementType->isDoubleTy()) + { + // Keep the pointer type, but track the need for de-quantization + return; + } + } + setQuantizedType(llvmIrCallInstruction, quantizedType); + } + } +} // Helper function to quantize floating-point parameters void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { + // Skip the function if it is MadgwickAHRSupdateIMU + if (func.getName() == "MadgwickAHRSupdateIMU") + { + llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; + return; + } + builder.SetInsertPoint(&func.getEntryBlock(), func.getEntryBlock().begin()); for (auto & arg : func.args()) @@ -924,13 +1038,13 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) // Create multiplication and rounding instructions llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); - llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".quantized")); + llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); // Attach metadata to each instruction llvm::MDNode * metadataNode = llvm::MDNode::get(arg.getContext(), llvm::MDString::get(arg.getContext(), "quantized")); scaled->setMetadata("quantized", metadataNode); rounded->setMetadata("quantized", metadataNode); - quantized->setMetadata("quantized", metadataNode); + quantized->setMetadata("quantized_changed", metadataNode); // Replace all occurrences of the original argument with the new quantized value for (Use * use : usesToReplace) @@ -938,6 +1052,9 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) Use & u = *use; u.set(quantized); } + //arg.replaceAllUsesWith(quantized); + + llvm::errs() << "Quantizing argument: " << arg.getName() << "\n"; llvm::errs() << " - Scaled value: " << *scaled << "\n"; @@ -969,6 +1086,35 @@ shouldSkipFunction(const std::string & functionName) return skipFunctions.find(functionName) != skipFunctions.end(); } +//void +//quantizeArguments(Function & llvmIrFunction, Type * quantizedType) +//{ +// for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) +// { +// auto paramOp = llvmIrFunction.getArg(idx); +// setQuantizedType(paramOp, quantizedType); +// } +//} + + +void quantizeArguments(llvm::Function &llvmIrFunction, llvm::Type *quantizedType) { + // Check if the function is specifically MadgwickAHRSupdateIMU + if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU") { + llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; + + // Process each argument of the function + for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) { + auto *paramOp = llvmIrFunction.getArg(idx); + setQuantizedType(paramOp, quantizedType); // Assuming setQuantizedType is defined elsewhere + } + + } +} + + + + + // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) @@ -985,6 +1131,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } + Type * quantizedType; switch (BIT_WIDTH) { @@ -1017,6 +1164,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Iterate over the function's arguments to apply quantization llvm::IRBuilder<> builder(llvmIrFunction.getContext()); quantizeFunctionArguments(llvmIrFunction, builder); + quantizeArguments(llvmIrFunction, quantizedType); // Perform bitcasting of float poiter arguments to i32* bitcastFloatPtrArgs(llvmIrFunction, builder); @@ -1035,21 +1183,30 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { + + Instruction * llvmIrInstruction = &*itBB++; + +// if (llvmIrInstruction->getMetadata("quantized_changed")) +// { +// llvm::errs() << "quantized_changed.\n"; +// return; // Skip processing this instruction +// } + llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; switch (llvmIrInstruction->getOpcode()) { case Instruction::Alloca: - { handleAlloca(cast(llvmIrInstruction), quantizedType); break; - } case Instruction::Call: + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); + break; case Instruction::GetElementPtr: case Instruction::Load: { llvm::errs() << "Handling load\n"; - handleLoad(llvmIrInstruction, quantizedType); + handleLoad(llvmIrInstruction, quantizedType); break; } break; @@ -1064,7 +1221,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * If either of the operands is constant, change it to a int value * */ llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + //setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); break; case Instruction::FMul: @@ -1104,11 +1261,32 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::SIToFP: case Instruction::UIToFP: + case Instruction::ZExt: case Instruction::SExt: case Instruction::Trunc: case Instruction::FPExt: case Instruction::FPTrunc: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + { + newInst = Builder.CreateSIToFP( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + else + { + newInst = Builder.CreateFPCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); + break; + } + case Instruction::BitCast: case Instruction::Ret: case Instruction::Switch: From 813b089be089313d9b71f9bc677d13b14fabf31e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 7 Oct 2024 16:26:09 +0100 Subject: [PATCH 106/213] save before change Addresses #1. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 47 ++++---- .../newton-irPass-LLVMIR-quantization.cpp | 113 ++++++++++++++---- 2 files changed, 111 insertions(+), 49 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 375d2ffb6..78523de4f 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -119,8 +119,7 @@ dequantizeResults(StoreInst * storeInst, Function & F) { auto * originalFloatPtr = bitcastInst->getOperand(0); Builder.CreateStore(dividedValue, originalFloatPtr); - // 标记原始指令以便删除 - // toRemove.push_back(storeInst); + } } } @@ -573,28 +572,28 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); // } // - // /* - // * simplify the condition of each branch - // * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // simplifyControlFlow(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // legacy::PassManager passManager; - // passManager.add(createCFGSimplificationPass()); - // passManager.add(createInstSimplifyLegacyPass()); - // passManager.add(createGlobalDCEPass()); - // passManager.run(*Mod); + /* + * simplify the condition of each branch + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + simplifyControlFlow(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + legacy::PassManager passManager; + passManager.add(createCFGSimplificationPass()); + passManager.add(createInstSimplifyLegacyPass()); + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); // // /* // * remove the functions that are optimized by passes. diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 9fc64eb44..6a79cfde2 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -118,7 +118,8 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorgetContext()), 1.0f / FRAC_BASE)); - // Equivalent of: %4 = fmul float %3, 0x3F50000000000000 - // fp_y = builder.CreateFMul(fp_y, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 0.0009765625)); - llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + //i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; @@ -331,7 +330,8 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point addition - Value * newInst = Builder.CreateNSWAdd(op0, op1); + //Value * newInst = Builder.CreateNSWAdd(op0, op1); + Value * newInst = Builder.CreateAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -368,7 +368,8 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point subtraction - Value * newInst = Builder.CreateNSWSub(op0, op1); + //Value * newInst = Builder.CreateNSWSub(op0, op1); + Value * newInst = Builder.CreateSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -702,8 +703,8 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix if (isa(lhs) || isa(rhs)) { llvm::errs() << "One of the operands is an integer constant, using mul\n"; - // Value * newInst = Builder.CreateMul(lhs, rhs); - Value * newInst = Builder.CreateNSWMul(lhs, rhs); + Value * newInst = Builder.CreateMul(lhs, rhs); + //Value * newInst = Builder.CreateNSWMul(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); return; @@ -895,6 +896,82 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) } } +//void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) +//{ +// // Check if the function is the specific one to be skipped +// if (F.getName() == "MadgwickAHRSupdateIMU") +// { +// llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; +// return; // Early exit if it's the function to skip +// } +// +// llvm::errs() << "Bitcasting float pointer arguments for function: " << F.getName() << "\n"; +// +// SmallVector, 4> argReplacements; +// // Iterate over all function arguments +// for (Argument & Arg : F.args()) +// { +// // Check if the argument is a pointer to float +// if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) +// { +// llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); +// // Create a bitcast instruction at the beginning of the function +// +// +// Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); +// +// Value *elemPtr = Builder.CreateGEP(&Arg, 0, "elemPtr"); +// //load +// Value *elem = Builder.CreateLoad(elemPtr, "elem"); +// Value *scaled = Builder.CreateFMul(&Arg, ConstantFP::get(Arg.getType()->getPointerElementType(), FRAC_BASE), Arg.getName() + ".scaled"); +// // 加0.5 +// //Value * rounded = Builder.CreateFAdd(scaled, ConstantFP::get(Arg.getType()->getPointerElementType(), 0.5f)); +// Value *rounded = Builder.CreateFAdd(scaled, ConstantFP::get(Arg.getType()->getPointerElementType(), 0.5f), Arg.getName() + ".rounded"); +// //Value * newArg = Builder.CreateFPToSI(rounded, i32PtrType); +// Value *newArg = Builder.CreateFPToSI(rounded, i32PtrType, Arg.getName() + ".quantized"); +// // llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); +// +// +// +// // Store the original argument and the bitcast result +// argReplacements.push_back({&Arg, newArg}); +// } +// } +// +// +// +// // Iterate over the function to replace uses of the original arguments +// for (auto & replacement : argReplacements) +// { +// Argument * oldArg = replacement.first; +// Value * newArg = replacement.second; +// +// // Replace all uses of the old argument with the new bitcasted value +// // 遍历替换所有使用原始参数的地方 +// for (auto & replacement : argReplacements) +// { +// Argument * oldArg = replacement.first; +// Value * newArg = replacement.second; +// +// SmallVector usesToReplace; +// for (auto & U : oldArg->uses()) +// { +// User * user = U.getUser(); +// if (user != newArg) +// { +// usesToReplace.push_back(&U); +// } +// } +// +// for (auto * use : usesToReplace) +// { +// use->set(newArg); +// } +// } +// } +// +//} + void @@ -1069,7 +1146,6 @@ shouldSkipFunction(const std::string & functionName) { // List of function names to skip static const std::unordered_set skipFunctions = { - "llvm.round.f32", "llvm.dbg.declare", "llvm.dbg.value", "llvm.dbg.label", @@ -1077,7 +1153,6 @@ shouldSkipFunction(const std::string & functionName) "fixsqrt", "fixrsqrt", "fixmul", - "sinf", "llvm.sqrt.f64", "llvm.sqrt.f32", "sqrt", @@ -1086,15 +1161,7 @@ shouldSkipFunction(const std::string & functionName) return skipFunctions.find(functionName) != skipFunctions.end(); } -//void -//quantizeArguments(Function & llvmIrFunction, Type * quantizedType) -//{ -// for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) -// { -// auto paramOp = llvmIrFunction.getArg(idx); -// setQuantizedType(paramOp, quantizedType); -// } -//} + void quantizeArguments(llvm::Function &llvmIrFunction, llvm::Type *quantizedType) { @@ -1112,9 +1179,6 @@ void quantizeArguments(llvm::Function &llvmIrFunction, llvm::Type *quantizedType } - - - // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) @@ -1127,7 +1191,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve std::string functionName = llvmIrFunction.getName().str(); if (shouldSkipFunction(functionName)) { - llvm::errs() << "Should Skipping function: " << functionName << "\n"; return; } @@ -1206,7 +1269,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::Load: { llvm::errs() << "Handling load\n"; - handleLoad(llvmIrInstruction, quantizedType); + //handleLoad(llvmIrInstruction, quantizedType); break; } break; From abdf5af7de9ae29844dedac1e8068cda313132c3 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 7 Oct 2024 18:34:59 +0100 Subject: [PATCH 107/213] solved quantization and dequantization issue Addresses #1. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 44 ++- .../newton-irPass-LLVMIR-quantization.cpp | 284 +++++++++++++----- 2 files changed, 226 insertions(+), 102 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 78523de4f..55d8ea315 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -93,14 +93,10 @@ dequantizeResults(StoreInst * storeInst, Function & F) if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) { - llvm::errs() << "Integer pointer type detected, proceeding with dequantization. Operand: " << *pointerOperand << "\n"; + auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); - llvm::errs() << "Loaded value from pointer: " << *loadInst << "\n"; Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); - llvm::errs() << "Converted integer to float: " << *convertedFloat << "\n"; Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 65536.0)); - llvm::errs() << "Divided value by FRAC_BASE: " << *dividedValue << "\n"; - if (auto * bitcastInst = dyn_cast(pointerOperand)) { // if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) @@ -575,25 +571,25 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl /* * simplify the condition of each branch * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - simplifyControlFlow(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } - - legacy::PassManager passManager; - passManager.add(createCFGSimplificationPass()); - passManager.add(createInstSimplifyLegacyPass()); - passManager.add(createGlobalDCEPass()); - passManager.run(*Mod); +// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// simplifyControlFlow(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// legacy::PassManager passManager; +// passManager.add(createCFGSimplificationPass()); +// passManager.add(createInstSimplifyLegacyPass()); +// passManager.add(createGlobalDCEPass()); +// passManager.run(*Mod); // // /* // * remove the functions that are optimized by passes. diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 6a79cfde2..e1b5ef976 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -114,14 +114,14 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorarg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - //llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); - llvm::Value * mulInst = builder.CreateMul(sext1, sext2); - llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + // llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); + llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); + llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); builder.CreateRet(truncInst); functionsToInsert.emplace_back(func); @@ -182,9 +182,9 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), 1.0f / FRAC_BASE)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - //i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + // i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); @@ -285,34 +285,99 @@ updateGlobalVariables(Module * module, Type * quantizedType) } } -void -handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +//void +//handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +//{ +// if (auto * loadInst = dyn_cast(llvmIrInstruction)) +// { +// IRBuilder<> Builder(loadInst); +// +// // Check if the loaded value is of floating-point type +// Type * loadedType = loadInst->getType(); +// // Type * pointerType = loadInst->getPointerOperandType(); +// +// if (loadedType->isFloatingPointTy()) +// { +// llvm::errs() << "Handling normal load instruction\n"; +// // Create a new load instruction with the original pointer operand +// LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, loadInst->getPointerOperand(), loadInst->getName() + ".orig"); +// +// // Replace all uses of the original load instruction with the new converted value +// loadInst->replaceAllUsesWith(newLoadInst); +// +// // Schedule the original load instruction for removal +// loadInst->eraseFromParent(); +// +// llvm::errs() << "Replaced floating-point load with quantized integer load.\n"; +// } +// } +//} + +void quantizePointer(LoadInst *loadInst, IRBuilder<> &Builder, Type *quantizedType, Type *loadedType) +{ + + Value *pointerOperand = loadInst->getPointerOperand(); + llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; + + + Value *loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); + + + Value *scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); + + + Value *quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); + + loadInst->replaceAllUsesWith(quantizedValue); + loadInst->eraseFromParent(); + + llvm::errs() << "Replaced load with quantized integer value.\n"; +} + +void handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) { if (auto * loadInst = dyn_cast(llvmIrInstruction)) { IRBuilder<> Builder(loadInst); - - // Check if the loaded value is of floating-point type Type * loadedType = loadInst->getType(); - // Type * pointerType = loadInst->getPointerOperandType(); - if (loadedType->isFloatingPointTy()) { - llvm::errs() << "Handling normal load instruction\n"; - // Create a new load instruction with the original pointer operand - LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, loadInst->getPointerOperand(), loadInst->getName() + ".orig"); + Value * pointerOperand = loadInst->getPointerOperand(); + Function *parentFunc = loadInst->getFunction(); - // Replace all uses of the original load instruction with the new converted value - loadInst->replaceAllUsesWith(newLoadInst); + if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU") + { + llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; + LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); + llvm::errs() << "New load instruction: " << *newLoadInst << "\n"; + loadInst->replaceAllUsesWith(newLoadInst); + loadInst->eraseFromParent(); + } + +// if (parentFunc->getName() == "MadgwickAHRSupdateIMU") +// { +// llvm::errs() << "Handling load for MadgwickAHRSupdateIMU: " << *pointerOperand << "\n"; +// LoadInst *newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); +// llvm ::errs() << "New load instruction: " << *newLoadInst << "\n"; +// loadInst->replaceAllUsesWith(newLoadInst); +// loadInst->eraseFromParent(); +// } - // Schedule the original load instruction for removal - loadInst->eraseFromParent(); - llvm::errs() << "Replaced floating-point load with quantized integer load.\n"; + else if (!isa(pointerOperand)) + { + quantizePointer(loadInst, Builder, quantizedType, loadedType); + } } } } + + + + + + void handleFAdd(Instruction * inInstruction, Type * quantizedType) { @@ -330,7 +395,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point addition - //Value * newInst = Builder.CreateNSWAdd(op0, op1); + // Value * newInst = Builder.CreateNSWAdd(op0, op1); Value * newInst = Builder.CreateAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition @@ -349,7 +414,6 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) Value * op0 = inInstruction->getOperand(0); Value * op1 = inInstruction->getOperand(1); - // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE if (ConstantFP * constFp = dyn_cast(op0)) { @@ -368,7 +432,7 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point subtraction - //Value * newInst = Builder.CreateNSWSub(op0, op1); + // Value * newInst = Builder.CreateNSWSub(op0, op1); Value * newInst = Builder.CreateSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction @@ -388,8 +452,6 @@ handleFNeg(Instruction * inInstruction, Type * quantizedType) Value * operand = inInstruction->getOperand(0); llvm::errs() << "Operand: " << *operand << "\n"; - - // Create a constant zero of the same integer type as the operand Value * zero = ConstantInt::get(operand->getType(), 0); @@ -704,7 +766,7 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix { llvm::errs() << "One of the operands is an integer constant, using mul\n"; Value * newInst = Builder.CreateMul(lhs, rhs); - //Value * newInst = Builder.CreateNSWMul(lhs, rhs); + // Value * newInst = Builder.CreateNSWMul(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); return; @@ -849,7 +911,6 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) // Iterate over all function arguments for (Argument & Arg : F.args()) { - // Check if the argument is a pointer to float if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) { @@ -863,6 +924,7 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) argReplacements.push_back({&Arg, newArg}); } } + llvm::errs() << "Starting use replacement\n"; // Log added // Iterate over the function to replace uses of the original arguments for (auto & replacement : argReplacements) @@ -871,7 +933,6 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) Value * newArg = replacement.second; // Replace all uses of the old argument with the new bitcasted value - // 遍历替换所有使用原始参数的地方 for (auto & replacement : argReplacements) { Argument * oldArg = replacement.first; @@ -882,6 +943,15 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) for (auto & U : oldArg->uses()) { User * user = U.getUser(); + + // Skip if the use is a Load instruction + if (isa(user)) + { + llvm::errs() << "Skipping load instruction: " << *user << "\n"; + continue; // 跳过 Load 指令 + } + + if (user != newArg) { usesToReplace.push_back(&U); @@ -896,7 +966,8 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) } } -//void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) + +// void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) //{ // // Check if the function is the specific one to be skipped // if (F.getName() == "MadgwickAHRSupdateIMU") @@ -970,9 +1041,7 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) // } // } // -//} - - +// } void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) @@ -1129,9 +1198,7 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) Use & u = *use; u.set(quantized); } - //arg.replaceAllUsesWith(quantized); - - + // arg.replaceAllUsesWith(quantized); llvm::errs() << "Quantizing argument: " << arg.getName() << "\n"; llvm::errs() << " - Scaled value: " << *scaled << "\n"; @@ -1161,23 +1228,88 @@ shouldSkipFunction(const std::string & functionName) return skipFunctions.find(functionName) != skipFunctions.end(); } - - - -void quantizeArguments(llvm::Function &llvmIrFunction, llvm::Type *quantizedType) { +void +quantizeArguments(llvm::Function & llvmIrFunction, llvm::Type * quantizedType) +{ // Check if the function is specifically MadgwickAHRSupdateIMU - if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU") { + if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU") + { llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; // Process each argument of the function - for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) { - auto *paramOp = llvmIrFunction.getArg(idx); + for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) + { + auto * paramOp = llvmIrFunction.getArg(idx); setQuantizedType(paramOp, quantizedType); // Assuming setQuantizedType is defined elsewhere } - } } +void +adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) +{ + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + switch (llvmIrInstruction->getOpcode()) + { + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::SIToFP: + case Instruction::UIToFP: + { + auto sourceOp = llvmIrInstruction->getOperand(0); + if (sourceOp->getType() == llvmIrInstruction->getType()) + { + llvmIrInstruction->replaceAllUsesWith(sourceOp); + llvmIrInstruction->removeFromParent(); + } + } + break; + // case Instruction::ZExt: + // case Instruction::SExt: + // case Instruction::Trunc: + /* + * since the src type changed, adapt the new instruction + * */ + case Instruction::FPExt: + case Instruction::FPTrunc: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + { + newInst = Builder.CreateSIToFP( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + else + { + newInst = Builder.CreateFPCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); + break; + } + case Instruction::BitCast: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = Builder.CreateBitCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); + break; + } + } + } + } +} // Main function to perform LLVM IR auto quantization void @@ -1194,7 +1326,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve return; } - Type * quantizedType; switch (BIT_WIDTH) { @@ -1246,15 +1377,13 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - - Instruction * llvmIrInstruction = &*itBB++; -// if (llvmIrInstruction->getMetadata("quantized_changed")) -// { -// llvm::errs() << "quantized_changed.\n"; -// return; // Skip processing this instruction -// } + // if (llvmIrInstruction->getMetadata("quantized_changed")) + // { + // llvm::errs() << "quantized_changed.\n"; + // return; // Skip processing this instruction + // } llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; switch (llvmIrInstruction->getOpcode()) @@ -1269,7 +1398,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::Load: { llvm::errs() << "Handling load\n"; - //handleLoad(llvmIrInstruction, quantizedType); + handleLoad(llvmIrInstruction, quantizedType); break; } break; @@ -1284,7 +1413,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * If either of the operands is constant, change it to a int value * */ llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; - //setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + // setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); break; case Instruction::FMul: @@ -1324,31 +1453,30 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::SIToFP: case Instruction::UIToFP: - case Instruction::ZExt: case Instruction::SExt: case Instruction::Trunc: case Instruction::FPExt: case Instruction::FPTrunc: - { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) - { - newInst = Builder.CreateSIToFP( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } - else - { - newInst = Builder.CreateFPCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); - break; - } + // { + // IRBuilder<> Builder(llvmIrInstruction); + // Instruction * insertPoint = llvmIrInstruction->getNextNode(); + // Builder.SetInsertPoint(insertPoint); + // Value * newInst = nullptr; + // if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + // { + // newInst = Builder.CreateSIToFP( + // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // } + // else + // { + // newInst = Builder.CreateFPCast( + // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // } + // llvmIrInstruction->replaceAllUsesWith(newInst); + // llvmIrInstruction->removeFromParent(); + // break; + // } case Instruction::BitCast: case Instruction::Ret: From 47649b87d74ec0d3c5b7fb1809137bdb38c34682 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 8 Oct 2024 14:35:20 +0100 Subject: [PATCH 108/213] fix some hard code issue Addresses #1. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 83 +++----- .../newton-irPass-LLVMIR-quantization.cpp | 191 ++++++++---------- 2 files changed, 116 insertions(+), 158 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 55d8ea315..f6a6bead8 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -84,7 +84,7 @@ std::set whitelist = { std::vector toRemove; void -dequantizeResults(StoreInst * storeInst, Function & F) +dequantizeResults(StoreInst * storeInst, Function & F,int maxPrecisionBits) { // IRBuilder<> Builder(storeInst); IRBuilder<> Builder(storeInst->getNextNode()); @@ -93,42 +93,25 @@ dequantizeResults(StoreInst * storeInst, Function & F) if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) { - - auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); - Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 65536.0)); + double fracBase = pow(2.0, maxPrecisionBits); + Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); + if (auto * bitcastInst = dyn_cast(pointerOperand)) { - // if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) - // { - // auto * originalFloatPtr = bitcastInst->getOperand(0); - // llvm::errs() << "Original float pointer: " << *originalFloatPtr << "\n"; - // llvm::errs() << "Storing dequantized value back to original float pointer.\n"; - // Builder.CreateStore(dividedValue, originalFloatPtr); - // storeInst->eraseFromParent(); // Remove the old store instruction - // llvm::errs() << "Original store instruction removed.\n"; - // } - - if (auto * bitcastInst = dyn_cast(pointerOperand)) + if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) { - if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) - { - auto * originalFloatPtr = bitcastInst->getOperand(0); - Builder.CreateStore(dividedValue, originalFloatPtr); - - } + auto * originalFloatPtr = bitcastInst->getOperand(0); + Builder.CreateStore(dividedValue, originalFloatPtr); } } } - else - { - llvm::errs() << "Non-integer pointer type detected, skipping dequantization.\n"; - } } // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist) +processWhitelistedFunctions(Module & module, const std::set & whitelist,int maxPrecisionBits) { for (auto & F : module) { @@ -144,7 +127,7 @@ processWhitelistedFunctions(Module & module, const std::set & white if (auto * storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F); + dequantizeResults(storeInst, F,maxPrecisionBits); } } } @@ -179,7 +162,7 @@ removeQuantizedSuffixInModule(llvm::Module & M) size_t pos = FuncName.find("_quantized"); if (pos != std::string::npos) { - FuncName.erase(pos, 10); // Remove "_quantized" + FuncName.erase(pos, 10); F.setName(FuncName); } } @@ -568,28 +551,28 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); // } // - /* - * simplify the condition of each branch - * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) + /* + * simplify the condition of each branch + * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) // { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } +// simplifyControlFlow(N, boundInfoIt->second, mi); // } +// // else +// // { +// // assert(false); +// // } +// } // -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); +// legacy::PassManager passManager; +// passManager.add(createCFGSimplificationPass()); +// passManager.add(createInstSimplifyLegacyPass()); +// passManager.add(createGlobalDCEPass()); +// passManager.run(*Mod); // // /* // * remove the functions that are optimized by passes. @@ -638,16 +621,16 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // overloadFunc(Mod, callerMap); // Finally, erase old functions - // eraseOldFunctions(); + eraseOldFunctions(); eraseOldGlobals(); // Perform text replacement to remove "_quantized" suffixes removeQuantizedSuffixInModule(*Mod); - // eraseOldInstructions(); + //eraseOldInstructions(); - processWhitelistedFunctions(*Mod, whitelist); + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); const char * homeDir = getenv("HOME"); if (!homeDir) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e1b5ef976..6b7f0cd57 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -118,8 +118,8 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorgetContext()), 1.0f / FRAC_BASE)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - // i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + //i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; @@ -227,6 +227,25 @@ eraseOldGlobals() globalsToErase.clear(); } + +// A list of functions to erase after processing +std::vector functionsToErase; + +// Function to actually erase functions after processing +void +eraseOldFunctions() +{ + llvm::errs() << "Entering eraseOldFunctions\n"; + for (auto * func : functionsToErase) + { + llvm::errs() << "Erasing old function: " << func->getName() << "\n"; + func->eraseFromParent(); + } + functionsToErase.clear(); + llvm::errs() << "Exiting eraseOldFunctions\n"; +} + + void updateGlobalVariables(Module * module, Type * quantizedType) { @@ -395,8 +414,8 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point addition - // Value * newInst = Builder.CreateNSWAdd(op0, op1); - Value * newInst = Builder.CreateAdd(op0, op1); + Value * newInst = Builder.CreateNSWAdd(op0, op1); + //Value * newInst = Builder.CreateAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -432,8 +451,8 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) } // Create fixed-point subtraction - // Value * newInst = Builder.CreateNSWSub(op0, op1); - Value * newInst = Builder.CreateSub(op0, op1); + Value * newInst = Builder.CreateNSWSub(op0, op1); + //Value * newInst = Builder.CreateSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -966,83 +985,6 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) } } - -// void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) -//{ -// // Check if the function is the specific one to be skipped -// if (F.getName() == "MadgwickAHRSupdateIMU") -// { -// llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; -// return; // Early exit if it's the function to skip -// } -// -// llvm::errs() << "Bitcasting float pointer arguments for function: " << F.getName() << "\n"; -// -// SmallVector, 4> argReplacements; -// // Iterate over all function arguments -// for (Argument & Arg : F.args()) -// { -// // Check if the argument is a pointer to float -// if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) -// { -// llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); -// // Create a bitcast instruction at the beginning of the function -// -// -// Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); -// -// Value *elemPtr = Builder.CreateGEP(&Arg, 0, "elemPtr"); -// //load -// Value *elem = Builder.CreateLoad(elemPtr, "elem"); -// Value *scaled = Builder.CreateFMul(&Arg, ConstantFP::get(Arg.getType()->getPointerElementType(), FRAC_BASE), Arg.getName() + ".scaled"); -// // 加0.5 -// //Value * rounded = Builder.CreateFAdd(scaled, ConstantFP::get(Arg.getType()->getPointerElementType(), 0.5f)); -// Value *rounded = Builder.CreateFAdd(scaled, ConstantFP::get(Arg.getType()->getPointerElementType(), 0.5f), Arg.getName() + ".rounded"); -// //Value * newArg = Builder.CreateFPToSI(rounded, i32PtrType); -// Value *newArg = Builder.CreateFPToSI(rounded, i32PtrType, Arg.getName() + ".quantized"); -// // llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); -// -// -// -// // Store the original argument and the bitcast result -// argReplacements.push_back({&Arg, newArg}); -// } -// } -// -// -// -// // Iterate over the function to replace uses of the original arguments -// for (auto & replacement : argReplacements) -// { -// Argument * oldArg = replacement.first; -// Value * newArg = replacement.second; -// -// // Replace all uses of the old argument with the new bitcasted value -// // 遍历替换所有使用原始参数的地方 -// for (auto & replacement : argReplacements) -// { -// Argument * oldArg = replacement.first; -// Value * newArg = replacement.second; -// -// SmallVector usesToReplace; -// for (auto & U : oldArg->uses()) -// { -// User * user = U.getUser(); -// if (user != newArg) -// { -// usesToReplace.push_back(&U); -// } -// } -// -// for (auto * use : usesToReplace) -// { -// use->set(newArg); -// } -// } -// } -// -// } - void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) { @@ -1068,33 +1010,53 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); - // Ensure operand is in the correct type + // Convert the operand to fixed-point format if necessary if (operand->getType()->isFloatingPointTy()) { operand = Builder.CreateFPToSI(operand, quantizedType); } - // Convert the operand from fixed-point (int32) to float - llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); - - // Call llvm.sqrt.f32 to compute the square root of the float value - llvm::Value * sqrtFloat = Builder.CreateCall( - Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), - {operandAsFloat}); - - // Convert the result back to int32 - llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); - - // Apply the shift-left operation (shl i32 %result, 5) - llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); - - // Replace the original instruction with the new fixed-point sqrt result - llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); + // Create call to the fixed-point sqrt function + llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + // No need to apply shl and compensation if it's already done in createFixSqrt + llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); + } +//void +//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto operand = llvmIrCallInstruction->getOperand(0); +// +// // Ensure operand is in the correct type +// if (operand->getType()->isFloatingPointTy()) +// { +// operand = Builder.CreateFPToSI(operand, quantizedType); +// } +// +// // Convert the operand from fixed-point (int32) to float +// llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); +// +// // Call llvm.sqrt.f32 to compute the square root of the float value +// llvm::Value * sqrtFloat = Builder.CreateCall( +// Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), +// {operandAsFloat}); +// +// // Convert the result back to int32 +// llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); +// +// // Apply the shift-left operation (shl i32 %result, 5) +// llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); +// +// // Replace the original instruction with the new fixed-point sqrt result +// llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); +// llvmIrCallInstruction->eraseFromParent(); +//} + void -// handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) + handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; @@ -1104,7 +1066,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "invSqrt" || funcName == "invSqrt_quantized") + if (funcName == "invSqrt" ) { IRBuilder<> Builder(llvmIrCallInstruction); Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); @@ -1112,6 +1074,12 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) // Check if the function has no more uses + { + functionsToErase.push_back(calledFunction); // Add it to the list for later erasure + } } if (!calledFunction->getName().startswith("llvm.dbg.value") && @@ -1125,13 +1093,15 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetNextNode(); Builder.SetInsertPoint(insertPoint); Value * newInst = nullptr; - - // std::string funcName = calledFunction->getName().str(); - - if (funcName == "sqrt" || funcName == "sqrtf" || funcName == "llvm.sqrt.f64" || funcName == "llvm.sqrt.f32") + if (funcName == "sqrt" || funcName == "sqrtf" ) { // For sqrt handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); + if (calledFunction->use_empty()) + { + functionsToErase.push_back(calledFunction); + } + } else { @@ -1311,6 +1281,9 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } } + + + // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) @@ -1518,5 +1491,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // adaptTypeCast(llvmIrFunction, quantizedType); + // Process functions that are whitelisted for dequantization + //processWhitelistedFunctions(*module, whitelist); return; } From 14d65a4be09b5334ba88b40e1451c37217ba3971 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 9 Oct 2024 15:50:49 +0100 Subject: [PATCH 109/213] inline fixmul and fixsqrt Addresses #1. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 8 +- .../newton-irPass-LLVMIR-quantization.cpp | 270 ++++++++++++------ 2 files changed, 189 insertions(+), 89 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index f6a6bead8..2aebc7d1d 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -382,7 +382,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - maxPrecisionBits = 16; + maxPrecisionBits = 12; /* * get const global variables @@ -639,10 +639,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl return; } // Save the optimized IR to a file - std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"; - saveModuleIR(*Mod, fileName); + //std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; + //saveModuleIR(*Mod, fileName); // Save the optimized IR to a file - // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); // 替换为$HOMR diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 6b7f0cd57..1e587cb31 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -2,6 +2,7 @@ #include "newton-irPass-LLVMIR-quantization.h" #include "llvm/Support/raw_ostream.h" #include +#include #include "llvm/IR/Metadata.h" using namespace llvm; @@ -9,6 +10,30 @@ unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 + + + +// 64bit fixed point multiplication +llvm::Value * +performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) +{ + // Sign extend the 32-bit operands to 64-bit integers + llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + + // Perform 64-bit multiplication + llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); + + // Right shift the result to simulate fixed-point division by FRAC_Q + llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + + // Truncate the 64-bit result back to 32-bit integer + llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + + return result32; +} + + extern "C" { // TODO : float version rsqrt llvm::Function * @@ -69,6 +94,7 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -114,12 +140,12 @@ createFixMul(Module * irModule, Type * quantizedType, std::vectorarg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); - //llvm::Value * mulInst = builder.CreateMul(sext1, sext2); + llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); + llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); + llvm::Function::arg_iterator arg2 = &*(++arg1); + llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); + llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); + // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); builder.CreateRet(truncInst); @@ -165,7 +191,8 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE)); - fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + //fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + //performFixedPointMul + llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - //i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + // i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + //llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + //performFixedPointMul + llvm::Value * mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); + //llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + llvm::Value * mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + //llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + llvm::Value * final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); // llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); @@ -227,7 +260,6 @@ eraseOldGlobals() globalsToErase.clear(); } - // A list of functions to erase after processing std::vector functionsToErase; @@ -245,7 +277,6 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } - void updateGlobalVariables(Module * module, Type * quantizedType) { @@ -304,8 +335,8 @@ updateGlobalVariables(Module * module, Type * quantizedType) } } -//void -//handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +// void +// handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) //{ // if (auto * loadInst = dyn_cast(llvmIrInstruction)) // { @@ -330,22 +361,19 @@ updateGlobalVariables(Module * module, Type * quantizedType) // llvm::errs() << "Replaced floating-point load with quantized integer load.\n"; // } // } -//} +// } -void quantizePointer(LoadInst *loadInst, IRBuilder<> &Builder, Type *quantizedType, Type *loadedType) +void +quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { - - Value *pointerOperand = loadInst->getPointerOperand(); + Value * pointerOperand = loadInst->getPointerOperand(); llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; + Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); - Value *loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); - - - Value *scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); - + Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); - Value *quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); + Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); loadInst->replaceAllUsesWith(quantizedValue); loadInst->eraseFromParent(); @@ -353,18 +381,19 @@ void quantizePointer(LoadInst *loadInst, IRBuilder<> &Builder, Type *quantizedTy llvm::errs() << "Replaced load with quantized integer value.\n"; } -void handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +void +handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) { if (auto * loadInst = dyn_cast(llvmIrInstruction)) { IRBuilder<> Builder(loadInst); - Type * loadedType = loadInst->getType(); + Type * loadedType = loadInst->getType(); if (loadedType->isFloatingPointTy()) { - Value * pointerOperand = loadInst->getPointerOperand(); - Function *parentFunc = loadInst->getFunction(); + Value * pointerOperand = loadInst->getPointerOperand(); + Function * parentFunc = loadInst->getFunction(); - if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU") + if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU") { llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); @@ -373,15 +402,14 @@ void handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) loadInst->eraseFromParent(); } -// if (parentFunc->getName() == "MadgwickAHRSupdateIMU") -// { -// llvm::errs() << "Handling load for MadgwickAHRSupdateIMU: " << *pointerOperand << "\n"; -// LoadInst *newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); -// llvm ::errs() << "New load instruction: " << *newLoadInst << "\n"; -// loadInst->replaceAllUsesWith(newLoadInst); -// loadInst->eraseFromParent(); -// } - + // if (parentFunc->getName() == "MadgwickAHRSupdateIMU") + // { + // llvm::errs() << "Handling load for MadgwickAHRSupdateIMU: " << *pointerOperand << "\n"; + // LoadInst *newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); + // llvm ::errs() << "New load instruction: " << *newLoadInst << "\n"; + // loadInst->replaceAllUsesWith(newLoadInst); + // loadInst->eraseFromParent(); + // } else if (!isa(pointerOperand)) { @@ -391,12 +419,6 @@ void handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) } } - - - - - - void handleFAdd(Instruction * inInstruction, Type * quantizedType) { @@ -415,7 +437,7 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) // Create fixed-point addition Value * newInst = Builder.CreateNSWAdd(op0, op1); - //Value * newInst = Builder.CreateAdd(op0, op1); + // Value * newInst = Builder.CreateAdd(op0, op1); // Replace the original FAdd instruction with the new fixed-point addition inInstruction->replaceAllUsesWith(newInst); @@ -452,7 +474,7 @@ handleFSub(Instruction * inInstruction, Type * quantizedType) // Create fixed-point subtraction Value * newInst = Builder.CreateNSWSub(op0, op1); - //Value * newInst = Builder.CreateSub(op0, op1); + // Value * newInst = Builder.CreateSub(op0, op1); // Replace the original FSub instruction with the new fixed-point subtraction inInstruction->replaceAllUsesWith(newInst); @@ -722,8 +744,10 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return false; } + + void -handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fixmul) +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FMul\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -809,22 +833,39 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType, Function * fix bool rhsIsInteger = rhs->getType()->isIntegerTy(); // if (lhsIsInteger && rhsIsInteger) - // { - // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - // //callInst->setCallingConv(llvm::CallingConv::Fast); - // //callInst->setTailCall(true); - // llvmIrInstruction->replaceAllUsesWith(callInst); - // llvmIrInstruction->eraseFromParent(); - // } if (lhsIsInteger && rhsIsInteger) - { - llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); - llvmIrInstruction->eraseFromParent(); - } + + // fixmul +// { +// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; +// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); +// llvmIrInstruction->replaceAllUsesWith(callInst); +// llvmIrInstruction->eraseFromParent(); +// } + // 64bit + { + llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + } + + // 32bit +// { +// llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; +// +// // Perform multiplication directly +// llvm::Value * mulResult = Builder.CreateMul(lhs, rhs, ""); +// +// // Perform right arithmetic shift +// llvm::Value * shiftResult = Builder.CreateLShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); +// +// // Replace all uses of the original instruction with the result of the shift +// llvmIrInstruction->replaceAllUsesWith(shiftResult); +// llvmIrInstruction->eraseFromParent(); +// } + + llvm::errs() << "Finished handling FMul\n"; } void @@ -970,7 +1011,6 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) continue; // 跳过 Load 指令 } - if (user != newArg) { usesToReplace.push_back(&U); @@ -1004,8 +1044,71 @@ handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function llvmIrCallInstruction->eraseFromParent(); } +/* + * // Call sqrt on the floating-point value +llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); +llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + +// Convert the result back to a fixed-point integer +llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); + +// Perform a left shift to scale the result +llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); + +// Apply compensation if FRAC_Q is odd +llvm::Value * finalRes = shlRes; +if (FRAC_Q % 2 != 0) +{ +llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562); +llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); +llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); +finalRes = builder.CreateFPToSI(compensated, quantizedType); +} + + */ + +llvm::Value * +performFixedPointSqrt(IRBuilder<> &builder, Module *irModule, Value *fixedPointValue, Type *quantizedType, int FRAC_Q, LLVMContext &context) +{ + // Convert the fixed-point value to floating-point for sqrt calculation + llvm::Value *fpValue = builder.CreateSIToFP(fixedPointValue, llvm::Type::getFloatTy(context)); + + // Call sqrt on the floating-point value + llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value *sqrtResult = builder.CreateCall(sqrtFunc, {fpValue}); + + // Convert the result back to a fixed-point integer + llvm::Value *res = builder.CreateFPToSI(sqrtResult, quantizedType); + + // Perform a left shift to scale the result (FRAC_Q / 2) + llvm::Value *shlRes = builder.CreateShl(res, FRAC_Q / 2); + + // Initialize the final result as the shifted result + llvm::Value *finalRes = shlRes; + + // Apply compensation if FRAC_Q is odd + if (FRAC_Q % 2 != 0) + { + // Compensation factor for odd FRAC_Q (1.414213562 ≈ sqrt(2)) + llvm::Value *compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562f); + + // Convert the shifted result back to float + llvm::Value *fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); + + // Multiply by the compensation factor to adjust for odd FRAC_Q + llvm::Value *compensated = builder.CreateFMul(fpShlRes, compensationFactor); + + // Convert the compensated value back to a fixed-point integer + finalRes = builder.CreateFPToSI(compensated, quantizedType); + } + + return finalRes; + +} + + void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); @@ -1017,15 +1120,16 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function } // Create call to the fixed-point sqrt function - llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + //手动写减少call overhead + llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); // No need to apply shl and compensation if it's already done in createFixSqrt llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); - } -//void -//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +// void +// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) //{ // IRBuilder<> Builder(llvmIrCallInstruction); // auto operand = llvmIrCallInstruction->getOperand(0); @@ -1053,11 +1157,11 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function // // Replace the original instruction with the new fixed-point sqrt result // llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); // llvmIrCallInstruction->eraseFromParent(); -//} +// } void -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); @@ -1066,7 +1170,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetName().str(); - if (funcName == "invSqrt" ) + if (funcName == "invSqrt") { IRBuilder<> Builder(llvmIrCallInstruction); Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); @@ -1076,9 +1180,9 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) // Check if the function has no more uses + if (calledFunction->use_empty()) // Check if the function has no more uses { - functionsToErase.push_back(calledFunction); // Add it to the list for later erasure + functionsToErase.push_back(calledFunction); // Add it to the list for later erasure } } @@ -1093,15 +1197,14 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetNextNode(); Builder.SetInsertPoint(insertPoint); Value * newInst = nullptr; - if (funcName == "sqrt" || funcName == "sqrtf" ) + if (funcName == "sqrt" || funcName == "sqrtf") { // For sqrt - handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); + handleSqrtCall(llvmIrCallInstruction, quantizedType); if (calledFunction->use_empty()) { functionsToErase.push_back(calledFunction); } - } else { @@ -1281,9 +1384,6 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } } - - - // Main function to perform LLVM IR auto quantization void irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) @@ -1342,8 +1442,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * generate hardcode function * */ - llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + //llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) @@ -1365,7 +1465,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(cast(llvmIrInstruction), quantizedType); break; case Instruction::Call: - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); break; case Instruction::GetElementPtr: case Instruction::Load: @@ -1391,7 +1491,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FMul: { - handleFMul(llvmIrInstruction, quantizedType, fixmul); + handleFMul(llvmIrInstruction, quantizedType); break; } @@ -1492,6 +1592,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // adaptTypeCast(llvmIrFunction, quantizedType); // Process functions that are whitelisted for dequantization - //processWhitelistedFunctions(*module, whitelist); + // processWhitelistedFunctions(*module, whitelist); return; } From ef1432ef7fa123208205b693f08767e1bbcbb98e Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 9 Oct 2024 16:36:55 +0100 Subject: [PATCH 110/213] env used for test diff fracq Addresses #1. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 2 +- .../newton-irPass-LLVMIR-quantization.cpp | 74 +++++++++---------- 2 files changed, 34 insertions(+), 42 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 2aebc7d1d..0c24d069c 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -382,7 +382,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - maxPrecisionBits = 12; + maxPrecisionBits = 13; /* * get const global variables diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 1e587cb31..3387e788d 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1107,61 +1107,51 @@ performFixedPointSqrt(IRBuilder<> &builder, Module *irModule, Value *fixedPointV } +//void +//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto operand = llvmIrCallInstruction->getOperand(0); +// +// // Convert the operand to fixed-point format if necessary +// if (operand->getType()->isFloatingPointTy()) +// { +// operand = Builder.CreateFPToSI(operand, quantizedType); +// } +// +// // Create call to the fixed-point sqrt function +// //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); +// //手动写减少call overhead +// llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); +// // No need to apply shl and compensation if it's already done in createFixSqrt +// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); +// llvmIrCallInstruction->eraseFromParent(); +//} + void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) { IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); - // Convert the operand to fixed-point format if necessary + // Ensure operand is in the correct type if (operand->getType()->isFloatingPointTy()) { operand = Builder.CreateFPToSI(operand, quantizedType); } // Create call to the fixed-point sqrt function - //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - //手动写减少call overhead - llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); - // No need to apply shl and compensation if it's already done in createFixSqrt + llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + + // Replace the original instruction with the new fixed-point sqrt result llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); } -// void -// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) -//{ -// IRBuilder<> Builder(llvmIrCallInstruction); -// auto operand = llvmIrCallInstruction->getOperand(0); -// -// // Ensure operand is in the correct type -// if (operand->getType()->isFloatingPointTy()) -// { -// operand = Builder.CreateFPToSI(operand, quantizedType); -// } -// -// // Convert the operand from fixed-point (int32) to float -// llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); -// -// // Call llvm.sqrt.f32 to compute the square root of the float value -// llvm::Value * sqrtFloat = Builder.CreateCall( -// Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), -// {operandAsFloat}); -// -// // Convert the result back to int32 -// llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); -// -// // Apply the shift-left operation (shl i32 %result, 5) -// llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); -// -// // Replace the original instruction with the new fixed-point sqrt result -// llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); -// llvmIrCallInstruction->eraseFromParent(); -// } - void -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) +//handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) + handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); @@ -1200,7 +1190,8 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) { functionsToErase.push_back(calledFunction); @@ -1443,7 +1434,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * generate hardcode function * */ //llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) @@ -1465,7 +1456,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(cast(llvmIrInstruction), quantizedType); break; case Instruction::Call: - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); + //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); break; case Instruction::GetElementPtr: case Instruction::Load: From d364b2e035986d58715081e661615d0c8f2fa6a7 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 11 Nov 2024 12:40:46 +0000 Subject: [PATCH 111/213] update support fpr Mahoney Addresses #1. --- ...d65a4be09b5334ba88b40e1451c37217ba3971.txt | 48 +++ ...6938a71d4d9f26283a2a7f3a9b4b5011591d18.txt | 48 +++ ...649b87d74ec0d3c5b7fb1809137bdb38c34682.txt | 48 +++ ...8766310a7770aba17a61bebddf161d7c15081e.txt | 48 +++ ...3b089be089313d9b71f9bc677d13b14fabf31e.txt | 48 +++ ...31c4f461edb6b6e796ca8432ee18a0a301b39a.txt | 48 +++ ...7e5888fd2234f368f99e40a827f00580284241.txt | 48 +++ ...df5af7de9ae29844dedac1e8068cda313132c3.txt | 48 +++ .../newton-irPass-LLVMIR-dequantization.cpp | 386 ------------------ .../newton-irPass-LLVMIR-quantization.cpp | 302 ++++++-------- 10 files changed, 517 insertions(+), 555 deletions(-) create mode 100644 analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt create mode 100644 analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt create mode 100644 analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt create mode 100644 analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt create mode 100644 analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt create mode 100644 analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt create mode 100644 analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt create mode 100644 analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt delete mode 100644 src/newton/newton-irPass-LLVMIR-dequantization.cpp diff --git a/analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt b/analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt new file mode 100644 index 000000000..e6c77574d --- /dev/null +++ b/analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt @@ -0,0 +1,48 @@ + +changeset: 1704:14d65a4be09b5334ba88b40e1451c37217ba3971 +char kNewtonVersion[] = "0.3-alpha-1704 (14d65a4be09b5334ba88b40e1451c37217ba3971) (build 10-09-2024-16:36-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt b/analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt new file mode 100644 index 000000000..fac9d0862 --- /dev/null +++ b/analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt @@ -0,0 +1,48 @@ + +changeset: 1697:406938a71d4d9f26283a2a7f3a9b4b5011591d18 +char kNewtonVersion[] = "0.3-alpha-1697 (406938a71d4d9f26283a2a7f3a9b4b5011591d18) (build 10-05-2024-12:46-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt b/analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt new file mode 100644 index 000000000..feba021d7 --- /dev/null +++ b/analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt @@ -0,0 +1,48 @@ + +changeset: 1703:47649b87d74ec0d3c5b7fb1809137bdb38c34682 +char kNewtonVersion[] = "0.3-alpha-1703 (47649b87d74ec0d3c5b7fb1809137bdb38c34682) (build 10-09-2024-15:50-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt b/analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt new file mode 100644 index 000000000..d1a04ea0b --- /dev/null +++ b/analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt @@ -0,0 +1,48 @@ + +changeset: 1699:4d8766310a7770aba17a61bebddf161d7c15081e +char kNewtonVersion[] = "0.3-alpha-1699 (4d8766310a7770aba17a61bebddf161d7c15081e) (build 10-07-2024-11:31-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt b/analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt new file mode 100644 index 000000000..d9a24de16 --- /dev/null +++ b/analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt @@ -0,0 +1,48 @@ + +changeset: 1701:813b089be089313d9b71f9bc677d13b14fabf31e +char kNewtonVersion[] = "0.3-alpha-1701 (813b089be089313d9b71f9bc677d13b14fabf31e) (build 10-07-2024-18:35-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt b/analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt new file mode 100644 index 000000000..aab128afb --- /dev/null +++ b/analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt @@ -0,0 +1,48 @@ + +changeset: 1700:8531c4f461edb6b6e796ca8432ee18a0a301b39a +char kNewtonVersion[] = "0.3-alpha-1700 (8531c4f461edb6b6e796ca8432ee18a0a301b39a) (build 10-07-2024-16:26-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt b/analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt new file mode 100644 index 000000000..eefbeee5d --- /dev/null +++ b/analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt @@ -0,0 +1,48 @@ + +changeset: 1698:9a7e5888fd2234f368f99e40a827f00580284241 +char kNewtonVersion[] = "0.3-alpha-1698 (9a7e5888fd2234f368f99e40a827f00580284241) (build 10-05-2024-14:17-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt b/analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt new file mode 100644 index 000000000..662a1cc0f --- /dev/null +++ b/analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt @@ -0,0 +1,48 @@ + +changeset: 1702:abdf5af7de9ae29844dedac1e8068cda313132c3 +char kNewtonVersion[] = "0.3-alpha-1702 (abdf5af7de9ae29844dedac1e8068cda313132c3) (build 10-08-2024-14:35-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-dequantization.cpp b/src/newton/newton-irPass-LLVMIR-dequantization.cpp deleted file mode 100644 index 7c6ae7532..000000000 --- a/src/newton/newton-irPass-LLVMIR-dequantization.cpp +++ /dev/null @@ -1,386 +0,0 @@ -#include "newton-irPass-LLVMIR-dequantization.h" -#include -using namespace llvm; - -#define FRAC_Q 16 -#define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 32 - -extern "C" { - -// Handle load and store instructions -void -handleLoadStoreInstructions(Function & llvmIrFunction, Type * floatType) -{ - llvm::errs() << "Entering handleLoadStoreInstructions\n"; - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) - { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) - { - Instruction * llvmIrInstruction = &*itBB++; - if (llvmIrInstruction->getOpcode() == Instruction::Load) - { - if (auto loadInst = dyn_cast(llvmIrInstruction)) - { - auto ptr = loadInst->getPointerOperand(); - if (isQuantizedType(ptr->getType()->getPointerElementType())) - { - ptr->mutateType(floatType->getPointerTo()); - } - } - } - else if (llvmIrInstruction->getOpcode() == Instruction::Store) - { - if (auto storeInst = dyn_cast(llvmIrInstruction)) - { - auto ptr = storeInst->getPointerOperand(); - if (isQuantizedType(ptr->getType()->getPointerElementType())) - { - ptr->mutateType(floatType->getPointerTo()); - } - } - } - } - } - llvm::errs() << "Exiting handleLoadStoreInstructions\n"; -} - -// Dequantize constants within an instruction -void -dequantizeConstant(Instruction * inInstruction, Type * floatType) -{ - llvm::errs() << "Entering dequantizeConstant\n"; - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) - { - Value * inValue = inInstruction->getOperand(idx); - - if (!isa(inValue)) - { - continue; - } - - auto * constInt = dyn_cast(inValue); - Value * newValue = nullptr; - - if (inValue->getType()->isIntegerTy()) - { - // Convert fixed-point to float - int64_t fixedPointValue = constInt->getSExtValue(); - double floatValue = static_cast(fixedPointValue) / FRAC_BASE; - newValue = ConstantFP::get(floatType, floatValue); - } - - if (newValue) - { - inInstruction->replaceUsesOfWith(inValue, newValue); - } - } - llvm::errs() << "Exiting dequantizeConstant\n"; -} - -// Handle floating-point instructions and dequantize them -void -handleFPInstructions(Instruction * inInstruction, Type * floatType) -{ - llvm::errs() << "Entering handleFPInstructions\n"; - IRBuilder<> Builder(inInstruction); - switch (inInstruction->getOpcode()) - { - case Instruction::Add: - case Instruction::Sub: - case Instruction::Mul: - case Instruction::UDiv: - case Instruction::SDiv: - case Instruction::URem: - case Instruction::SRem: - { - Value * lhs = inInstruction->getOperand(0); - Value * rhs = inInstruction->getOperand(1); - if (isQuantizedType(lhs->getType())) - { - // Convert integer to float - lhs = Builder.CreateSIToFP(lhs, floatType); - } - if (isQuantizedType(rhs->getType())) - { - rhs = Builder.CreateSIToFP(rhs, floatType); - } - Instruction * newInst = nullptr; - switch (inInstruction->getOpcode()) - { - case Instruction::Add: - newInst = BinaryOperator::CreateFAdd(lhs, rhs, "", inInstruction); - break; - case Instruction::Sub: - newInst = BinaryOperator::CreateFSub(lhs, rhs, "", inInstruction); - break; - case Instruction::Mul: - newInst = BinaryOperator::CreateFMul(lhs, rhs, "", inInstruction); - break; - case Instruction::UDiv: - case Instruction::SDiv: - newInst = BinaryOperator::CreateFDiv(lhs, rhs, "", inInstruction); - break; - case Instruction::URem: - case Instruction::SRem: - newInst = BinaryOperator::CreateFRem(lhs, rhs, "", inInstruction); - break; - } - inInstruction->replaceAllUsesWith(newInst); - inInstruction->eraseFromParent(); - break; - } - default: - llvm::errs() << "Unhandled instruction: " << *inInstruction << "\n"; - break; - } - llvm::errs() << "Exiting handleFPInstructions\n"; -} - -// Create a dequantized function with the same signature as the original function -Function * -createDequantizedFunction(Function & llvmIrFunction, Type * floatType) -{ - std::vector params; - for (auto & arg : llvmIrFunction.args()) - { - if (isQuantizedType(arg.getType())) - { - // Change quantized type to float - params.push_back(floatType); - llvm::errs() << "Dequantizing parameter: " << arg.getName() << " from " << *arg.getType() << " to " << *floatType << "\n"; - } - else - { - // Keep original type - params.push_back(arg.getType()); - } - } - - Type * returnType = isQuantizedType(llvmIrFunction.getReturnType()) ? floatType : llvmIrFunction.getReturnType(); - FunctionType * newFuncType = FunctionType::get(returnType, params, false); - Function * newFunc = Function::Create(newFuncType, llvmIrFunction.getLinkage(), llvmIrFunction.getName() + "_dequantized", llvmIrFunction.getParent()); - return newFunc; -} - -// Clone the function body from the original function to the new dequantized function -void -cloneFunctionBody(Function & oldFunc, Function * newFunc) -{ - ValueToValueMapTy vmap; - Function::arg_iterator newArgIt = newFunc->arg_begin(); - for (auto & oldArg : oldFunc.args()) - { - newArgIt->setName(oldArg.getName()); - vmap[&oldArg] = &*newArgIt++; - } - SmallVector returns; - llvm::CloneFunctionInto(newFunc, &oldFunc, vmap, CloneFunctionChangeType::LocalChangesOnly, returns); -} - -// Replace all uses of the original function with the new dequantized function -void -replaceFunctionUses(Function & oldFunc, Function * newFunc) -{ - std::vector users(oldFunc.user_begin(), oldFunc.user_end()); - for (auto * U : users) - { - if (CallInst * callInst = dyn_cast(U)) - { - std::vector args; - for (auto & arg : callInst->args()) - { - args.push_back(arg); - } - CallInst * newCall = CallInst::Create(newFunc, args, "", callInst); - newCall->setCallingConv(callInst->getCallingConv()); - newCall->setDebugLoc(callInst->getDebugLoc()); - llvm::errs() << "Replacing call: " << *callInst << " with " << *newCall << "\n"; - callInst->replaceAllUsesWith(newCall); - callInst->eraseFromParent(); - callInst->eraseFromParent(); - } - } -} - -// Set the dequantized type for a given value -void -setDequantizedType(Value * inValue, Type * floatType) -{ - llvm::errs() << "Entering setDequantizedType\n"; - if (inValue == nullptr) - { - llvm::errs() << "inValue is nullptr\n"; - return; - } - auto valueType = inValue->getType(); - unsigned pointerAddr; - bool isPointer = false; - - if (valueType != nullptr) - { - if (valueType->isPointerTy()) - { - isPointer = true; - pointerAddr = valueType->getPointerAddressSpace(); - valueType = valueType->getPointerElementType(); - } - - if (isQuantizedType(valueType) || valueType->isArrayTy()) - { - llvm::errs() << "Original type: " << *valueType << "\n"; - llvm::errs() << "New dequantized type: " << *floatType << "\n"; - if (isPointer) - { - inValue->mutateType(floatType->getPointerTo(pointerAddr)); - } - else - { - inValue->mutateType(floatType); - } - } - else - { - llvm::errs() << "Unsupported type for dequantization: " << *valueType << "\n"; - } - } - else - { - llvm::errs() << "Value type is nullptr\n"; - } -} -} - -// Adapt type casts to the dequantized type -void -adaptTypeCast(Function & llvmIrFunction, Type * floatType) -{ - llvm::errs() << "Entering adaptTypeCast\n"; - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) - { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) - { - Instruction * llvmIrInstruction = &*itBB++; - switch (llvmIrInstruction->getOpcode()) - { - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::UIToFP: - case Instruction::SIToFP: - { - IRBuilder<> Builder(llvmIrInstruction); - Value * operand = llvmIrInstruction->getOperand(0); - Value * newInst = nullptr; - - if (llvmIrInstruction->getOpcode() == Instruction::FPToUI || llvmIrInstruction->getOpcode() == Instruction::FPToSI) - { - if (isQuantizedType(operand->getType())) - { - newInst = Builder.CreateSIToFP(operand, floatType); - } - } - else if (llvmIrInstruction->getOpcode() == Instruction::UIToFP || llvmIrInstruction->getOpcode() == Instruction::SIToFP) - { - if (isQuantizedType(llvmIrInstruction->getType())) - { - newInst = Builder.CreateSIToFP(operand, floatType); - } - } - - if (newInst) - { - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - } - break; - } - case Instruction::FPExt: - case Instruction::FPTrunc: - { - IRBuilder<> Builder(llvmIrInstruction); - Value * operand = llvmIrInstruction->getOperand(0); - Value * newInst = nullptr; - - if (isQuantizedType(operand->getType())) - { - newInst = Builder.CreateSIToFP(operand, floatType); - } - else - { - newInst = Builder.CreateFPCast(operand, floatType); - } - - if (newInst) - { - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - } - break; - } - case Instruction::BitCast: - { - IRBuilder<> Builder(llvmIrInstruction); - Value * operand = llvmIrInstruction->getOperand(0); - Value * newInst = Builder.CreateBitCast(operand, floatType); - - if (newInst) - { - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - } - break; - } - default: - break; - } - } - } - llvm::errs() << "Exiting adaptTypeCast\n"; -} - -// Main function to perform LLVM IR auto dequantization -void -irPassLLVMIRAutoDequantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert) -{ - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto dequantization.\n"); - llvm::errs() << "Entering irPassLLVMIRAutoDequantization\n"; - - std::string functionName = llvmIrFunction.getName().str(); - if (functionName == "llvm.dbg.declare" || functionName == "llvm.dbg.value" || functionName == "llvm.dbg.label" || functionName == "fixmul" || functionName == "floatIntMul" || functionName == "sqrt") - { - llvm::errs() << "Skipping function: " << functionName << "\n"; - return; - } - - Type * floatType = Type::getFloatTy(llvmIrFunction.getContext()); - - Function * newFunc = createDequantizedFunction(llvmIrFunction, floatType); - cloneFunctionBody(llvmIrFunction, newFunc); - replaceFunctionUses(llvmIrFunction, newFunc); - - llvm::errs() << "Finished handling function signature for: " << newFunc->getName() << "\n"; - - handleLoadStoreInstructions(llvmIrFunction, floatType); - - for (BasicBlock & BB : llvmIrFunction) - { - for (BasicBlock::iterator it = BB.begin(); it != BB.end();) - { - Instruction * I = &*it++; - if (isQuantizedType(I->getType())) - { - setDequantizedType(I, floatType); - } - else - { - handleFPInstructions(I, floatType); - dequantizeConstant(I, floatType); - } - } - } - - adaptTypeCast(llvmIrFunction, floatType); - - llvm::errs() << "Exiting irPassLLVMIRAutoDequantization\n"; -} -} \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 3387e788d..30f63fc47 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -2,7 +2,6 @@ #include "newton-irPass-LLVMIR-quantization.h" #include "llvm/Support/raw_ostream.h" #include -#include #include "llvm/IR/Metadata.h" using namespace llvm; @@ -10,30 +9,6 @@ unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 - - - -// 64bit fixed point multiplication -llvm::Value * -performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) -{ - // Sign extend the 32-bit operands to 64-bit integers - llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); - - // Perform 64-bit multiplication - llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); - - // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); - - // Truncate the 64-bit result back to 32-bit integer - llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); - - return result32; -} - - extern "C" { // TODO : float version rsqrt llvm::Function * @@ -94,7 +69,6 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -191,8 +165,7 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE)); - //fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); - //performFixedPointMul - + fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); @@ -219,14 +190,10 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE)), quantizedType); // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - //llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - //performFixedPointMul - llvm::Value * mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); - //llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - llvm::Value * mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); + llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - //llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - llvm::Value * final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); + llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); // llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); // llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); @@ -393,7 +360,7 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) Value * pointerOperand = loadInst->getPointerOperand(); Function * parentFunc = loadInst->getFunction(); - if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU") + if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU" || parentFunc->getName() == "MahonyAHRSupdateIMU") { llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); @@ -744,10 +711,69 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return false; } +//void +//updateMulInstructionBasedOnRange(State * N, Instruction * mulInst, Value * lhs, Value * rhs, +// const std::map> & virtualRegisterRange, +// IRBuilder<> & Builder, int precisionBits) +//{ +// // Retrieve ranges for both operands +// auto lhsRange = virtualRegisterRange.find(lhs); +// auto rhsRange = virtualRegisterRange.find(rhs); +// +// if (lhsRange != virtualRegisterRange.end() && rhsRange != virtualRegisterRange.end()) +// { +// // Apply quantization factor to the range +// llvm::errs() << "LHS Range: [" << lhsRange->second.first << ", " << lhsRange->second.second << "]\n"; +// llvm::errs() << "RHS Range: [" << rhsRange->second.first << ", " << rhsRange->second.second << "]\n"; +// +// double quantizationFactor = pow(2.0, precisionBits); +// +// // For quantization: scaling and rounding +// auto quantize = [&](double value) -> double { +// return floor(value * quantizationFactor + 0.5); +// }; +// +// double lhsMin = quantize(lhsRange->second.first); +// double lhsMax = quantize(lhsRange->second.second); +// double rhsMin = quantize(rhsRange->second.first); +// double rhsMax = quantize(rhsRange->second.second); +// +// // Log the quantized values +// llvm::errs() << "Quantized LHS Min: " << lhsMin << ", Quantized LHS Max: " << lhsMax << "\n"; +// llvm::errs() << "Quantized RHS Min: " << rhsMin << ", Quantized RHS Max: " << rhsMax << "\n"; +// +// // Calculate the range for the multiplication result +// double resultMin = std::min({std::abs(lhsMin * rhsMin), std::abs(lhsMin * rhsMax), std::abs(lhsMax * rhsMin), std::abs(lhsMax * rhsMax)}); +// double resultMax = std::max({std::abs(lhsMin * rhsMin), std::abs(lhsMin * rhsMax), std::abs(lhsMax * rhsMin), std::abs(lhsMax * rhsMax)}); +// +// // If the result exceeds INT32_MAX, switch to i64 multiplication +// if (resultMax > INT32_MAX) +// { +// llvm::errs() << "Switching to i64 multiplication\n"; +// +// // Cast the operands to i64 if necessary +// Value * lhsI64 = Builder.CreateIntCast(lhs, Type::getInt64Ty(lhs->getContext()), true); +// Value * rhsI64 = Builder.CreateIntCast(rhs, Type::getInt64Ty(rhs->getContext()), true); +// +// // Replace the mul instruction with i64 multiplication +// Value * i64MulResult = Builder.CreateMul(lhsI64, rhsI64); +// mulInst->replaceAllUsesWith(i64MulResult); +// mulInst->eraseFromParent(); +// } +// else +// { +// // Proceed with i32 multiplication +// llvm::errs() << "Using i32 multiplication\n"; +// } +// } +//} + -void -handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) +void handleFMul(Instruction *llvmIrInstruction, Type *quantizedType, Function *fixmul, + const std::map> &typeRange, + const std::map>> &virtualRegisterVectorRange, + int precisionBits) { llvm::errs() << "Handling FMul\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -833,39 +859,23 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) bool rhsIsInteger = rhs->getType()->isIntegerTy(); // if (lhsIsInteger && rhsIsInteger) + // { + // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + // //callInst->setCallingConv(llvm::CallingConv::Fast); + // //callInst->setTailCall(true); + // llvmIrInstruction->replaceAllUsesWith(callInst); + // llvmIrInstruction->eraseFromParent(); + // } if (lhsIsInteger && rhsIsInteger) - - // fixmul -// { -// llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; -// llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); -// llvmIrInstruction->replaceAllUsesWith(callInst); -// llvmIrInstruction->eraseFromParent(); -// } - // 64bit - { - llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - } - - // 32bit -// { -// llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; -// -// // Perform multiplication directly -// llvm::Value * mulResult = Builder.CreateMul(lhs, rhs, ""); -// -// // Perform right arithmetic shift -// llvm::Value * shiftResult = Builder.CreateLShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); -// -// // Replace all uses of the original instruction with the result of the shift -// llvmIrInstruction->replaceAllUsesWith(shiftResult); -// llvmIrInstruction->eraseFromParent(); -// } - - llvm::errs() << "Finished handling FMul\n"; + { + // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + llvm::errs() << "Both operands are integers, performing range-based analysis...\n"; + llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + llvmIrInstruction->replaceAllUsesWith(callInst); + llvmIrInstruction->eraseFromParent(); + } } void @@ -961,7 +971,11 @@ void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { // Check if the function is the specific one to be skipped - if (F.getName() == "MadgwickAHRSupdateIMU") + if (F.getName() == "MadgwickAHRSupdateIMU" || F.getName() == "MahonyAHRSupdateIMU") + { + llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; + return; // Early exit if it's the function to skip + } { llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; return; // Early exit if it's the function to skip @@ -1044,114 +1058,59 @@ handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function llvmIrCallInstruction->eraseFromParent(); } -/* - * // Call sqrt on the floating-point value -llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); -llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); - -// Convert the result back to a fixed-point integer -llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); - -// Perform a left shift to scale the result -llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); - -// Apply compensation if FRAC_Q is odd -llvm::Value * finalRes = shlRes; -if (FRAC_Q % 2 != 0) -{ -llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562); -llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); -llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); -finalRes = builder.CreateFPToSI(compensated, quantizedType); -} - - */ - -llvm::Value * -performFixedPointSqrt(IRBuilder<> &builder, Module *irModule, Value *fixedPointValue, Type *quantizedType, int FRAC_Q, LLVMContext &context) +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) { - // Convert the fixed-point value to floating-point for sqrt calculation - llvm::Value *fpValue = builder.CreateSIToFP(fixedPointValue, llvm::Type::getFloatTy(context)); - - // Call sqrt on the floating-point value - llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); - llvm::Value *sqrtResult = builder.CreateCall(sqrtFunc, {fpValue}); - - // Convert the result back to a fixed-point integer - llvm::Value *res = builder.CreateFPToSI(sqrtResult, quantizedType); - - // Perform a left shift to scale the result (FRAC_Q / 2) - llvm::Value *shlRes = builder.CreateShl(res, FRAC_Q / 2); - - // Initialize the final result as the shifted result - llvm::Value *finalRes = shlRes; + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); - // Apply compensation if FRAC_Q is odd - if (FRAC_Q % 2 != 0) + // Convert the operand to fixed-point format if necessary + if (operand->getType()->isFloatingPointTy()) { - // Compensation factor for odd FRAC_Q (1.414213562 ≈ sqrt(2)) - llvm::Value *compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562f); - - // Convert the shifted result back to float - llvm::Value *fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); - - // Multiply by the compensation factor to adjust for odd FRAC_Q - llvm::Value *compensated = builder.CreateFMul(fpShlRes, compensationFactor); - - // Convert the compensated value back to a fixed-point integer - finalRes = builder.CreateFPToSI(compensated, quantizedType); + operand = Builder.CreateFPToSI(operand, quantizedType); } - return finalRes; - + // Create call to the fixed-point sqrt function + llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + // No need to apply shl and compensation if it's already done in createFixSqrt + llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); + llvmIrCallInstruction->eraseFromParent(); } - -//void -//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +// void +// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) //{ // IRBuilder<> Builder(llvmIrCallInstruction); // auto operand = llvmIrCallInstruction->getOperand(0); // -// // Convert the operand to fixed-point format if necessary +// // Ensure operand is in the correct type // if (operand->getType()->isFloatingPointTy()) // { // operand = Builder.CreateFPToSI(operand, quantizedType); // } // -// // Create call to the fixed-point sqrt function -// //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); -// //手动写减少call overhead -// llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); -// // No need to apply shl and compensation if it's already done in createFixSqrt -// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); +// // Convert the operand from fixed-point (int32) to float +// llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); +// +// // Call llvm.sqrt.f32 to compute the square root of the float value +// llvm::Value * sqrtFloat = Builder.CreateCall( +// Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), +// {operandAsFloat}); +// +// // Convert the result back to int32 +// llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); +// +// // Apply the shift-left operation (shl i32 %result, 5) +// llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); +// +// // Replace the original instruction with the new fixed-point sqrt result +// llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); // llvmIrCallInstruction->eraseFromParent(); -//} - -void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) -{ - IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); - - // Ensure operand is in the correct type - if (operand->getType()->isFloatingPointTy()) - { - operand = Builder.CreateFPToSI(operand, quantizedType); - } - - // Create call to the fixed-point sqrt function - llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - - // Replace the original instruction with the new fixed-point sqrt result - llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); - llvmIrCallInstruction->eraseFromParent(); -} +// } void -//handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) - handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); @@ -1190,7 +1149,6 @@ void if (funcName == "sqrt" || funcName == "sqrtf") { // For sqrt - //handleSqrtCall(llvmIrCallInstruction, quantizedType); handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); if (calledFunction->use_empty()) { @@ -1227,7 +1185,11 @@ void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { // Skip the function if it is MadgwickAHRSupdateIMU - if (func.getName() == "MadgwickAHRSupdateIMU") + if (func.getName() == "MadgwickAHRSupdateIMU" ||func.getName() == "MahonyAHRSupdateIMU") + { + llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; + return; + } { llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; return; @@ -1296,7 +1258,7 @@ void quantizeArguments(llvm::Function & llvmIrFunction, llvm::Type * quantizedType) { // Check if the function is specifically MadgwickAHRSupdateIMU - if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU") + if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU"|| llvmIrFunction.getName() =="MahonyAHRSupdateIMU") { llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; @@ -1377,7 +1339,10 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) // Main function to perform LLVM IR auto quantization void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) +// irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) + +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const std::map> & typeRange, + const std::map>> & virtualRegisterVectorRange, std::vector & functionsToInsert, int maxPrecisionBits) { FRAC_Q = maxPrecisionBits; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); @@ -1433,7 +1398,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve /* * generate hardcode function * */ - //llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); @@ -1456,7 +1421,6 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve handleAlloca(cast(llvmIrInstruction), quantizedType); break; case Instruction::Call: - //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); break; case Instruction::GetElementPtr: @@ -1483,7 +1447,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve case Instruction::FMul: { - handleFMul(llvmIrInstruction, quantizedType); + handleFMul(llvmIrInstruction, quantizedType, fixmul, typeRange,virtualRegisterVectorRange, maxPrecisionBits); break; } @@ -1586,4 +1550,4 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve // Process functions that are whitelisted for dequantization // processWhitelistedFunctions(*module, whitelist); return; -} +} \ No newline at end of file From a51bf1c6058a1292ee80592733c87130294252fe Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 12 Nov 2024 14:35:28 +0000 Subject: [PATCH 112/213] have some bug, save before roll back Addresses #1. --- ...1432ef7fa123208205b693f08767e1bbcbb98e.txt | 48 +++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 174 ++++++++++++++---- .../newton-irPass-LLVMIR-quantization.cpp | 42 +++++ .../newton-irPass-LLVMIR-quantization.h | 7 +- 4 files changed, 238 insertions(+), 33 deletions(-) create mode 100644 analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt diff --git a/analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt b/analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt new file mode 100644 index 000000000..3460d0b77 --- /dev/null +++ b/analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt @@ -0,0 +1,48 @@ + +changeset: 1705:ef1432ef7fa123208205b693f08767e1bbcbb98e +char kNewtonVersion[] = "0.3-alpha-1705 (ef1432ef7fa123208205b693f08767e1bbcbb98e) (build 11-11-2024-12:40-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 0c24d069c..8a784f8ff 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -84,7 +84,7 @@ std::set whitelist = { std::vector toRemove; void -dequantizeResults(StoreInst * storeInst, Function & F,int maxPrecisionBits) +dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) { // IRBuilder<> Builder(storeInst); IRBuilder<> Builder(storeInst->getNextNode()); @@ -95,8 +95,9 @@ dequantizeResults(StoreInst * storeInst, Function & F,int maxPrecisionBits) { auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); - double fracBase = pow(2.0, maxPrecisionBits); - Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); + double fracBase = pow(2.0, maxPrecisionBits); + // Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); + Value * dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); if (auto * bitcastInst = dyn_cast(pointerOperand)) { @@ -109,9 +110,57 @@ dequantizeResults(StoreInst * storeInst, Function & F,int maxPrecisionBits) } } +// void +// dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) +//{ +// IRBuilder<> Builder(storeInst->getNextNode()); +// auto * pointerOperand = storeInst->getPointerOperand(); +// llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; +// +// // Check if the stored value is an integer (fixed-point) +// if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) +// { +// auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); +// +// // Get the integer part by shifting right by FRAC_Q +// Value * intPart = Builder.CreateAShr(loadInst, maxPrecisionBits, "int_part"); +// +// // Get the fractional part by masking out the integer bits +// Value * mask = ConstantInt::get(loadInst->getType(), (1 << maxPrecisionBits) - 1); +// Value * fracPart = Builder.CreateAnd(loadInst, mask, "frac_part"); +// +// // Convert the integer and fractional parts to float +// Value * floatIntPart = Builder.CreateSIToFP(intPart, Type::getFloatTy(F.getContext()), "float_int_part"); +// Value * floatFracPart = Builder.CreateSIToFP(fracPart, Type::getFloatTy(F.getContext()), "float_frac_part"); +// +// // Reconstruct the floating-point value: float_value = int_part + (frac_part / FRAC_BASE) +//// double fracBase = pow(2.0, maxPrecisionBits); +//// Value * scaledFracPart = Builder.CreateFDiv(floatFracPart, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase), "scaled_frac_part"); +// +// +// +// double reciprocalFracBase = 1.0 / pow(2.0, maxPrecisionBits); +// Value * reciprocalValue = ConstantFP::get(Type::getFloatTy(F.getContext()), reciprocalFracBase); +// Value * scaledFracPart = Builder.CreateFMul(floatFracPart, reciprocalValue, "scaled_frac_part"); +// +// // Final floating-point value +// Value * finalFloatValue = Builder.CreateFAdd(floatIntPart, scaledFracPart, "final_float_value"); +// +// // If the pointer operand was originally a float, store the reconstructed floating-point value +// if (auto * bitcastInst = dyn_cast(pointerOperand)) +// { +// if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) +// { +// auto * originalFloatPtr = bitcastInst->getOperand(0); +// Builder.CreateStore(finalFloatValue, originalFloatPtr); +// } +// } +// } +//} + // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist,int maxPrecisionBits) +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) { for (auto & F : module) { @@ -127,7 +176,7 @@ processWhitelistedFunctions(Module & module, const std::set & white if (auto * storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F,maxPrecisionBits); + dequantizeResults(storeInst, F, maxPrecisionBits); } } } @@ -382,7 +431,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - maxPrecisionBits = 13; + maxPrecisionBits = 16; /* * get const global variables @@ -458,15 +507,45 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } + /* + * analyze the range of all local variables in each function + * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// std::map callerMap; +// callerMap.clear(); +// funcBoundInfo.clear(); +// bool useOverLoad = false; + + + +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } + + if (enableQuantization) { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + + llvm::errs() << "Auto quantization enabled\n"; std::vector functionsToInsert; for (auto & mi : *Mod) { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + irPassLLVMIRAutoQuantization(N, mi, typeRange,virtualRegisterVectorRange, functionsToInsert, maxPrecisionBits); } for (auto mi : functionsToInsert) { @@ -475,14 +554,45 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } - // Range Analysis + // TODO : i32 i64 check + /* + * analyze the range of all local variables in each function + * */ + + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // std::map callerMap; + // callerMap.clear(); + // funcBoundInfo.clear(); + // bool useOverLoad = false; // for (auto & mi : *Mod) - // { - // rangeAnalysis(mi); +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink mul data type by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // shrinkType(N, boundInfoIt->second, mi); + // } // } + // Range Analysis + + // for (auto & mi : *Mod) + // { + // rangeAnalysis(mi); + // + // } + // /* // * analyze the range of all local variables in each function // * */ @@ -554,25 +664,25 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl /* * simplify the condition of each branch * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); + // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // simplifyControlFlow(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // legacy::PassManager passManager; + // passManager.add(createCFGSimplificationPass()); + // passManager.add(createInstSimplifyLegacyPass()); + // passManager.add(createGlobalDCEPass()); + // passManager.run(*Mod); // // /* // * remove the functions that are optimized by passes. @@ -628,7 +738,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // Perform text replacement to remove "_quantized" suffixes removeQuantizedSuffixInModule(*Mod); - //eraseOldInstructions(); + // eraseOldInstructions(); processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); @@ -639,8 +749,8 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl return; } // Save the optimized IR to a file - //std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; - //saveModuleIR(*Mod, fileName); + // std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; + // saveModuleIR(*Mod, fileName); // Save the optimized IR to a file saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 30f63fc47..603132443 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -767,7 +767,25 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct // } // } //} +// 64bit fixed point multiplication +llvm::Value * +performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) +{ + // Sign extend the 32-bit operands to 64-bit integers + llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + + // Perform 64-bit multiplication + llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); + + // Right shift the result to simulate fixed-point division by FRAC_Q + llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + // Truncate the 64-bit result back to 32-bit integer + llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + + return result32; +} void handleFMul(Instruction *llvmIrInstruction, Type *quantizedType, Function *fixmul, @@ -876,6 +894,30 @@ void handleFMul(Instruction *llvmIrInstruction, Type *quantizedType, Function *f llvmIrInstruction->replaceAllUsesWith(callInst); llvmIrInstruction->eraseFromParent(); } + + +// // 64bit +// { +// llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); +// llvmIrInstruction->replaceAllUsesWith(newInst); +// llvmIrInstruction->eraseFromParent(); +// } +// +// +// // 32bit +// // { +// // llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; +// // +// // // Perform multiplication directly +// // llvm::Value * mulResult = Builder.CreateMul(lhs, rhs, ""); +// // +// // // Perform right arithmetic shift +// // llvm::Value * shiftResult = Builder.CreateLShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); +// // +// // // Replace all uses of the original instruction with the result of the shift +// // llvmIrInstruction->replaceAllUsesWith(shiftResult); +// // llvmIrInstruction->eraseFromParent(); +// // } } void diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 8cbe12721..f1925fafb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -38,8 +38,13 @@ extern "C" extern std::vector functionsToErase; extern std::vector globalsToErase; extern std::vector instructionsToErase; +//void +//irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); + + void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const std::map> & typeRange, + const std::map>> & virtualRegisterVectorRange,std::vector& functionsToInsert,int maxPrecisionBits); extern From 0067a50a9ae7f94b11f311a9a771c92ab245c54c Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 13 Nov 2024 19:10:18 +0000 Subject: [PATCH 113/213] fix diff frac_q env inline bug Addresses #1. --- ...64b2e035986d58715081e661615d0c8f2fa6a7.txt | 48 +++ .../newton-irPass-LLVMIR-quantization.cpp | 336 +++++++++--------- 2 files changed, 209 insertions(+), 175 deletions(-) create mode 100644 analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt diff --git a/analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt b/analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt new file mode 100644 index 000000000..45eda60e8 --- /dev/null +++ b/analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt @@ -0,0 +1,48 @@ + +changeset: 1706:d364b2e035986d58715081e661615d0c8f2fa6a7 +char kNewtonVersion[] = "0.3-alpha-1706 (d364b2e035986d58715081e661615d0c8f2fa6a7) (build 11-12-2024-14:35-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 603132443..1c40ebbfa 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -2,6 +2,7 @@ #include "newton-irPass-LLVMIR-quantization.h" #include "llvm/Support/raw_ostream.h" #include +#include #include "llvm/IR/Metadata.h" using namespace llvm; @@ -9,6 +10,30 @@ unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) #define BIT_WIDTH 32 + + + +// 64bit fixed point multiplication +llvm::Value * +performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) +{ + // Sign extend the 32-bit operands to 64-bit integers + llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + + // Perform 64-bit multiplication + llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); + + // Right shift the result to simulate fixed-point division by FRAC_Q + llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + + // Truncate the 64-bit result back to 32-bit integer + llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + + return result32; +} + + extern "C" { // TODO : float version rsqrt llvm::Function * @@ -69,6 +94,7 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -167,19 +193,18 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext())); // Added step: fp_y = fp_y / FRAC_BASE.0; - // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE.0)); - - // fp_y = builder.CreateFDiv(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + //performFixedPointMul + llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); @@ -190,14 +215,14 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), FRAC_BASE)), quantizedType); // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + //llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); + //performFixedPointMul + llvm::Value * mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); + //llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); + llvm::Value * mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - // llvm::Value * mulfix1 = builder.CreateAShr(builder.CreateMul(halfBase, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); - // llvm::Value * mulfix2 = builder.CreateAShr(builder.CreateMul(mulfix1, int_y), llvm::ConstantInt::get(quantizedType, FRAC_Q)); - // llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - // llvm::Value * final_y = builder.CreateAShr(builder.CreateMul(int_y, correction), llvm::ConstantInt::get(quantizedType, FRAC_Q)); + //llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); + llvm::Value * final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); // Return the final fixed-point result builder.CreateRet(final_y); @@ -360,7 +385,7 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) Value * pointerOperand = loadInst->getPointerOperand(); Function * parentFunc = loadInst->getFunction(); - if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU" || parentFunc->getName() == "MahonyAHRSupdateIMU") + if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU") { llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); @@ -711,87 +736,10 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return false; } -//void -//updateMulInstructionBasedOnRange(State * N, Instruction * mulInst, Value * lhs, Value * rhs, -// const std::map> & virtualRegisterRange, -// IRBuilder<> & Builder, int precisionBits) -//{ -// // Retrieve ranges for both operands -// auto lhsRange = virtualRegisterRange.find(lhs); -// auto rhsRange = virtualRegisterRange.find(rhs); -// -// if (lhsRange != virtualRegisterRange.end() && rhsRange != virtualRegisterRange.end()) -// { -// // Apply quantization factor to the range -// llvm::errs() << "LHS Range: [" << lhsRange->second.first << ", " << lhsRange->second.second << "]\n"; -// llvm::errs() << "RHS Range: [" << rhsRange->second.first << ", " << rhsRange->second.second << "]\n"; -// -// double quantizationFactor = pow(2.0, precisionBits); -// -// // For quantization: scaling and rounding -// auto quantize = [&](double value) -> double { -// return floor(value * quantizationFactor + 0.5); -// }; -// -// double lhsMin = quantize(lhsRange->second.first); -// double lhsMax = quantize(lhsRange->second.second); -// double rhsMin = quantize(rhsRange->second.first); -// double rhsMax = quantize(rhsRange->second.second); -// -// // Log the quantized values -// llvm::errs() << "Quantized LHS Min: " << lhsMin << ", Quantized LHS Max: " << lhsMax << "\n"; -// llvm::errs() << "Quantized RHS Min: " << rhsMin << ", Quantized RHS Max: " << rhsMax << "\n"; -// -// // Calculate the range for the multiplication result -// double resultMin = std::min({std::abs(lhsMin * rhsMin), std::abs(lhsMin * rhsMax), std::abs(lhsMax * rhsMin), std::abs(lhsMax * rhsMax)}); -// double resultMax = std::max({std::abs(lhsMin * rhsMin), std::abs(lhsMin * rhsMax), std::abs(lhsMax * rhsMin), std::abs(lhsMax * rhsMax)}); -// -// // If the result exceeds INT32_MAX, switch to i64 multiplication -// if (resultMax > INT32_MAX) -// { -// llvm::errs() << "Switching to i64 multiplication\n"; -// -// // Cast the operands to i64 if necessary -// Value * lhsI64 = Builder.CreateIntCast(lhs, Type::getInt64Ty(lhs->getContext()), true); -// Value * rhsI64 = Builder.CreateIntCast(rhs, Type::getInt64Ty(rhs->getContext()), true); -// -// // Replace the mul instruction with i64 multiplication -// Value * i64MulResult = Builder.CreateMul(lhsI64, rhsI64); -// mulInst->replaceAllUsesWith(i64MulResult); -// mulInst->eraseFromParent(); -// } -// else -// { -// // Proceed with i32 multiplication -// llvm::errs() << "Using i32 multiplication\n"; -// } -// } -//} -// 64bit fixed point multiplication -llvm::Value * -performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) -{ - // Sign extend the 32-bit operands to 64-bit integers - llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); - - // Perform 64-bit multiplication - llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); - // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); - // Truncate the 64-bit result back to 32-bit integer - llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); - - return result32; -} - - -void handleFMul(Instruction *llvmIrInstruction, Type *quantizedType, Function *fixmul, - const std::map> &typeRange, - const std::map>> &virtualRegisterVectorRange, - int precisionBits) +void +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { llvm::errs() << "Handling FMul\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -877,47 +825,39 @@ void handleFMul(Instruction *llvmIrInstruction, Type *quantizedType, Function *f bool rhsIsInteger = rhs->getType()->isIntegerTy(); // if (lhsIsInteger && rhsIsInteger) - // { - // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - // //callInst->setCallingConv(llvm::CallingConv::Fast); - // //callInst->setTailCall(true); - // llvmIrInstruction->replaceAllUsesWith(callInst); - // llvmIrInstruction->eraseFromParent(); - // } if (lhsIsInteger && rhsIsInteger) + + // fixmul + // { + // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; + // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); + // llvmIrInstruction->replaceAllUsesWith(callInst); + // llvmIrInstruction->eraseFromParent(); + // } + // 64bit { - // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - llvm::errs() << "Both operands are integers, performing range-based analysis...\n"; - llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - llvmIrInstruction->replaceAllUsesWith(callInst); + llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); + llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); } - -// // 64bit -// { -// llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); -// llvmIrInstruction->replaceAllUsesWith(newInst); -// llvmIrInstruction->eraseFromParent(); -// } -// -// -// // 32bit -// // { -// // llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; -// // -// // // Perform multiplication directly -// // llvm::Value * mulResult = Builder.CreateMul(lhs, rhs, ""); -// // -// // // Perform right arithmetic shift -// // llvm::Value * shiftResult = Builder.CreateLShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); -// // -// // // Replace all uses of the original instruction with the result of the shift -// // llvmIrInstruction->replaceAllUsesWith(shiftResult); -// // llvmIrInstruction->eraseFromParent(); -// // } + // 32bit + // { + // llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; + // + // // Perform multiplication directly + // llvm::Value * mulResult = Builder.CreateMul(lhs, rhs, ""); + // + // // Perform right arithmetic shift + // llvm::Value * shiftResult = Builder.CreateLShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); + // + // // Replace all uses of the original instruction with the result of the shift + // llvmIrInstruction->replaceAllUsesWith(shiftResult); + // llvmIrInstruction->eraseFromParent(); + // } + + llvm::errs() << "Finished handling FMul\n"; } void @@ -1013,11 +953,7 @@ void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { // Check if the function is the specific one to be skipped - if (F.getName() == "MadgwickAHRSupdateIMU" || F.getName() == "MahonyAHRSupdateIMU") - { - llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; - return; // Early exit if it's the function to skip - } + if (F.getName() == "MadgwickAHRSupdateIMU") { llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; return; // Early exit if it's the function to skip @@ -1100,58 +1036,113 @@ handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function llvmIrCallInstruction->eraseFromParent(); } -void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +/* + * // Call sqrt on the floating-point value +llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); +llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + +// Convert the result back to a fixed-point integer +llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); + +// Perform a left shift to scale the result +llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); + +// Apply compensation if FRAC_Q is odd +llvm::Value * finalRes = shlRes; +if (FRAC_Q % 2 != 0) { - IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); +llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562); +llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); +llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); +finalRes = builder.CreateFPToSI(compensated, quantizedType); +} - // Convert the operand to fixed-point format if necessary - if (operand->getType()->isFloatingPointTy()) + */ + +llvm::Value * +performFixedPointSqrt(IRBuilder<> &builder, Module *irModule, Value *fixedPointValue, Type *quantizedType, int FRAC_Q, LLVMContext &context) +{ + // Convert the fixed-point value to floating-point for sqrt calculation + llvm::Value *fpValue = builder.CreateSIToFP(fixedPointValue, llvm::Type::getFloatTy(context)); + + // Call sqrt on the floating-point value + llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value *sqrtResult = builder.CreateCall(sqrtFunc, {fpValue}); + + // Convert the result back to a fixed-point integer + llvm::Value *res = builder.CreateFPToSI(sqrtResult, quantizedType); + + // Perform a left shift to scale the result (FRAC_Q / 2) + llvm::Value *shlRes = builder.CreateShl(res, FRAC_Q / 2); + + // Initialize the final result as the shifted result + llvm::Value *finalRes = shlRes; + + // Apply compensation if FRAC_Q is odd + if (FRAC_Q % 2 != 0) { - operand = Builder.CreateFPToSI(operand, quantizedType); + // Compensation factor for odd FRAC_Q (1.414213562 ≈ sqrt(2)) + llvm::Value *compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562f); + + // Convert the shifted result back to float + llvm::Value *fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); + + // Multiply by the compensation factor to adjust for odd FRAC_Q + llvm::Value *compensated = builder.CreateFMul(fpShlRes, compensationFactor); + + // Convert the compensated value back to a fixed-point integer + finalRes = builder.CreateFPToSI(compensated, quantizedType); } - // Create call to the fixed-point sqrt function - llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - // No need to apply shl and compensation if it's already done in createFixSqrt - llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); - llvmIrCallInstruction->eraseFromParent(); + return finalRes; + } -// void -// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) + +//void +//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) //{ // IRBuilder<> Builder(llvmIrCallInstruction); // auto operand = llvmIrCallInstruction->getOperand(0); // -// // Ensure operand is in the correct type +// // Convert the operand to fixed-point format if necessary // if (operand->getType()->isFloatingPointTy()) // { // operand = Builder.CreateFPToSI(operand, quantizedType); // } // -// // Convert the operand from fixed-point (int32) to float -// llvm::Value * operandAsFloat = Builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); -// -// // Call llvm.sqrt.f32 to compute the square root of the float value -// llvm::Value * sqrtFloat = Builder.CreateCall( -// Intrinsic::getDeclaration(llvmIrCallInstruction->getModule(), Intrinsic::sqrt, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())), -// {operandAsFloat}); -// -// // Convert the result back to int32 -// llvm::Value * sqrtFixedPoint = Builder.CreateFPToSI(sqrtFloat, quantizedType); -// -// // Apply the shift-left operation (shl i32 %result, 5) -// llvm::Value * shiftedSqrt = Builder.CreateShl(sqrtFixedPoint, ConstantInt::get(quantizedType, 5)); -// -// // Replace the original instruction with the new fixed-point sqrt result -// llvmIrCallInstruction->replaceAllUsesWith(shiftedSqrt); +// // Create call to the fixed-point sqrt function +// //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); +// //手动写减少call overhead +// llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); +// // No need to apply shl and compensation if it's already done in createFixSqrt +// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); // llvmIrCallInstruction->eraseFromParent(); -// } +//} + +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +{ + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + // Ensure operand is in the correct type + if (operand->getType()->isFloatingPointTy()) + { + operand = Builder.CreateFPToSI(operand, quantizedType); + } + + // Create call to the fixed-point sqrt function + llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + + // Replace the original instruction with the new fixed-point sqrt result + llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); + llvmIrCallInstruction->eraseFromParent(); +} void +//handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; @@ -1191,6 +1182,7 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) { @@ -1227,11 +1219,7 @@ void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { // Skip the function if it is MadgwickAHRSupdateIMU - if (func.getName() == "MadgwickAHRSupdateIMU" ||func.getName() == "MahonyAHRSupdateIMU") - { - llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; - return; - } + if (func.getName() == "MadgwickAHRSupdateIMU") { llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; return; @@ -1300,7 +1288,7 @@ void quantizeArguments(llvm::Function & llvmIrFunction, llvm::Type * quantizedType) { // Check if the function is specifically MadgwickAHRSupdateIMU - if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU"|| llvmIrFunction.getName() =="MahonyAHRSupdateIMU") + if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU") { llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; @@ -1381,10 +1369,7 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) // Main function to perform LLVM IR auto quantization void -// irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) - -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const std::map> & typeRange, - const std::map>> & virtualRegisterVectorRange, std::vector & functionsToInsert, int maxPrecisionBits) +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) { FRAC_Q = maxPrecisionBits; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); @@ -1440,7 +1425,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const s /* * generate hardcode function * */ - llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + //llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); @@ -1463,6 +1448,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const s handleAlloca(cast(llvmIrInstruction), quantizedType); break; case Instruction::Call: + //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); break; case Instruction::GetElementPtr: @@ -1489,7 +1475,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const s case Instruction::FMul: { - handleFMul(llvmIrInstruction, quantizedType, fixmul, typeRange,virtualRegisterVectorRange, maxPrecisionBits); + handleFMul(llvmIrInstruction, quantizedType); break; } From 41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Wed, 13 Nov 2024 20:39:31 +0000 Subject: [PATCH 114/213] fix bug of Mahony Addresses #1. --- ...1bf1c6058a1292ee80592733c87130294252fe.txt | 48 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 1045 ++++++++--------- .../newton-irPass-LLVMIR-quantization.cpp | 37 +- .../newton-irPass-LLVMIR-quantization.h | 65 +- 4 files changed, 574 insertions(+), 621 deletions(-) create mode 100644 analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt diff --git a/analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt b/analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt new file mode 100644 index 000000000..01aad8836 --- /dev/null +++ b/analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt @@ -0,0 +1,48 @@ + +changeset: 1707:a51bf1c6058a1292ee80592733c87130294252fe +char kNewtonVersion[] = "0.3-alpha-1707 (a51bf1c6058a1292ee80592733c87130294252fe) (build 11-13-2024-19:10-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 8a784f8ff..4c820c0e8 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -1,39 +1,39 @@ /* - Authored 2022. Pei Mu. +Authored 2022. Pei Mu. - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ +are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the following +disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ #ifdef __cplusplus #include "newton-irPass-LLVMIR-rangeAnalysis.h" @@ -78,234 +78,203 @@ using namespace llvm; std::set whitelist = { - "MadgwickAHRSupdate"}; + "MadgwickAHRSupdate", + "MahonyAHRSupdate" +}; // Define the dequantizeResult method std::vector toRemove; void -dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) +dequantizeResults(StoreInst * storeInst, Function & F,int maxPrecisionBits) +{ +// IRBuilder<> Builder(storeInst); +IRBuilder<> Builder(storeInst->getNextNode()); +auto * pointerOperand = storeInst->getPointerOperand(); +llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; + +// 检查指针操作数和加载的值是否涉及全局变量 +if (isa(pointerOperand)) { - // IRBuilder<> Builder(storeInst); - IRBuilder<> Builder(storeInst->getNextNode()); - auto * pointerOperand = storeInst->getPointerOperand(); - llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; + llvm::errs() << "Skipping StoreInst due to global variable in pointer operand.\n"; + return; // 如果pointerOperand涉及全局变量,直接返回 +} - if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) +if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) +{ + auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + // 检查加载的值是否涉及全局变量 + if (isa(loadInst->getPointerOperand())) { - auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); - Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); - double fracBase = pow(2.0, maxPrecisionBits); - // Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); - Value * dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); + llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; + return; // 如果加载的值涉及全局变量,直接返回 + } + + + Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); + double fracBase = pow(2.0, maxPrecisionBits); + //Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); + Value * dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); - if (auto * bitcastInst = dyn_cast(pointerOperand)) + if (auto * bitcastInst = dyn_cast(pointerOperand)) + { + if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) { - if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) - { - auto * originalFloatPtr = bitcastInst->getOperand(0); - Builder.CreateStore(dividedValue, originalFloatPtr); - } + auto * originalFloatPtr = bitcastInst->getOperand(0); + Builder.CreateStore(dividedValue, originalFloatPtr); } } } - -// void -// dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) -//{ -// IRBuilder<> Builder(storeInst->getNextNode()); -// auto * pointerOperand = storeInst->getPointerOperand(); -// llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; -// -// // Check if the stored value is an integer (fixed-point) -// if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) -// { -// auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); -// -// // Get the integer part by shifting right by FRAC_Q -// Value * intPart = Builder.CreateAShr(loadInst, maxPrecisionBits, "int_part"); -// -// // Get the fractional part by masking out the integer bits -// Value * mask = ConstantInt::get(loadInst->getType(), (1 << maxPrecisionBits) - 1); -// Value * fracPart = Builder.CreateAnd(loadInst, mask, "frac_part"); -// -// // Convert the integer and fractional parts to float -// Value * floatIntPart = Builder.CreateSIToFP(intPart, Type::getFloatTy(F.getContext()), "float_int_part"); -// Value * floatFracPart = Builder.CreateSIToFP(fracPart, Type::getFloatTy(F.getContext()), "float_frac_part"); -// -// // Reconstruct the floating-point value: float_value = int_part + (frac_part / FRAC_BASE) -//// double fracBase = pow(2.0, maxPrecisionBits); -//// Value * scaledFracPart = Builder.CreateFDiv(floatFracPart, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase), "scaled_frac_part"); -// -// -// -// double reciprocalFracBase = 1.0 / pow(2.0, maxPrecisionBits); -// Value * reciprocalValue = ConstantFP::get(Type::getFloatTy(F.getContext()), reciprocalFracBase); -// Value * scaledFracPart = Builder.CreateFMul(floatFracPart, reciprocalValue, "scaled_frac_part"); -// -// // Final floating-point value -// Value * finalFloatValue = Builder.CreateFAdd(floatIntPart, scaledFracPart, "final_float_value"); -// -// // If the pointer operand was originally a float, store the reconstructed floating-point value -// if (auto * bitcastInst = dyn_cast(pointerOperand)) -// { -// if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) -// { -// auto * originalFloatPtr = bitcastInst->getOperand(0); -// Builder.CreateStore(finalFloatValue, originalFloatPtr); -// } -// } -// } -//} +} // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) +processWhitelistedFunctions(Module & module, const std::set & whitelist,int maxPrecisionBits) { - for (auto & F : module) +for (auto & F : module) +{ + if (whitelist.find(F.getName().str()) != whitelist.end()) { - if (whitelist.find(F.getName().str()) != whitelist.end()) - { - llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; + llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; - for (auto & B : F) + for (auto & B : F) + { + for (auto & I : B) { - for (auto & I : B) + llvm::errs() << "Processing instruction: " << I << "\n"; + if (auto * storeInst = dyn_cast(&I)) { - llvm::errs() << "Processing instruction: " << I << "\n"; - if (auto * storeInst = dyn_cast(&I)) - { - llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F, maxPrecisionBits); - } + llvm::errs() << "Found valid StoreInst.\n"; + dequantizeResults(storeInst, F,maxPrecisionBits); } } } } } +} // Function to save the IR of a module to a file void saveModuleIR(llvm::Module & M, const std::string & fileName) { - std::error_code EC; - llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); - if (EC) - { - llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; - return; - } - M.print(file, nullptr); - llvm::errs() << "IR saved to " << fileName << "\n"; - file.close(); +std::error_code EC; +llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); +if (EC) +{ + llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; + return; +} +M.print(file, nullptr); +llvm::errs() << "IR saved to " << fileName << "\n"; +file.close(); } void removeQuantizedSuffixInModule(llvm::Module & M) { - for (auto & F : M) +for (auto & F : M) +{ + if (F.hasName()) { - if (F.hasName()) + std::string FuncName = F.getName().str(); + size_t pos = FuncName.find("_quantized"); + if (pos != std::string::npos) { - std::string FuncName = F.getName().str(); - size_t pos = FuncName.find("_quantized"); - if (pos != std::string::npos) - { - FuncName.erase(pos, 10); - F.setName(FuncName); - } + FuncName.erase(pos, 10); + F.setName(FuncName); } } +} - // Remove suffix from global variables - for (auto & G : M.globals()) +// Remove suffix from global variables +for (auto & G : M.globals()) +{ + if (G.hasName()) { - if (G.hasName()) + std::string GlobalName = G.getName().str(); + size_t pos = GlobalName.find("_quantized"); + if (pos != std::string::npos) { - std::string GlobalName = G.getName().str(); - size_t pos = GlobalName.find("_quantized"); - if (pos != std::string::npos) - { - GlobalName.erase(pos, 10); // Remove "_quantized" - G.setName(GlobalName); - } + GlobalName.erase(pos, 10); // Remove "_quantized" + G.setName(GlobalName); } } } +} void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { - StringRef filePath(N->llvmIR); - std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; - std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; - std::string filePathStr = dirPath + fileName; - filePath = StringRef(filePathStr); - - flexprint(N->Fe, N->Fm, N->Fpinfo, "Dump IR of: %s\n", filePath.str().c_str()); - std::error_code errorCode(errno, std::generic_category()); - raw_fd_ostream dumpedFile(filePath, errorCode); - WriteBitcodeToFile(*Mod, dumpedFile); - dumpedFile.close(); +StringRef filePath(N->llvmIR); +std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; +std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; +std::string filePathStr = dirPath + fileName; +filePath = StringRef(filePathStr); + +flexprint(N->Fe, N->Fm, N->Fpinfo, "Dump IR of: %s\n", filePath.str().c_str()); +std::error_code errorCode(errno, std::generic_category()); +raw_fd_ostream dumpedFile(filePath, errorCode); +WriteBitcodeToFile(*Mod, dumpedFile); +dumpedFile.close(); } void mergeBoundInfo(BoundInfo * dst, const BoundInfo * src) { - dst->virtualRegisterRange.insert(src->virtualRegisterRange.begin(), - src->virtualRegisterRange.end()); - return; +dst->virtualRegisterRange.insert(src->virtualRegisterRange.begin(), + src->virtualRegisterRange.end()); +return; } void collectCalleeInfo(std::vector & calleeNames, - std::map & funcBoundInfo, - const BoundInfo * boundInfo) + std::map & funcBoundInfo, + const BoundInfo * boundInfo) { - for (auto & calleeInfo : boundInfo->calleeBound) - { - calleeNames.emplace_back(calleeInfo.first); - funcBoundInfo.emplace(calleeInfo.first, calleeInfo.second); - collectCalleeInfo(calleeNames, funcBoundInfo, calleeInfo.second); - } - return; +for (auto & calleeInfo : boundInfo->calleeBound) +{ + calleeNames.emplace_back(calleeInfo.first); + funcBoundInfo.emplace(calleeInfo.first, calleeInfo.second); + collectCalleeInfo(calleeNames, funcBoundInfo, calleeInfo.second); +} +return; } class FunctionNode { - mutable AssertingVH F; - FunctionComparator::FunctionHash Hash; +mutable AssertingVH F; +FunctionComparator::FunctionHash Hash; - public: - // Note the hash is recalculated potentially multiple times, but it is cheap. - FunctionNode(Function * F) - : F(F), Hash(FunctionComparator::functionHash(*F)) {} +public: +// Note the hash is recalculated potentially multiple times, but it is cheap. +FunctionNode(Function * F) + : F(F), Hash(FunctionComparator::functionHash(*F)) {} - Function * - getFunc() const - { - return F; - } +Function * +getFunc() const +{ + return F; +} - FunctionComparator::FunctionHash - getHash() const - { - return Hash; - } +FunctionComparator::FunctionHash +getHash() const +{ + return Hash; +} }; GlobalNumberState GlobalNumbers; class FunctionNodeCmp { - public: - bool - operator()(const FunctionNode & LHS, const FunctionNode & RHS) const - { - // Order first by hashes, then full function comparison. - if (LHS.getHash() != RHS.getHash()) - return LHS.getHash() < RHS.getHash(); - FunctionComparator FCmp(LHS.getFunc(), RHS.getFunc(), &GlobalNumbers); - return FCmp.compare() == -1; - } +public: +bool +operator()(const FunctionNode & LHS, const FunctionNode & RHS) const +{ + // Order first by hashes, then full function comparison. + if (LHS.getHash() != RHS.getHash()) + return LHS.getHash() < RHS.getHash(); + FunctionComparator FCmp(LHS.getFunc(), RHS.getFunc(), &GlobalNumbers); + return FCmp.compare() == -1; +} }; using hashFuncSet = std::set; @@ -313,211 +282,233 @@ using hashFuncSet = std::set; void cleanFunctionMap(const std::unique_ptr & Mod, std::map & callerMap) { - for (auto itFunc = callerMap.begin(); itFunc != callerMap.end();) - { - if (nullptr == Mod->getFunction(itFunc->first)) - itFunc = callerMap.erase(itFunc); - else - ++itFunc; - } +for (auto itFunc = callerMap.begin(); itFunc != callerMap.end();) +{ + if (nullptr == Mod->getFunction(itFunc->first)) + itFunc = callerMap.erase(itFunc); + else + ++itFunc; +} } void overloadFunc(std::unique_ptr & Mod, std::map & callerMap) { +/* + * compare the functions and remove the redundant one + * */ +hashFuncSet baseFuncs; +auto baseFuncNum = baseFuncs.size(); +for (auto itFunc = Mod->getFunctionList().rbegin(); itFunc != Mod->getFunctionList().rend(); itFunc++) +{ + if (!itFunc->hasName() || itFunc->getName().empty()) + continue; + if (itFunc->getName().startswith("llvm.dbg.value") || + itFunc->getName().startswith("llvm.dbg.declare")) + continue; + if (itFunc->isDeclaration()) + continue; + baseFuncs.emplace(FunctionNode(&(*itFunc))); /* - * compare the functions and remove the redundant one + * find the function with the same implementation and change the callInst * */ - hashFuncSet baseFuncs; - auto baseFuncNum = baseFuncs.size(); - for (auto itFunc = Mod->getFunctionList().rbegin(); itFunc != Mod->getFunctionList().rend(); itFunc++) + if (baseFuncNum == baseFuncs.size()) { - if (!itFunc->hasName() || itFunc->getName().empty()) - continue; - if (itFunc->getName().startswith("llvm.dbg.value") || - itFunc->getName().startswith("llvm.dbg.declare")) - continue; - if (itFunc->isDeclaration()) - continue; - baseFuncs.emplace(FunctionNode(&(*itFunc))); - /* - * find the function with the same implementation and change the callInst - * */ - if (baseFuncNum == baseFuncs.size()) - { - auto callerIt = callerMap.find(itFunc->getName().str()); - assert(callerIt != callerMap.end()); - auto currentCallerInst = callerIt->second; - auto currentFuncNode = FunctionNode(&(*itFunc)); - GlobalNumberState cmpGlobalNumbers; - auto sameImplIt = std::find_if(baseFuncs.begin(), baseFuncs.end(), - [currentFuncNode, &cmpGlobalNumbers](const FunctionNode & func) { - FunctionComparator FCmp(func.getFunc(), currentFuncNode.getFunc(), &cmpGlobalNumbers); - return func.getHash() == currentFuncNode.getHash() && FCmp.compare() == 0; - }); - assert(sameImplIt != baseFuncs.end()); - currentCallerInst->setCalledFunction(sameImplIt->getFunc()); - } - else - baseFuncNum = baseFuncs.size(); + auto callerIt = callerMap.find(itFunc->getName().str()); + assert(callerIt != callerMap.end()); + auto currentCallerInst = callerIt->second; + auto currentFuncNode = FunctionNode(&(*itFunc)); + GlobalNumberState cmpGlobalNumbers; + auto sameImplIt = std::find_if(baseFuncs.begin(), baseFuncs.end(), + [currentFuncNode, &cmpGlobalNumbers](const FunctionNode & func) { + FunctionComparator FCmp(func.getFunc(), currentFuncNode.getFunc(), &cmpGlobalNumbers); + return func.getHash() == currentFuncNode.getHash() && FCmp.compare() == 0; + }); + assert(sameImplIt != baseFuncs.end()); + currentCallerInst->setCalledFunction(sameImplIt->getFunc()); } + else + baseFuncNum = baseFuncs.size(); +} - legacy::PassManager passManager; - passManager.add(createGlobalDCEPass()); - passManager.run(*Mod); +legacy::PassManager passManager; +passManager.add(createGlobalDCEPass()); +passManager.run(*Mod); } void irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverload, bool enableBuiltinAssume) { - llvm::errs() << "Entering irPassLLVMIROptimizeByRange\n"; - if (N->llvmIR == nullptr) - { - flexprint(N->Fe, N->Fm, N->Fperr, "Please specify the LLVM IR input file\n"); - llvm::errs() << "Error: llvmIR is nullptr\n"; - fatal(N, Esanity); - } +llvm::errs() << "Entering irPassLLVMIROptimizeByRange\n"; +if (N->llvmIR == nullptr) +{ + flexprint(N->Fe, N->Fm, N->Fperr, "Please specify the LLVM IR input file\n"); + llvm::errs() << "Error: llvmIR is nullptr\n"; + fatal(N, Esanity); +} - SMDiagnostic Err; - LLVMContext Context; - std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); - if (!Mod) - { - flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); - llvm::errs() << "Error: Couldn't parse IR file: " << N->llvmIR << "\n"; - fatal(N, Esanity); - } - llvm::errs() << "Module successfully parsed: " << N->llvmIR << "\n"; - auto globalBoundInfo = new BoundInfo(); - std::map funcBoundInfo; +SMDiagnostic Err; +LLVMContext Context; +std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); +if (!Mod) +{ + flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); + llvm::errs() << "Error: Couldn't parse IR file: " << N->llvmIR << "\n"; + fatal(N, Esanity); +} +llvm::errs() << "Module successfully parsed: " << N->llvmIR << "\n"; +auto globalBoundInfo = new BoundInfo(); +std::map funcBoundInfo; - /* - * get sensor info, we only concern the id and range here - * */ - std::map> typeRange; - if (N->sensorList != NULL) +/* + * get sensor info, we only concern the id and range here + * */ +std::map> typeRange; +if (N->sensorList != NULL) +{ + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) { - for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) - { - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); - flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeLowerBound: %f\n", currentModality->rangeLowerBound); - flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeUpperBound: %f\n", currentModality->rangeUpperBound); - typeRange.emplace(currentModality->identifier, std::make_pair(currentModality->rangeLowerBound, currentModality->rangeUpperBound)); - } + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeLowerBound: %f\n", currentModality->rangeLowerBound); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeUpperBound: %f\n", currentModality->rangeUpperBound); + typeRange.emplace(currentModality->identifier, std::make_pair(currentModality->rangeLowerBound, currentModality->rangeUpperBound)); } +} - /* - *TODO get sensor info, we only concern the id and precisionBits here - * */ - std::map typePrecisionBits; - if (N->sensorList != NULL) +/* + *TODO get sensor info, we only concern the id and precisionBits here + * */ +std::map typePrecisionBits; +if (N->sensorList != NULL) +{ + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) { - for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) - { - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); - flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); - typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); - } + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); + typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); } +} - int maxPrecisionBits = 0; - for (auto & typePrecisionBit : typePrecisionBits) +int maxPrecisionBits = 0; +for (auto & typePrecisionBit : typePrecisionBits) +{ + if (typePrecisionBit.second > maxPrecisionBits) { - if (typePrecisionBit.second > maxPrecisionBits) - { - maxPrecisionBits = typePrecisionBit.second; - } + maxPrecisionBits = typePrecisionBit.second; } +} - flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); +flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - maxPrecisionBits = 16; +maxPrecisionBits = 16; - /* - * get const global variables - * */ - std::map>> virtualRegisterVectorRange; - for (auto & globalVar : Mod->getGlobalList()) +/* + * get const global variables + * */ +std::map>> virtualRegisterVectorRange; +for (auto & globalVar : Mod->getGlobalList()) +{ + if (!globalVar.hasInitializer()) { - if (!globalVar.hasInitializer()) - { - llvm::errs() << "Global variable " << globalVar.getName() << " has no initializer\n"; - continue; - } - llvm::errs() << "Processing global variable: " << globalVar.getName() << "\n"; - auto constValue = globalVar.getInitializer(); - if (ConstantFP * constFp = llvm::dyn_cast(constValue)) + llvm::errs() << "Global variable " << globalVar.getName() << " has no initializer\n"; + continue; + } + llvm::errs() << "Processing global variable: " << globalVar.getName() << "\n"; + auto constValue = globalVar.getInitializer(); + if (ConstantFP * constFp = llvm::dyn_cast(constValue)) + { + if (constValue->getType()->isFloatTy()) { - if (constValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); - } - else if (constValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); - } + float constValue = constFp->getValueAPF().convertToFloat(); + globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); } - else if (ConstantInt * constInt = llvm::dyn_cast(constValue)) + else if (constValue->getType()->isDoubleTy()) { - auto constValue = constInt->getSExtValue(); - globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(static_cast(constValue), - static_cast(constValue))); + double constValue = constFp->getValueAPF().convertToDouble(); + globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); } - else if (ConstantDataArray * constArr = llvm::dyn_cast(constValue)) + } + else if (ConstantInt * constInt = llvm::dyn_cast(constValue)) + { + auto constValue = constInt->getSExtValue(); + globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(static_cast(constValue), + static_cast(constValue))); + } + else if (ConstantDataArray * constArr = llvm::dyn_cast(constValue)) + { + auto arrType = constArr->getElementType(); + if (arrType->isDoubleTy()) { - auto arrType = constArr->getElementType(); - if (arrType->isDoubleTy()) + for (size_t idx = 0; idx < constArr->getNumElements(); idx++) { - for (size_t idx = 0; idx < constArr->getNumElements(); idx++) - { - double dbValue = constArr->getElementAsDouble(idx); - virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(dbValue, dbValue)); - } + double dbValue = constArr->getElementAsDouble(idx); + virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(dbValue, dbValue)); } - else if (arrType->isFloatTy()) - { - for (size_t idx = 0; idx < constArr->getNumElements(); idx++) - { - double ftValue = constArr->getElementAsFloat(idx); - virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(ftValue, ftValue)); - } - } - else if (arrType->isIntegerTy()) - { - for (size_t idx = 0; idx < constArr->getNumElements(); idx++) - { - uint64_t intValue = constArr->getElementAsInteger(idx); - virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(intValue, intValue)); - } - } - else if (arrType->isPointerTy()) + } + else if (arrType->isFloatTy()) + { + for (size_t idx = 0; idx < constArr->getNumElements(); idx++) { - flexprint(N->Fe, N->Fm, N->Fperr, "\t\tTODO: Didn't support const pointer!\n"); + double ftValue = constArr->getElementAsFloat(idx); + virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(ftValue, ftValue)); } - else + } + else if (arrType->isIntegerTy()) + { + for (size_t idx = 0; idx < constArr->getNumElements(); idx++) { - flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown constant type!\n"); + uint64_t intValue = constArr->getElementAsInteger(idx); + virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(intValue, intValue)); } } + else if (arrType->isPointerTy()) + { + flexprint(N->Fe, N->Fm, N->Fperr, "\t\tTODO: Didn't support const pointer!\n"); + } else { - flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown type!\n"); + flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown constant type!\n"); } } + else + { + flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown type!\n"); + } +} - /* - * analyze the range of all local variables in each function - * */ +if (enableQuantization) +{ + flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; + std::vector functionsToInsert; + for (auto & mi : *Mod) + { + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + } + for (auto mi : functionsToInsert) + { + Mod->getFunctionList().remove(mi); + Mod->getFunctionList().push_front(mi); + } +} + +// Range Analysis + +// for (auto & mi : *Mod) +// { +// rangeAnalysis(mi); +// +// } + +// /* +// * analyze the range of all local variables in each function +// * */ // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); // std::map callerMap; // callerMap.clear(); // funcBoundInfo.clear(); // bool useOverLoad = false; - - - // for (auto & mi : *Mod) // { // auto boundInfo = new BoundInfo(); @@ -527,45 +518,48 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // std::vector calleeNames; // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); // } - - - if (enableQuantization) - { - flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - - - llvm::errs() << "Auto quantization enabled\n"; - std::vector functionsToInsert; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - - llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); - irPassLLVMIRAutoQuantization(N, mi, typeRange,virtualRegisterVectorRange, functionsToInsert, maxPrecisionBits); - } - for (auto mi : functionsToInsert) - { - Mod->getFunctionList().remove(mi); - Mod->getFunctionList().push_front(mi); - } - } - - // TODO : i32 i64 check - - /* - * analyze the range of all local variables in each function - * */ - - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // std::map callerMap; - // callerMap.clear(); - // funcBoundInfo.clear(); - // bool useOverLoad = false; - // for (auto & mi : *Mod) +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// shrinkType(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// memoryAlignment(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); +// +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = true; +// for (auto & mi : *Mod) // { // auto boundInfo = new BoundInfo(); // mergeBoundInfo(boundInfo, globalBoundInfo); @@ -574,192 +568,107 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // std::vector calleeNames; // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink mul data type by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // shrinkType(N, boundInfoIt->second, mi); - // } - // } - - // Range Analysis - - // for (auto & mi : *Mod) - // { - // rangeAnalysis(mi); - // - // } - - // /* - // * analyze the range of all local variables in each function - // * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // std::map callerMap; - // callerMap.clear(); - // funcBoundInfo.clear(); - // bool useOverLoad = false; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // shrinkType(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // memoryAlignment(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = true; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // - /* - * simplify the condition of each branch - * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // simplifyControlFlow(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // legacy::PassManager passManager; - // passManager.add(createCFGSimplificationPass()); - // passManager.add(createInstSimplifyLegacyPass()); - // passManager.add(createGlobalDCEPass()); - // passManager.run(*Mod); - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = false; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // constantSubstitution(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - - // Finally, erase old functions - eraseOldFunctions(); - - eraseOldGlobals(); - - // Perform text replacement to remove "_quantized" suffixes - removeQuantizedSuffixInModule(*Mod); - - // eraseOldInstructions(); - - processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); - - const char * homeDir = getenv("HOME"); - if (!homeDir) - { - llvm::errs() << "Error: HOME environment variable not set.\n"; - return; - } - // Save the optimized IR to a file - // std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; - // saveModuleIR(*Mod, fileName); - // Save the optimized IR to a file - saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); - // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); +// +/* + * simplify the condition of each branch + * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// simplifyControlFlow(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// legacy::PassManager passManager; +// passManager.add(createCFGSimplificationPass()); +// passManager.add(createInstSimplifyLegacyPass()); +// passManager.add(createGlobalDCEPass()); +// passManager.run(*Mod); +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = false; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// constantSubstitution(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); - // 替换为$HOMR +// Finally, erase old functions +eraseOldFunctions(); - /* - * Dump BC file to a file. - * */ - dumpIR(N, "output", Mod); - llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; +eraseOldGlobals(); + +// Perform text replacement to remove "_quantized" suffixes +removeQuantizedSuffixInModule(*Mod); + +//eraseOldInstructions(); + +processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); + +const char * homeDir = getenv("HOME"); +if (!homeDir) +{ + llvm::errs() << "Error: HOME environment variable not set.\n"; + return; } +// Save the optimized IR to a file +//std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; +//saveModuleIR(*Mod, fileName); +// Save the optimized IR to a file +saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); +saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MahonyAHRS_output.ll"); +// saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); + +// 替换为$HOMR + +/* + * Dump BC file to a file. + * */ +dumpIR(N, "output", Mod); +llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; +} \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 1c40ebbfa..a44178ec4 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -191,7 +191,7 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vector & Builder, Type * quantizedType llvm::errs() << "Replaced load with quantized integer value.\n"; } - +/** + * handleLoad + */ void handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -385,7 +387,9 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) Value * pointerOperand = loadInst->getPointerOperand(); Function * parentFunc = loadInst->getFunction(); - if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU") + if (isa(pointerOperand) || + parentFunc->getName() == "MadgwickAHRSupdateIMU" || + parentFunc->getName() == "MahonyAHRSupdateIMU") { llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); @@ -394,15 +398,7 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) loadInst->eraseFromParent(); } - // if (parentFunc->getName() == "MadgwickAHRSupdateIMU") - // { - // llvm::errs() << "Handling load for MadgwickAHRSupdateIMU: " << *pointerOperand << "\n"; - // LoadInst *newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); - // llvm ::errs() << "New load instruction: " << *newLoadInst << "\n"; - // loadInst->replaceAllUsesWith(newLoadInst); - // loadInst->eraseFromParent(); - // } - + // Quantize local pointers else if (!isa(pointerOperand)) { quantizePointer(loadInst, Builder, quantizedType, loadedType); @@ -948,13 +944,13 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } } - +bool isTargetFunction(Function &func); void bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { // Check if the function is the specific one to be skipped - if (F.getName() == "MadgwickAHRSupdateIMU") - { + //if is target function,skip + if (isTargetFunction(F)){ llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; return; // Early exit if it's the function to skip } @@ -1219,7 +1215,7 @@ void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { // Skip the function if it is MadgwickAHRSupdateIMU - if (func.getName() == "MadgwickAHRSupdateIMU") + if (isTargetFunction(func)) { llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; return; @@ -1284,11 +1280,15 @@ shouldSkipFunction(const std::string & functionName) return skipFunctions.find(functionName) != skipFunctions.end(); } +bool isTargetFunction(Function &func) { + return func.getName() == "MadgwickAHRSupdateIMU" || + func.getName() == "MahonyAHRSupdateIMU"; +} + void quantizeArguments(llvm::Function & llvmIrFunction, llvm::Type * quantizedType) { - // Check if the function is specifically MadgwickAHRSupdateIMU - if (llvmIrFunction.getName() == "MadgwickAHRSupdateIMU") + if (isTargetFunction(llvmIrFunction)) { llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; @@ -1471,6 +1471,7 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve * */ llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; // setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + handleStore(llvmIrInstruction, quantizedType); break; case Instruction::FMul: diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index f1925fafb..70d6a84aa 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -1,32 +1,32 @@ /* - Authored 2022. Pei Mu. - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. +Authored 2022. Pei Mu. +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ #include "newton-irPass-LLVMIR-rangeAnalysis.h" @@ -38,13 +38,8 @@ extern "C" extern std::vector functionsToErase; extern std::vector globalsToErase; extern std::vector instructionsToErase; -//void -//irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); - - void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, const std::map> & typeRange, - const std::map>> & virtualRegisterVectorRange,std::vector& functionsToInsert,int maxPrecisionBits); +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); extern @@ -53,4 +48,4 @@ void eraseOldInstructions(); void eraseOldGlobals(); #ifdef __cplusplus } /* extern "C" */ -#endif /* __cplusplus */ +#endif /* __cplusplus */ \ No newline at end of file From 43a23acc2f56fd83f793d72017f63d0798febe2c Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Fri, 15 Nov 2024 22:53:18 +0000 Subject: [PATCH 115/213] try to support i16 fix version Addresses #1. --- ...67a50a9ae7f94b11f311a9a771c92ab245c54c.txt | 48 +++++ .../newton-irPass-LLVMIR-quantization.cpp | 164 ++++++++++++++---- 2 files changed, 183 insertions(+), 29 deletions(-) create mode 100644 analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt diff --git a/analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt b/analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt new file mode 100644 index 000000000..95e7259e8 --- /dev/null +++ b/analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt @@ -0,0 +1,48 @@ + +changeset: 1708:0067a50a9ae7f94b11f311a9a771c92ab245c54c +char kNewtonVersion[] = "0.3-alpha-1708 (0067a50a9ae7f94b11f311a9a771c92ab245c54c) (build 11-13-2024-20:39-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a44178ec4..a8e7fe78b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -2,35 +2,61 @@ #include "newton-irPass-LLVMIR-quantization.h" #include "llvm/Support/raw_ostream.h" #include -#include #include "llvm/IR/Metadata.h" using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) + #define BIT_WIDTH 32 +llvm::Value * +performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned FRAC_Q) +{ + llvm::Value *result = nullptr; + switch (BIT_WIDTH) + { + case 16: + { + // Sign extend the 16-bit operands to 32-bit integers + llvm::Value *lhs32 = Builder.CreateSExt(lhs, llvm::Type::getInt32Ty(Builder.getContext())); + llvm::Value *rhs32 = Builder.CreateSExt(rhs, llvm::Type::getInt32Ty(Builder.getContext())); + // Perform 32-bit multiplication + llvm::Value *mulResult32 = Builder.CreateNSWMul(lhs32, rhs32); -// 64bit fixed point multiplication -llvm::Value * -performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned FRAC_Q) -{ - // Sign extend the 32-bit operands to 64-bit integers - llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + // Right shift the result to simulate fixed-point division by FRAC_Q + llvm::Value *divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); - // Perform 64-bit multiplication - llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); + // Truncate the result back to 16-bit integer + result = Builder.CreateTrunc(divResult32, llvm::Type::getInt16Ty(Builder.getContext())); + break; + } + case 32: + { + // Sign extend the 32-bit operands to 64-bit integers + llvm::Value *lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value *rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + + // Perform 64-bit multiplication + llvm::Value *mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); - // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + // Right shift the result to simulate fixed-point division by FRAC_Q + llvm::Value *divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); - // Truncate the 64-bit result back to 32-bit integer - llvm::Value * result32 = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + // Truncate the 64-bit result back to 32-bit integer + result = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + break; + } + default: // 处理不支持的位宽 + { + llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; + break; + } + } - return result32; + return result; } @@ -205,10 +231,36 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext()), 1.0f / FRAC_BASE)); //performFixedPointMul + //llvm::Value *i = nullptr; +// switch (BIT_WIDTH) +// { +// case 16: +// { +// // Cast fp_y to 16-bit integer +// i = builder.CreateFPToSI(fp_y, llvm::Type::getInt16Ty(irModule->getContext())); +// // Perform adjustment for 16-bit +// i = builder.CreateNSWSub( +// llvm::ConstantInt::get(llvm::Type::getInt16Ty(irModule->getContext()), 0x5f3759df), // 16-bit equivalent +// builder.CreateLShr(i, 1)); +// fp_y = builder.CreateSIToFP(i, llvm::Type::getFloatTy(irModule->getContext())); +// break; +// } +// default: +// { +// // Cast fp_y to 32-bit integer +// i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); +// // Perform adjustment for 32-bit +// i = builder.CreateNSWSub( +// llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), // 32-bit equivalent +// builder.CreateLShr(i, 1)); +// fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); +// break; +// } +// } + llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - // i = builder.CreateSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); // Step 3: int_y = fp_y * FRAC_BASE; @@ -694,7 +746,19 @@ void simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) { llvm::IRBuilder<> Builder(instruction); - Type * intType = Type::getInt32Ty(instruction->getContext()); + //Type * intType = Type::getInt32Ty(instruction->getContext()); + llvm::Type *intType = nullptr; + + switch (BIT_WIDTH) + { + case 16: + intType = llvm::Type::getInt16Ty(instruction->getContext()); // Use 16-bit integer type + break; + + default: + intType = llvm::Type::getInt32Ty(instruction->getContext()); // Use 32-bit integer type + break; + } Value * shiftValue = ConstantInt::get(intType, shiftAmount); Instruction * shiftInst; @@ -920,8 +984,23 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) { IRBuilder<> Builder(llvmIrStoreInstruction); - + auto valueOperand = llvmIrStoreInstruction->getValueOperand(); + auto pointerOperand = llvmIrStoreInstruction->getPointerOperand(); auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); + auto pointerType = pointerOperand->getType()->getPointerElementType(); + + // 如果左边是 i16,右边是 i32*,扩展 i16 为 i32 +// if (valueType->isIntegerTy(16) && pointerType->isIntegerTy(32)) +// { +// llvm::errs() << "Expanding i16 value to i32 for store\n"; +// +// // 扩展 i16 到 i32 +// auto extendedValue = Builder.CreateSExt(valueOperand, Type::getInt32Ty(llvmIrInstruction->getContext())); +// +// // 更新 Store 指令的值操作数 +// llvmIrStoreInstruction->setOperand(0, extendedValue); +// } + if (valueType->isFloatTy() || valueType->isDoubleTy()) { llvm::errs() << "Original store value type: " << *valueType << "\n"; @@ -933,7 +1012,7 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) llvmIrStoreInstruction->setOperand(0, quantizedValue); } - auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); + //auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; @@ -968,6 +1047,14 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) Value * newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); // llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); + // Second bitcast: i32* -> i16*, if BIT_WIDTH == 16 +// if (BIT_WIDTH == 16) +// { +// llvm::Type *i16Type = llvm::Type::getInt16Ty(F.getContext()); +// Value *truncArg = Builder.CreateBitCast(newArg, i16Type->getPointerTo(), Arg.getName() + ".to_i16_ptr"); +// newArg = truncArg; // Update newArg to the truncated value +// } + // Store the original argument and the bitcast result argReplacements.push_back({&Arg, newArg}); } @@ -980,12 +1067,6 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) Argument * oldArg = replacement.first; Value * newArg = replacement.second; - // Replace all uses of the old argument with the new bitcasted value - for (auto & replacement : argReplacements) - { - Argument * oldArg = replacement.first; - Value * newArg = replacement.second; - // 替换前,移除 bitcast 指令自身的使用 SmallVector usesToReplace; for (auto & U : oldArg->uses()) @@ -998,20 +1079,20 @@ bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) llvm::errs() << "Skipping load instruction: " << *user << "\n"; continue; // 跳过 Load 指令 } - + // Skip if the user is the newArg itself if (user != newArg) { usesToReplace.push_back(&U); } } - + // Perform the replacements for (auto * use : usesToReplace) { use->set(newArg); } } } -} + void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) @@ -1236,7 +1317,32 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) // Create multiplication and rounding instructions llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); - llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); + //llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); + llvm::Instruction *quantized = nullptr; + + switch (BIT_WIDTH) + { + case 16: + { + // Convert floating-point to 16-bit integer + quantized = cast( + builder.CreateFPToSI( + rounded, + llvm::Type::getInt16Ty(arg.getContext()), // Use 16-bit integer type + arg.getName() + ".changed")); + break; + } + default: + { + // Convert floating-point to 32-bit integer + quantized = cast( + builder.CreateFPToSI( + rounded, + llvm::Type::getInt32Ty(arg.getContext()), // Use 32-bit integer type + arg.getName() + ".changed")); + break; + } + } // Attach metadata to each instruction llvm::MDNode * metadataNode = llvm::MDNode::get(arg.getContext(), llvm::MDString::get(arg.getContext(), "quantized")); From cbaa642317262daf0bb3b5daf0e16f47fa54df42 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 16 Nov 2024 12:52:26 +0000 Subject: [PATCH 116/213] fixrsqrt support i16 now Addresses #1. --- ...c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt | 48 ++++++ .../newton-irPass-LLVMIR-quantization.cpp | 143 ++++++++++-------- 2 files changed, 124 insertions(+), 67 deletions(-) create mode 100644 analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt diff --git a/analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt b/analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt new file mode 100644 index 000000000..deb1c65f0 --- /dev/null +++ b/analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt @@ -0,0 +1,48 @@ + +changeset: 1709:41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a +char kNewtonVersion[] = "0.3-alpha-1709 (41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a) (build 11-15-2024-22:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a8e7fe78b..798a7184b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -8,7 +8,7 @@ using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 32 +#define BIT_WIDTH 16 llvm::Value * performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned FRAC_Q) @@ -181,9 +181,9 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vector &functionsToInsert) { llvm::errs() << "Entering createFixRsqrt\n"; @@ -195,7 +195,7 @@ createFixRsqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); + llvm::BasicBlock *entryBB = llvm::BasicBlock::Create(irModule->getContext(), "", func); + llvm::IRBuilder<> builder(entryBB); // Get the function argument (x) llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value * x = &*args++; - - // Create the fixed-point multiplication function - //llvm::Function * fixMulFunc = createFixMul(irModule, quantizedType, functionsToInsert); - + llvm::Value *x = &*args++; - // Step 1: int_halfx = mulfix(0.5 * FRAC_BASE, x); - llvm::Value * halfBase = builder.CreateLShr(x, ConstantInt::get(quantizedType, 1)); + llvm::Value *fpX = nullptr, *scaledFpX = nullptr, *approx = nullptr, *intApprox = nullptr, *shiftedX = nullptr, *result = nullptr; - // Step 2: Convert x to floating-point and perform the initial approximation - llvm::Value * fp_y = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); - - // Added step: fp_y = fp_y / FRAC_BASE.0; - - fp_y = builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); - //performFixedPointMul - - //llvm::Value *i = nullptr; -// switch (BIT_WIDTH) -// { -// case 16: -// { -// // Cast fp_y to 16-bit integer -// i = builder.CreateFPToSI(fp_y, llvm::Type::getInt16Ty(irModule->getContext())); -// // Perform adjustment for 16-bit -// i = builder.CreateNSWSub( -// llvm::ConstantInt::get(llvm::Type::getInt16Ty(irModule->getContext()), 0x5f3759df), // 16-bit equivalent -// builder.CreateLShr(i, 1)); -// fp_y = builder.CreateSIToFP(i, llvm::Type::getFloatTy(irModule->getContext())); -// break; -// } -// default: -// { -// // Cast fp_y to 32-bit integer -// i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); -// // Perform adjustment for 32-bit -// i = builder.CreateNSWSub( -// llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), // 32-bit equivalent -// builder.CreateLShr(i, 1)); -// fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); -// break; -// } -// } + // Switch based on BIT_WIDTH + switch (BIT_WIDTH) + { + case 16: + { + // Step 1: Shift x to compute %1 (x >> 1) + shiftedX = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + + // Step 2: Convert x to float and scale + llvm::Value *sextX = builder.CreateSExt(x, llvm::Type::getInt32Ty(irModule->getContext())); + fpX = builder.CreateSIToFP(sextX, llvm::Type::getFloatTy(irModule->getContext())); + scaledFpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + + // Step 3: Approximation using magic number + llvm::Value *bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value *shiftedFpX = builder.CreateLShr(bitcastFpX, 1); + llvm::Value *magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); + approx = builder.CreateSub(magicNumber, shiftedFpX); + llvm::Value *approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Value *scaledApprox = builder.CreateFMul(approxFp, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + intApprox = builder.CreateFPToSI(scaledApprox, llvm::Type::getInt32Ty(irModule->getContext())); + + // Step 4: Newton-Raphson refinement + llvm::Value *sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value *mul1 = builder.CreateMul(sextShiftedX, intApprox); + llvm::Value *mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); + + llvm::Value *mul2 = builder.CreateMul(mul1Shifted, intApprox); + llvm::Value *mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); + + int correctionValue = static_cast(1.5f * FRAC_BASE); + llvm::Value *correction = builder.CreateSub( + llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), correctionValue), + mul2Shifted); + llvm::Value *finalMul = builder.CreateMul(intApprox, correction); + llvm::Value *finalShifted = builder.CreateLShr(finalMul, FRAC_Q); + + // Step 5: Truncate the result back to i16 + result = builder.CreateTrunc(finalShifted, quantizedType); + break; + } + case 32: + default: + { + // Step 1: Shift x to compute %1 (x >> 1) + llvm::Value *halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + // Step 2: Convert x to floating-point and perform the initial approximation + fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); + fpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); - llvm::Value * i = builder.CreateBitCast(fp_y, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateNSWSub(ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - fp_y = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Value *i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); + i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); - // Step 3: int_y = fp_y * FRAC_BASE; - llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fp_y, ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + llvm::Value *int_y = builder.CreateFPToSI(builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + llvm::Value *mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); + llvm::Value *mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); + llvm::Value *correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value *final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); - // Step 4: int_y = mulfix(int_y, ((int32_t)(1.5f * FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - //llvm::Value * mulfix1 = builder.CreateCall(fixMulFunc, {halfBase, int_y}); - //performFixedPointMul - llvm::Value * mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); - //llvm::Value * mulfix2 = builder.CreateCall(fixMulFunc, {mulfix1, int_y}); - llvm::Value * mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); - llvm::Value * correction = builder.CreateSub(ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - //llvm::Value * final_y = builder.CreateCall(fixMulFunc, {int_y, correction}); - llvm::Value * final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); + result = final_y; + break; + } + } - // Return the final fixed-point result - builder.CreateRet(final_y); + // Return the result + builder.CreateRet(result); functionsToInsert.emplace_back(func); return func; } + + // A list of global variables to erase after processing std::vector globalsToErase; void From e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 16 Nov 2024 13:13:07 +0000 Subject: [PATCH 117/213] update fast quantize and dequantize Addresses #1. --- ...a23acc2f56fd83f793d72017f63d0798febe2c.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 14 +++++- .../newton-irPass-LLVMIR-quantization.cpp | 22 +++++++-- 3 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt diff --git a/analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt b/analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt new file mode 100644 index 000000000..ebf84d579 --- /dev/null +++ b/analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt @@ -0,0 +1,48 @@ + +changeset: 1710:43a23acc2f56fd83f793d72017f63d0798febe2c +char kNewtonVersion[] = "0.3-alpha-1710 (43a23acc2f56fd83f793d72017f63d0798febe2c) (build 11-16-2024-12:52-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 4c820c0e8..9932fb18d 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -114,7 +114,19 @@ if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); double fracBase = pow(2.0, maxPrecisionBits); //Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); - Value * dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); + //Value * dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); + + // 创建 Fast-Math 标志 + llvm::FastMathFlags FMF; + FMF.setFast(); // 启用所有 Fast-Math 优化 + Value *dividedValue = Builder.CreateFMul( + convertedFloat, + ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase), + "dividedValue"); + // 创建 FMul 指令并设置 Fast-Math + if (llvm::Instruction *instr = llvm::dyn_cast(dividedValue)) { + instr->setFastMathFlags(FMF); // 设置 Fast-Math 标志 + } if (auto * bitcastInst = dyn_cast(pointerOperand)) { diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 798a7184b..a57d04afb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -8,7 +8,7 @@ using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 16 +#define BIT_WIDTH 32 llvm::Value * performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned FRAC_Q) @@ -1324,8 +1324,24 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) } // Create multiplication and rounding instructions - llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); - llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); +// llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); +// llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); + + + //TODO FAST MATH + llvm::FastMathFlags FMF; + FMF.setFast(); // 启用所有 Fast-Math 优化 + // 创建乘法指令并应用 Fast-Math + llvm::Instruction *scaled = cast( + builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + scaled->setFastMathFlags(FMF); // 设置 Fast-Math 标志 + + // 创建加法指令并应用 Fast-Math + llvm::Instruction *rounded = cast( + builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); + rounded->setFastMathFlags(FMF); // 设置 Fast-Math 标志 + + //llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); llvm::Instruction *quantized = nullptr; From 38a2089f18acf70a8f55e8433f7d03bc9f992471 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sat, 16 Nov 2024 23:20:22 +0000 Subject: [PATCH 118/213] update test scriptes for euler_angle and rms error Addresses #1. --- ...aa642317262daf0bb3b5daf0e16f47fa54df42.txt | 48 ++++++++ .../newton/llvm-ir/euler_angle_error.py | 75 ++++++++++++ applications/newton/llvm-ir/test_madgwick.c | 112 +++++++----------- 3 files changed, 168 insertions(+), 67 deletions(-) create mode 100644 analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt create mode 100644 applications/newton/llvm-ir/euler_angle_error.py diff --git a/analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt b/analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt new file mode 100644 index 000000000..3040b25ab --- /dev/null +++ b/analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt @@ -0,0 +1,48 @@ + +changeset: 1711:cbaa642317262daf0bb3b5daf0e16f47fa54df42 +char kNewtonVersion[] = "0.3-alpha-1711 (cbaa642317262daf0bb3b5daf0e16f47fa54df42) (build 11-16-2024-13:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/euler_angle_error.py b/applications/newton/llvm-ir/euler_angle_error.py new file mode 100644 index 000000000..a465dd929 --- /dev/null +++ b/applications/newton/llvm-ir/euler_angle_error.py @@ -0,0 +1,75 @@ +import math + +# Function to convert quaternions to Euler angles +def quaternion_to_euler(q0, q1, q2, q3): + roll = math.atan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2)) + pitch = math.asin(max(-1.0, min(1.0, 2 * (q0 * q2 - q3 * q1)))) # Clamp to avoid numerical errors + yaw = math.atan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2)) + return roll, pitch, yaw + +# Parse the quaternion values from a file +def parse_quaternions(filename, prefix): + quaternions = [] + with open(filename, 'r') as file: + for line in file: + if line.startswith(prefix): + parts = line.split(',') + q0 = float(parts[0].split('=')[1].strip()) + q1 = float(parts[1].split('=')[1].strip()) + q2 = float(parts[2].split('=')[1].strip()) + q3 = float(parts[3].split('=')[1].strip()) + quaternions.append((q0, q1, q2, q3)) + return quaternions + +# Calculate RMS error between two lists of Euler angles +def calculate_rms_error(fp_euler, int_euler): + errors_roll = [] + errors_pitch = [] + errors_yaw = [] + overall_errors = [] + + for fp, integer in zip(fp_euler, int_euler): + roll_error = math.degrees(fp[0] - integer[0]) # Convert to degrees + pitch_error = math.degrees(fp[1] - integer[1]) + yaw_error = math.degrees(fp[2] - integer[2]) + + errors_roll.append(roll_error**2) + errors_pitch.append(pitch_error**2) + errors_yaw.append(yaw_error**2) + + # Accumulate the overall squared error for all components + overall_errors.append(roll_error**2 + pitch_error**2 + yaw_error**2) + + rms_roll = math.sqrt(sum(errors_roll) / len(errors_roll)) + rms_pitch = math.sqrt(sum(errors_pitch) / len(errors_pitch)) + rms_yaw = math.sqrt(sum(errors_yaw) / len(errors_yaw)) + + # Overall RMS error + rms_overall = math.sqrt(sum(overall_errors) / len(overall_errors)) + + return rms_roll, rms_pitch, rms_yaw, rms_overall + +# Main function to compute the average Euler angle errors +def main(): + fp_quaternions = parse_quaternions('fp_result.txt', 'Original') + int_quaternions = parse_quaternions('int_result.txt', 'FIX') + + if len(fp_quaternions) != len(int_quaternions): + print("Mismatch in the number of quaternions between files!") + return + + # Convert quaternions to Euler angles + fp_euler_angles = [quaternion_to_euler(*q) for q in fp_quaternions] + int_euler_angles = [quaternion_to_euler(*q) for q in int_quaternions] + + # Calculate RMS error for roll, pitch, yaw, and overall + rms_roll, rms_pitch, rms_yaw, rms_overall = calculate_rms_error(fp_euler_angles, int_euler_angles) + + print(f"RMS Error (φₑ - Roll): {rms_roll:.6f} degrees") + print(f"RMS Error (θₑ - Pitch): {rms_pitch:.6f} degrees") + print(f"RMS Error (ψₑ - Yaw): {rms_yaw:.6f} degrees") + print(f"Overall RMS Error: {rms_overall:.6f} degrees") + +# Run the main function +if __name__ == "__main__": + main() diff --git a/applications/newton/llvm-ir/test_madgwick.c b/applications/newton/llvm-ir/test_madgwick.c index fda10f215..53f62921c 100644 --- a/applications/newton/llvm-ir/test_madgwick.c +++ b/applications/newton/llvm-ir/test_madgwick.c @@ -18,8 +18,10 @@ #include #if defined(INT_DATA_TYPE) -extern volatile int32_t q0, q1, q2, q3; +//extern volatile int32_t q0, q1, q2, q3; +extern volatile float q0, q1, q2, q3; // #include "c-files/MadgwickAHRSfix.h" +#include "c-files/MadgwickAHRS.h" #elif defined(FP_DATA_TYPE) extern volatile float q0, q1, q2, q3; #if defined(SOFT_FLOAT_LIB) @@ -33,7 +35,7 @@ extern volatile float q0, q1, q2, q3; #endif #define DATA_SIZE 1000 -#define FRAC_Q 14 +#define FRAC_Q 16 #define FRAC_BASE (1 << FRAC_Q) #define ITERATION 10 @@ -135,19 +137,20 @@ main() double time[DATA_SIZE]; #if defined(INT_DATA_TYPE) - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; // quaternion of sensor frame relative to auxiliary frame for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = (0.64306622f * FRAC_BASE); - q1[i] = (0.02828862f * FRAC_BASE); - q2[i] = (-0.00567953f * FRAC_BASE); - q3[i] = (-0.76526684f * FRAC_BASE); + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; } - int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + #elif defined(FP_DATA_TYPE) float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; // quaternion of sensor frame relative to auxiliary frame @@ -187,63 +190,72 @@ main() break; case 1: #if defined(INT_DATA_TYPE) - mag_x[row - 2] = round(atof(value) * FRAC_BASE); + + mag_x[row - 2] = atof(value); #elif defined(FP_DATA_TYPE) mag_x[row - 2] = atof(value); #endif break; case 2: #if defined(INT_DATA_TYPE) - mag_y[row - 2] = round(atof(value) * FRAC_BASE); + + mag_y[row - 2] = atof(value); #elif defined(FP_DATA_TYPE) mag_y[row - 2] = atof(value); #endif break; case 3: #if defined(INT_DATA_TYPE) - mag_z[row - 2] = round(atof(value) * FRAC_BASE); + + mag_z[row - 2] = atof(value); #elif defined(FP_DATA_TYPE) mag_z[row - 2] = atof(value); #endif break; case 4: #if defined(INT_DATA_TYPE) - gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_x[row - 2] = atof(value) * 61; #elif defined(FP_DATA_TYPE) gyr_x[row - 2] = atof(value) * 61; #endif break; case 5: #if defined(INT_DATA_TYPE) - gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_y[row - 2] = atof(value) * 61; #elif defined(FP_DATA_TYPE) gyr_y[row - 2] = atof(value) * 61; #endif break; case 6: #if defined(INT_DATA_TYPE) - gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_z[row - 2] = atof(value) * 61; #elif defined(FP_DATA_TYPE) gyr_z[row - 2] = atof(value) * 61; #endif break; case 7: #if defined(INT_DATA_TYPE) - acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; #elif defined(FP_DATA_TYPE) acc_x[row - 2] = atof(value) * 2; #endif break; case 8: #if defined(INT_DATA_TYPE) - acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_y[row - 2] = atof(value) * 2; #elif defined(FP_DATA_TYPE) acc_y[row - 2] = atof(value) * 2; #endif break; case 9: #if defined(INT_DATA_TYPE) - acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_z[row - 2] = atof(value) * 2; #elif defined(FP_DATA_TYPE) acc_z[row - 2] = atof(value) * 2; #endif @@ -284,9 +296,6 @@ main() mag_x[ts], mag_y[ts], mag_z[ts], &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - // MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - // acc_x[ts], acc_y[ts], acc_z[ts], - // mag_x[ts], mag_y[ts], mag_z[ts]); } // end[0] = end[0] - start[0] - overhead; @@ -307,8 +316,6 @@ main() FILE * fptr = fopen("fp_result.txt", "w"); for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); } @@ -317,52 +324,23 @@ main() FILE * fptr = fopen("int_result.txt", "w"); for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, (double)q0[ts]/FRAC_BASE, - // ts, (double)q1[ts]/FRAC_BASE, - // ts, (double)q2[ts]/FRAC_BASE, - // ts, (double)q3[ts]/FRAC_BASE); - // fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, (double)q0[ts]/FRAC_BASE, - // ts, (double)q1[ts]/FRAC_BASE, - // ts, (double)q2[ts]/FRAC_BASE, - // ts, (double)q3[ts]/FRAC_BASE); - - // fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, (float)q0[ts] / FRAC_BASE, - // ts, (float)q1[ts] / FRAC_BASE, - // ts, (float)q2[ts] / FRAC_BASE, - // ts, (float)q3[ts] / FRAC_BASE); - - // 分离整数部分和小数部分 - int q0_int_part = q0[ts] >> FRAC_Q; // 整数部分 - int q0_frac_part = q0[ts] & ((1 << FRAC_Q) - 1); // 小数部分 - float q0_value = (float)q0_int_part + (float)q0_frac_part / FRAC_BASE; - //float q0_value = (float)q0_int_part + (float)(q0_frac_part >> FRAC_Q); - - int q1_int_part = q1[ts] >> FRAC_Q; - int q1_frac_part = q1[ts] & ((1 << FRAC_Q) - 1); - float q1_value = (float)q1_int_part + (float)q1_frac_part / FRAC_BASE; - - int q2_int_part = q2[ts] >> FRAC_Q; - int q2_frac_part = q2[ts] & ((1 << FRAC_Q) - 1); - float q2_value = (float)q2_int_part + (float)q2_frac_part / FRAC_BASE; - - int q3_int_part = q3[ts] >> FRAC_Q; - int q3_frac_part = q3[ts] & ((1 << FRAC_Q) - 1); - float q3_value = (float)q3_int_part + (float)q3_frac_part / FRAC_BASE; - - // 使用定点数计算的结果来写入文件 - fprintf(fptr, "FIX: q0[%zu]=%f, q1[%zu]=%f, q2[%zu]=%f, q3[%zu]=%f\n", - ts, q0_value, ts, q1_value, ts, q2_value, ts, q3_value); -} -fclose(fptr); -// printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", -// DISPLAY_INT(q0), DISPLAY_FRAC(q0), -// DISPLAY_INT(q1), DISPLAY_FRAC(q1), -// DISPLAY_INT(q2), DISPLAY_FRAC(q2), -// DISPLAY_INT(q3), DISPLAY_FRAC(q3)); + + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + + +// void Mahony::computeAngles() +// { +// roll = atan2f(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2); +// pitch = asinf(-2.0f * (q1*q3 - q0*q2)); +// yaw = atan2f(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3); +// anglesComputed = 1; +// } + + #endif return 0; } From 6ab3928eeefd23988db60d9401e77c3c1a7e836f Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 17 Nov 2024 11:13:18 +0000 Subject: [PATCH 119/213] fix i16 bitcast issue Addresses #1. --- ...8c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt | 48 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 977 +++++++++--------- .../newton-irPass-LLVMIR-quantization.cpp | 157 +-- 3 files changed, 639 insertions(+), 543 deletions(-) create mode 100644 analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt diff --git a/analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt b/analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt new file mode 100644 index 000000000..ad8654baf --- /dev/null +++ b/analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt @@ -0,0 +1,48 @@ + +changeset: 1712:e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3 +char kNewtonVersion[] = "0.3-alpha-1712 (e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3) (build 11-16-2024-23:20-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 9932fb18d..f7525f3b7 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -76,217 +76,255 @@ POSSIBILITY OF SUCH DAMAGE. #include "llvm/IR/Function.h" using namespace llvm; +#define BIT_WIDTH 16 std::set whitelist = { "MadgwickAHRSupdate", - "MahonyAHRSupdate" -}; + "MahonyAHRSupdate"}; // Define the dequantizeResult method std::vector toRemove; -void -dequantizeResults(StoreInst * storeInst, Function & F,int maxPrecisionBits) -{ -// IRBuilder<> Builder(storeInst); -IRBuilder<> Builder(storeInst->getNextNode()); -auto * pointerOperand = storeInst->getPointerOperand(); -llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; -// 检查指针操作数和加载的值是否涉及全局变量 -if (isa(pointerOperand)) +void +dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) { - llvm::errs() << "Skipping StoreInst due to global variable in pointer operand.\n"; - return; // 如果pointerOperand涉及全局变量,直接返回 -} + // IRBuilder<> Builder(storeInst); + IRBuilder<> Builder(storeInst->getNextNode()); + auto *pointerOperand = storeInst->getPointerOperand(); + llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; -if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(32)) -{ - auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); - // 检查加载的值是否涉及全局变量 - if (isa(loadInst->getPointerOperand())) + if (isa(pointerOperand)) { - llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; - return; // 如果加载的值涉及全局变量,直接返回 + llvm::errs() << "Skipping StoreInst due to global variable in pointer operand.\n"; + return; } - - Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); - double fracBase = pow(2.0, maxPrecisionBits); - //Value * dividedValue = Builder.CreateFDiv(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), fracBase)); - //Value * dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); - - // 创建 Fast-Math 标志 - llvm::FastMathFlags FMF; - FMF.setFast(); // 启用所有 Fast-Math 优化 - Value *dividedValue = Builder.CreateFMul( - convertedFloat, - ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase), - "dividedValue"); - // 创建 FMul 指令并设置 Fast-Math - if (llvm::Instruction *instr = llvm::dyn_cast(dividedValue)) { - instr->setFastMathFlags(FMF); // 设置 Fast-Math 标志 + Type *targetType = nullptr; + switch (BIT_WIDTH) + { + case 16: + targetType = Type::getInt16Ty(F.getContext()); + break; + case 32: + default: + targetType = Type::getInt32Ty(F.getContext()); + break; } - if (auto * bitcastInst = dyn_cast(pointerOperand)) + if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(BIT_WIDTH)) { - if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) + auto *loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + + if (isa(loadInst->getPointerOperand())) { - auto * originalFloatPtr = bitcastInst->getOperand(0); - Builder.CreateStore(dividedValue, originalFloatPtr); + llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; + return; + } + + Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); + double fracBase = pow(2.0, maxPrecisionBits); + Value *dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); + + if (auto *bitcastInst = dyn_cast(pointerOperand)) + { + Value *finalStorePtr = nullptr; + bool isValidSource = false; + + // Determine the final store pointer based on BIT_WIDTH + switch (BIT_WIDTH) + { + case 16: + // Check if the source type is i32 and find the original float* + if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) + { + auto *i32Ptr = bitcastInst->getOperand(0); + if (auto *floatBitcast = dyn_cast(i32Ptr)) + { + if (floatBitcast->getSrcTy()->getPointerElementType()->isFloatTy()) + { + finalStorePtr = floatBitcast->getOperand(0); // Original float* + isValidSource = true; + } + } + } + break; + + case 32: + // Check if the source type is float* + if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) + { + finalStorePtr = bitcastInst->getOperand(0); // Original float* + isValidSource = true; + } + break; + + default: + llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; + return; + } + + if (isValidSource && finalStorePtr) + { + Builder.CreateStore(dividedValue, finalStorePtr); + } + else + { + llvm::errs() << "Invalid source for StoreInst: " << *storeInst << "\n"; + } } } } -} + // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist,int maxPrecisionBits) -{ -for (auto & F : module) +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) { - if (whitelist.find(F.getName().str()) != whitelist.end()) + for (auto & F : module) { - llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; - - for (auto & B : F) + if (whitelist.find(F.getName().str()) != whitelist.end()) { - for (auto & I : B) + llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; + + for (auto & B : F) { - llvm::errs() << "Processing instruction: " << I << "\n"; - if (auto * storeInst = dyn_cast(&I)) + for (auto & I : B) { - llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F,maxPrecisionBits); + llvm::errs() << "Processing instruction: " << I << "\n"; + if (auto * storeInst = dyn_cast(&I)) + { + llvm::errs() << "Found valid StoreInst.\n"; + dequantizeResults(storeInst, F, maxPrecisionBits); + } } } } } } -} // Function to save the IR of a module to a file void saveModuleIR(llvm::Module & M, const std::string & fileName) { -std::error_code EC; -llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); -if (EC) -{ - llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; - return; -} -M.print(file, nullptr); -llvm::errs() << "IR saved to " << fileName << "\n"; -file.close(); + std::error_code EC; + llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); + if (EC) + { + llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; + return; + } + M.print(file, nullptr); + llvm::errs() << "IR saved to " << fileName << "\n"; + file.close(); } void removeQuantizedSuffixInModule(llvm::Module & M) { -for (auto & F : M) -{ - if (F.hasName()) + for (auto & F : M) { - std::string FuncName = F.getName().str(); - size_t pos = FuncName.find("_quantized"); - if (pos != std::string::npos) + if (F.hasName()) { - FuncName.erase(pos, 10); - F.setName(FuncName); + std::string FuncName = F.getName().str(); + size_t pos = FuncName.find("_quantized"); + if (pos != std::string::npos) + { + FuncName.erase(pos, 10); + F.setName(FuncName); + } } } -} -// Remove suffix from global variables -for (auto & G : M.globals()) -{ - if (G.hasName()) + // Remove suffix from global variables + for (auto & G : M.globals()) { - std::string GlobalName = G.getName().str(); - size_t pos = GlobalName.find("_quantized"); - if (pos != std::string::npos) + if (G.hasName()) { - GlobalName.erase(pos, 10); // Remove "_quantized" - G.setName(GlobalName); + std::string GlobalName = G.getName().str(); + size_t pos = GlobalName.find("_quantized"); + if (pos != std::string::npos) + { + GlobalName.erase(pos, 10); // Remove "_quantized" + G.setName(GlobalName); + } } } } -} void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { -StringRef filePath(N->llvmIR); -std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; -std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; -std::string filePathStr = dirPath + fileName; -filePath = StringRef(filePathStr); - -flexprint(N->Fe, N->Fm, N->Fpinfo, "Dump IR of: %s\n", filePath.str().c_str()); -std::error_code errorCode(errno, std::generic_category()); -raw_fd_ostream dumpedFile(filePath, errorCode); -WriteBitcodeToFile(*Mod, dumpedFile); -dumpedFile.close(); + StringRef filePath(N->llvmIR); + std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; + std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; + std::string filePathStr = dirPath + fileName; + filePath = StringRef(filePathStr); + + flexprint(N->Fe, N->Fm, N->Fpinfo, "Dump IR of: %s\n", filePath.str().c_str()); + std::error_code errorCode(errno, std::generic_category()); + raw_fd_ostream dumpedFile(filePath, errorCode); + WriteBitcodeToFile(*Mod, dumpedFile); + dumpedFile.close(); } void mergeBoundInfo(BoundInfo * dst, const BoundInfo * src) { -dst->virtualRegisterRange.insert(src->virtualRegisterRange.begin(), - src->virtualRegisterRange.end()); -return; + dst->virtualRegisterRange.insert(src->virtualRegisterRange.begin(), + src->virtualRegisterRange.end()); + return; } void collectCalleeInfo(std::vector & calleeNames, - std::map & funcBoundInfo, - const BoundInfo * boundInfo) -{ -for (auto & calleeInfo : boundInfo->calleeBound) + std::map & funcBoundInfo, + const BoundInfo * boundInfo) { - calleeNames.emplace_back(calleeInfo.first); - funcBoundInfo.emplace(calleeInfo.first, calleeInfo.second); - collectCalleeInfo(calleeNames, funcBoundInfo, calleeInfo.second); -} -return; + for (auto & calleeInfo : boundInfo->calleeBound) + { + calleeNames.emplace_back(calleeInfo.first); + funcBoundInfo.emplace(calleeInfo.first, calleeInfo.second); + collectCalleeInfo(calleeNames, funcBoundInfo, calleeInfo.second); + } + return; } class FunctionNode { -mutable AssertingVH F; -FunctionComparator::FunctionHash Hash; + mutable AssertingVH F; + FunctionComparator::FunctionHash Hash; -public: -// Note the hash is recalculated potentially multiple times, but it is cheap. -FunctionNode(Function * F) - : F(F), Hash(FunctionComparator::functionHash(*F)) {} + public: + // Note the hash is recalculated potentially multiple times, but it is cheap. + FunctionNode(Function * F) + : F(F), Hash(FunctionComparator::functionHash(*F)) {} -Function * -getFunc() const -{ - return F; -} + Function * + getFunc() const + { + return F; + } -FunctionComparator::FunctionHash -getHash() const -{ - return Hash; -} + FunctionComparator::FunctionHash + getHash() const + { + return Hash; + } }; GlobalNumberState GlobalNumbers; class FunctionNodeCmp { -public: -bool -operator()(const FunctionNode & LHS, const FunctionNode & RHS) const -{ - // Order first by hashes, then full function comparison. - if (LHS.getHash() != RHS.getHash()) - return LHS.getHash() < RHS.getHash(); - FunctionComparator FCmp(LHS.getFunc(), RHS.getFunc(), &GlobalNumbers); - return FCmp.compare() == -1; -} + public: + bool + operator()(const FunctionNode & LHS, const FunctionNode & RHS) const + { + // Order first by hashes, then full function comparison. + if (LHS.getHash() != RHS.getHash()) + return LHS.getHash() < RHS.getHash(); + FunctionComparator FCmp(LHS.getFunc(), RHS.getFunc(), &GlobalNumbers); + return FCmp.compare() == -1; + } }; using hashFuncSet = std::set; @@ -294,393 +332,392 @@ using hashFuncSet = std::set; void cleanFunctionMap(const std::unique_ptr & Mod, std::map & callerMap) { -for (auto itFunc = callerMap.begin(); itFunc != callerMap.end();) -{ - if (nullptr == Mod->getFunction(itFunc->first)) - itFunc = callerMap.erase(itFunc); - else - ++itFunc; -} + for (auto itFunc = callerMap.begin(); itFunc != callerMap.end();) + { + if (nullptr == Mod->getFunction(itFunc->first)) + itFunc = callerMap.erase(itFunc); + else + ++itFunc; + } } void overloadFunc(std::unique_ptr & Mod, std::map & callerMap) { -/* - * compare the functions and remove the redundant one - * */ -hashFuncSet baseFuncs; -auto baseFuncNum = baseFuncs.size(); -for (auto itFunc = Mod->getFunctionList().rbegin(); itFunc != Mod->getFunctionList().rend(); itFunc++) -{ - if (!itFunc->hasName() || itFunc->getName().empty()) - continue; - if (itFunc->getName().startswith("llvm.dbg.value") || - itFunc->getName().startswith("llvm.dbg.declare")) - continue; - if (itFunc->isDeclaration()) - continue; - baseFuncs.emplace(FunctionNode(&(*itFunc))); /* - * find the function with the same implementation and change the callInst + * compare the functions and remove the redundant one * */ - if (baseFuncNum == baseFuncs.size()) + hashFuncSet baseFuncs; + auto baseFuncNum = baseFuncs.size(); + for (auto itFunc = Mod->getFunctionList().rbegin(); itFunc != Mod->getFunctionList().rend(); itFunc++) { - auto callerIt = callerMap.find(itFunc->getName().str()); - assert(callerIt != callerMap.end()); - auto currentCallerInst = callerIt->second; - auto currentFuncNode = FunctionNode(&(*itFunc)); - GlobalNumberState cmpGlobalNumbers; - auto sameImplIt = std::find_if(baseFuncs.begin(), baseFuncs.end(), - [currentFuncNode, &cmpGlobalNumbers](const FunctionNode & func) { - FunctionComparator FCmp(func.getFunc(), currentFuncNode.getFunc(), &cmpGlobalNumbers); - return func.getHash() == currentFuncNode.getHash() && FCmp.compare() == 0; - }); - assert(sameImplIt != baseFuncs.end()); - currentCallerInst->setCalledFunction(sameImplIt->getFunc()); + if (!itFunc->hasName() || itFunc->getName().empty()) + continue; + if (itFunc->getName().startswith("llvm.dbg.value") || + itFunc->getName().startswith("llvm.dbg.declare")) + continue; + if (itFunc->isDeclaration()) + continue; + baseFuncs.emplace(FunctionNode(&(*itFunc))); + /* + * find the function with the same implementation and change the callInst + * */ + if (baseFuncNum == baseFuncs.size()) + { + auto callerIt = callerMap.find(itFunc->getName().str()); + assert(callerIt != callerMap.end()); + auto currentCallerInst = callerIt->second; + auto currentFuncNode = FunctionNode(&(*itFunc)); + GlobalNumberState cmpGlobalNumbers; + auto sameImplIt = std::find_if(baseFuncs.begin(), baseFuncs.end(), + [currentFuncNode, &cmpGlobalNumbers](const FunctionNode & func) { + FunctionComparator FCmp(func.getFunc(), currentFuncNode.getFunc(), &cmpGlobalNumbers); + return func.getHash() == currentFuncNode.getHash() && FCmp.compare() == 0; + }); + assert(sameImplIt != baseFuncs.end()); + currentCallerInst->setCalledFunction(sameImplIt->getFunc()); + } + else + baseFuncNum = baseFuncs.size(); } - else - baseFuncNum = baseFuncs.size(); -} -legacy::PassManager passManager; -passManager.add(createGlobalDCEPass()); -passManager.run(*Mod); + legacy::PassManager passManager; + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); } void irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverload, bool enableBuiltinAssume) { -llvm::errs() << "Entering irPassLLVMIROptimizeByRange\n"; -if (N->llvmIR == nullptr) -{ - flexprint(N->Fe, N->Fm, N->Fperr, "Please specify the LLVM IR input file\n"); - llvm::errs() << "Error: llvmIR is nullptr\n"; - fatal(N, Esanity); -} - -SMDiagnostic Err; -LLVMContext Context; -std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); -if (!Mod) -{ - flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); - llvm::errs() << "Error: Couldn't parse IR file: " << N->llvmIR << "\n"; - fatal(N, Esanity); -} -llvm::errs() << "Module successfully parsed: " << N->llvmIR << "\n"; -auto globalBoundInfo = new BoundInfo(); -std::map funcBoundInfo; - -/* - * get sensor info, we only concern the id and range here - * */ -std::map> typeRange; -if (N->sensorList != NULL) -{ - for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + llvm::errs() << "Entering irPassLLVMIROptimizeByRange\n"; + if (N->llvmIR == nullptr) { - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); - flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeLowerBound: %f\n", currentModality->rangeLowerBound); - flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeUpperBound: %f\n", currentModality->rangeUpperBound); - typeRange.emplace(currentModality->identifier, std::make_pair(currentModality->rangeLowerBound, currentModality->rangeUpperBound)); + flexprint(N->Fe, N->Fm, N->Fperr, "Please specify the LLVM IR input file\n"); + llvm::errs() << "Error: llvmIR is nullptr\n"; + fatal(N, Esanity); } -} -/* - *TODO get sensor info, we only concern the id and precisionBits here - * */ -std::map typePrecisionBits; -if (N->sensorList != NULL) -{ - for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + SMDiagnostic Err; + LLVMContext Context; + std::unique_ptr Mod(parseIRFile(N->llvmIR, Err, Context)); + if (!Mod) { - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); - flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); - typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); + flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); + llvm::errs() << "Error: Couldn't parse IR file: " << N->llvmIR << "\n"; + fatal(N, Esanity); } -} + llvm::errs() << "Module successfully parsed: " << N->llvmIR << "\n"; + auto globalBoundInfo = new BoundInfo(); + std::map funcBoundInfo; -int maxPrecisionBits = 0; -for (auto & typePrecisionBit : typePrecisionBits) -{ - if (typePrecisionBit.second > maxPrecisionBits) + /* + * get sensor info, we only concern the id and range here + * */ + std::map> typeRange; + if (N->sensorList != NULL) { - maxPrecisionBits = typePrecisionBit.second; + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeLowerBound: %f\n", currentModality->rangeLowerBound); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeUpperBound: %f\n", currentModality->rangeUpperBound); + typeRange.emplace(currentModality->identifier, std::make_pair(currentModality->rangeLowerBound, currentModality->rangeUpperBound)); + } } -} - -flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - -maxPrecisionBits = 16; -/* - * get const global variables - * */ -std::map>> virtualRegisterVectorRange; -for (auto & globalVar : Mod->getGlobalList()) -{ - if (!globalVar.hasInitializer()) - { - llvm::errs() << "Global variable " << globalVar.getName() << " has no initializer\n"; - continue; - } - llvm::errs() << "Processing global variable: " << globalVar.getName() << "\n"; - auto constValue = globalVar.getInitializer(); - if (ConstantFP * constFp = llvm::dyn_cast(constValue)) + /* + *TODO get sensor info, we only concern the id and precisionBits here + * */ + std::map typePrecisionBits; + if (N->sensorList != NULL) { - if (constValue->getType()->isFloatTy()) + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) { - float constValue = constFp->getValueAPF().convertToFloat(); - globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); - } - else if (constValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); + typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); } } - else if (ConstantInt * constInt = llvm::dyn_cast(constValue)) + + int maxPrecisionBits = 0; + for (auto & typePrecisionBit : typePrecisionBits) { - auto constValue = constInt->getSExtValue(); - globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(static_cast(constValue), - static_cast(constValue))); + if (typePrecisionBit.second > maxPrecisionBits) + { + maxPrecisionBits = typePrecisionBit.second; + } } - else if (ConstantDataArray * constArr = llvm::dyn_cast(constValue)) + + flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); + + maxPrecisionBits = 10; + + /* + * get const global variables + * */ + std::map>> virtualRegisterVectorRange; + for (auto & globalVar : Mod->getGlobalList()) { - auto arrType = constArr->getElementType(); - if (arrType->isDoubleTy()) + if (!globalVar.hasInitializer()) { - for (size_t idx = 0; idx < constArr->getNumElements(); idx++) - { - double dbValue = constArr->getElementAsDouble(idx); - virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(dbValue, dbValue)); - } + llvm::errs() << "Global variable " << globalVar.getName() << " has no initializer\n"; + continue; } - else if (arrType->isFloatTy()) + llvm::errs() << "Processing global variable: " << globalVar.getName() << "\n"; + auto constValue = globalVar.getInitializer(); + if (ConstantFP * constFp = llvm::dyn_cast(constValue)) { - for (size_t idx = 0; idx < constArr->getNumElements(); idx++) + if (constValue->getType()->isFloatTy()) { - double ftValue = constArr->getElementAsFloat(idx); - virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(ftValue, ftValue)); + float constValue = constFp->getValueAPF().convertToFloat(); + globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); } - } - else if (arrType->isIntegerTy()) - { - for (size_t idx = 0; idx < constArr->getNumElements(); idx++) + else if (constValue->getType()->isDoubleTy()) { - uint64_t intValue = constArr->getElementAsInteger(idx); - virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(intValue, intValue)); + double constValue = constFp->getValueAPF().convertToDouble(); + globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(constValue, constValue)); } } - else if (arrType->isPointerTy()) + else if (ConstantInt * constInt = llvm::dyn_cast(constValue)) { - flexprint(N->Fe, N->Fm, N->Fperr, "\t\tTODO: Didn't support const pointer!\n"); + auto constValue = constInt->getSExtValue(); + globalBoundInfo->virtualRegisterRange.emplace(&globalVar, std::make_pair(static_cast(constValue), + static_cast(constValue))); + } + else if (ConstantDataArray * constArr = llvm::dyn_cast(constValue)) + { + auto arrType = constArr->getElementType(); + if (arrType->isDoubleTy()) + { + for (size_t idx = 0; idx < constArr->getNumElements(); idx++) + { + double dbValue = constArr->getElementAsDouble(idx); + virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(dbValue, dbValue)); + } + } + else if (arrType->isFloatTy()) + { + for (size_t idx = 0; idx < constArr->getNumElements(); idx++) + { + double ftValue = constArr->getElementAsFloat(idx); + virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(ftValue, ftValue)); + } + } + else if (arrType->isIntegerTy()) + { + for (size_t idx = 0; idx < constArr->getNumElements(); idx++) + { + uint64_t intValue = constArr->getElementAsInteger(idx); + virtualRegisterVectorRange[&globalVar].emplace_back(std::make_pair(intValue, intValue)); + } + } + else if (arrType->isPointerTy()) + { + flexprint(N->Fe, N->Fm, N->Fperr, "\t\tTODO: Didn't support const pointer!\n"); + } + else + { + flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown constant type!\n"); + } } else { - flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown constant type!\n"); + flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown type!\n"); } } - else - { - flexprint(N->Fe, N->Fm, N->Fperr, "\t\tUnknown type!\n"); - } -} -if (enableQuantization) -{ - flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - llvm::errs() << "Auto quantization enabled\n"; - std::vector functionsToInsert; - for (auto & mi : *Mod) + if (enableQuantization) { - llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; + std::vector functionsToInsert; + for (auto & mi : *Mod) + { + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + } + for (auto mi : functionsToInsert) + { + Mod->getFunctionList().remove(mi); + Mod->getFunctionList().push_front(mi); + } } - for (auto mi : functionsToInsert) + + // Range Analysis + + // for (auto & mi : *Mod) + // { + // rangeAnalysis(mi); + // + // } + + // /* + // * analyze the range of all local variables in each function + // * */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // std::map callerMap; + // callerMap.clear(); + // funcBoundInfo.clear(); + // bool useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // shrinkType(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // memoryAlignment(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = true; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + /* + * simplify the condition of each branch + * */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // simplifyControlFlow(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // legacy::PassManager passManager; + // passManager.add(createCFGSimplificationPass()); + // passManager.add(createInstSimplifyLegacyPass()); + // passManager.add(createGlobalDCEPass()); + // passManager.run(*Mod); + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // constantSubstitution(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + + // Finally, erase old functions + eraseOldFunctions(); + + eraseOldGlobals(); + + // Perform text replacement to remove "_quantized" suffixes + removeQuantizedSuffixInModule(*Mod); + + // eraseOldInstructions(); + + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); + + const char * homeDir = getenv("HOME"); + if (!homeDir) { - Mod->getFunctionList().remove(mi); - Mod->getFunctionList().push_front(mi); + llvm::errs() << "Error: HOME environment variable not set.\n"; + return; } -} + // Save the optimized IR to a file + // std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; + // saveModuleIR(*Mod, fileName); + // Save the optimized IR to a file + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MahonyAHRS_output.ll"); + // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); -// Range Analysis - -// for (auto & mi : *Mod) -// { -// rangeAnalysis(mi); -// -// } - -// /* -// * analyze the range of all local variables in each function -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// std::map callerMap; -// callerMap.clear(); -// funcBoundInfo.clear(); -// bool useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// shrinkType(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// memoryAlignment(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = true; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -/* - * simplify the condition of each branch - * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// constantSubstitution(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); - -// Finally, erase old functions -eraseOldFunctions(); - -eraseOldGlobals(); - -// Perform text replacement to remove "_quantized" suffixes -removeQuantizedSuffixInModule(*Mod); - -//eraseOldInstructions(); - -processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); - -const char * homeDir = getenv("HOME"); -if (!homeDir) -{ - llvm::errs() << "Error: HOME environment variable not set.\n"; - return; -} -// Save the optimized IR to a file -//std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; -//saveModuleIR(*Mod, fileName); -// Save the optimized IR to a file -saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); -saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MahonyAHRS_output.ll"); -// saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); -// 替换为$HOMR - -/* - * Dump BC file to a file. - * */ -dumpIR(N, "output", Mod); -llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; + /* + * Dump BC file to a file. + * */ + dumpIR(N, "output", Mod); + llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; } \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a57d04afb..ab45182bd 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -8,10 +8,10 @@ using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 32 +#define BIT_WIDTH 16 llvm::Value * -performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned FRAC_Q) +performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned int FRAC_Q) { llvm::Value *result = nullptr; @@ -49,7 +49,7 @@ performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value * result = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); break; } - default: // 处理不支持的位宽 + default: { llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; break; @@ -375,7 +375,19 @@ updateGlobalVariables(Module * module, Type * quantizedType) globalVar.getLinkage(), globalVar.getInitializer(), quantizedName); - newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + //newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + switch (BIT_WIDTH) + { + case 16: + newGlobalVar->setAlignment(llvm::MaybeAlign(2)); + break; + case 32: + newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + break; + default: + llvm::errs() << "Unsupported bit width: " << BIT_WIDTH << "\n"; + break; + } newGlobalVar->setDSOLocal(true); // Replace all uses of the old global variable with the new one @@ -998,17 +1010,6 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); auto pointerType = pointerOperand->getType()->getPointerElementType(); - // 如果左边是 i16,右边是 i32*,扩展 i16 为 i32 -// if (valueType->isIntegerTy(16) && pointerType->isIntegerTy(32)) -// { -// llvm::errs() << "Expanding i16 value to i32 for store\n"; -// -// // 扩展 i16 到 i32 -// auto extendedValue = Builder.CreateSExt(valueOperand, Type::getInt32Ty(llvmIrInstruction->getContext())); -// -// // 更新 Store 指令的值操作数 -// llvmIrStoreInstruction->setOperand(0, extendedValue); -// } if (valueType->isFloatTy() || valueType->isDoubleTy()) { @@ -1033,74 +1034,100 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } bool isTargetFunction(Function &func); -void -bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) + +void bitcastFloatPtrArgs(Function &F, IRBuilder<> &Builder) { // Check if the function is the specific one to be skipped - //if is target function,skip - if (isTargetFunction(F)){ + if (isTargetFunction(F)) + { llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; - return; // Early exit if it's the function to skip + return; // Early exit if it's the function to skip } SmallVector, 4> argReplacements; + // Iterate over all function arguments - for (Argument & Arg : F.args()) + for (Argument &Arg : F.args()) { // Check if the argument is a pointer to float if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) { - llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); - // Create a bitcast instruction at the beginning of the function + // Create a bitcast to i32* at the beginning of the function + llvm::PointerType *i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); - Value * newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); - // llvm::Value *newArg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".toI32Ptr"); - - // Second bitcast: i32* -> i16*, if BIT_WIDTH == 16 -// if (BIT_WIDTH == 16) -// { -// llvm::Type *i16Type = llvm::Type::getInt16Ty(F.getContext()); -// Value *truncArg = Builder.CreateBitCast(newArg, i16Type->getPointerTo(), Arg.getName() + ".to_i16_ptr"); -// newArg = truncArg; // Update newArg to the truncated value -// } - - // Store the original argument and the bitcast result + Value *i32Arg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); + + // Additional bitcast to i16* if BIT_WIDTH == 16 + Value *newArg = nullptr; + switch (BIT_WIDTH) + { + case 16: + { + llvm::PointerType *i16PtrType = llvm::Type::getInt16PtrTy(F.getContext()); + newArg = Builder.CreateBitCast(i32Arg, i16PtrType, Arg.getName() + ".to_i16_ptr"); + break; + } + case 32: + default: + newArg = i32Arg; // Use i32* as the new argument + break; + } + + // Store the original argument and the final bitcast result argReplacements.push_back({&Arg, newArg}); } } - llvm::errs() << "Starting use replacement\n"; // Log added + + llvm::errs() << "Starting use replacement\n"; // Log added // Iterate over the function to replace uses of the original arguments - for (auto & replacement : argReplacements) + for (auto &replacement : argReplacements) { - Argument * oldArg = replacement.first; - Value * newArg = replacement.second; + Argument *oldArg = replacement.first; + Value *newArg = replacement.second; + + // Collect all uses of the old argument for replacement + SmallVector usesToReplace; + for (auto &U : oldArg->uses()) + { + User *user = U.getUser(); - // 替换前,移除 bitcast 指令自身的使用 - SmallVector usesToReplace; - for (auto & U : oldArg->uses()) + // Skip if the use is a Load instruction + if (isa(user)) { - User * user = U.getUser(); + llvm::errs() << "Skipping load instruction: " << *user << "\n"; + continue; // Skip Load instructions + } - // Skip if the use is a Load instruction - if (isa(user)) - { - llvm::errs() << "Skipping load instruction: " << *user << "\n"; - continue; // 跳过 Load 指令 - } - // Skip if the user is the newArg itself - if (user != newArg) + // Skip uses where the original float* argument is directly bitcasted + if (auto *bitcastInst = dyn_cast(user)) + { + if (bitcastInst->getSrcTy() == oldArg->getType()) { - usesToReplace.push_back(&U); + llvm::errs() << "Skipping original bitcast: " << *bitcastInst << "\n"; + continue; } } - // Perform the replacements - for (auto * use : usesToReplace) + + // Skip if the user is the newArg itself + if (user == newArg) { - use->set(newArg); + continue; } + + usesToReplace.push_back(&U); + } + + // Perform the replacements + for (auto *use : usesToReplace) + { + use->set(newArg); } } +} + + + void @@ -1324,24 +1351,8 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) } // Create multiplication and rounding instructions -// llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); -// llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); - - - //TODO FAST MATH - llvm::FastMathFlags FMF; - FMF.setFast(); // 启用所有 Fast-Math 优化 - // 创建乘法指令并应用 Fast-Math - llvm::Instruction *scaled = cast( - builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); - scaled->setFastMathFlags(FMF); // 设置 Fast-Math 标志 - - // 创建加法指令并应用 Fast-Math - llvm::Instruction *rounded = cast( - builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); - rounded->setFastMathFlags(FMF); // 设置 Fast-Math 标志 - - + llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); //llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); llvm::Instruction *quantized = nullptr; From fcb71f9a689fdb93526a37f32bb00684de2ab1fc Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Sun, 17 Nov 2024 12:55:27 +0000 Subject: [PATCH 120/213] update config of bitWidth Addresses #1. --- ...a2089f18acf70a8f55e8433f7d03bc9f992471.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 26 ++++++---- .../newton-irPass-LLVMIR-quantization.cpp | 28 +++++++++-- .../newton-irPass-LLVMIR-quantization.h | 2 +- 4 files changed, 90 insertions(+), 14 deletions(-) create mode 100644 analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt diff --git a/analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt b/analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt new file mode 100644 index 000000000..87986b421 --- /dev/null +++ b/analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt @@ -0,0 +1,48 @@ + +changeset: 1713:38a2089f18acf70a8f55e8433f7d03bc9f992471 +char kNewtonVersion[] = "0.3-alpha-1713 (38a2089f18acf70a8f55e8433f7d03bc9f992471) (build 11-17-2024-11:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index f7525f3b7..1b065777e 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -76,7 +76,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "llvm/IR/Function.h" using namespace llvm; -#define BIT_WIDTH 16 + std::set whitelist = { "MadgwickAHRSupdate", @@ -87,7 +87,7 @@ std::set whitelist = { std::vector toRemove; void -dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) +dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth) { // IRBuilder<> Builder(storeInst); IRBuilder<> Builder(storeInst->getNextNode()); @@ -101,7 +101,9 @@ dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) } Type *targetType = nullptr; - switch (BIT_WIDTH) + //BIT_WIDTH = bitWidth; + + switch (bitWidth) { case 16: targetType = Type::getInt16Ty(F.getContext()); @@ -112,7 +114,7 @@ dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) break; } - if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(BIT_WIDTH)) + if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(bitWidth)) { auto *loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); @@ -132,7 +134,7 @@ dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) bool isValidSource = false; // Determine the final store pointer based on BIT_WIDTH - switch (BIT_WIDTH) + switch (bitWidth) { case 16: // Check if the source type is i32 and find the original float* @@ -160,7 +162,7 @@ dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) break; default: - llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; + llvm::errs() << "Unsupported BIT_WIDTH: " << bitWidth << "\n"; return; } @@ -179,7 +181,7 @@ dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth) { for (auto & F : module) { @@ -195,7 +197,7 @@ processWhitelistedFunctions(Module & module, const std::set & white if (auto * storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F, maxPrecisionBits); + dequantizeResults(storeInst, F, maxPrecisionBits,bitWidth); } } } @@ -450,6 +452,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); + /** + * Config + */ + int bitWidth = 32; maxPrecisionBits = 10; /* @@ -534,7 +540,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl for (auto & mi : *Mod) { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth); } for (auto mi : functionsToInsert) { @@ -698,7 +704,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // eraseOldInstructions(); - processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits, bitWidth); const char * homeDir = getenv("HOME"); if (!homeDir) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index ab45182bd..8241a2d5b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -6,9 +6,10 @@ using namespace llvm; unsigned int FRAC_Q; +unsigned int BIT_WIDTH; #define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 16 + llvm::Value * performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned int FRAC_Q) @@ -428,6 +429,24 @@ updateGlobalVariables(Module * module, Type * quantizedType) // } // } +//void +//quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +//{ +// Value * pointerOperand = loadInst->getPointerOperand(); +// llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; +// +// Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); +// +// Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); +// +// Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); +// +// loadInst->replaceAllUsesWith(quantizedValue); +// loadInst->eraseFromParent(); +// +// llvm::errs() << "Replaced load with quantized integer value.\n"; +//} + void quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { @@ -438,7 +457,9 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); - Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); + Value * roundingValue = Builder.CreateFAdd(scaledValue, ConstantFP::get(loadedType, 0.5), loadInst->getName() + ".rounded_ptr"); + + Value * quantizedValue = Builder.CreateFPToSI(roundingValue, quantizedType, loadInst->getName() + ".quantized_ptr"); loadInst->replaceAllUsesWith(quantizedValue); loadInst->eraseFromParent(); @@ -1511,9 +1532,10 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) // Main function to perform LLVM IR auto quantization void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits) +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth) { FRAC_Q = maxPrecisionBits; + BIT_WIDTH = bitWidth; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 70d6a84aa..ba495ba6f 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -39,7 +39,7 @@ extern std::vector functionsToErase; extern std::vector globalsToErase; extern std::vector instructionsToErase; void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits,int bitWidth); extern From ac7b00410dd70f1ef49b8cee3eda71abda55a094 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 18 Nov 2024 20:49:11 +0000 Subject: [PATCH 121/213] add rms error analysis of quaternions results Addresses #1. --- ...b3928eeefd23988db60d9401e77c3c1a7e836f.txt | 48 ++++++++++++ applications/newton/llvm-ir/rms_error.py | 78 +++++++++++++++++++ 2 files changed, 126 insertions(+) create mode 100644 analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt create mode 100644 applications/newton/llvm-ir/rms_error.py diff --git a/analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt b/analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt new file mode 100644 index 000000000..a7f3e886a --- /dev/null +++ b/analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt @@ -0,0 +1,48 @@ + +changeset: 1714:6ab3928eeefd23988db60d9401e77c3c1a7e836f +char kNewtonVersion[] = "0.3-alpha-1714 (6ab3928eeefd23988db60d9401e77c3c1a7e836f) (build 11-17-2024-12:55-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/rms_error.py b/applications/newton/llvm-ir/rms_error.py new file mode 100644 index 000000000..39391c477 --- /dev/null +++ b/applications/newton/llvm-ir/rms_error.py @@ -0,0 +1,78 @@ +import math + +# Function to parse the data from the files after removing the prefix +def parse_data(file_path, prefix_to_remove): + """ + Reads a file and extracts q0, q1, q2, q3 values from each line after removing the specified prefix. + Returns a list of tuples containing the extracted values. + """ + with open(file_path, 'r') as file: + data = [] + for line in file: + if line.startswith(prefix_to_remove): + # Remove the prefix and strip the line + clean_line = line.replace(prefix_to_remove, "").strip() + values = clean_line.split(", ") + # Extract q0, q1, q2, q3 values and convert them to floats + parsed_values = [float(value.split("=")[1]) for value in values] + data.append(parsed_values) + return data + +# Calculate RMS errors for q0, q1, q2, q3 +def calculate_rms_errors(fp_data, int_data): + errors_q0 = [] + errors_q1 = [] + errors_q2 = [] + errors_q3 = [] + overall_errors = [] + + for fp, integer in zip(fp_data, int_data): + q0_error = fp[0] - integer[0] + q1_error = fp[1] - integer[1] + q2_error = fp[2] - integer[2] + q3_error = fp[3] - integer[3] + + # Square the errors + errors_q0.append(q0_error ** 2) + errors_q1.append(q1_error ** 2) + errors_q2.append(q2_error ** 2) + errors_q3.append(q3_error ** 2) + + # Accumulate the overall squared error for all components + overall_errors.append(q0_error ** 2 + q1_error ** 2 + q2_error ** 2 + q3_error ** 2) + + rms_q0 = math.sqrt(sum(errors_q0) / len(errors_q0)) + rms_q1 = math.sqrt(sum(errors_q1) / len(errors_q1)) + rms_q2 = math.sqrt(sum(errors_q2) / len(errors_q2)) + rms_q3 = math.sqrt(sum(errors_q3) / len(errors_q3)) + + # Overall RMS error + rms_overall = math.sqrt(sum(overall_errors) / len(overall_errors)) + + # Average of RMS values + avg_rms = (rms_q0 + rms_q1 + rms_q2 + rms_q3) / 4 + + return rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms + +# Main function to compute RMS errors +def main(): + fp_data = parse_data('fp_result.txt', 'Original: ') + int_data = parse_data('int_result.txt', 'FIX: ') + + if len(fp_data) != len(int_data): + print("Mismatch in the number of quaternions between files!") + return + + # Calculate RMS errors + rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms = calculate_rms_errors(fp_data, int_data) + + print(f"RMS Error (q0): {rms_q0:.6f}") + print(f"RMS Error (q1): {rms_q1:.6f}") + print(f"RMS Error (q2): {rms_q2:.6f}") + print(f"RMS Error (q3): {rms_q3:.6f}") + print(f"Overall RMS Error: {rms_overall:.6f}") + print(f"Average of RMS Values: {avg_rms:.6f}") + +# Run the main function +if __name__ == "__main__": + main() From db7f5a7f5cb056f7d01fde115d43244ba0075468 Mon Sep 17 00:00:00 2001 From: Wzw2000426 Date: Thu, 21 Nov 2024 17:14:05 +0000 Subject: [PATCH 122/213] try to add support for vectorization Addresses #1. --- ...b71f9a689fdb93526a37f32bb00684de2ab1fc.txt | 48 ++++++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 9 ++-- .../newton-irPass-LLVMIR-quantization.cpp | 50 ++++++++++--------- .../newton-irPass-LLVMIR-quantization.h | 3 +- 4 files changed, 82 insertions(+), 28 deletions(-) create mode 100644 analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt diff --git a/analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt b/analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt new file mode 100644 index 000000000..05f85f7a1 --- /dev/null +++ b/analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt @@ -0,0 +1,48 @@ + +changeset: 1715:fcb71f9a689fdb93526a37f32bb00684de2ab1fc +char kNewtonVersion[] = "0.3-alpha-1715 (fcb71f9a689fdb93526a37f32bb00684de2ab1fc) (build 11-18-2024-20:49-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 1b065777e..fe7428977 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -455,8 +455,9 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl /** * Config */ - int bitWidth = 32; - maxPrecisionBits = 10; + int bitWidth = 16; + maxPrecisionBits = 11; + bool enableVectorization = true; /* * get const global variables @@ -540,7 +541,8 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl for (auto & mi : *Mod) { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth); + //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth, enableVectorization); } for (auto mi : functionsToInsert) { @@ -548,7 +550,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl Mod->getFunctionList().push_front(mi); } } - // Range Analysis // for (auto & mi : *Mod) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 8241a2d5b..961f1ea78 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -429,24 +429,6 @@ updateGlobalVariables(Module * module, Type * quantizedType) // } // } -//void -//quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) -//{ -// Value * pointerOperand = loadInst->getPointerOperand(); -// llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; -// -// Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); -// -// Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); -// -// Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); -// -// loadInst->replaceAllUsesWith(quantizedValue); -// loadInst->eraseFromParent(); -// -// llvm::errs() << "Replaced load with quantized integer value.\n"; -//} - void quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { @@ -457,15 +439,33 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); - Value * roundingValue = Builder.CreateFAdd(scaledValue, ConstantFP::get(loadedType, 0.5), loadInst->getName() + ".rounded_ptr"); - - Value * quantizedValue = Builder.CreateFPToSI(roundingValue, quantizedType, loadInst->getName() + ".quantized_ptr"); + Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); loadInst->replaceAllUsesWith(quantizedValue); loadInst->eraseFromParent(); llvm::errs() << "Replaced load with quantized integer value.\n"; } + +//void +//quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +//{ +// Value * pointerOperand = loadInst->getPointerOperand(); +// llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; +// +// Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); +// +// Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); +// +// Value * roundingValue = Builder.CreateFAdd(scaledValue, ConstantFP::get(loadedType, 0.5), loadInst->getName() + ".rounded_ptr"); +// +// Value * quantizedValue = Builder.CreateFPToSI(roundingValue, quantizedType, loadInst->getName() + ".quantized_ptr"); +// +// loadInst->replaceAllUsesWith(quantizedValue); +// loadInst->eraseFromParent(); +// +// llvm::errs() << "Replaced load with quantized integer value.\n"; +//} /** * handleLoad */ @@ -1531,14 +1531,18 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } // Main function to perform LLVM IR auto quantization -void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth) +void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, int maxPrecisionBits, int bitWidth, bool enableVectorization) { { FRAC_Q = maxPrecisionBits; BIT_WIDTH = bitWidth; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; + // Handle vectorization initialization + if (enableVectorization) { + llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; + } + // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); if (shouldSkipFunction(functionName)) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index ba495ba6f..986cfa540 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -39,7 +39,8 @@ extern std::vector functionsToErase; extern std::vector globalsToErase; extern std::vector instructionsToErase; void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits,int bitWidth); +irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, int maxPrecisionBits, int bitWidth, bool enableVectorization); + extern From 037273f277374e0717f7025a54ac65f8874e5123 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 21 Nov 2024 17:45:03 +0000 Subject: [PATCH 123/213] test Addresses #2. --- .../ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt diff --git a/analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt b/analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt new file mode 100644 index 000000000..f4e21638e --- /dev/null +++ b/analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt @@ -0,0 +1,7 @@ + +changeset: 1716:ac7b00410dd70f1ef49b8cee3eda71abda55a094 +char kNewtonVersion[] = "0.3-alpha-1716 (ac7b00410dd70f1ef49b8cee3eda71abda55a094) (build 11-21-2024-17:14-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt From c2c03fce474fe62166871bd538e0a8e900045acf Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 22 Nov 2024 10:39:58 +0000 Subject: [PATCH 124/213] add support for sin and cos Addresses #2. --- ...7f5a7f5cb056f7d01fde115d43244ba0075468.txt | 7 + .../newton-irPass-LLVMIR-quantization.cpp | 671 +++++++++--------- 2 files changed, 357 insertions(+), 321 deletions(-) create mode 100644 analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt diff --git a/analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt b/analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt new file mode 100644 index 000000000..9bd16dba7 --- /dev/null +++ b/analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt @@ -0,0 +1,7 @@ + +changeset: 1717:db7f5a7f5cb056f7d01fde115d43244ba0075468 +char kNewtonVersion[] = "0.3-alpha-1717 (db7f5a7f5cb056f7d01fde115d43244ba0075468) (build 11-21-2024-17:45-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 961f1ea78..d7a0be0c8 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -9,26 +9,24 @@ unsigned int FRAC_Q; unsigned int BIT_WIDTH; #define FRAC_BASE (1 << FRAC_Q) - - llvm::Value * -performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value *rhs, unsigned int FRAC_Q) +performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned int FRAC_Q) { - llvm::Value *result = nullptr; + llvm::Value * result = nullptr; switch (BIT_WIDTH) { case 16: { // Sign extend the 16-bit operands to 32-bit integers - llvm::Value *lhs32 = Builder.CreateSExt(lhs, llvm::Type::getInt32Ty(Builder.getContext())); - llvm::Value *rhs32 = Builder.CreateSExt(rhs, llvm::Type::getInt32Ty(Builder.getContext())); + llvm::Value * lhs32 = Builder.CreateSExt(lhs, llvm::Type::getInt32Ty(Builder.getContext())); + llvm::Value * rhs32 = Builder.CreateSExt(rhs, llvm::Type::getInt32Ty(Builder.getContext())); // Perform 32-bit multiplication - llvm::Value *mulResult32 = Builder.CreateNSWMul(lhs32, rhs32); + llvm::Value * mulResult32 = Builder.CreateNSWMul(lhs32, rhs32); // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value *divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); // Truncate the result back to 16-bit integer result = Builder.CreateTrunc(divResult32, llvm::Type::getInt16Ty(Builder.getContext())); @@ -37,14 +35,14 @@ performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value * case 32: { // Sign extend the 32-bit operands to 64-bit integers - llvm::Value *lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); - llvm::Value *rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); // Perform 64-bit multiplication - llvm::Value *mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); + llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value *divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); // Truncate the 64-bit result back to 32-bit integer result = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); @@ -60,7 +58,6 @@ performFixedPointMul(llvm::IRBuilder<> &Builder, llvm::Value *lhs, llvm::Value * return result; } - extern "C" { // TODO : float version rsqrt llvm::Function * @@ -121,7 +118,6 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) @@ -182,9 +178,8 @@ createFixMul(Module * irModule, Type * quantizedType, std::vector &functionsToInsert) +createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector & functionsToInsert) { llvm::errs() << "Entering createFixRsqrt\n"; @@ -196,7 +191,7 @@ createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vectorgetContext(), "", func); - llvm::IRBuilder<> builder(entryBB); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "", func); + llvm::IRBuilder<> builder(entryBB); // Get the function argument (x) llvm::Function::arg_iterator args = func->arg_begin(); - llvm::Value *x = &*args++; + llvm::Value * x = &*args++; llvm::Value *fpX = nullptr, *scaledFpX = nullptr, *approx = nullptr, *intApprox = nullptr, *shiftedX = nullptr, *result = nullptr; @@ -227,33 +222,33 @@ createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vectorgetContext())); - fpX = builder.CreateSIToFP(sextX, llvm::Type::getFloatTy(irModule->getContext())); - scaledFpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + llvm::Value * sextX = builder.CreateSExt(x, llvm::Type::getInt32Ty(irModule->getContext())); + fpX = builder.CreateSIToFP(sextX, llvm::Type::getFloatTy(irModule->getContext())); + scaledFpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); // Step 3: Approximation using magic number - llvm::Value *bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); - llvm::Value *shiftedFpX = builder.CreateLShr(bitcastFpX, 1); - llvm::Value *magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); - approx = builder.CreateSub(magicNumber, shiftedFpX); - llvm::Value *approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); - llvm::Value *scaledApprox = builder.CreateFMul(approxFp, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); - intApprox = builder.CreateFPToSI(scaledApprox, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); + llvm::Value * magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); + approx = builder.CreateSub(magicNumber, shiftedFpX); + llvm::Value * approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Value * scaledApprox = builder.CreateFMul(approxFp, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + intApprox = builder.CreateFPToSI(scaledApprox, llvm::Type::getInt32Ty(irModule->getContext())); // Step 4: Newton-Raphson refinement - llvm::Value *sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); - llvm::Value *mul1 = builder.CreateMul(sextShiftedX, intApprox); - llvm::Value *mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); + llvm::Value * sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * mul1 = builder.CreateMul(sextShiftedX, intApprox); + llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); - llvm::Value *mul2 = builder.CreateMul(mul1Shifted, intApprox); - llvm::Value *mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); + llvm::Value * mul2 = builder.CreateMul(mul1Shifted, intApprox); + llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); - int correctionValue = static_cast(1.5f * FRAC_BASE); - llvm::Value *correction = builder.CreateSub( + int correctionValue = static_cast(1.5f * FRAC_BASE); + llvm::Value * correction = builder.CreateSub( llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), correctionValue), mul2Shifted); - llvm::Value *finalMul = builder.CreateMul(intApprox, correction); - llvm::Value *finalShifted = builder.CreateLShr(finalMul, FRAC_Q); + llvm::Value * finalMul = builder.CreateMul(intApprox, correction); + llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); // Step 5: Truncate the result back to i16 result = builder.CreateTrunc(finalShifted, quantizedType); @@ -263,21 +258,21 @@ createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vector> 1) - llvm::Value *halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); // Step 2: Convert x to floating-point and perform the initial approximation fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); fpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); - llvm::Value *i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); - fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); + i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); - llvm::Value *int_y = builder.CreateFPToSI(builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); - llvm::Value *mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); - llvm::Value *mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); - llvm::Value *correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); - llvm::Value *final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); + llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + llvm::Value * mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); + llvm::Value * mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); + llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value * final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); result = final_y; break; @@ -291,8 +286,6 @@ createFixRsqrt(llvm::Module *irModule, llvm::Type *quantizedType, std::vector globalsToErase; void @@ -376,7 +369,7 @@ updateGlobalVariables(Module * module, Type * quantizedType) globalVar.getLinkage(), globalVar.getInitializer(), quantizedName); - //newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + // newGlobalVar->setAlignment(llvm::MaybeAlign(4)); switch (BIT_WIDTH) { case 16: @@ -447,8 +440,8 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType llvm::errs() << "Replaced load with quantized integer value.\n"; } -//void -//quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +// void +// quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) //{ // Value * pointerOperand = loadInst->getPointerOperand(); // llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; @@ -465,7 +458,7 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType // loadInst->eraseFromParent(); // // llvm::errs() << "Replaced load with quantized integer value.\n"; -//} +// } /** * handleLoad */ @@ -788,21 +781,21 @@ void simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) { llvm::IRBuilder<> Builder(instruction); - //Type * intType = Type::getInt32Ty(instruction->getContext()); - llvm::Type *intType = nullptr; + // Type * intType = Type::getInt32Ty(instruction->getContext()); + llvm::Type * intType = nullptr; switch (BIT_WIDTH) { case 16: - intType = llvm::Type::getInt16Ty(instruction->getContext()); // Use 16-bit integer type + intType = llvm::Type::getInt16Ty(instruction->getContext()); // Use 16-bit integer type break; default: - intType = llvm::Type::getInt32Ty(instruction->getContext()); // Use 32-bit integer type + intType = llvm::Type::getInt32Ty(instruction->getContext()); // Use 32-bit integer type break; } - Value * shiftValue = ConstantInt::get(intType, shiftAmount); - Instruction * shiftInst; + Value * shiftValue = ConstantInt::get(intType, shiftAmount); + Instruction * shiftInst; if (isRightShift) { @@ -838,8 +831,6 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return false; } - - void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1026,11 +1017,10 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) { IRBuilder<> Builder(llvmIrStoreInstruction); - auto valueOperand = llvmIrStoreInstruction->getValueOperand(); - auto pointerOperand = llvmIrStoreInstruction->getPointerOperand(); - auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); - auto pointerType = pointerOperand->getType()->getPointerElementType(); - + auto valueOperand = llvmIrStoreInstruction->getValueOperand(); + auto pointerOperand = llvmIrStoreInstruction->getPointerOperand(); + auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); + auto pointerType = pointerOperand->getType()->getPointerElementType(); if (valueType->isFloatTy() || valueType->isDoubleTy()) { @@ -1043,7 +1033,7 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) llvmIrStoreInstruction->setOperand(0, quantizedValue); } - //auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); + // auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); if (pointerType->isFloatTy() || pointerType->isDoubleTy()) { llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; @@ -1054,43 +1044,44 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) } } } -bool isTargetFunction(Function &func); +bool isTargetFunction(Function & func); -void bitcastFloatPtrArgs(Function &F, IRBuilder<> &Builder) +void +bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) { // Check if the function is the specific one to be skipped if (isTargetFunction(F)) { llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; - return; // Early exit if it's the function to skip + return; // Early exit if it's the function to skip } SmallVector, 4> argReplacements; // Iterate over all function arguments - for (Argument &Arg : F.args()) + for (Argument & Arg : F.args()) { // Check if the argument is a pointer to float if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) { // Create a bitcast to i32* at the beginning of the function - llvm::PointerType *i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); + llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); - Value *i32Arg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); + Value * i32Arg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); // Additional bitcast to i16* if BIT_WIDTH == 16 - Value *newArg = nullptr; + Value * newArg = nullptr; switch (BIT_WIDTH) { case 16: { - llvm::PointerType *i16PtrType = llvm::Type::getInt16PtrTy(F.getContext()); - newArg = Builder.CreateBitCast(i32Arg, i16PtrType, Arg.getName() + ".to_i16_ptr"); + llvm::PointerType * i16PtrType = llvm::Type::getInt16PtrTy(F.getContext()); + newArg = Builder.CreateBitCast(i32Arg, i16PtrType, Arg.getName() + ".to_i16_ptr"); break; } case 32: default: - newArg = i32Arg; // Use i32* as the new argument + newArg = i32Arg; // Use i32* as the new argument break; } @@ -1099,29 +1090,29 @@ void bitcastFloatPtrArgs(Function &F, IRBuilder<> &Builder) } } - llvm::errs() << "Starting use replacement\n"; // Log added + llvm::errs() << "Starting use replacement\n"; // Log added // Iterate over the function to replace uses of the original arguments - for (auto &replacement : argReplacements) + for (auto & replacement : argReplacements) { - Argument *oldArg = replacement.first; - Value *newArg = replacement.second; + Argument * oldArg = replacement.first; + Value * newArg = replacement.second; // Collect all uses of the old argument for replacement SmallVector usesToReplace; - for (auto &U : oldArg->uses()) + for (auto & U : oldArg->uses()) { - User *user = U.getUser(); + User * user = U.getUser(); // Skip if the use is a Load instruction if (isa(user)) { llvm::errs() << "Skipping load instruction: " << *user << "\n"; - continue; // Skip Load instructions + continue; // Skip Load instructions } // Skip uses where the original float* argument is directly bitcasted - if (auto *bitcastInst = dyn_cast(user)) + if (auto * bitcastInst = dyn_cast(user)) { if (bitcastInst->getSrcTy() == oldArg->getType()) { @@ -1140,17 +1131,13 @@ void bitcastFloatPtrArgs(Function &F, IRBuilder<> &Builder) } // Perform the replacements - for (auto *use : usesToReplace) + for (auto * use : usesToReplace) { use->set(newArg); } } } - - - - void handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) { @@ -1194,47 +1181,45 @@ finalRes = builder.CreateFPToSI(compensated, quantizedType); */ llvm::Value * -performFixedPointSqrt(IRBuilder<> &builder, Module *irModule, Value *fixedPointValue, Type *quantizedType, int FRAC_Q, LLVMContext &context) +performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPointValue, Type * quantizedType, int FRAC_Q, LLVMContext & context) { // Convert the fixed-point value to floating-point for sqrt calculation - llvm::Value *fpValue = builder.CreateSIToFP(fixedPointValue, llvm::Type::getFloatTy(context)); + llvm::Value * fpValue = builder.CreateSIToFP(fixedPointValue, llvm::Type::getFloatTy(context)); // Call sqrt on the floating-point value - llvm::Function *sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); - llvm::Value *sqrtResult = builder.CreateCall(sqrtFunc, {fpValue}); + llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fpValue}); // Convert the result back to a fixed-point integer - llvm::Value *res = builder.CreateFPToSI(sqrtResult, quantizedType); + llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); // Perform a left shift to scale the result (FRAC_Q / 2) - llvm::Value *shlRes = builder.CreateShl(res, FRAC_Q / 2); + llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); // Initialize the final result as the shifted result - llvm::Value *finalRes = shlRes; + llvm::Value * finalRes = shlRes; // Apply compensation if FRAC_Q is odd if (FRAC_Q % 2 != 0) { // Compensation factor for odd FRAC_Q (1.414213562 ≈ sqrt(2)) - llvm::Value *compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562f); + llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562f); // Convert the shifted result back to float - llvm::Value *fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); + llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); // Multiply by the compensation factor to adjust for odd FRAC_Q - llvm::Value *compensated = builder.CreateFMul(fpShlRes, compensationFactor); + llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); // Convert the compensated value back to a fixed-point integer finalRes = builder.CreateFPToSI(compensated, quantizedType); } return finalRes; - } - -//void -//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +// void +// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) //{ // IRBuilder<> Builder(llvmIrCallInstruction); // auto operand = llvmIrCallInstruction->getOperand(0); @@ -1252,7 +1237,34 @@ performFixedPointSqrt(IRBuilder<> &builder, Module *irModule, Value *fixedPointV // // No need to apply shl and compensation if it's already done in createFixSqrt // llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); // llvmIrCallInstruction->eraseFromParent(); -//} +// } + +void +handleSinCosCall(CallInst * llvmIrCallInstruction, Type * quantizedType, const std::string & mathFunc) +{ + IRBuilder<> builder(llvmIrCallInstruction); + + Value * operand = llvmIrCallInstruction->getOperand(0); + + Value * floatOperand = builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + + // use float version sin/cos + llvm::Function * floatFunc = Intrinsic::getDeclaration( + llvmIrCallInstruction->getModule(), + mathFunc == "sin" ? Intrinsic::sin : Intrinsic::cos, + llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + + Value * floatResult = builder.CreateCall(floatFunc, {floatOperand}); + + Value * scaledResult = builder.CreateFMul( + floatResult, + llvm::ConstantFP::get(llvm::Type::getFloatTy(llvmIrCallInstruction->getContext()), FRAC_BASE)); + + Value * quantizedResult = builder.CreateFPToSI(scaledResult, quantizedType); + + llvmIrCallInstruction->replaceAllUsesWith(quantizedResult); + llvmIrCallInstruction->eraseFromParent(); +} void handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) @@ -1276,7 +1288,7 @@ handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function void -//handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) +// handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; @@ -1313,38 +1325,49 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetNextNode(); Builder.SetInsertPoint(insertPoint); Value * newInst = nullptr; + + // Handle specific math functions if (funcName == "sqrt" || funcName == "sqrtf") { // For sqrt - //handleSqrtCall(llvmIrCallInstruction, quantizedType); + // handleSqrtCall(llvmIrCallInstruction, quantizedType); handleSqrtCall(llvmIrCallInstruction, quantizedType, fixsqrt); if (calledFunction->use_empty()) { functionsToErase.push_back(calledFunction); } } - else + else if (funcName == "sin" || funcName == "sinf") { - /* - * for other lib functions, de-quantize the arguments and quantize the return value - */ - // dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); + llvm::errs() << "Handling sin call\n"; + handleSinCosCall(llvmIrCallInstruction, quantizedType, "sin"); } + else if (funcName == "cos" || funcName == "cosf") + { + llvm::errs() << "Handling cos call\n"; + handleSinCosCall(llvmIrCallInstruction, quantizedType, "cos"); + } + // else + // { + // /* + // * for other lib functions, de-quantize the arguments and quantize the return value + // */ + // // dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); } - else + } + else + { + // Quantize the return type if necessary + if (llvmIrCallInstruction->getType()->isPointerTy()) { - // Quantize the return type if necessary - if (llvmIrCallInstruction->getType()->isPointerTy()) + Type * returnElementType = llvmIrCallInstruction->getType()->getPointerElementType(); + if (returnElementType->isFloatTy() || returnElementType->isDoubleTy()) { - Type * returnElementType = llvmIrCallInstruction->getType()->getPointerElementType(); - if (returnElementType->isFloatTy() || returnElementType->isDoubleTy()) - { - // Keep the pointer type, but track the need for de-quantization - return; - } + // Keep the pointer type, but track the need for de-quantization + return; } - setQuantizedType(llvmIrCallInstruction, quantizedType); } + setQuantizedType(llvmIrCallInstruction, quantizedType); } } @@ -1372,10 +1395,10 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) } // Create multiplication and rounding instructions - llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); - llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); - //llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); - llvm::Instruction *quantized = nullptr; + llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); + // llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); + llvm::Instruction * quantized = nullptr; switch (BIT_WIDTH) { @@ -1443,7 +1466,9 @@ shouldSkipFunction(const std::string & functionName) return skipFunctions.find(functionName) != skipFunctions.end(); } -bool isTargetFunction(Function &func) { +bool +isTargetFunction(Function & func) +{ return func.getName() == "MadgwickAHRSupdateIMU" || func.getName() == "MahonyAHRSupdateIMU"; } @@ -1531,220 +1556,224 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } // Main function to perform LLVM IR auto quantization -void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, int maxPrecisionBits, int bitWidth, bool enableVectorization) { +void +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth, bool enableVectorization) { - FRAC_Q = maxPrecisionBits; - BIT_WIDTH = bitWidth; - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); - llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - - // Handle vectorization initialization - if (enableVectorization) { - llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; - } - - // Usage in the original function - std::string functionName = llvmIrFunction.getName().str(); - if (shouldSkipFunction(functionName)) { - return; - } + FRAC_Q = maxPrecisionBits; + BIT_WIDTH = bitWidth; + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); + llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - Type * quantizedType; - switch (BIT_WIDTH) - { - case 8: - quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); - break; - case 16: - quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); - break; - case 32: - quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); - break; - case 64: - quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); - break; - default: - flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); - llvm_unreachable("Unknown bit width for quantization"); - return; - } + // Handle vectorization initialization + if (enableVectorization) + { + llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; + } - // Save the parent module - Module * module = llvmIrFunction.getParent(); - if (!module) - { - llvm::errs() << "Error: Function does not have a parent module.\n"; - return; - } + // Usage in the original function + std::string functionName = llvmIrFunction.getName().str(); + if (shouldSkipFunction(functionName)) + { + return; + } - // Iterate over the function's arguments to apply quantization - llvm::IRBuilder<> builder(llvmIrFunction.getContext()); - quantizeFunctionArguments(llvmIrFunction, builder); - quantizeArguments(llvmIrFunction, quantizedType); + Type * quantizedType; + switch (BIT_WIDTH) + { + case 8: + quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); + break; + case 16: + quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); + break; + case 32: + quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); + break; + case 64: + quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); + break; + default: + flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); + llvm_unreachable("Unknown bit width for quantization"); + return; + } - // Perform bitcasting of float poiter arguments to i32* - bitcastFloatPtrArgs(llvmIrFunction, builder); + // Save the parent module + Module * module = llvmIrFunction.getParent(); + if (!module) + { + llvm::errs() << "Error: Function does not have a parent module.\n"; + return; + } - // Update global variables to integer type - updateGlobalVariables(module, quantizedType); + // Iterate over the function's arguments to apply quantization + llvm::IRBuilder<> builder(llvmIrFunction.getContext()); + quantizeFunctionArguments(llvmIrFunction, builder); + quantizeArguments(llvmIrFunction, quantizedType); - /* - * generate hardcode function - * */ - //llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); - llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + // Perform bitcasting of float poiter arguments to i32* + bitcastFloatPtrArgs(llvmIrFunction, builder); - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) - { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) - { - Instruction * llvmIrInstruction = &*itBB++; + // Update global variables to integer type + updateGlobalVariables(module, quantizedType); - // if (llvmIrInstruction->getMetadata("quantized_changed")) - // { - // llvm::errs() << "quantized_changed.\n"; - // return; // Skip processing this instruction - // } + /* + * generate hardcode function + * */ + // llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); + llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); - llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; - switch (llvmIrInstruction->getOpcode()) + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - case Instruction::Alloca: - handleAlloca(cast(llvmIrInstruction), quantizedType); - break; - case Instruction::Call: - //handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); - break; - case Instruction::GetElementPtr: - case Instruction::Load: - { - llvm::errs() << "Handling load\n"; - handleLoad(llvmIrInstruction, quantizedType); - break; - } - break; - case Instruction::PHI: - { - setQuantizedType(llvmIrInstruction, quantizedType); - } - break; + Instruction * llvmIrInstruction = &*itBB++; - case Instruction::Store: - /* - * If either of the operands is constant, change it to a int value - * */ - llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; - // setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - handleStore(llvmIrInstruction, quantizedType); - break; + // if (llvmIrInstruction->getMetadata("quantized_changed")) + // { + // llvm::errs() << "quantized_changed.\n"; + // return; // Skip processing this instruction + // } - case Instruction::FMul: + llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; + switch (llvmIrInstruction->getOpcode()) { - handleFMul(llvmIrInstruction, quantizedType); + case Instruction::Alloca: + handleAlloca(cast(llvmIrInstruction), quantizedType); + break; + case Instruction::Call: + // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); + break; + case Instruction::GetElementPtr: + case Instruction::Load: + { + llvm::errs() << "Handling load\n"; + handleLoad(llvmIrInstruction, quantizedType); + break; + } + break; + case Instruction::PHI: + { + setQuantizedType(llvmIrInstruction, quantizedType); + } break; - } - /* - * If either one of the operands is a constant value, quantize it, - * then replace the instruction to the int version. - * */ - case Instruction::FDiv: + case Instruction::Store: + /* + * If either of the operands is constant, change it to a int value + * */ + llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; + // setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + handleStore(llvmIrInstruction, quantizedType); + break; - case Instruction::FCmp: - handleFCmp(llvmIrInstruction, quantizedType); - break; - case Instruction::FAdd: - { - handleFAdd(llvmIrInstruction, quantizedType); - break; - } - case Instruction::FSub: - { - handleFSub(llvmIrInstruction, quantizedType); - break; - } - case Instruction::FRem: - case Instruction::FNeg: - { - handleFNeg(llvmIrInstruction, quantizedType); - break; - } + case Instruction::FMul: + { + handleFMul(llvmIrInstruction, quantizedType); + break; + } - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::SIToFP: - case Instruction::UIToFP: + /* + * If either one of the operands is a constant value, quantize it, + * then replace the instruction to the int version. + * */ + case Instruction::FDiv: - case Instruction::ZExt: - case Instruction::SExt: - case Instruction::Trunc: - case Instruction::FPExt: - case Instruction::FPTrunc: - // { - // IRBuilder<> Builder(llvmIrInstruction); - // Instruction * insertPoint = llvmIrInstruction->getNextNode(); - // Builder.SetInsertPoint(insertPoint); - // Value * newInst = nullptr; - // if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) - // { - // newInst = Builder.CreateSIToFP( - // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - // } - // else - // { - // newInst = Builder.CreateFPCast( - // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - // } - // llvmIrInstruction->replaceAllUsesWith(newInst); - // llvmIrInstruction->removeFromParent(); - // break; - // } + case Instruction::FCmp: + handleFCmp(llvmIrInstruction, quantizedType); + break; + case Instruction::FAdd: + { + handleFAdd(llvmIrInstruction, quantizedType); + break; + } + case Instruction::FSub: + { + handleFSub(llvmIrInstruction, quantizedType); + break; + } + case Instruction::FRem: + case Instruction::FNeg: + { + handleFNeg(llvmIrInstruction, quantizedType); + break; + } - case Instruction::BitCast: - case Instruction::Ret: - case Instruction::Switch: - case Instruction::Br: - case Instruction::Select: - case Instruction::IndirectBr: - case Instruction::Invoke: - case Instruction::Resume: - case Instruction::Unreachable: - case Instruction::CleanupRet: - case Instruction::CatchRet: - case Instruction::CatchSwitch: - case Instruction::CallBr: - case Instruction::Fence: - case Instruction::AtomicCmpXchg: - case Instruction::AtomicRMW: - case Instruction::PtrToInt: - case Instruction::IntToPtr: - case Instruction::AddrSpaceCast: - case Instruction::CleanupPad: - case Instruction::CatchPad: - case Instruction::UserOp1: - case Instruction::UserOp2: - case Instruction::VAArg: - case Instruction::ExtractElement: - case Instruction::InsertElement: - case Instruction::ShuffleVector: - case Instruction::ExtractValue: - case Instruction::InsertValue: - case Instruction::LandingPad: - case Instruction::Freeze: - break; - default: - break; + case Instruction::FPToUI: + case Instruction::FPToSI: + case Instruction::SIToFP: + case Instruction::UIToFP: + + case Instruction::ZExt: + case Instruction::SExt: + case Instruction::Trunc: + case Instruction::FPExt: + case Instruction::FPTrunc: + // { + // IRBuilder<> Builder(llvmIrInstruction); + // Instruction * insertPoint = llvmIrInstruction->getNextNode(); + // Builder.SetInsertPoint(insertPoint); + // Value * newInst = nullptr; + // if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + // { + // newInst = Builder.CreateSIToFP( + // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // } + // else + // { + // newInst = Builder.CreateFPCast( + // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // } + // llvmIrInstruction->replaceAllUsesWith(newInst); + // llvmIrInstruction->removeFromParent(); + // break; + // } + + case Instruction::BitCast: + case Instruction::Ret: + case Instruction::Switch: + case Instruction::Br: + case Instruction::Select: + case Instruction::IndirectBr: + case Instruction::Invoke: + case Instruction::Resume: + case Instruction::Unreachable: + case Instruction::CleanupRet: + case Instruction::CatchRet: + case Instruction::CatchSwitch: + case Instruction::CallBr: + case Instruction::Fence: + case Instruction::AtomicCmpXchg: + case Instruction::AtomicRMW: + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::AddrSpaceCast: + case Instruction::CleanupPad: + case Instruction::CatchPad: + case Instruction::UserOp1: + case Instruction::UserOp2: + case Instruction::VAArg: + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + case Instruction::ExtractValue: + case Instruction::InsertValue: + case Instruction::LandingPad: + case Instruction::Freeze: + break; + default: + break; + } } } - } - // adaptTypeCast(llvmIrFunction, quantizedType); + // adaptTypeCast(llvmIrFunction, quantizedType); - // Process functions that are whitelisted for dequantization - // processWhitelistedFunctions(*module, whitelist); - return; + // Process functions that are whitelisted for dequantization + // processWhitelistedFunctions(*module, whitelist); + return; + } } \ No newline at end of file From acf066c5238870fe52ca924795148f82bcf76a81 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 22 Nov 2024 21:14:41 +0000 Subject: [PATCH 125/213] update rounding for gloabl variable to improve precision Addresses #2. --- ...7273f277374e0717f7025a54ac65f8874e5123.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt diff --git a/analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt b/analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt new file mode 100644 index 000000000..56e57764b --- /dev/null +++ b/analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt @@ -0,0 +1,48 @@ + +changeset: 1718:037273f277374e0717f7025a54ac65f8874e5123 +char kNewtonVersion[] = "0.3-alpha-1718 (037273f277374e0717f7025a54ac65f8874e5123) (build 11-22-2024-10:39-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index d7a0be0c8..a0c250d44 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -347,7 +347,7 @@ updateGlobalVariables(Module * module, Type * quantizedType) if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) { double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round(value * FRAC_BASE)); + int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); } } From 9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 22 Nov 2024 23:08:21 +0000 Subject: [PATCH 126/213] try to add support for rangeAnalysis Addresses #2. --- ...c03fce474fe62166871bd538e0a8e900045acf.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 7 ++- .../newton-irPass-LLVMIR-quantization.cpp | 14 +++++- .../newton-irPass-LLVMIR-quantization.h | 15 +++++- 4 files changed, 80 insertions(+), 4 deletions(-) create mode 100644 analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt diff --git a/analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt b/analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt new file mode 100644 index 000000000..94c487a51 --- /dev/null +++ b/analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt @@ -0,0 +1,48 @@ + +changeset: 1719:c2c03fce474fe62166871bd538e0a8e900045acf +char kNewtonVersion[] = "0.3-alpha-1719 (c2c03fce474fe62166871bd538e0a8e900045acf) (build 11-22-2024-21:14-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index fe7428977..2dca967c4 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -458,6 +458,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl int bitWidth = 16; maxPrecisionBits = 11; bool enableVectorization = true; + bool enableRangeAnalysis = true; /* * get const global variables @@ -542,7 +543,11 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth); - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth, enableVectorization); + //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth, enableVectorization); + //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, virtualRegisterVectorRange, bitWidth, enableVectorization, enableRangeAnalysis); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis); + + } for (auto mi : functionsToInsert) { diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a0c250d44..e8a8baf45 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1556,8 +1556,15 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } // Main function to perform LLVM IR auto quantization +//void +//irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth, std::vector>> &virtualRegisterVectorRange,bool enableVectorization,bool enableRangeAnalysis) +//void irPassLLVMIRAutoQuantization(State *N, Function &F, std::vector &functionsToInsert, +// int maxPrecisionBits, std::map>> &virtualRegisterVectorRange, +// int bitWidth, bool enableVectorization, bool enableRangeAnalysis) void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth, bool enableVectorization) +irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, + std::map>> &virtualRegisterVectorRange, + int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis) { { FRAC_Q = maxPrecisionBits; @@ -1571,6 +1578,11 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; } + if (enableRangeAnalysis) + { + llvm::errs() << "Range analysis enabled. Applying range analysis.\n"; + } + // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); if (shouldSkipFunction(functionName)) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 986cfa540..0d46ba154 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -37,9 +37,20 @@ extern "C" #endif /* __cplusplus */ extern std::vector functionsToErase; extern std::vector globalsToErase; -extern std::vector instructionsToErase; +//extern std::vector instructionsToErase; void -irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, int maxPrecisionBits, int bitWidth, bool enableVectorization); +irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, + std::map>> &virtualRegisterVectorRange, + int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis); + +//void irPassLLVMIRAutoQuantization(State *N, Function &F, std::vector &functionsToInsert, +// int maxPrecisionBits, std::map>> &virtualRegisterVectorRange, +// int bitWidth, bool enableVectorization, bool enableRangeAnalysis); + + + + + From 177de14acdebe0b852e417df322fa55174bec455 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sat, 23 Nov 2024 11:53:53 +0000 Subject: [PATCH 127/213] save env for 11/23 perf test Addresses #2. --- ...f066c5238870fe52ca924795148f82bcf76a81.txt | 48 +++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 102 +++++++++--------- 2 files changed, 99 insertions(+), 51 deletions(-) create mode 100644 analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt diff --git a/analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt b/analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt new file mode 100644 index 000000000..6d749d501 --- /dev/null +++ b/analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt @@ -0,0 +1,48 @@ + +changeset: 1720:acf066c5238870fe52ca924795148f82bcf76a81 +char kNewtonVersion[] = "0.3-alpha-1720 (acf066c5238870fe52ca924795148f82bcf76a81) (build 11-22-2024-23:08-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e8a8baf45..d5e09ab9a 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -347,7 +347,7 @@ updateGlobalVariables(Module * module, Type * quantizedType) if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) { double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); + int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); } } @@ -1218,26 +1218,26 @@ performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPoi return finalRes; } -// void -// handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) -//{ -// IRBuilder<> Builder(llvmIrCallInstruction); -// auto operand = llvmIrCallInstruction->getOperand(0); -// -// // Convert the operand to fixed-point format if necessary -// if (operand->getType()->isFloatingPointTy()) -// { -// operand = Builder.CreateFPToSI(operand, quantizedType); -// } -// -// // Create call to the fixed-point sqrt function -// //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); -// //手动写减少call overhead -// llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); -// // No need to apply shl and compensation if it's already done in createFixSqrt -// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); -// llvmIrCallInstruction->eraseFromParent(); -// } + void + handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +{ + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + // Convert the operand to fixed-point format if necessary + if (operand->getType()->isFloatingPointTy()) + { + operand = Builder.CreateFPToSI(operand, quantizedType); + } + + // Create call to the fixed-point sqrt function + //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + //手动写减少call overhead + llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); + // No need to apply shl and compensation if it's already done in createFixSqrt + llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); + llvmIrCallInstruction->eraseFromParent(); + } void handleSinCosCall(CallInst * llvmIrCallInstruction, Type * quantizedType, const std::string & mathFunc) @@ -1266,30 +1266,30 @@ handleSinCosCall(CallInst * llvmIrCallInstruction, Type * quantizedType, const s llvmIrCallInstruction->eraseFromParent(); } -void -handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) -{ - IRBuilder<> Builder(llvmIrCallInstruction); - auto operand = llvmIrCallInstruction->getOperand(0); - - // Ensure operand is in the correct type - if (operand->getType()->isFloatingPointTy()) - { - operand = Builder.CreateFPToSI(operand, quantizedType); - } - - // Create call to the fixed-point sqrt function - llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - - // Replace the original instruction with the new fixed-point sqrt result - llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); - llvmIrCallInstruction->eraseFromParent(); -} +//void +//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto operand = llvmIrCallInstruction->getOperand(0); +// +// // Ensure operand is in the correct type +// if (operand->getType()->isFloatingPointTy()) +// { +// operand = Builder.CreateFPToSI(operand, quantizedType); +// } +// +// // Create call to the fixed-point sqrt function +// llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); +// +// // Replace the original instruction with the new fixed-point sqrt result +// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); +// llvmIrCallInstruction->eraseFromParent(); +//} void // handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) -handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixsqrt, llvm::Function * fixrsqrt) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) { llvm::errs() << "Handling Call\n"; Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); @@ -1330,8 +1330,8 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) { functionsToErase.push_back(calledFunction); @@ -1563,8 +1563,8 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) // int bitWidth, bool enableVectorization, bool enableRangeAnalysis) void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, - std::map>> &virtualRegisterVectorRange, - int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis) + std::map>> &virtualRegisterVectorRange, + int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis) { { FRAC_Q = maxPrecisionBits; @@ -1578,10 +1578,10 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; } - if (enableRangeAnalysis) - { - llvm::errs() << "Range analysis enabled. Applying range analysis.\n"; - } + if (enableRangeAnalysis) + { + llvm::errs() << "Range analysis enabled. Applying range analysis.\n"; + } // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); @@ -1634,7 +1634,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect * generate hardcode function * */ // llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); - llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) @@ -1657,7 +1657,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect break; case Instruction::Call: // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); - handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixsqrt, fixrsqrt); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); break; case Instruction::GetElementPtr: case Instruction::Load: From fe1fbfff733526eaef18b487fbc61675aed1ce90 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 16 Jan 2025 19:18:45 +0000 Subject: [PATCH 128/213] Add support for global variables Addresses #2. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 220 ++++++++----- .../newton-irPass-LLVMIR-quantization.cpp | 301 +++++++++++------- .../newton-irPass-LLVMIR-quantization.h | 2 +- 3 files changed, 327 insertions(+), 196 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 2dca967c4..59dc16791 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -75,113 +75,172 @@ POSSIBILITY OF SUCH DAMAGE. #include "llvm/Support/FileSystem.h" #include "llvm/IR/Function.h" +#include +#include + + using namespace llvm; std::set whitelist = { "MadgwickAHRSupdate", - "MahonyAHRSupdate"}; + "MahonyAHRSupdate", + "sensfusion6UpdateQImpl" +}; + + +void handleGlobalStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits) { + auto *pointerOperand = storeInst->getPointerOperand(); + + // Ensure the operation is on a global variable + if (auto *quantizedGlobalVar = dyn_cast(pointerOperand)) { + llvm::errs() << "Processing quantized global variable: " << quantizedGlobalVar->getName() << "\n"; + + // Identify the corresponding original global variable (e.g., remove "_quantized" suffix) + std::string originalName = quantizedGlobalVar->getName().str(); + if (originalName.size() > 10 && originalName.compare(originalName.size() - 10, 10, "_quantized") == 0) { + originalName = originalName.substr(0, originalName.size() - 10); + } else { + llvm::errs() << "Skipping: No matching original global for " << quantizedGlobalVar->getName() << "\n"; + return; + } + + // Find the original global variable + GlobalVariable *originalGlobalVar = quantizedGlobalVar->getParent()->getNamedGlobal(originalName); + if (!originalGlobalVar || !originalGlobalVar->getType()->getElementType()->isFloatingPointTy()) { + llvm::errs() << "Skipping: Original global variable not found or not floating-point: " << originalName << "\n"; + return; + } + + llvm::errs() << "Found corresponding original global variable: " << originalGlobalVar->getName() << "\n"; + + // Check if the previous instruction is `trunc` + Instruction *prevInst = storeInst->getPrevNode(); + if (!prevInst || !isa(prevInst)) { + llvm::errs() << "Skipping: Previous instruction is not trunc.\n"; + return; + } + + // Load the integer value from the quantized global variable + auto *loadInst = Builder.CreateLoad(quantizedGlobalVar->getType()->getPointerElementType(), quantizedGlobalVar); + + // Convert the integer value to a floating-point value + Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); + + // Compute the fractional base + double fracBase = pow(2.0, maxPrecisionBits); + + // Perform dequantization + Value *dequantizedValue = Builder.CreateFMul( + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / fracBase)); + + // Store the dequantized floating-point value back into the original global variable + Builder.CreateStore(dequantizedValue, originalGlobalVar); + + llvm::errs() << "Dequantized and stored value for original global variable: " << originalGlobalVar->getName() << "\n"; + } else { + llvm::errs() << "Pointer operand is not a global variable. Skipping.\n"; + } +} -// Define the dequantizeResult method -std::vector toRemove; void -dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth) +handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits, int bitWidth) { - // IRBuilder<> Builder(storeInst); - IRBuilder<> Builder(storeInst->getNextNode()); auto *pointerOperand = storeInst->getPointerOperand(); - llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; - if (isa(pointerOperand)) + if (!pointerOperand->getType()->getPointerElementType()->isIntegerTy(bitWidth)) { - llvm::errs() << "Skipping StoreInst due to global variable in pointer operand.\n"; + llvm::errs() << "Pointer operand type is not an integer of expected bit width.\n"; return; } - Type *targetType = nullptr; - //BIT_WIDTH = bitWidth; - - switch (bitWidth) + auto *loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + if (isa(loadInst->getPointerOperand())) { - case 16: - targetType = Type::getInt16Ty(F.getContext()); - break; - case 32: - default: - targetType = Type::getInt32Ty(F.getContext()); - break; + llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; + return; } - if (pointerOperand->getType()->getPointerElementType()->isIntegerTy(bitWidth)) - { - auto *loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); + double fracBase = pow(2.0, maxPrecisionBits); + Value *dividedValue = Builder.CreateFMul( + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / fracBase)); - if (isa(loadInst->getPointerOperand())) - { - llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; - return; - } - - Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(F.getContext())); - double fracBase = pow(2.0, maxPrecisionBits); - Value *dividedValue = Builder.CreateFMul(convertedFloat, ConstantFP::get(Type::getFloatTy(F.getContext()), 1.0 / fracBase)); + if (auto *bitcastInst = dyn_cast(pointerOperand)) + { + Value *finalStorePtr = nullptr; + bool isValidSource = false; - if (auto *bitcastInst = dyn_cast(pointerOperand)) + // Determine the final store pointer based on bit width + switch (bitWidth) { - Value *finalStorePtr = nullptr; - bool isValidSource = false; - - // Determine the final store pointer based on BIT_WIDTH - switch (bitWidth) - { - case 16: - // Check if the source type is i32 and find the original float* - if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) + case 16: + if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) + { + auto *i32Ptr = bitcastInst->getOperand(0); + if (auto *floatBitcast = dyn_cast(i32Ptr)) { - auto *i32Ptr = bitcastInst->getOperand(0); - if (auto *floatBitcast = dyn_cast(i32Ptr)) + if (floatBitcast->getSrcTy()->getPointerElementType()->isFloatTy()) { - if (floatBitcast->getSrcTy()->getPointerElementType()->isFloatTy()) - { - finalStorePtr = floatBitcast->getOperand(0); // Original float* - isValidSource = true; - } + finalStorePtr = floatBitcast->getOperand(0); // Original float* + isValidSource = true; } } - break; + } + break; - case 32: - // Check if the source type is float* - if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) - { - finalStorePtr = bitcastInst->getOperand(0); // Original float* - isValidSource = true; - } - break; + case 32: + if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) + { + finalStorePtr = bitcastInst->getOperand(0); // Original float* + isValidSource = true; + } + break; - default: - llvm::errs() << "Unsupported BIT_WIDTH: " << bitWidth << "\n"; - return; - } + default: + llvm::errs() << "Unsupported BIT_WIDTH: " << bitWidth << "\n"; + return; + } - if (isValidSource && finalStorePtr) - { - Builder.CreateStore(dividedValue, finalStorePtr); - } - else - { - llvm::errs() << "Invalid source for StoreInst: " << *storeInst << "\n"; - } + if (isValidSource && finalStorePtr) + { + Builder.CreateStore(dividedValue, finalStorePtr); + llvm::errs() << "Dequantized and stored value for pointer.\n"; } + else + { + llvm::errs() << "Invalid source for StoreInst: " << *storeInst << "\n"; + } + } +} + + + + +void +dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth, bool isPointer) +{ + IRBuilder<> Builder(storeInst->getNextNode()); + llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; + + if (isPointer) + { + handlePointerStore(storeInst, Builder, maxPrecisionBits, bitWidth); + } + else + { + handleGlobalStore(storeInst, Builder, maxPrecisionBits); + } } + // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth) +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth, bool isPointer) { for (auto & F : module) { @@ -197,12 +256,13 @@ processWhitelistedFunctions(Module & module, const std::set & white if (auto * storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F, maxPrecisionBits,bitWidth); + dequantizeResults(storeInst, F, maxPrecisionBits,bitWidth,isPointer); } } } } } + } // Function to save the IR of a module to a file @@ -455,10 +515,13 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl /** * Config */ +// int bitWidth = 32; int bitWidth = 16; - maxPrecisionBits = 11; +// maxPrecisionBits = 16; + maxPrecisionBits = 10; bool enableVectorization = true; - bool enableRangeAnalysis = true; + bool enableRangeAnalysis = true; + bool isPointer = false; /* * get const global variables @@ -545,7 +608,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth); //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth, enableVectorization); //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, virtualRegisterVectorRange, bitWidth, enableVectorization, enableRangeAnalysis); - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis, isPointer); } @@ -703,14 +766,14 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // Finally, erase old functions eraseOldFunctions(); - eraseOldGlobals(); + //eraseOldGlobals(); // Perform text replacement to remove "_quantized" suffixes - removeQuantizedSuffixInModule(*Mod); + //removeQuantizedSuffixInModule(*Mod); // eraseOldInstructions(); - processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits, bitWidth); + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits, bitWidth, isPointer); const char * homeDir = getenv("HOME"); if (!homeDir) @@ -724,6 +787,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // Save the optimized IR to a file saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MahonyAHRS_output.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/sensfusion6_output.ll"); // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index d5e09ab9a..d0d395623 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -119,64 +119,68 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -{ - llvm::errs() << "Entering createFixMul\n"; - - // Check if irModule is valid - if (!irModule) - { - llvm::errs() << "Error: irModule is nullptr\n"; - return nullptr; - } - - std::string fixmulFuncName = "fixmul"; - for (auto & function : *irModule) - { - if (function.getName() == fixmulFuncName) - { - llvm::errs() << "fixmul already exists\n"; - return &function; - } - } - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - - llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); - - // Create fixed-point multiplication instruction - Type * higherQuantizedType; - switch (BIT_WIDTH) - { - case 8: - higherQuantizedType = Type::getInt16Ty(irModule->getContext()); - break; - case 16: - higherQuantizedType = Type::getInt32Ty(irModule->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(irModule->getContext()); - break; - } - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); - // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); - llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); - builder.CreateRet(truncInst); - functionsToInsert.emplace_back(func); - llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; - return func; -} +//llvm::Function * +//createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +//{ +// llvm::errs() << "Entering createFixMul\n"; +// +// // Check if irModule is valid +// if (!irModule) +// { +// llvm::errs() << "Error: irModule is nullptr\n"; +// return nullptr; +// } +// +// std::string fixmulFuncName = "fixmul"; +// for (auto & function : *irModule) +// { +// if (function.getName() == fixmulFuncName) +// { +// llvm::errs() << "fixmul already exists\n"; +// return &function; +// } +// } +// +// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); +// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); +// +// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); +// llvm::IRBuilder<> builder(entryBB); +// builder.SetInsertPoint(entryBB); +// +// // Create fixed-point multiplication instruction +// Type * higherQuantizedType; +// switch (BIT_WIDTH) +// { +// case 8: +// higherQuantizedType = Type::getInt16Ty(irModule->getContext()); +// break; +// case 16: +// higherQuantizedType = Type::getInt32Ty(irModule->getContext()); +// break; +// default: +// higherQuantizedType = Type::getInt64Ty(irModule->getContext()); +// break; +// } +// +// llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); +// llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); +// llvm::Function::arg_iterator arg2 = &*(++arg1); +// llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); +// llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); +// // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); +// llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); +// llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); +// builder.CreateRet(truncInst); +// +// functionsToInsert.emplace_back(func); +// llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; +// return func; +//} +// llvm::Function * createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector & functionsToInsert) @@ -219,7 +223,7 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< case 16: { // Step 1: Shift x to compute %1 (x >> 1) - shiftedX = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + shiftedX = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); // Step 2: Convert x to float and scale llvm::Value * sextX = builder.CreateSExt(x, llvm::Type::getInt32Ty(irModule->getContext())); @@ -324,76 +328,122 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } -void -updateGlobalVariables(Module * module, Type * quantizedType) -{ - llvm::errs() << "Updating global variables\n"; +bool isWhitelistedGlobal(const std::string &globalName) { + // Define the whitelist of global variables + static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz"}; + return whitelist.find(globalName) != whitelist.end(); +} - for (GlobalVariable & globalVar : module->globals()) - { - if (globalVar.getType()->getElementType()->isFloatTy() || globalVar.getType()->getElementType()->isDoubleTy()) - { - llvm::errs() << "Quantizing global variable: " << globalVar.getName() << "\n"; +bool shouldProcessFunction(Function &F) { + // List of function names to process + static const std::set targetFunctions = { + "sensfusion6UpdateQImpl", + "MadgwickAHRSupdate", + "MadgwickAHRSupdateIMU" + }; - // Ensure the new global is dso_local by specifying correct linkage - GlobalValue::LinkageTypes linkage = GlobalValue::InternalLinkage; + // Check if the function name is in the set + return targetFunctions.find(F.getName().str()) != targetFunctions.end(); +} - // Create the new integer type pointer - Type * newType = quantizedType->getPointerTo(); +// Helper to create or retrieve the quantized global variable +GlobalVariable *getOrCreateQuantizedGlobal(Module *module, GlobalVariable &globalVar, Type *quantizedType) { + std::string quantizedName = globalVar.getName().str() + "_quantized"; - // Update the initializer of the global variable - if (llvm::Constant * init = globalVar.getInitializer()) - { - if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) - { - double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); - globalVar.setInitializer(llvm::ConstantInt::get(quantizedType, quantizedValue)); - } - } + // Check if the quantized version already exists + if (GlobalVariable *existingGlobal = module->getNamedGlobal(quantizedName)) { + llvm::errs() << "Quantized global already exists: " << quantizedName << "\n"; + return existingGlobal; + } - // Check if a quantized version of the global variable already exists - std::string quantizedName = globalVar.getName().str() + "_quantized"; - if (GlobalVariable * existingGlobalVar = module->getNamedGlobal(quantizedName)) - { - // Replace all uses of the old global variable with the existing quantized one - globalVar.replaceAllUsesWith(existingGlobalVar); - } - else - { - // Create a new global variable with the updated type and initializer - GlobalVariable * newGlobalVar = new GlobalVariable( - *module, - quantizedType, - globalVar.isConstant(), - globalVar.getLinkage(), - globalVar.getInitializer(), - quantizedName); - // newGlobalVar->setAlignment(llvm::MaybeAlign(4)); - switch (BIT_WIDTH) - { - case 16: - newGlobalVar->setAlignment(llvm::MaybeAlign(2)); - break; - case 32: - newGlobalVar->setAlignment(llvm::MaybeAlign(4)); - break; - default: - llvm::errs() << "Unsupported bit width: " << BIT_WIDTH << "\n"; - break; - } - newGlobalVar->setDSOLocal(true); + // Calculate initializer for the quantized global + llvm::Constant *initializer = nullptr; + if (llvm::Constant *init = globalVar.getInitializer()) { + if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { + double value = constFp->getValueAPF().convertToDouble(); + int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); + initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); + } + } + + // Create the quantized global variable + GlobalVariable *newGlobalVar = new GlobalVariable( + *module, + quantizedType, + globalVar.isConstant(), + globalVar.getLinkage(), + initializer, + quantizedName); + + // Set alignment based on bit width + switch (BIT_WIDTH) { + case 16: + newGlobalVar->setAlignment(llvm::MaybeAlign(2)); + break; + case 32: + newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + break; + default: + llvm::errs() << "Unsupported bit width: " << BIT_WIDTH << "\n"; + break; + } + + newGlobalVar->setDSOLocal(true); + llvm::errs() << "Created quantized global: " << quantizedName << "\n"; + return newGlobalVar; +} - // Replace all uses of the old global variable with the new one - globalVar.replaceAllUsesWith(newGlobalVar); +// Helper to replace uses of the original global in whitelisted functions +void replaceUsesInWhitelistedFunctions(GlobalVariable &originalGlobal, GlobalVariable &quantizedGlobal) { + for (auto it = originalGlobal.use_begin(), end = originalGlobal.use_end(); it != end;) { + Use &use = *it++; + if (Instruction *inst = dyn_cast(use.getUser())) { + Function *parentFunc = inst->getFunction(); + if (parentFunc && shouldProcessFunction(*parentFunc)) { + llvm::errs() << "Replacing use of " << originalGlobal.getName() + << " in function: " << parentFunc->getName() << "\n"; + use.set(&quantizedGlobal); } + } + } +} +void updateGlobalVariables(Module *module, Type *quantizedType) { + llvm::errs() << "Updating global variables\n"; + + for (GlobalVariable &globalVar : module->globals()) { + std::string globalName = globalVar.getName().str(); + + // Skip non-whitelisted globals + if (!isWhitelistedGlobal(globalName)) { + llvm::errs() << "Skipping global variable not in whitelist: " << globalName << "\n"; + continue; + } - // Add the old global variable to the list of globals to erase - globalsToErase.push_back(&globalVar); + // Process only float or double global variables + if (!globalVar.getType()->getElementType()->isFloatTy() && + !globalVar.getType()->getElementType()->isDoubleTy()) { + llvm::errs() << "Skipping non-float global variable: " << globalName << "\n"; + continue; } + + llvm::errs() << "Quantizing global variable: " << globalName << "\n"; + + // Create or retrieve the quantized version of the global variable + GlobalVariable *quantizedGlobal = getOrCreateQuantizedGlobal(module, globalVar, quantizedType); + + // Replace uses of the original global in whitelisted functions + replaceUsesInWhitelistedFunctions(globalVar, *quantizedGlobal); + + // The original global variable remains untouched + llvm::errs() << "Original global variable preserved: " << globalName << "\n"; } } + + + + + // void // handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) //{ @@ -1555,6 +1605,10 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } } + + + + // Main function to perform LLVM IR auto quantization //void //irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth, std::vector>> &virtualRegisterVectorRange,bool enableVectorization,bool enableRangeAnalysis) @@ -1564,7 +1618,7 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, std::map>> &virtualRegisterVectorRange, - int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis) + int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis,bool isPointer) { { FRAC_Q = maxPrecisionBits; @@ -1590,6 +1644,11 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect return; } + if (!shouldProcessFunction(llvmIrFunction)) { + llvm::errs() << "Skipping function: " << llvmIrFunction.getName() << "\n"; + return; + } + Type * quantizedType; switch (BIT_WIDTH) { @@ -1624,8 +1683,16 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect quantizeFunctionArguments(llvmIrFunction, builder); quantizeArguments(llvmIrFunction, quantizedType); - // Perform bitcasting of float poiter arguments to i32* - bitcastFloatPtrArgs(llvmIrFunction, builder); +// // Perform bitcasting of float poiter arguments to i32* +// bitcastFloatPtrArgs(llvmIrFunction, builder); + + // 根据 isPointer 参数决定是否执行 bitcastFloatPtrArgs + if (isPointer) { + llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; + bitcastFloatPtrArgs(llvmIrFunction, builder); + } else { + llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; + } // Update global variables to integer type updateGlobalVariables(module, quantizedType); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 0d46ba154..5ec787f8e 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -41,7 +41,7 @@ extern std::vector globalsToErase; void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, std::map>> &virtualRegisterVectorRange, - int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis); + int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis,bool isPointer); //void irPassLLVMIRAutoQuantization(State *N, Function &F, std::vector &functionsToInsert, // int maxPrecisionBits, std::map>> &virtualRegisterVectorRange, From 498a1e350786d8527b315bc202ea91f5b6df797c Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 26 Jan 2025 11:33:04 +0000 Subject: [PATCH 129/213] Add support for LTO across multiple file Addresses #2. --- applications/newton/llvm-ir/LTO/LTO.sh | 92 +++++++++++++ .../newton/llvm-ir/LTO/MadgwickAHRS.h | 29 +++++ .../newton/llvm-ir/LTO/MadgwickAHRSupdate.c | 122 ++++++++++++++++++ .../newton/llvm-ir/LTO/MadgwickAHRSupdate.h | 29 +++++ applications/newton/llvm-ir/LTO/MadgwickIMU.c | 84 ++++++++++++ applications/newton/llvm-ir/LTO/MadgwickIMU.h | 20 +++ .../newton/llvm-ir/LTO/MadgwickMath.c | 12 ++ .../newton/llvm-ir/LTO/MadgwickMath.h | 7 + 8 files changed, 395 insertions(+) create mode 100755 applications/newton/llvm-ir/LTO/LTO.sh create mode 100644 applications/newton/llvm-ir/LTO/MadgwickAHRS.h create mode 100644 applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c create mode 100644 applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h create mode 100644 applications/newton/llvm-ir/LTO/MadgwickIMU.c create mode 100644 applications/newton/llvm-ir/LTO/MadgwickIMU.h create mode 100644 applications/newton/llvm-ir/LTO/MadgwickMath.c create mode 100644 applications/newton/llvm-ir/LTO/MadgwickMath.h diff --git a/applications/newton/llvm-ir/LTO/LTO.sh b/applications/newton/llvm-ir/LTO/LTO.sh new file mode 100755 index 000000000..fc31322b6 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/LTO.sh @@ -0,0 +1,92 @@ +#!/bin/bash + +# 定义路径 +BASE_PATH="$HOME/CoSense/applications/newton/llvm-ir" +SRC_PATH="$BASE_PATH/LTO" +OUTPUT_PATH="$BASE_PATH/LTO/ir" + + +echo "Step 1: Generate LLVM IR files and bitcode files for all source files" +for file in "$SRC_PATH"/*.c; do + filename=$(basename -- "$file" .c) + ir_file="$OUTPUT_PATH/${filename}.ll" + bc_file="$OUTPUT_PATH/${filename}.bc" + + echo "Generating LLVM IR for $file -> $ir_file" + clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o "$ir_file" "$file" + if [ $? -ne 0 ]; then + echo "Error generating LLVM IR for $file" + exit 1 + fi + + echo "Optimizing $ir_file with mem2reg" + opt --mem2reg -S -o "$ir_file" "$ir_file" + if [ $? -ne 0 ]; then + echo "Error optimizing $ir_file with mem2reg" + exit 1 + fi + + echo "Converting $ir_file to $bc_file" + llvm-as "$ir_file" -o "$bc_file" + + + + if [ $? -ne 0 ]; then + echo "Error converting $ir_file to bitcode" + exit 1 + fi +done + + llvm-nm $OUTPUT_PATH/*.bc + + + +echo "Step 2: Merge LLVM bitcode files into merged.bc" +llvm-link --only-needed "$OUTPUT_PATH"/*.bc -o "$OUTPUT_PATH/merged.bc" +if [ $? -ne 0 ]; then + echo "Error merging bitcode files" + exit 1 +fi +echo "Merged bitcode files into $OUTPUT_PATH/merged.bc" + +echo "Step 3: Convert merged.bc back to LLVM IR (merged.ll)" +llvm-dis "$OUTPUT_PATH/merged.bc" -o "$OUTPUT_PATH/merged.ll" +if [ $? -ne 0 ]; then + echo "Error converting merged.bc to LLVM IR" + exit 1 +fi +echo "Converted $OUTPUT_PATH/merged.bc to $OUTPUT_PATH/merged.ll" + + +echo "Step 3: Use newton for optimization and quantization" +cd $HOME/CoSense/src/newton && ./newton-linux-EN \ + --llvm-ir=$OUTPUT_PATH/merged.ll \ + --llvm-ir-liveness-check \ + --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/BMX055.nt + +#echo "Step 4: Optimize the quantized LLVM IR file" +#opt -inline -S -o $OUTPUT_PATH/out.ll $OUTPUT_PATH/merged_output.ll + + +#echo "Step 5: Compile the optimized LLVM IR file to bitcode" +#llvm-as $OUTPUT_PATH/out.ll -o $OUTPUT_PATH/out.bc +# +# +#echo "Step 6: Compile the bitcode file to assembly" +#llc $OUTPUT_PATH/out.bc -o $OUTPUT_PATH/out.s +# +# +#echo "Step 7: Compile the assembly file to object file" +#clang -c $OUTPUT_PATH/out.s -o $OUTPUT_PATH/out.o +# +# +#echo "Step 8: Package the object file into a static library" +#ar -rc $OUTPUT_PATH/libout.a $OUTPUT_PATH/out.o +# +# +#echo "Step 9: Compile the test file and link with the static library" +#clang $OUTPUT_PATH/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$OUTPUT_PATH -lout -O3 -Os -g -o $OUTPUT_PATH/main_out -lm +# +# +#echo "Step 10: Run the test executable" +#$OUTPUT_PATH/main_out diff --git a/applications/newton/llvm-ir/LTO/MadgwickAHRS.h b/applications/newton/llvm-ir/LTO/MadgwickAHRS.h new file mode 100644 index 000000000..85d794523 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickAHRS.h @@ -0,0 +1,29 @@ +#ifndef MADGWICK_AHRS_H +#define MADGWICK_AHRS_H + +#include + + + +// Type Definitions +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + +// Function Declarations +static void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif // MADGWICK_AHRS_H diff --git a/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c new file mode 100644 index 000000000..b2316f2b8 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c @@ -0,0 +1,122 @@ +#include "MadgwickAHRSupdate.h" +#include "MadgwickMath.h" +#include "MadgwickIMU.h" +#include "SharedVariables.h" +//--------------------------------------------------------------------------------------------------- +// Variable definitions + + + +void +MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float hx, hy; + float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + + + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2.0f * q0 * mx; + _2q0my = 2.0f * q0 * my; + _2q0mz = 2.0f * q0 * mz; + _2q1mx = 2.0f * q1 * mx; + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _2q0q2 = 2.0f * q0 * q2; + _2q2q3 = 2.0f * q2 * q3; + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; + hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; + _2bx = sqrtf(hx * hx + hy * hy); + _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h new file mode 100644 index 000000000..f7d06af61 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h @@ -0,0 +1,29 @@ +#ifndef MADGWICK_AHRS_H +#define MADGWICK_AHRS_H + +#include + + + +// Type Definitions +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + +// Function Declarations +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif // MADGWICK_AHRS_H diff --git a/applications/newton/llvm-ir/LTO/MadgwickIMU.c b/applications/newton/llvm-ir/LTO/MadgwickIMU.c new file mode 100644 index 000000000..24742ff24 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickIMU.c @@ -0,0 +1,84 @@ +#include "MadgwickIMU.h" +#include "MadgwickMath.h" +#include "SharedVariables.h" + + + +void +MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _4q0 = 4.0f * q0; + _4q1 = 4.0f * q1; + _4q2 = 4.0f * q2; + _8q1 = 8.0f * q1; + _8q2 = 8.0f * q2; + q0q0 = q0 * q0; + q1q1 = q1 * q1; + q2q2 = q2 * q2; + q3q3 = q3 * q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; + s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; + s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; + s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} diff --git a/applications/newton/llvm-ir/LTO/MadgwickIMU.h b/applications/newton/llvm-ir/LTO/MadgwickIMU.h new file mode 100644 index 000000000..251d4022d --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickIMU.h @@ -0,0 +1,20 @@ +#ifndef MADGWICK_IMU_H +#define MADGWICK_IMU_H + +// Type Definitions +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + +// Function Declarations +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif // MADGWICK_IMU_H diff --git a/applications/newton/llvm-ir/LTO/MadgwickMath.c b/applications/newton/llvm-ir/LTO/MadgwickMath.c new file mode 100644 index 000000000..7f63732d2 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickMath.c @@ -0,0 +1,12 @@ +#include "MadgwickMath.h" + +// Fast inverse square root +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i >> 1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} diff --git a/applications/newton/llvm-ir/LTO/MadgwickMath.h b/applications/newton/llvm-ir/LTO/MadgwickMath.h new file mode 100644 index 000000000..a5899673d --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickMath.h @@ -0,0 +1,7 @@ +#ifndef MADGWICK_MATH_H +#define MADGWICK_MATH_H + +// Function Declarations +float invSqrt(float x); + +#endif // MADGWICK_MATH_H From 9b02f825b35cc1885256ad00679db4fbf23a3c9d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 3 Feb 2025 15:00:51 +0000 Subject: [PATCH 130/213] change to use config file to organize quantization Addresses #2. --- ...7de14acdebe0b852e417df322fa55174bec455.txt | 48 +++ ...09b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt | 48 +++ ...1fbfff733526eaef18b487fbc61675aed1ce90.txt | 7 + applications/newton/llvm-ir/c-files/test_m4.c | 340 ------------------ src/newton/config.h | 9 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 39 +- .../newton-irPass-LLVMIR-quantization.cpp | 87 +++-- .../newton-irPass-LLVMIR-quantization.h | 10 +- 8 files changed, 192 insertions(+), 396 deletions(-) create mode 100644 analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt create mode 100644 analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt create mode 100644 analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt delete mode 100644 applications/newton/llvm-ir/c-files/test_m4.c create mode 100644 src/newton/config.h diff --git a/analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt b/analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt new file mode 100644 index 000000000..d833b1d7c --- /dev/null +++ b/analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt @@ -0,0 +1,48 @@ + +changeset: 1722:177de14acdebe0b852e417df322fa55174bec455 +char kNewtonVersion[] = "0.3-alpha-1722 (177de14acdebe0b852e417df322fa55174bec455) (build 01-16-2025-19:18-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt b/analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt new file mode 100644 index 000000000..33381e2c0 --- /dev/null +++ b/analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt @@ -0,0 +1,48 @@ + +changeset: 1721:9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0 +char kNewtonVersion[] = "0.3-alpha-1721 (9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0) (build 11-23-2024-11:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt b/analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt new file mode 100644 index 000000000..a93ff5292 --- /dev/null +++ b/analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt @@ -0,0 +1,7 @@ + +changeset: 1723:fe1fbfff733526eaef18b487fbc61675aed1ce90 +char kNewtonVersion[] = "0.3-alpha-1723 (fe1fbfff733526eaef18b487fbc61675aed1ce90) (build 01-26-2025-11:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/applications/newton/llvm-ir/c-files/test_m4.c b/applications/newton/llvm-ir/c-files/test_m4.c deleted file mode 100644 index 3521c8945..000000000 --- a/applications/newton/llvm-ir/c-files/test_m4.c +++ /dev/null @@ -1,340 +0,0 @@ -/* - * Madgwick test case (run locally) - * Compilation command in applications/newton/llvm-ir/performance_test/Makefile - * - * How to compile and run? - * 1. `make perf_madgwick` FP hardware (by default) - * 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) - * 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) - * 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format - * */ -#include "../Include/stm32f3xx.h" -#include -#include -#include -#include -#include -#include - -#if defined(INT_DATA_TYPE) -extern volatile int32_t q0, q1, q2, q3; -#elif defined(FP_DATA_TYPE) -extern volatile float q0, q1, q2, q3; -#if defined(SOFT_FLOAT_LIB) -#include "MadgwickAHRS_softfloat.h" -#else -#include "MadgwickAHRS.h" -#endif - -#else -#error "Must set data type: FP or INT" -#endif - -#define DATA_SIZE 10 -#define FRAC_Q 10 -#define FRAC_BASE (1 << FRAC_Q) -#define ITERATION 10 - -//#define SYSTICK_FREQUENCY_HZ 1000 // 假设 SysTick 配置为 1ms (1000Hz) - -static volatile uint32_t system_ticks = 0; // 用于记录系统启动后的总滴答数 -// 声明并定义全局变量 elapsed_time_tbl,用于存储计时信息 - -// 定义 ARM_CM_DWT_CYCCNT 宏 -#define ARM_CM_DWT_CYCCNT (DWT->CYCCNT) - -/*************************************** - * Timer functions of the test framework - ***************************************/ - -typedef struct { - uint32_t start; - uint32_t current; - uint32_t max; - uint32_t min; -} ELAPSED_TIME; - -ELAPSED_TIME elapsed_time_tbl[ITERATION]; - -void -elapsed_time_start(uint32_t i) -{ - elapsed_time_tbl[i].start = ARM_CM_DWT_CYCCNT; -} - -void -elapsed_time_stop(uint32_t i) -{ - uint32_t stop = ARM_CM_DWT_CYCCNT; - ELAPSED_TIME * p_tbl = &elapsed_time_tbl[i]; - p_tbl->current = stop - p_tbl->start; - if (p_tbl->max < p_tbl->current) - { - p_tbl->max = p_tbl->current; - } - if (p_tbl->min > p_tbl->current) - { - p_tbl->min = p_tbl->current; - } -} - -// 启动 DWT 计数器 -void -init_dwt_counter(void) -{ - CoreDebug->DEMCR |= CoreDebug_DEMCR_TRCENA_Msk; // Enable DWT - DWT->CYCCNT = 0; // Clear DWT cycle counter - DWT->CTRL |= DWT_CTRL_CYCCNTENA_Msk; // Enable DWT cycle counter -} - -// SysTick 中断处理程序,用于增加系统滴答计数 -//void -//SysTick_Handler(void) -//{ -// system_ticks++; -//} -// -//typedef struct { -// uint32_t tv_sec; // 秒 -// uint32_t tv_nsec; // 纳秒 -//} timespec; -// -//void -//init_systick(void) -//{ -// // 配置 SysTick 以产生 1ms 的中断 -// if (SysTick_Config(SystemCoreClock / SYSTICK_FREQUENCY_HZ)) -// { -// // 如果 SysTick 配置失败,处理错误 -// while (1) -// ; -// } -//} -// -//timespec -//get_current_time(void) -//{ -// timespec current_time; -// uint32_t ticks = system_ticks; -// uint32_t systick_val = SysTick->VAL; -// -// current_time.tv_sec = ticks / SYSTICK_FREQUENCY_HZ; -// current_time.tv_nsec = (1000000 - (systick_val * 1000 / (SystemCoreClock / SYSTICK_FREQUENCY_HZ))) * 1000; -// -// return current_time; -//} -// -//timespec -//diff(timespec start, timespec end) -//{ -// timespec temp; -// if ((end.tv_nsec - start.tv_nsec) < 0) -// { -// temp.tv_sec = end.tv_sec - start.tv_sec - 1; -// temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; -// } -// else -// { -// temp.tv_sec = end.tv_sec - start.tv_sec; -// temp.tv_nsec = end.tv_nsec - start.tv_nsec; -// } -// return temp; -//} -// -//void -//printTimeSpec(timespec t, const char * prefix) -//{ -// printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); -//} -// -//timespec -//tic(void) -//{ -// return get_current_time(); -//} -// -//timespec -//toc(timespec * start_time, const char * prefix) -//{ -// timespec current_time = get_current_time(); -// timespec time_consump = diff(*start_time, current_time); -// printTimeSpec(time_consump, prefix); -// *start_time = current_time; -// return time_consump; -//} - -int -main() -{ -#if defined(INT_DATA_TYPE) - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) - { - q0[i] = (0.64306622f * FRAC_BASE); - q1[i] = (0.02828862f * FRAC_BASE); - q2[i] = (-0.00567953f * FRAC_BASE); - q3[i] = (-0.76526684f * FRAC_BASE); - } - -#elif defined(FP_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) - { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } - -#else -#error "Must set data type: FP or INT" -#endif - -#if defined(FP_DATA_TYPE) - double time[DATA_SIZE] = { - 0.07969094, 0.08969094, 0.09969094, 0.10969094, 0.11969094, - 0.12969094, 0.13969094, 0.14969094, 0.15969094, 0.16969094}; - - float mag_x[DATA_SIZE] = { - -7.249095917, -6.654678345, -7.249095917, -7.548351288, -7.311084747, - -5.958099365, -8.100597382, -6.394374847, -7.610450745, -6.729293823}; - - float mag_y[DATA_SIZE] = { - 26.43893433, 26.09465027, 26.43893433, 24.87819672, 23.94067001, - 24.8057251, 25.37778282, 25.43608284, 26.11058998, 25.38845825}; - - float mag_z[DATA_SIZE] = { - -37.16656494, -37.29600525, -37.16656494, -39.49668884, -40.19360352, - -37.20587158, -37.85224915, -37.70759583, -38.66853333, -38.77174377}; - - float gyr_x[DATA_SIZE] = { - -0.1184487343, -0.1184487343, -0.1184487343, -0.1184487343, -0.1184487343, - -0.1184487343, -0.1184487343, -0.1151283234, -0.08246348053, -0.03473320603}; - - float gyr_y[DATA_SIZE] = { - 0.001258035656, 0.001258035656, 0.001258035656, 0.001258035656, 0.001258035656, - 0.001258035656, 0.001258035656, -0.00251355581, 0.00447106408, 0.04428362101}; - - float gyr_z[DATA_SIZE] = { - 0.008988874033, 0.008988874033, 0.008988874033, 0.008988874033, 0.008988874033, - 0.008988874033, 0.008988874033, 0.01010152325, 0.004323757719, 0.0009580431506}; - - float acc_x[DATA_SIZE] = { - -0.3570917249, -0.3570917249, -0.3570917249, -0.3570917249, -0.3570917249, - -0.3570917249, -0.3570917249, -0.2793728709, -0.2367789149, -0.2995947599}; - - float acc_y[DATA_SIZE] = { - 0.3941296637, 0.3941296637, 0.3941296637, 0.3941296637, 0.3941296637, - 0.3941296637, 0.3941296637, 0.4129949808, 0.4112356901, 0.416141957}; - - float acc_z[DATA_SIZE] = { - 9.963726044, 9.963726044, 9.963726044, 9.963726044, 9.963726044, - 9.963726044, 9.963726044, 10.27848434, 10.43229961, 10.24356842}; - -#elif defined(INT_DATA_TYPE) - int32_t time[DATA_SIZE] = { - round(0.07969094 * FRAC_BASE), round(0.08969094 * FRAC_BASE), - round(0.09969094 * FRAC_BASE), round(0.10969094 * FRAC_BASE), - round(0.11969094 * FRAC_BASE), round(0.12969094 * FRAC_BASE), - round(0.13969094 * FRAC_BASE), round(0.14969094 * FRAC_BASE), - round(0.15969094 * FRAC_BASE), round(0.16969094 * FRAC_BASE)}; - - int32_t mag_x[DATA_SIZE] = { - round(-7.249095917 * FRAC_BASE), round(-6.654678345 * FRAC_BASE), - round(-7.249095917 * FRAC_BASE), round(-7.548351288 * FRAC_BASE), - round(-7.311084747 * FRAC_BASE), round(-5.958099365 * FRAC_BASE), - round(-8.100597382 * FRAC_BASE), round(-6.394374847 * FRAC_BASE), - round(-7.610450745 * FRAC_BASE), round(-6.729293823 * FRAC_BASE)}; - - int32_t mag_y[DATA_SIZE] = { - round(26.43893433 * FRAC_BASE), round(26.09465027 * FRAC_BASE), - round(26.43893433 * FRAC_BASE), round(24.87819672 * FRAC_BASE), - round(23.94067001 * FRAC_BASE), round(24.8057251 * FRAC_BASE), - round(25.37778282 * FRAC_BASE), round(25.43608284 * FRAC_BASE), - round(26.11058998 * FRAC_BASE), round(25.38845825 * FRAC_BASE)}; - - int32_t mag_z[DATA_SIZE] = { - round(-37.16656494 * FRAC_BASE), round(-37.29600525 * FRAC_BASE), - round(-37.16656494 * FRAC_BASE), round(-39.49668884 * FRAC_BASE), - round(-40.19360352 * FRAC_BASE), round(-37.20587158 * FRAC_BASE), - round(-37.85224915 * FRAC_BASE), round(-37.70759583 * FRAC_BASE), - round(-38.66853333 * FRAC_BASE), round(-38.77174377 * FRAC_BASE)}; - - int32_t gyr_x[DATA_SIZE] = { - round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1184487343 * FRAC_BASE) * 61, - round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1184487343 * FRAC_BASE) * 61, - round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1184487343 * FRAC_BASE) * 61, - round(-0.1184487343 * FRAC_BASE) * 61, round(-0.1151283234 * FRAC_BASE) * 61, - round(-0.08246348053 * FRAC_BASE) * 61, round(-0.03473320603 * FRAC_BASE) * 61}; - - int32_t gyr_y[DATA_SIZE] = { - round(0.001258035656 * FRAC_BASE) * 61, round(0.001258035656 * FRAC_BASE) * 61, - round(0.001258035656 * FRAC_BASE) * 61, round(0.001258035656 * FRAC_BASE) * 61, - round(0.001258035656 * FRAC_BASE) * 61, round(0.001258035656 * FRAC_BASE) * 61, - round(0.001258035656 * FRAC_BASE) * 61, round(-0.00251355581 * FRAC_BASE) * 61, - round(0.00447106408 * FRAC_BASE) * 61, round(0.04428362101 * FRAC_BASE) * 61}; - - int32_t gyr_z[DATA_SIZE] = { - round(0.008988874033 * FRAC_BASE) * 61, round(0.008988874033 * FRAC_BASE) * 61, - round(0.008988874033 * FRAC_BASE) * 61, round(0.008988874033 * FRAC_BASE) * 61, - round(0.008988874033 * FRAC_BASE) * 61, round(0.008988874033 * FRAC_BASE) * 61, - round(0.008988874033 * FRAC_BASE) * 61, round(0.01010152325 * FRAC_BASE) * 61, - round(0.004323757719 * FRAC_BASE) * 61, round(0.0009580431506 * FRAC_BASE) * 61}; - - int32_t acc_x[DATA_SIZE] = { - round(-0.3570917249 * FRAC_BASE) * 2, round(-0.3570917249 * FRAC_BASE) * 2, - round(-0.3570917249 * FRAC_BASE) * 2, round(-0.3570917249 * FRAC_BASE) * 2, - round(-0.3570917249 * FRAC_BASE) * 2, round(-0.3570917249 * FRAC_BASE) * 2, - round(-0.3570917249 * FRAC_BASE) * 2, round(-0.2793728709 * FRAC_BASE) * 2, - round(-0.2367789149 * FRAC_BASE) * 2, round(-0.2995947599 * FRAC_BASE) * 2}; - - int32_t acc_y[DATA_SIZE] = { - round(0.3941296637 * FRAC_BASE) * 2, round(0.3941296637 * FRAC_BASE) * 2, - round(0.3941296637 * FRAC_BASE) * 2, round(0.3941296637 * FRAC_BASE) * 2, - round(0.3941296637 * FRAC_BASE) * 2, round(0.3941296637 * FRAC_BASE) * 2, - round(0.3941296637 * FRAC_BASE) * 2, round(0.4129949808 * FRAC_BASE) * 2, - round(0.4112356901 * FRAC_BASE) * 2, round(0.416141957 * FRAC_BASE) * 2}; - - int32_t acc_z[DATA_SIZE] = { - round(9.963726044 * FRAC_BASE) * 2, round(9.963726044 * FRAC_BASE) * 2, - round(9.963726044 * FRAC_BASE) * 2, round(9.963726044 * FRAC_BASE) * 2, - round(9.963726044 * FRAC_BASE) * 2, round(9.963726044 * FRAC_BASE) * 2, - round(9.963726044 * FRAC_BASE) * 2, round(10.27848434 * FRAC_BASE) * 2, - round(10.43229961 * FRAC_BASE) * 2, round(10.24356842 * FRAC_BASE) * 2}; -#endif - - init_dwt_counter(); // 初始化 DWT 计数器 - - u_int64_t time_slots[ITERATION]; - - for (size_t idx = 0; idx < ITERATION; idx++) - { - elapsed_time_start(idx); // 开始计时 - // timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) - { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - } - - //time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - - elapsed_time_stop(idx); // 停止计时 - time_slots[idx] = elapsed_time_tbl[idx].current; - printf("Elapsed cycles for iteration %lu: %lu cycles\n", idx, time_slots[idx]); - } - - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) - { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); - - return 0; -} diff --git a/src/newton/config.h b/src/newton/config.h new file mode 100644 index 000000000..b6b1ae39c --- /dev/null +++ b/src/newton/config.h @@ -0,0 +1,9 @@ +#ifndef CONFIG_H +#define CONFIG_H + + +#define IS_POINTER 0 +#define IS_MATRIX 1 +#define BIT_WIDTH 32 + +#endif // CONFIG_H diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 59dc16791..1d8881a32 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -75,6 +75,8 @@ POSSIBILITY OF SUCH DAMAGE. #include "llvm/Support/FileSystem.h" #include "llvm/IR/Function.h" +#include "config.h" + #include #include @@ -216,31 +218,24 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB } } - - - void -dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth, bool isPointer) +dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth) { IRBuilder<> Builder(storeInst->getNextNode()); llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; - if (isPointer) - { - handlePointerStore(storeInst, Builder, maxPrecisionBits, bitWidth); - } - else - { - handleGlobalStore(storeInst, Builder, maxPrecisionBits); - - } +#ifdef IS_POINTER + handlePointerStore(storeInst, Builder, maxPrecisionBits, bitWidth); +#else + handleGlobalStore(storeInst, Builder, maxPrecisionBits); +#endif } // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth, bool isPointer) +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth) { for (auto & F : module) { @@ -256,7 +251,7 @@ processWhitelistedFunctions(Module & module, const std::set & white if (auto * storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F, maxPrecisionBits,bitWidth,isPointer); + dequantizeResults(storeInst, F, maxPrecisionBits,bitWidth); } } } @@ -516,12 +511,13 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl * Config */ // int bitWidth = 32; - int bitWidth = 16; + int bitWidth = BIT_WIDTH; // maxPrecisionBits = 16; - maxPrecisionBits = 10; + maxPrecisionBits = 16; bool enableVectorization = true; bool enableRangeAnalysis = true; - bool isPointer = false; + bool isMatrix = IS_MATRIX; + //bool isPointer = IS_POINTER; /* * get const global variables @@ -605,10 +601,11 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl for (auto & mi : *Mod) { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth); + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth, enableVectorization); //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, virtualRegisterVectorRange, bitWidth, enableVectorization, enableRangeAnalysis); - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis, isPointer); + //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis, isPointer); +// irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis, isPointer); } @@ -773,7 +770,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // eraseOldInstructions(); - processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits, bitWidth, isPointer); + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits, bitWidth); const char * homeDir = getenv("HOME"); if (!homeDir) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index d0d395623..08be6861d 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -3,10 +3,11 @@ #include "llvm/Support/raw_ostream.h" #include #include "llvm/IR/Metadata.h" +#include "config.h" using namespace llvm; unsigned int FRAC_Q; -unsigned int BIT_WIDTH; +//unsigned int BIT_WIDTH; #define FRAC_BASE (1 << FRAC_Q) llvm::Value * @@ -339,7 +340,9 @@ bool shouldProcessFunction(Function &F) { static const std::set targetFunctions = { "sensfusion6UpdateQImpl", "MadgwickAHRSupdate", - "MadgwickAHRSupdateIMU" + "MadgwickAHRSupdateIMU", + "__kernel_sin", + "__kernel_cos", }; // Check if the function name is in the set @@ -356,15 +359,25 @@ GlobalVariable *getOrCreateQuantizedGlobal(Module *module, GlobalVariable &globa return existingGlobal; } +// // Calculate initializer for the quantized global +// llvm::Constant *initializer = nullptr; +// if (llvm::Constant *init = globalVar.getInitializer()) { +// if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { +// double value = constFp->getValueAPF().convertToDouble(); +// int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); +// initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); +// } + // } + // Calculate initializer for the quantized global - llvm::Constant *initializer = nullptr; - if (llvm::Constant *init = globalVar.getInitializer()) { - if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { - double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); - initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); + llvm::Constant *initializer = nullptr; + if (llvm::Constant *init = globalVar.getInitializer()) { + if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { + double value = constFp->getValueAPF().convertToDouble(); + int64_t quantizedValue = static_cast(round((value * FRAC_BASE))); + initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); + } } - } // Create the quantized global variable GlobalVariable *newGlobalVar = new GlobalVariable( @@ -1617,25 +1630,25 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) // int bitWidth, bool enableVectorization, bool enableRangeAnalysis) void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, - std::map>> &virtualRegisterVectorRange, - int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis,bool isPointer) + + int maxPrecisionBits) { { - FRAC_Q = maxPrecisionBits; - BIT_WIDTH = bitWidth; + FRAC_Q = maxPrecisionBits; + // BIT_WIDTH = bitWidth; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; - // Handle vectorization initialization - if (enableVectorization) - { - llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; - } - - if (enableRangeAnalysis) - { - llvm::errs() << "Range analysis enabled. Applying range analysis.\n"; - } +// // Handle vectorization initialization +// if (enableVectorization) +// { +// llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; +// } +// +// if (enableRangeAnalysis) +// { +// llvm::errs() << "Range analysis enabled. Applying range analysis.\n"; +// } // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); @@ -1644,7 +1657,8 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect return; } - if (!shouldProcessFunction(llvmIrFunction)) { + if (!shouldProcessFunction(llvmIrFunction)) + { llvm::errs() << "Skipping function: " << llvmIrFunction.getName() << "\n"; return; } @@ -1683,16 +1697,25 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect quantizeFunctionArguments(llvmIrFunction, builder); quantizeArguments(llvmIrFunction, quantizedType); -// // Perform bitcasting of float poiter arguments to i32* -// bitcastFloatPtrArgs(llvmIrFunction, builder); + // // Perform bitcasting of float poiter arguments to i32* + // bitcastFloatPtrArgs(llvmIrFunction, builder); // 根据 isPointer 参数决定是否执行 bitcastFloatPtrArgs - if (isPointer) { - llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; - bitcastFloatPtrArgs(llvmIrFunction, builder); - } else { - llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; - } + // if (isPointer) { + // llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; + // bitcastFloatPtrArgs(llvmIrFunction, builder); + // } else { + // llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; + // } +#ifdef IS_POINTER + llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; + bitcastFloatPtrArgs(llvmIrFunction, builder); +#else + llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; +#endif + + + // Update global variables to integer type updateGlobalVariables(module, quantizedType); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 5ec787f8e..97bf6918f 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -38,10 +38,14 @@ extern "C" extern std::vector functionsToErase; extern std::vector globalsToErase; //extern std::vector instructionsToErase; + void -irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, - std::map>> &virtualRegisterVectorRange, - int maxPrecisionBits, int bitWidth, bool enableVectorization,bool enableRangeAnalysis,bool isPointer); +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); + +//void +//irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, +// std::map>> &virtualRegisterVectorRange, +// int maxPrecisionBits); //void irPassLLVMIRAutoQuantization(State *N, Function &F, std::vector &functionsToInsert, // int maxPrecisionBits, std::map>> &virtualRegisterVectorRange, From e9ecc97f8a487a9fb9366d286e1cea515c914a98 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 4 Feb 2025 10:03:07 +0000 Subject: [PATCH 131/213] try to support martrix, but have store type issue Addresses #2. --- ...8a1e350786d8527b315bc202ea91f5b6df797c.txt | 48 ++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 50 +++++++++++- .../newton-irPass-LLVMIR-quantization.cpp | 78 ++++++++++++++++--- 3 files changed, 160 insertions(+), 16 deletions(-) create mode 100644 analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt diff --git a/analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt b/analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt new file mode 100644 index 000000000..fc8b19b89 --- /dev/null +++ b/analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt @@ -0,0 +1,48 @@ + +changeset: 1724:498a1e350786d8527b315bc202ea91f5b6df797c +char kNewtonVersion[] = "0.3-alpha-1724 (498a1e350786d8527b315bc202ea91f5b6df797c) (build 02-03-2025-15:00-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 1d8881a32..98c693baf 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -87,7 +87,8 @@ using namespace llvm; std::set whitelist = { "MadgwickAHRSupdate", "MahonyAHRSupdate", - "sensfusion6UpdateQImpl" + "sensfusion6UpdateQImpl", + "matrixMul" }; @@ -218,13 +219,53 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB } } -void -dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth) + + +void handleMatrixStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits) { + Value *valueOperand = storeInst->getValueOperand(); + Value *pointerOperand = storeInst->getPointerOperand(); + + // Ensure the stored value is an integer and the destination is a float pointer + Type *valueType = valueOperand->getType(); + Type *pointerElementType = pointerOperand->getType()->getPointerElementType(); + + if (valueType->isIntegerTy() && pointerElementType->isFloatingPointTy()) { + llvm::errs() << "Processing matrix store (quantized to dequantized): " << *storeInst << "\n"; + + // Convert integer value to floating-point (dequantization step 1) + llvm::errs() << "Converting integer to float: " << *valueOperand << "\n"; + Value *convertedFloat = Builder.CreateSIToFP(valueOperand, Type::getFloatTy(storeInst->getContext()), storeInst->getName() + ".dequantized"); + + // Compute the fractional base: `fracBase = 2^maxPrecisionBits` + double fracBase = pow(2.0, maxPrecisionBits); + + // Perform dequantization by multiplying by (1 / fracBase) + Value *dequantizedValue = Builder.CreateFMul( + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / fracBase), storeInst->getName() + ".scaled_back"); + + // Store the dequantized floating-point value back to the original float memory location + Builder.CreateStore(dequantizedValue, pointerOperand); + + llvm::errs() << "Dequantized and stored float value at: " << *pointerOperand << "\n"; + + // Remove the original store instruction + storeInst->eraseFromParent(); + } else { + llvm::errs() << "Skipping store: Not storing i32 into float*.\n"; + } +} + + + + +void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth) { IRBuilder<> Builder(storeInst->getNextNode()); llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; -#ifdef IS_POINTER +#ifdef IS_MATRIX + handleMatrixStore(storeInst, Builder, maxPrecisionBits); +#elif IS_POINTER handlePointerStore(storeInst, Builder, maxPrecisionBits, bitWidth); #else handleGlobalStore(storeInst, Builder, maxPrecisionBits); @@ -233,6 +274,7 @@ dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int b + // Process functions that are whitelisted for dequantization void processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 08be6861d..dc4efc1a1 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -343,6 +343,9 @@ bool shouldProcessFunction(Function &F) { "MadgwickAHRSupdateIMU", "__kernel_sin", "__kernel_cos", + "matrixMul", + "matrixAdd", + "matrixSub", }; // Check if the function name is in the set @@ -502,6 +505,30 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType llvm::errs() << "Replaced load with quantized integer value.\n"; } +void quantizeMatrixFloat(LoadInst *loadInst, IRBuilder<> &Builder, Type *quantizedType, Type *loadedType) +{ + // Get the pointer operand of the load instruction + Value *pointerOperand = loadInst->getPointerOperand(); + Type *pointerElementType = pointerOperand->getType()->getPointerElementType(); + + if (pointerElementType->isFloatingPointTy()) + { + llvm::errs() << "Quantizing load from local float pointer: " << *pointerOperand << "\n"; + + Value *loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); + Value *scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled"); + Value *quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized"); + loadInst->replaceAllUsesWith(quantizedValue); + loadInst->eraseFromParent(); + + llvm::errs() << "Replaced load with quantized integer value.\n"; + } + else + { + llvm::errs() << "Skipping quantization for load: " << *loadInst << " (Not a float load)\n"; + } +} + // void // quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) @@ -549,10 +576,17 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) } // Quantize local pointers +#ifndef IS_MATRIX else if (!isa(pointerOperand)) { quantizePointer(loadInst, Builder, quantizedType, loadedType); } +#else + else if (!isa(pointerOperand)) + { + quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); + } +#endif } } } @@ -1085,6 +1119,16 @@ handleStore(Instruction * llvmIrInstruction, Type * quantizedType) auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); auto pointerType = pointerOperand->getType()->getPointerElementType(); + // **Check if pointerOperand comes from GEP float*** + if (auto gepInstruction = dyn_cast(pointerOperand)) + { + if (gepInstruction->getSourceElementType()->isFloatTy()) + { + llvm::errs() << "Skipping store quantization for GEP(float*) case.\n"; + return; + } + } + if (valueType->isFloatTy() || valueType->isDoubleTy()) { llvm::errs() << "Original store value type: " << *valueType << "\n"; @@ -1434,6 +1478,27 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetSourceElementType(); + + // Process only `getelementptr inbounds float, float*` (not `float**`) + if (elementType->isFloatingPointTy()) + { + IRBuilder<> Builder(gepInst); + + // Bitcast the result of GEP from `float*` to `i32*` + Value *bitcastPtr = Builder.CreateBitCast(gepInst, quantizedType->getPointerTo(), gepInst->getName() + "_i32"); + + // Replace all uses of the original GEP result with the new `i32*` + gepInst->replaceAllUsesWith(bitcastPtr); + + llvm::errs() << "Replaced GEP(float*) with bitcast to i32*: " << *gepInst << "\n"; + } +} + + // Helper function to quantize floating-point parameters void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) @@ -1697,16 +1762,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect quantizeFunctionArguments(llvmIrFunction, builder); quantizeArguments(llvmIrFunction, quantizedType); - // // Perform bitcasting of float poiter arguments to i32* - // bitcastFloatPtrArgs(llvmIrFunction, builder); - // 根据 isPointer 参数决定是否执行 bitcastFloatPtrArgs - // if (isPointer) { - // llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; - // bitcastFloatPtrArgs(llvmIrFunction, builder); - // } else { - // llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; - // } #ifdef IS_POINTER llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; bitcastFloatPtrArgs(llvmIrFunction, builder); @@ -1714,9 +1770,6 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; #endif - - - // Update global variables to integer type updateGlobalVariables(module, quantizedType); @@ -1750,6 +1803,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); break; case Instruction::GetElementPtr: + //handleGetElementPtr(cast(llvmIrInstruction), quantizedType); case Instruction::Load: { llvm::errs() << "Handling load\n"; From f5bcd551dea1c1a407d13ef2c5b6de248077c09c Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 4 Feb 2025 11:57:28 +0000 Subject: [PATCH 132/213] update support for internal constant Addresses #2. --- ...02f825b35cc1885256ad00679db4fbf23a3c9d.txt | 48 +++++++++ applications/newton/llvm-ir/c-files/newtest.c | 56 ----------- .../newton-irPass-LLVMIR-quantization.cpp | 98 +++++++++++++++++++ 3 files changed, 146 insertions(+), 56 deletions(-) create mode 100644 analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt delete mode 100644 applications/newton/llvm-ir/c-files/newtest.c diff --git a/analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt b/analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt new file mode 100644 index 000000000..40ef56884 --- /dev/null +++ b/analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt @@ -0,0 +1,48 @@ + +changeset: 1725:9b02f825b35cc1885256ad00679db4fbf23a3c9d +char kNewtonVersion[] = "0.3-alpha-1725 (9b02f825b35cc1885256ad00679db4fbf23a3c9d) (build 02-04-2025-10:03-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/newtest.c b/applications/newton/llvm-ir/c-files/newtest.c deleted file mode 100644 index 6a8ed2b8e..000000000 --- a/applications/newton/llvm-ir/c-files/newtest.c +++ /dev/null @@ -1,56 +0,0 @@ -#include -#include "MadgwickAHRS.h" - -#define FRAC_BASE 1024 -//volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; -extern volatile float q0, q1, q2, q3; -// 测试函数 -void testMadgwickUpdate() { - // 初始化四元数 - q0 = 1.0f; - q1 = 0.0f; - q2 = 0.0f; - q3 = 0.0f; - -// q0 = 0.64306622f; -// q1= 0.02828862f; -// q2 = -0.00567953f; -// q3 = -0.76526684f; - - // 模拟的传感器数据 (这些值可以根据实际情况调整) - // 使用提取的数据替换模拟传感器数据 - float gx = -0.1184487343f * 61; // 陀螺仪 x 轴角速度 (乘以 61) - float gy = 0.001258035656f * 61; // 陀螺仪 y 轴角速度 (乘以 61) - float gz = 0.008988874033f * 61; // 陀螺仪 z 轴角速度 (乘以 61) - - float ax = -0.3570917249f * 2; // 加速度计 x 轴加速度 (乘以 2) - float ay = 0.3941296637f * 2; // 加速度计 y 轴加速度 (乘以 2) - float az = 9.963726044f * 2; // 加速度计 z 轴加速度 (乘以 2) - - - float mx = -7.249095917f; // 磁力计 x 轴磁场强度 (单位:µT) - float my = 26.43893433f; // 磁力计 y 轴磁场强度 - float mz = -37.16656494f; // 磁力计 z 轴磁场强度 - - - // 调用 MadgwickAHRSupdate 函数 - MadgwickAHRSupdate(gx, gy, gz, ax, ay, az, mx, my, mz); - - // 打印四元数结果 - q0= (double)q0/FRAC_BASE; - q1= (double)q1/FRAC_BASE; - q2= (double)q2/FRAC_BASE; - q3= (double)q3/FRAC_BASE; - - - printf("q0 = %f\n", q0); - printf("q1 = %f\n", q1); - printf("q2 = %f\n", q2); - printf("q3 = %f\n", q3); -} - -int main() { - // 调用测试函数 - testMadgwickUpdate(); - return 0; -} diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index dc4efc1a1..2926d7691 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1,4 +1,9 @@ #include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/GlobalVariable.h" +#include +#include #include "newton-irPass-LLVMIR-quantization.h" #include "llvm/Support/raw_ostream.h" #include @@ -409,6 +414,68 @@ GlobalVariable *getOrCreateQuantizedGlobal(Module *module, GlobalVariable &globa return newGlobalVar; } + +// Helper to create or retrieve the quantized internal constant +GlobalVariable *getOrCreateQuantizedConstant(Module *module, GlobalVariable &origConst, Type *quantizedType) { + std::string quantizedName = origConst.getName().str() + "_quantized"; + + // Check if the quantized version already exists + if (GlobalVariable *existingConst = module->getNamedGlobal(quantizedName)) { + errs() << "Quantized internal constant already exists: " << quantizedName << "\n"; + return existingConst; + } + + // Ensure the original constant has an initializer + if (!origConst.hasInitializer()) { + errs() << "Skipping quantization: constant has no initializer: " << origConst.getName() << "\n"; + return nullptr; + } + + llvm::Constant *init = origConst.getInitializer(); + ArrayType *origArrayType = dyn_cast(origConst.getType()->getElementType()); + + // Ensure the global variable is an array of floating-point values + if (!origArrayType || (!origArrayType->getArrayElementType()->isFloatTy() && + !origArrayType->getArrayElementType()->isDoubleTy())) { + errs() << "Skipping non-float internal constant: " << origConst.getName() << "\n"; + return nullptr; + } + + errs() << "Quantizing internal constant: " << origConst.getName() << "\n"; + + std::vector quantizedValues; + Type *intType = Type::getInt32Ty(module->getContext()); // Use int32 for quantized values + + // Iterate over array elements and quantize each floating-point value + for (unsigned i = 0; i < origArrayType->getNumElements(); ++i) { + llvm::ConstantFP *fpVal = dyn_cast(init->getAggregateElement(i)); + if (!fpVal) continue; + + double floatValue = fpVal->getValueAPF().convertToDouble(); + int quantizedValue = static_cast(round(floatValue * FRAC_BASE)); + + quantizedValues.push_back(llvm::ConstantInt::get(intType, quantizedValue)); + } + + // Create a new quantized array type + ArrayType *quantizedArrayType = ArrayType::get(intType, quantizedValues.size()); + llvm::Constant *newInit = llvm::ConstantArray::get(quantizedArrayType, quantizedValues); + + // Create the new quantized GlobalVariable + GlobalVariable *quantizedConst = new GlobalVariable( + *module, quantizedArrayType, true, origConst.getLinkage(), + newInit, quantizedName); + + // Set appropriate alignment based on bit width + quantizedConst->setAlignment(llvm::MaybeAlign(4)); // Align to 4 bytes for int32 + + quantizedConst->setDSOLocal(true); + errs() << "Created quantized internal constant: " << quantizedName << "\n"; + + return quantizedConst; +} + + // Helper to replace uses of the original global in whitelisted functions void replaceUsesInWhitelistedFunctions(GlobalVariable &originalGlobal, GlobalVariable &quantizedGlobal) { for (auto it = originalGlobal.use_begin(), end = originalGlobal.use_end(); it != end;) { @@ -455,6 +522,35 @@ void updateGlobalVariables(Module *module, Type *quantizedType) { } } +void updateInternalConstants(Module *module, Type *quantizedType) { + llvm::errs() << "Updating internal constants\n"; + + for (GlobalVariable &globalVar : module->globals()) { + std::string globalName = globalVar.getName().str(); + + if (!globalVar.isConstant() || !globalVar.hasInitializer()) { + llvm::errs() << "Skipping non-constant or uninitialized global: " << globalName << "\n"; + continue; + } + + Type *elementType = globalVar.getType()->getElementType(); + if (!elementType->isArrayTy() || + (!elementType->getArrayElementType()->isFloatTy() && + !elementType->getArrayElementType()->isDoubleTy())) { + llvm::errs() << "Skipping non-float internal constant: " << globalName << "\n"; + continue; + } + + llvm::errs() << "Quantizing internal constant: " << globalName << "\n"; + + GlobalVariable *quantizedConst = getOrCreateQuantizedConstant(module, globalVar, quantizedType); + + + replaceUsesInWhitelistedFunctions(globalVar, *quantizedConst); + + llvm::errs() << "Original internal constant preserved: " << globalName << "\n"; + } +} @@ -1773,6 +1869,8 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect // Update global variables to integer type updateGlobalVariables(module, quantizedType); + updateInternalConstants(module, quantizedType); + /* * generate hardcode function * */ From f34c0396e8c254265bcc9a7c702cab9c88df00d6 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 5 Feb 2025 11:53:22 +0000 Subject: [PATCH 133/213] use auto-quantiztion flag to control Addresses #2. --- ...ecc97f8a487a9fb9366d286e1cea515c914a98.txt | 48 +++++++++++++++++++ src/newton/config.h | 2 +- .../newton-irPass-LLVMIR-quantization.cpp | 6 ++- 3 files changed, 53 insertions(+), 3 deletions(-) create mode 100644 analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt diff --git a/analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt b/analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt new file mode 100644 index 000000000..a6d65bc17 --- /dev/null +++ b/analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt @@ -0,0 +1,48 @@ + +changeset: 1726:e9ecc97f8a487a9fb9366d286e1cea515c914a98 +char kNewtonVersion[] = "0.3-alpha-1726 (e9ecc97f8a487a9fb9366d286e1cea515c914a98) (build 02-04-2025-11:57-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/config.h b/src/newton/config.h index b6b1ae39c..1b055721d 100644 --- a/src/newton/config.h +++ b/src/newton/config.h @@ -1,7 +1,7 @@ #ifndef CONFIG_H #define CONFIG_H - +#define AUTO_QUANTIZATION 1 #define IS_POINTER 0 #define IS_MATRIX 1 #define BIT_WIDTH 32 diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 2926d7691..7717c19fa 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -346,11 +346,13 @@ bool shouldProcessFunction(Function &F) { "sensfusion6UpdateQImpl", "MadgwickAHRSupdate", "MadgwickAHRSupdateIMU", - "__kernel_sin", - "__kernel_cos", +// "__kernel_sin", +// "__kernel_cos", "matrixMul", "matrixAdd", "matrixSub", + "qzero" + }; // Check if the function name is in the set From 0b5aaf8e012c49df771e7f337ec6b6d74a6c5166 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 5 Feb 2025 12:04:08 +0000 Subject: [PATCH 134/213] use config file to control BIT_WIDTH Addresses #2. --- ...bcd551dea1c1a407d13ef2c5b6de248077c09c.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 34 ++++++------- .../newton-irPass-LLVMIR-quantization.cpp | 3 +- 3 files changed, 66 insertions(+), 19 deletions(-) create mode 100644 analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt diff --git a/analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt b/analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt new file mode 100644 index 000000000..a74fe599f --- /dev/null +++ b/analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt @@ -0,0 +1,48 @@ + +changeset: 1727:f5bcd551dea1c1a407d13ef2c5b6de248077c09c +char kNewtonVersion[] = "0.3-alpha-1727 (f5bcd551dea1c1a407d13ef2c5b6de248077c09c) (build 02-05-2025-11:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 98c693baf..47a0d7a3b 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -149,11 +149,11 @@ void handleGlobalStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis void -handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits, int bitWidth) +handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits) { auto *pointerOperand = storeInst->getPointerOperand(); - if (!pointerOperand->getType()->getPointerElementType()->isIntegerTy(bitWidth)) + if (!pointerOperand->getType()->getPointerElementType()->isIntegerTy(BIT_WIDTH)) { llvm::errs() << "Pointer operand type is not an integer of expected bit width.\n"; return; @@ -177,7 +177,7 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB bool isValidSource = false; // Determine the final store pointer based on bit width - switch (bitWidth) + switch (BIT_WIDTH) { case 16: if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) @@ -203,7 +203,7 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB break; default: - llvm::errs() << "Unsupported BIT_WIDTH: " << bitWidth << "\n"; + llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; return; } @@ -258,7 +258,7 @@ void handleMatrixStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis -void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, int bitWidth) +void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) { IRBuilder<> Builder(storeInst->getNextNode()); llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; @@ -266,7 +266,7 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, #ifdef IS_MATRIX handleMatrixStore(storeInst, Builder, maxPrecisionBits); #elif IS_POINTER - handlePointerStore(storeInst, Builder, maxPrecisionBits, bitWidth); + handlePointerStore(storeInst, Builder, maxPrecisionBits, BIT_WIDTH); #else handleGlobalStore(storeInst, Builder, maxPrecisionBits); #endif @@ -277,7 +277,7 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits, // Process functions that are whitelisted for dequantization void -processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits, int bitWidth) +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) { for (auto & F : module) { @@ -293,7 +293,7 @@ processWhitelistedFunctions(Module & module, const std::set & white if (auto * storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; - dequantizeResults(storeInst, F, maxPrecisionBits,bitWidth); + dequantizeResults(storeInst, F, maxPrecisionBits); } } } @@ -552,13 +552,12 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl /** * Config */ -// int bitWidth = 32; +// int BIT_WIDTH = 32; int bitWidth = BIT_WIDTH; // maxPrecisionBits = 16; maxPrecisionBits = 16; bool enableVectorization = true; bool enableRangeAnalysis = true; - bool isMatrix = IS_MATRIX; //bool isPointer = IS_POINTER; /* @@ -635,7 +634,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } - if (enableQuantization) +#ifdef AUTO_QUANTIZATION { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); llvm::errs() << "Auto quantization enabled\n"; @@ -657,13 +656,14 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl Mod->getFunctionList().push_front(mi); } } +#endif // Range Analysis - // for (auto & mi : *Mod) - // { - // rangeAnalysis(mi); - // - // } +// for (auto & mi : *Mod) +// { +// rangeAnalysis(mi); +// +// } // /* // * analyze the range of all local variables in each function @@ -812,7 +812,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // eraseOldInstructions(); - processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits, bitWidth); + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); const char * homeDir = getenv("HOME"); if (!homeDir) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 7717c19fa..f20cef9ec 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -12,7 +12,7 @@ using namespace llvm; unsigned int FRAC_Q; -//unsigned int BIT_WIDTH; + #define FRAC_BASE (1 << FRAC_Q) llvm::Value * @@ -1798,7 +1798,6 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect { { FRAC_Q = maxPrecisionBits; - // BIT_WIDTH = bitWidth; flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; From f9cc3b3e46d9a72bf92ab99f199db18b1105927c Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 5 Feb 2025 13:16:03 +0000 Subject: [PATCH 135/213] refine code Addresses #2. --- ...4c0396e8c254265bcc9a7c702cab9c88df00d6.txt | 48 +++++++++++++++++++ src/newton/config.h | 6 +-- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 22 ++++----- .../newton-irPass-LLVMIR-quantization.cpp | 12 ----- 4 files changed, 61 insertions(+), 27 deletions(-) create mode 100644 analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt diff --git a/analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt b/analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt new file mode 100644 index 000000000..729ce6157 --- /dev/null +++ b/analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt @@ -0,0 +1,48 @@ + +changeset: 1728:f34c0396e8c254265bcc9a7c702cab9c88df00d6 +char kNewtonVersion[] = "0.3-alpha-1728 (f34c0396e8c254265bcc9a7c702cab9c88df00d6) (build 02-05-2025-12:04-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/config.h b/src/newton/config.h index 1b055721d..75d659f66 100644 --- a/src/newton/config.h +++ b/src/newton/config.h @@ -2,8 +2,8 @@ #define CONFIG_H #define AUTO_QUANTIZATION 1 -#define IS_POINTER 0 -#define IS_MATRIX 1 -#define BIT_WIDTH 32 +#define IS_POINTER 1 +#define IS_MATRIX 0 +#define BIT_WIDTH 16 #endif // CONFIG_H diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 47a0d7a3b..b2f7460e9 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -82,7 +82,7 @@ POSSIBILITY OF SUCH DAMAGE. using namespace llvm; - +#define FRAC_BASE (1 << maxPrecisionBits) std::set whitelist = { "MadgwickAHRSupdate", @@ -130,12 +130,11 @@ void handleGlobalStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis // Convert the integer value to a floating-point value Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); - // Compute the fractional base - double fracBase = pow(2.0, maxPrecisionBits); + // Perform dequantization Value *dequantizedValue = Builder.CreateFMul( - convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / fracBase)); + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE)); // Store the dequantized floating-point value back into the original global variable Builder.CreateStore(dequantizedValue, originalGlobalVar); @@ -167,9 +166,8 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB } Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); - double fracBase = pow(2.0, maxPrecisionBits); Value *dividedValue = Builder.CreateFMul( - convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / fracBase)); + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE)); if (auto *bitcastInst = dyn_cast(pointerOperand)) { @@ -236,12 +234,10 @@ void handleMatrixStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis llvm::errs() << "Converting integer to float: " << *valueOperand << "\n"; Value *convertedFloat = Builder.CreateSIToFP(valueOperand, Type::getFloatTy(storeInst->getContext()), storeInst->getName() + ".dequantized"); - // Compute the fractional base: `fracBase = 2^maxPrecisionBits` - double fracBase = pow(2.0, maxPrecisionBits); // Perform dequantization by multiplying by (1 / fracBase) Value *dequantizedValue = Builder.CreateFMul( - convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / fracBase), storeInst->getName() + ".scaled_back"); + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE), storeInst->getName() + ".scaled_back"); // Store the dequantized floating-point value back to the original float memory location Builder.CreateStore(dequantizedValue, pointerOperand); @@ -263,10 +259,12 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) IRBuilder<> Builder(storeInst->getNextNode()); llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; -#ifdef IS_MATRIX + +#if IS_MATRIX handleMatrixStore(storeInst, Builder, maxPrecisionBits); -#elif IS_POINTER - handlePointerStore(storeInst, Builder, maxPrecisionBits, BIT_WIDTH); +#elif IS_POINTER + handlePointerStore(storeInst, Builder, maxPrecisionBits); + #else handleGlobalStore(storeInst, Builder, maxPrecisionBits); #endif diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index f20cef9ec..9c13eaa82 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1801,17 +1801,6 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; -// // Handle vectorization initialization -// if (enableVectorization) -// { -// llvm::errs() << "Vectorization enabled. Applying SIMD optimizations.\n"; -// } -// -// if (enableRangeAnalysis) -// { -// llvm::errs() << "Range analysis enabled. Applying range analysis.\n"; -// } - // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); if (shouldSkipFunction(functionName)) @@ -1875,7 +1864,6 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect /* * generate hardcode function * */ - // llvm::Function * fixmul = createFixMul(module, quantizedType, functionsToInsert); //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); From ac03ffeef2b0b3d7ea4a525f7c685ea227b45896 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 6 Feb 2025 22:53:49 +0000 Subject: [PATCH 136/213] add static analysis to decide auto-quantization Addresses #2. --- ...5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt | 48 ++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 75 +++++++++++++++++-- 2 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt diff --git a/analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt b/analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt new file mode 100644 index 000000000..b2f47712d --- /dev/null +++ b/analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt @@ -0,0 +1,48 @@ + +changeset: 1729:0b5aaf8e012c49df771e7f337ec6b6d74a6c5166 +char kNewtonVersion[] = "0.3-alpha-1729 (0b5aaf8e012c49df771e7f337ec6b6d74a6c5166) (build 02-05-2025-13:16-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index b2f7460e9..250268a65 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -270,7 +270,66 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) #endif } +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool enableAutoQuantization = false; + +void detectFloatingPointOps(Module &Mod) { + bool hasFloatOps = false; + std::map floatOpCounts; // Map to store the count of floating-point operations per function + + for (auto &F : Mod) { + int functionFloatOpCount = 0; // Counter for floating-point operations in the current function + + for (auto &BB : F) { + for (auto &I : BB) { + // Check if the instruction is a floating-point operation + if (I.getOpcode() == Instruction::FAdd || + I.getOpcode() == Instruction::FMul || + I.getOpcode() == Instruction::FSub || + I.getOpcode() == Instruction::FDiv) { + hasFloatOps = true; + functionFloatOpCount++; // Increment the counter for this function +// llvm::errs() << "Detected floating-point operation in function: " +// << F.getName() << " - Instruction: " << I << "\n"; + } + } + } + + // Store the count for this function + if (functionFloatOpCount > 0) { + floatOpCounts[F.getName().str()] = functionFloatOpCount; + } + } + // Output the results + if (hasFloatOps) { + llvm::errs() << "Floating-point operations detected in the module.\n"; + for (const auto &entry : floatOpCounts) { + llvm::errs() << "Function: " << entry.first + << " - Floating-point operations: " << entry.second << "\n"; + } + llvm::errs() << "Enabling Auto-Quantization.\n"; + enableAutoQuantization = true; + } else { + llvm::errs() << "No floating-point operations detected. Skipping Auto-Quantization.\n"; + } +} + + +void checkFPUAvailability() { +#if defined(__ARM_FP) || defined(__VFP_FP__) + llvm::errs() << "FPU is available on this platform.\n"; +#else + llvm::errs() << "FPU is not available on this platform. Enabling Auto-Quantization.\n"; + enableAutoQuantization = true; +#endif +} + + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Process functions that are whitelisted for dequantization @@ -551,12 +610,8 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl * Config */ // int BIT_WIDTH = 32; - int bitWidth = BIT_WIDTH; // maxPrecisionBits = 16; - maxPrecisionBits = 16; - bool enableVectorization = true; - bool enableRangeAnalysis = true; - //bool isPointer = IS_POINTER; + maxPrecisionBits = 10; /* * get const global variables @@ -632,7 +687,13 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } -#ifdef AUTO_QUANTIZATION + detectFloatingPointOps(*Mod); + checkFPUAvailability(); + + + +//#ifdef AUTO_QUANTIZATION + if (enableQuantization) { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); llvm::errs() << "Auto quantization enabled\n"; @@ -654,7 +715,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl Mod->getFunctionList().push_front(mi); } } -#endif +//#endif // Range Analysis // for (auto & mi : *Mod) From 922e4aa42d6bb0caf0244616d3a8febb79a02072 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sat, 8 Feb 2025 11:37:54 +0000 Subject: [PATCH 137/213] update target featture fpu checking Addresses #2. --- ...cc3b3e46d9a72bf92ab99f199db18b1105927c.txt | 48 ++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 103 ++++++++++++++++-- 2 files changed, 142 insertions(+), 9 deletions(-) create mode 100644 analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt diff --git a/analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt b/analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt new file mode 100644 index 000000000..ce9d5b05d --- /dev/null +++ b/analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt @@ -0,0 +1,48 @@ + +changeset: 1730:f9cc3b3e46d9a72bf92ab99f199db18b1105927c +char kNewtonVersion[] = "0.3-alpha-1730 (f9cc3b3e46d9a72bf92ab99f199db18b1105927c) (build 02-06-2025-22:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 250268a65..0de3d8699 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -281,6 +281,37 @@ void detectFloatingPointOps(Module &Mod) { for (auto &F : Mod) { int functionFloatOpCount = 0; // Counter for floating-point operations in the current function + // Analyze function parameters + int paramCount = 0; + int returnCount = 0; + std::vector paramTypes; // To store parameter types + for (auto &Arg : F.args()) { + paramCount++; + if (Arg.getType()->isPointerTy()) { + paramTypes.push_back("pointer"); + } else if (Arg.getType()->isFloatingPointTy()) { + paramTypes.push_back("floating-point"); + } else if (Arg.getType()->isIntegerTy()) { + paramTypes.push_back("integer"); + } else { + paramTypes.push_back("unknown"); + } + } + + // Analyze function return type + std::string returnType = "void"; // Default return type + if (!F.getReturnType()->isVoidTy()) { + if (F.getReturnType()->isPointerTy()) { + returnType = "pointer"; + } else if (F.getReturnType()->isFloatingPointTy()) { + returnType = "floating-point"; + } else if (F.getReturnType()->isIntegerTy()) { + returnType = "integer"; + } else { + returnType = "unknown"; + } + } + for (auto &BB : F) { for (auto &I : BB) { // Check if the instruction is a floating-point operation @@ -293,6 +324,12 @@ void detectFloatingPointOps(Module &Mod) { // llvm::errs() << "Detected floating-point operation in function: " // << F.getName() << " - Instruction: " << I << "\n"; } + + + // Check if the instruction is a return + if (isa(I)) { + returnCount++; + } } } @@ -300,6 +337,17 @@ void detectFloatingPointOps(Module &Mod) { if (functionFloatOpCount > 0) { floatOpCounts[F.getName().str()] = functionFloatOpCount; } + + // Output function details + llvm::errs() << "Function: " << F.getName() << "\n"; + llvm::errs() << " Return Type: " << returnType << "\n"; + llvm::errs() << " Parameter Count: " << paramCount << "\n"; + llvm::errs() << " Parameter Types: "; + for (const auto &type : paramTypes) { + llvm::errs() << type << " "; + } + llvm::errs() << "\n"; +//f } // Output the results @@ -317,16 +365,53 @@ void detectFloatingPointOps(Module &Mod) { } -void checkFPUAvailability() { -#if defined(__ARM_FP) || defined(__VFP_FP__) - llvm::errs() << "FPU is available on this platform.\n"; -#else - llvm::errs() << "FPU is not available on this platform. Enabling Auto-Quantization.\n"; - enableAutoQuantization = true; -#endif -} +void checkFPUAvailability(Module &Mod) { + bool hasFPU = false; + std::set detectedFeatures; + // Iterate over functions to check attributes + for (auto &F : Mod) { + if (F.hasFnAttribute("target-features")) { + std::string features = F.getFnAttribute("target-features").getValueAsString().str(); + detectedFeatures.insert(features); + + // Check for x86 floating-point features + if (features.find("+sse") != std::string::npos || + features.find("+sse2") != std::string::npos || + features.find("+avx") != std::string::npos || + features.find("+x87") != std::string::npos || + features.find("+fma") != std::string::npos) { + hasFPU = true; + } + + // Check for ARM floating-point features + if (features.find("+vfp") != std::string::npos || + features.find("+neon") != std::string::npos || + features.find("+fp-armv8") != std::string::npos || + features.find("+fp16") != std::string::npos) { + hasFPU = true; + } + } + } + + // Print detected target features (only once) + if (!detectedFeatures.empty()) { + llvm::errs() << "Target Features: "; + for (const auto &feature : detectedFeatures) { + llvm::errs() << feature << " "; + } + llvm::errs() << "\n"; + } + + // Final decision based on FPU detection + if (hasFPU) { + llvm::errs() << "FPU detected via function attributes. \n"; + } else { + llvm::errs() << "No FPU detected. Enabling Auto-Quantization.\n"; + enableAutoQuantization = true; + } +} /////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -688,7 +773,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } detectFloatingPointOps(*Mod); - checkFPUAvailability(); + checkFPUAvailability(*Mod); From 6b57f139a0634a2a9d3598739471fc1c70f70a4d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 10 Feb 2025 21:18:16 +0000 Subject: [PATCH 138/213] try to add support for e_y0 and e_j0 benchmark Addresses #2. --- ...03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 426 +++++++++++++++--- 2 files changed, 404 insertions(+), 70 deletions(-) create mode 100644 analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt diff --git a/analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt b/analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt new file mode 100644 index 000000000..0bb256ec9 --- /dev/null +++ b/analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt @@ -0,0 +1,48 @@ + +changeset: 1731:ac03ffeef2b0b3d7ea4a525f7c685ea227b45896 +char kNewtonVersion[] = "0.3-alpha-1731 (ac03ffeef2b0b3d7ea4a525f7c685ea227b45896) (build 02-08-2025-11:37-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 9c13eaa82..444d6e223 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1,6 +1,7 @@ #include "llvm/IR/IRBuilder.h" #include "llvm/IR/Module.h" #include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" #include "llvm/IR/GlobalVariable.h" #include #include @@ -14,6 +15,7 @@ using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 llvm::Value * performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned int FRAC_Q) @@ -351,6 +353,7 @@ bool shouldProcessFunction(Function &F) { "matrixMul", "matrixAdd", "matrixSub", + "pzero", "qzero" }; @@ -524,6 +527,166 @@ void updateGlobalVariables(Module *module, Type *quantizedType) { } } +// 辅助函数:对于 ConstantExpr 类型的使用,如果它是 GEP,则尝试构造新的 constant GEP 表达式 +static void handleConstantExprUse(llvm::ConstantExpr *constExpr, + llvm::GlobalVariable &origConst, + llvm::GlobalVariable &quantizedConst) { + // 如果 constant-expression 是 getelementptr,则重建一个新的 constant-expression + if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) { + llvm::SmallVector Indices; + // 从操作数1开始(operand0 是指针) + for (unsigned i = 1, e = constExpr->getNumOperands(); i < e; ++i) { + if (llvm::Constant *C = llvm::dyn_cast(constExpr->getOperand(i))) + Indices.push_back(C); + } + // 直接使用量化后的全局变量,不做 bitcast + llvm::Constant *newGEP = llvm::ConstantExpr::getGetElementPtr( + quantizedConst.getType()->getPointerElementType(), &quantizedConst, Indices); + // 用新构造的 constant 替换所有对该 constant-expression 的使用 + constExpr->replaceAllUsesWith(newGEP); + llvm::errs() << "Replaced constant GEP for " << origConst.getName() << "\n"; + } else { + // 对于非 GEP 的 constant-expression,则转换为指令 + llvm::Instruction *insertPt = nullptr; + for (llvm::Use &U : constExpr->uses()) { + if (llvm::Instruction *inst = llvm::dyn_cast(U.getUser())) { + insertPt = inst; + break; + } + } + if (insertPt) { + llvm::Instruction *newInst = constExpr->getAsInstruction(); + newInst->insertBefore(insertPt); + constExpr->replaceAllUsesWith(newInst); + llvm::errs() << "Converted constant expr to instruction for " << origConst.getName() << "\n"; + } + } +} +//static void handleConstantExprUse(llvm::ConstantExpr *constExpr, +// llvm::GlobalVariable &origConst, +// llvm::GlobalVariable &quantizedConst) { +// // 如果 constant-expression 是 getelementptr,则重建一个新的 constant-expression +// if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) { +// llvm::SmallVector Indices; +// // 从操作数1开始(operand0 是指针) +// for (unsigned i = 1, e = constExpr->getNumOperands(); i < e; ++i) { +// if (llvm::Constant *C = llvm::dyn_cast(constExpr->getOperand(i))) +// Indices.push_back(C); +// } +// // 构造 constant bitcast,将量化后的全局变量从 [N x i32]* 转换为原全局变量的类型(例如 [N x double]*) +// llvm::Constant *bitcasted = llvm::ConstantExpr::getBitCast(&quantizedConst, origConst.getType()); +// // 构造新的 constant GEP 表达式 +// llvm::Constant *newGEP = llvm::ConstantExpr::getGetElementPtr( +// origConst.getType()->getPointerElementType(), bitcasted, Indices); +// // 用新构造的 constant 替换所有对该 constant-expression 的使用 +// constExpr->replaceAllUsesWith(newGEP); +// llvm::errs() << "Replaced constant GEP for " << origConst.getName() << "\n"; +// } else { +// // 对于非 GEP 的 constant-expression,则转换为指令 +// llvm::Instruction *insertPt = nullptr; +// for (llvm::Use &U : constExpr->uses()) { +// if (llvm::Instruction *inst = llvm::dyn_cast(U.getUser())) { +// insertPt = inst; +// break; +// } +// } +// if (insertPt) { +// llvm::Instruction *newInst = constExpr->getAsInstruction(); +// newInst->insertBefore(insertPt); +// constExpr->replaceAllUsesWith(newInst); +// llvm::errs() << "Converted constant expr to instruction for " << origConst.getName() << "\n"; +// } +// } +//} + + +//// 辅助函数:处理函数内部的 GEP 指令(非 constant-expression),即将计算结果转换为常量表达式等价形式 +//static void handleGEPInstruction(GetElementPtrInst *gep, +// GlobalVariable &origConst, +// GlobalVariable &quantizedConst) { +// IRBuilder<> builder(gep); +// // 将量化后的全局变量 bitcast 成原全局变量的类型(例如 [6 x double]*) +// Value *quantizedBitcast = builder.CreateBitCast(&quantizedConst, origConst.getType(), +// quantizedConst.getName() + ".bitcast"); +// // 收集 GEP 的索引(保持原有索引不变) +// SmallVector Indices; +// for (Value *idx : gep->indices()) +// Indices.push_back(idx); +// // 重建 GEP:注意,这里我们希望得到的结果仍然是 double*(与原来的 GEP 相同) +// Value *newGEP = builder.CreateGEP(origConst.getType()->getPointerElementType(), +// quantizedBitcast, Indices, +// gep->getName() + ".quantized_gep"); +// // 如果新得到的 newGEP 类型与原来的不一致,使用 bitcast 调整回来 +// Value *castedGEP = builder.CreateBitCast(newGEP, gep->getType(), +// gep->getName() + ".casted"); +// gep->replaceAllUsesWith(castedGEP); +// gep->eraseFromParent(); +// errs() << "Replaced GEP instruction for " << origConst.getName() << "\n"; +//} + +static void handleGEPInstruction(llvm::GetElementPtrInst *gep, + llvm::GlobalVariable &origConst, + llvm::GlobalVariable &quantizedConst) { + llvm::IRBuilder<> builder(gep); + // 直接使用量化后的全局变量,无需 bitcast + llvm::SmallVector Indices; + for (llvm::Value *idx : gep->indices()) + Indices.push_back(idx); + // 重建 GEP:注意这里使用 quantizedConst 的类型(例如 [6 x i32]),因此 newGEP 的类型为 i32* + llvm::Value *newGEP = builder.CreateGEP(quantizedConst.getType()->getPointerElementType(), + &quantizedConst, Indices, + gep->getName() + ".quantized_gep"); + // newGEP 应该直接就是你期望的类型,即 i32* + gep->replaceAllUsesWith(newGEP); + gep->eraseFromParent(); + llvm::errs() << "Replaced GEP instruction for " << origConst.getName() << "\n"; +} + +// 主函数:遍历 origConst 的所有使用,在白名单函数中替换为量化后的全局变量 +void replaceInternalConstantUses(Module *module, + GlobalVariable &origConst, + GlobalVariable &quantizedConst) { + std::vector usesToReplace; + + for (auto it = origConst.use_begin(), end = origConst.use_end(); it != end; ) { + Use &use = *it++; + Value *user = use.getUser(); + + // 如果用户是 ConstantExpr + if (ConstantExpr *constExpr = dyn_cast(user)) { + handleConstantExprUse(constExpr, origConst, quantizedConst); + continue; + } + + // 如果用户是指令,且所在函数在白名单中 + Instruction *inst = dyn_cast(user); + if (!inst || !inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) + continue; + + // 如果该指令是 GEP,并且结果类型为 double*,则进行替换 + if (GetElementPtrInst *gep = dyn_cast(inst)) { + if (gep->getResultElementType()->isDoubleTy()) { + handleGEPInstruction(gep, origConst, quantizedConst); + continue; // 该 GEP 指令已经被替换,不需要再添加到 usesToReplace + } + } + // 其他情况,将该 use 加入待统一替换列表 + usesToReplace.push_back(&use); + } + + // 对剩余未处理的使用,统一替换为量化后的全局变量 + for (Use *use : usesToReplace) + use->set(&quantizedConst); + + errs() << "Replaced all uses of " << origConst.getName() + << " with " << quantizedConst.getName() + << " in whitelisted functions.\n"; +} + + + + + void updateInternalConstants(Module *module, Type *quantizedType) { llvm::errs() << "Updating internal constants\n"; @@ -547,8 +710,9 @@ void updateInternalConstants(Module *module, Type *quantizedType) { GlobalVariable *quantizedConst = getOrCreateQuantizedConstant(module, globalVar, quantizedType); - - replaceUsesInWhitelistedFunctions(globalVar, *quantizedConst); + if (quantizedConst) { + replaceInternalConstantUses(module, globalVar, *quantizedConst); + } llvm::errs() << "Original internal constant preserved: " << globalName << "\n"; } @@ -558,34 +722,6 @@ void updateInternalConstants(Module *module, Type *quantizedType) { -// void -// handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) -//{ -// if (auto * loadInst = dyn_cast(llvmIrInstruction)) -// { -// IRBuilder<> Builder(loadInst); -// -// // Check if the loaded value is of floating-point type -// Type * loadedType = loadInst->getType(); -// // Type * pointerType = loadInst->getPointerOperandType(); -// -// if (loadedType->isFloatingPointTy()) -// { -// llvm::errs() << "Handling normal load instruction\n"; -// // Create a new load instruction with the original pointer operand -// LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, loadInst->getPointerOperand(), loadInst->getName() + ".orig"); -// -// // Replace all uses of the original load instruction with the new converted value -// loadInst->replaceAllUsesWith(newLoadInst); -// -// // Schedule the original load instruction for removal -// loadInst->eraseFromParent(); -// -// llvm::errs() << "Replaced floating-point load with quantized integer load.\n"; -// } -// } -// } - void quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { @@ -627,26 +763,6 @@ void quantizeMatrixFloat(LoadInst *loadInst, IRBuilder<> &Builder, Type *quantiz } } - -// void -// quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) -//{ -// Value * pointerOperand = loadInst->getPointerOperand(); -// llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; -// -// Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); -// -// Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); -// -// Value * roundingValue = Builder.CreateFAdd(scaledValue, ConstantFP::get(loadedType, 0.5), loadInst->getName() + ".rounded_ptr"); -// -// Value * quantizedValue = Builder.CreateFPToSI(roundingValue, quantizedType, loadInst->getName() + ".quantized_ptr"); -// -// loadInst->replaceAllUsesWith(quantizedValue); -// loadInst->eraseFromParent(); -// -// llvm::errs() << "Replaced load with quantized integer value.\n"; -// } /** * handleLoad */ @@ -664,8 +780,11 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU" || - parentFunc->getName() == "MahonyAHRSupdateIMU") + parentFunc->getName() == "MahonyAHRSupdateIMU" || + parentFunc->getName() == "pzero" || + parentFunc->getName() == "qzero") { + llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); llvm::errs() << "New load instruction: " << *newLoadInst << "\n"; @@ -674,17 +793,31 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) } // Quantize local pointers -#ifndef IS_MATRIX +//#ifndef IS_MATRIX +// else if (!isa(pointerOperand)) +// { +// quantizePointer(loadInst, Builder, quantizedType, loadedType); +// } +//#else +// else if (!isa(pointerOperand)) +// { +// quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); +// } +//#endif + +#ifdef IS_POINTER else if (!isa(pointerOperand)) { quantizePointer(loadInst, Builder, quantizedType, loadedType); } + #else else if (!isa(pointerOperand)) { quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); } #endif + } } } @@ -698,6 +831,23 @@ handleFAdd(Instruction * inInstruction, Type * quantizedType) Value * op0 = inInstruction->getOperand(0); Value * op1 = inInstruction->getOperand(1); + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + if (ConstantFP * constFp = dyn_cast(op0)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + + if (ConstantFP * constFp = dyn_cast(op1)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + // Check if this instruction has metadata indicating it's quantized if (inInstruction->getMetadata("quantized")) { @@ -1148,6 +1298,84 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) llvm::errs() << "Finished handling FMul\n"; } + +void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { + llvm::errs() << "Handling FDiv\n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + // 如果已有 quantized 元数据,则跳过处理 + if (llvmIrInstruction->getMetadata("quantized")) { + llvm::errs() << "Skipping already quantized instruction.\n"; + return; + } + + // 获取左右操作数 + Value *lhs = llvmIrInstruction->getOperand(0); + Value *rhs = llvmIrInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + // 判断操作数是否为浮点型(float 或 double) + bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + // 检查常量:如果其中一个操作数是常量FP,则尝试简化 + if (auto rhsConst = dyn_cast(rhs)) { + if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) + return; + } + if (auto lhsConst = dyn_cast(lhs)) { + if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) + return; + } + + // 如果任一操作数是浮点常量,则通过 simplifyConstant 将其转换为固定点整数 + if (isa(lhs)) { + llvm::errs() << "LHS is a floating-point constant, handling it with simplifyConstant\n"; + simplifyConstant(llvmIrInstruction, quantizedType); + lhs = llvmIrInstruction->getOperand(0); + lhsIsFloat = false; // 更新状态,现已转换为固定点整数 + } + if (isa(rhs)) { + llvm::errs() << "RHS is a floating-point constant, handling it with simplifyConstant\n"; + simplifyConstant(llvmIrInstruction, quantizedType); + rhs = llvmIrInstruction->getOperand(1); + rhsIsFloat = false; + } + + // 如果任一操作数是整数常量,则直接用整数除法 + if (isa(lhs) || isa(rhs)) { + llvm::errs() << "One of the operands is an integer constant, using division directly\n"; + Value *newInst = Builder.CreateSDiv(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + return; + } + + // 如果任一操作数仍为浮点,则转换为固定点整数 + if (lhsIsFloat) { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + if (rhsIsFloat) { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } + + // 此时,lhs 和 rhs 均为整数(固定点表示),根据要求: + // 除法过程中不需要左移(即不乘 FRAC_BASE),也不在除法后做位移 + // 所以直接进行整数除法即可 + llvm::Value *newInst = Builder.CreateSDiv(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FDiv\n"; +} + + + void setQuantizedType(Value * inValue, Type * quantizedType) { @@ -1206,6 +1434,42 @@ handleAlloca(AllocaInst * llvmIrAllocaInstruction, Type * quantizedType) } } + +void handleGEPForQuantization(GetElementPtrInst *gep, Type *quantizedType) { + // 检查原来的元素类型是否为浮点型 + if (gep->getResultElementType()->isFloatingPointTy()) { + IRBuilder<> Builder(gep); + llvm::errs() << "Handling GEP quantization for: " << *gep << "\n"; + + // 获取原指针操作数 + Value *ptr = gep->getPointerOperand(); + // 如果指针操作数的元素类型不是 quantizedType,则转换 + if (ptr->getType()->getPointerElementType() != quantizedType) { + // 注意:如果 ptr 原本不是 quantizedType*,这里我们进行 bitcast + ptr = Builder.CreateBitCast(ptr, PointerType::getUnqual(quantizedType), + ptr->getName() + ".casted"); + } + + // 收集原来的索引(不包括第一个操作数) + SmallVector Indices; + for (Value *idx : gep->indices()) { + Indices.push_back(idx); + } + + // 创建新的 GEP 指令,元素类型直接使用 quantizedType + Value *newGEP = Builder.CreateGEP(quantizedType, ptr, Indices, gep->getName() + ".quantized"); + + // 替换所有使用,并删除原来的 GEP 指令 + gep->replaceAllUsesWith(newGEP); + gep->eraseFromParent(); + + llvm::errs() << "Replaced GEP with quantized version: " << *newGEP << "\n"; + } else { + llvm::errs() << "GEP element type is not floating-point. No quantization performed for: " + << *gep << "\n"; + } +} + void handleStore(Instruction * llvmIrInstruction, Type * quantizedType) { @@ -1576,23 +1840,34 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectorgetSourceElementType(); - // Process only `getelementptr inbounds float, float*` (not `float**`) - if (elementType->isFloatingPointTy()) - { + // 仅处理原来操作数为浮点类型的情况(例如 double 或 float) + if (elementType->isFloatingPointTy()) { IRBuilder<> Builder(gepInst); - // Bitcast the result of GEP from `float*` to `i32*` - Value *bitcastPtr = Builder.CreateBitCast(gepInst, quantizedType->getPointerTo(), gepInst->getName() + "_i32"); + // 获取基指针 + Value *basePtr = gepInst->getPointerOperand(); + + // 收集原 GEP 指令中的所有索引 + SmallVector Indices; + for (Value *Idx : gepInst->indices()) + Indices.push_back(Idx); + + // 直接构造新的 getelementptr 指令, + // 指定元素类型为 quantizedType,从而生成的指针类型为 quantizedType* + // 使用原始指令的名称,不附加后缀 + Value *newGEP = Builder.CreateGEP(quantizedType, basePtr, Indices, gepInst->getName()); - // Replace all uses of the original GEP result with the new `i32*` - gepInst->replaceAllUsesWith(bitcastPtr); + // 替换所有对原 GEP 指令的使用,并删除原指令 + gepInst->replaceAllUsesWith(newGEP); + gepInst->eraseFromParent(); - llvm::errs() << "Replaced GEP(float*) with bitcast to i32*: " << *gepInst << "\n"; + llvm::errs() << "Replaced GEP with quantized version: " << *newGEP << "\n"; + } else { + llvm::errs() << "GEP element type is not floating-point, no quantization applied: " << *gepInst << "\n"; } } @@ -1619,9 +1894,21 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { usesToReplace.push_back(&use); } + //TODO Check if the argument is a float or double + Value * processedArg = &arg; + llvm::Instruction * castedFloat = nullptr; + if (arg.getType()->isDoubleTy()) { + castedFloat = cast( + builder.CreateFPTrunc(&arg, llvm::Type::getFloatTy(arg.getContext()), arg.getName() + ".casted_float")); + processedArg = castedFloat; + } + + // Create multiplication and rounding instructions - llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + //llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + llvm::Instruction * scaled = cast( + builder.CreateFMul(processedArg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); // llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); llvm::Instruction * quantized = nullptr; @@ -1873,11 +2160,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect { Instruction * llvmIrInstruction = &*itBB++; - // if (llvmIrInstruction->getMetadata("quantized_changed")) - // { - // llvm::errs() << "quantized_changed.\n"; - // return; // Skip processing this instruction - // } + llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; switch (llvmIrInstruction->getOpcode()) @@ -1890,7 +2173,8 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); break; case Instruction::GetElementPtr: - //handleGetElementPtr(cast(llvmIrInstruction), quantizedType); + handleGetElementPtr(cast(llvmIrInstruction), quantizedType); + break; case Instruction::Load: { llvm::errs() << "Handling load\n"; @@ -1924,6 +2208,8 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect * then replace the instruction to the int version. * */ case Instruction::FDiv: + handleFDiv(llvmIrInstruction, quantizedType); + break; case Instruction::FCmp: handleFCmp(llvmIrInstruction, quantizedType); From 8db4d93d4e04855d20909600a0d074ccb2ed4611 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 10 Feb 2025 21:59:29 +0000 Subject: [PATCH 139/213] add dequantization support for return value Addresses #2. --- ...2e4aa42d6bb0caf0244616d3a8febb79a02072.txt | 48 +++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 118 +++++++++++++++--- 2 files changed, 148 insertions(+), 18 deletions(-) create mode 100644 analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt diff --git a/analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt b/analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt new file mode 100644 index 000000000..ab712c97f --- /dev/null +++ b/analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt @@ -0,0 +1,48 @@ + +changeset: 1732:922e4aa42d6bb0caf0244616d3a8febb79a02072 +char kNewtonVersion[] = "0.3-alpha-1732 (922e4aa42d6bb0caf0244616d3a8febb79a02072) (build 02-10-2025-21:18-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 0de3d8699..514fa3bae 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -83,12 +83,14 @@ POSSIBILITY OF SUCH DAMAGE. using namespace llvm; #define FRAC_BASE (1 << maxPrecisionBits) - +#define BIT_WIDTH 32 std::set whitelist = { "MadgwickAHRSupdate", "MahonyAHRSupdate", "sensfusion6UpdateQImpl", - "matrixMul" + "matrixMul", + "pzero", + "qzero" }; @@ -173,9 +175,10 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB { Value *finalStorePtr = nullptr; bool isValidSource = false; - + llvm::errs() << "BIT_WIDTH: " << BIT_WIDTH << "\n"; // Determine the final store pointer based on bit width switch (BIT_WIDTH) + { case 16: if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) @@ -251,6 +254,39 @@ void handleMatrixStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis } } +void handleReturnValue(ReturnInst *retInst, int maxPrecisionBits) { + // 如果返回指令没有返回值,则不处理 + if (!retInst->getReturnValue()) + return; + + Value *retVal = retInst->getReturnValue(); + + // 如果返回值不是整数类型,则不做反量化处理 + if (!retVal->getType()->isIntegerTy()) { + errs() << "Return value is not integer type, skipping dequantization.\n"; + return; + } + + IRBuilder<> Builder(retInst); + Type *targetType = Type::getDoubleTy(retInst->getContext()); + + // 将固定点整数转换为浮点数(double) + Value *fpVal = Builder.CreateSIToFP(retVal, targetType); + + // 创建常量 1/FRAC_BASE,注意用 llvm::ConstantFP 避免歧义 + llvm::Constant *oneDivFrac = llvm::ConstantFP::get(targetType, 1.0 / FRAC_BASE); + + // 计算反量化结果:fpVal * (1/FRAC_BASE) + Value *dequantizedVal = Builder.CreateFMul(fpVal, oneDivFrac); + + // 构造新的返回指令,返回 dequantizedVal(double 类型) + ReturnInst *newRet = ReturnInst::Create(retInst->getContext(), dequantizedVal, retInst); + + // 删除原有返回指令 + retInst->eraseFromParent(); + + errs() << "Replaced return with dequantized value: " << *newRet << "\n"; +} @@ -268,6 +304,7 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) #else handleGlobalStore(storeInst, Builder, maxPrecisionBits); #endif + } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -418,22 +455,34 @@ void checkFPUAvailability(Module &Mod) { // Process functions that are whitelisted for dequantization -void -processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) -{ - for (auto & F : module) - { - if (whitelist.find(F.getName().str()) != whitelist.end()) - { + +void processWhitelistedFunctions(Module &module, const std::set &whitelist, int maxPrecisionBits) { + // 对于白名单中的函数,我们先遍历,收集所有需要处理的返回指令 + for (Function &F : module) { + if (whitelist.find(F.getName().str()) != whitelist.end()) { llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; + std::vector retWorkList; + // 遍历函数基本块,并将所有返回指令收集到 retWorkList 中 + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { + if (ReturnInst *retInst = dyn_cast(&I)) { + // 仅处理返回值存在且为整数类型的返回指令 + if (retInst->getReturnValue() && retInst->getReturnValue()->getType()->isIntegerTy()) { + retWorkList.push_back(retInst); + } + } + } + } + // 依次处理收集到的返回指令 + for (ReturnInst *retInst : retWorkList) { + handleReturnValue(retInst, maxPrecisionBits); + } - for (auto & B : F) - { - for (auto & I : B) - { + // 同时继续处理其他指令,例如 StoreInst(这部分你已有代码) + for (BasicBlock &BB : F) { + for (Instruction &I : BB) { llvm::errs() << "Processing instruction: " << I << "\n"; - if (auto * storeInst = dyn_cast(&I)) - { + if (auto *storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; dequantizeResults(storeInst, F, maxPrecisionBits); } @@ -441,9 +490,41 @@ processWhitelistedFunctions(Module & module, const std::set & white } } } - } + +//void +//processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) +//{ +// for (auto & F : module) +// { +// if (whitelist.find(F.getName().str()) != whitelist.end()) +// { +// llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; +// +// +// +// +// for (auto & B : F) +// { +// for (auto & I : B) +// { +// llvm::errs() << "Processing instruction: " << I << "\n"; +// if (auto * storeInst = dyn_cast(&I)) +// { +// llvm::errs() << "Found valid StoreInst.\n"; +// dequantizeResults(storeInst, F, maxPrecisionBits); +// } +// +// } +// } +// +// +// } +// } +// +//} + // Function to save the IR of a module to a file void saveModuleIR(llvm::Module & M, const std::string & fileName) @@ -690,13 +771,14 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); + flexprint(N->Fe, N->Fm, N->Fpinfo, "bitwidth: %d\n", BIT_WIDTH); /** * Config */ // int BIT_WIDTH = 32; // maxPrecisionBits = 16; - maxPrecisionBits = 10; + maxPrecisionBits = 16; /* * get const global variables From ef4437405003dfbd61c14ae2d8deac27809467bb Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 14 Feb 2025 13:06:57 +0000 Subject: [PATCH 140/213] save before change Addresses #2. --- .../newton/llvm-ir/performance_test/Makefile | 3 +- .../newton/llvm-ir/performance_test/main.c | 2 +- applications/newton/sensors/BMX055.nt | 14 +- src/newton/config.h | 5 +- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 224 +++++++++--------- .../newton-irPass-LLVMIR-quantization.cpp | 110 +++++++-- 6 files changed, 218 insertions(+), 140 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index d7724f3be..8473b8e7a 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -57,7 +57,8 @@ endif endif # ENABLE_OVERLOAD=true ENABLE_BUILTIN_ASSUME=true make ... -NEWTON_FLAG=--llvm-ir-liveness-check +NEWTON_FLAG=--llvm-ir-liveness-check --llvm-ir-auto-quantization +#NEWTON_FLAG=--llvm-ir-liveness-check ifdef ENABLE_OVERLOAD NEWTON_FLAG+=--llvm-ir-enable-overload endif diff --git a/applications/newton/llvm-ir/performance_test/main.c b/applications/newton/llvm-ir/performance_test/main.c index fc89e1b67..718f12d31 100644 --- a/applications/newton/llvm-ir/performance_test/main.c +++ b/applications/newton/llvm-ir/performance_test/main.c @@ -320,7 +320,7 @@ main(int argc, char** argv) } printf("results: %f\t%f\t%f\t%f\t%f\n", result[0], result[1], result[2], result[3], result[4]); - printf("int results: %d\t%d\t%d\t%d\t%d\n", intResult[0], intResult[1], intResult[2], intResult[3], intResult[4]); + //printf("int results: %d\t%d\t%d\t%d\t%d\n", intResult[0], intResult[1], intResult[2], intResult[3], intResult[4]); return 0; } diff --git a/applications/newton/sensors/BMX055.nt b/applications/newton/sensors/BMX055.nt index c87828bf6..74a342280 100644 --- a/applications/newton/sensors/BMX055.nt +++ b/applications/newton/sensors/BMX055.nt @@ -37,7 +37,7 @@ # # Description: Base signals used in most Newton descriptions. # -include "applications/newton/include/NewtonBaseSignals.nt" +include "NewtonBaseSignals.nt" # # Description: Additional signals used in this particular newton description. @@ -205,7 +205,7 @@ bmx055: sensor ( # # Get MSB and LSB of zAccel data... # - + zAccelHigh := read 16r07; zAccelLow := read 16r06; bmx055zAcceleration = (zAccelHigh << 4) | (zAccelLow >> 4); @@ -218,9 +218,9 @@ bmx055: sensor ( # interface bmx055Temperature == i2c (address: 16r18) { - # + # # Start up at normal mode, modify (write value register) if set to other modes ... - # + # # Get Temp data... # bmx055Temperature = read 16r08; @@ -235,7 +235,7 @@ bmx055: sensor ( { # # Start up at normal mode, modify (write value register) if set to other modes ... - # + # # Get MSB and LSB of xMagneto data... # xMagnetoHigh := read 16r43; @@ -289,7 +289,7 @@ bmx055: sensor ( # # Start up at normal mode, modify (write value register) if set to other modes ... # - # Get MSB and LSB of yAngularRate data... + # Get MSB and LSB of yAngularRate data... # yAngularRateHigh := read 16r05; yAngularRateLow := read 16r04; @@ -473,4 +473,4 @@ bmx055: sensor ( accuracy bmx055xAngularRate == { (0.1 ajf, 0.012) } -} +} \ No newline at end of file diff --git a/src/newton/config.h b/src/newton/config.h index 75d659f66..5eed18ee1 100644 --- a/src/newton/config.h +++ b/src/newton/config.h @@ -1,9 +1,10 @@ #ifndef CONFIG_H #define CONFIG_H +//#define BIT_WIDTH 32 #define AUTO_QUANTIZATION 1 -#define IS_POINTER 1 +#define IS_POINTER 0 #define IS_MATRIX 0 -#define BIT_WIDTH 16 + #endif // CONFIG_H diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 514fa3bae..74dc585fe 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -481,7 +481,7 @@ void processWhitelistedFunctions(Module &module, const std::set &wh // 同时继续处理其他指令,例如 StoreInst(这部分你已有代码) for (BasicBlock &BB : F) { for (Instruction &I : BB) { - llvm::errs() << "Processing instruction: " << I << "\n"; + //llvm::errs() << "Processing instruction: " << I << "\n"; if (auto *storeInst = dyn_cast(&I)) { llvm::errs() << "Found valid StoreInst.\n"; dequantizeResults(storeInst, F, maxPrecisionBits); @@ -860,127 +860,123 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl //#ifdef AUTO_QUANTIZATION - if (enableQuantization) - { - flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - llvm::errs() << "Auto quantization enabled\n"; - std::vector functionsToInsert; + +//#endif + + + /* + * analyze the range of all local variables in each function + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + std::map callerMap; + callerMap.clear(); + funcBoundInfo.clear(); + bool useOverLoad = false; for (auto & mi : *Mod) { - llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); - //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, bitWidth, enableVectorization); - //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits, virtualRegisterVectorRange, bitWidth, enableVectorization, enableRangeAnalysis); - //irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis, isPointer); -// irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, virtualRegisterVectorRange, maxPrecisionBits, bitWidth, enableVectorization, enableRangeAnalysis, isPointer); + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } - } - for (auto mi : functionsToInsert) + // + flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + for (auto & mi : *Mod) { - Mod->getFunctionList().remove(mi); - Mod->getFunctionList().push_front(mi); + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + shrinkType(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } } - } -//#endif - // Range Analysis + if (enableQuantization) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; + std::vector functionsToInsert; + for (auto & mi : *Mod) + { + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + + } + for (auto mi : functionsToInsert) + { + Mod->getFunctionList().remove(mi); + Mod->getFunctionList().push_front(mi); + } + } + // +// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); // for (auto & mi : *Mod) // { -// rangeAnalysis(mi); +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// memoryAlignment(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } + // + /* + * remove the functions that are optimized by passes. + * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); // +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = true; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); // } - // /* - // * analyze the range of all local variables in each function - // * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // std::map callerMap; - // callerMap.clear(); - // funcBoundInfo.clear(); - // bool useOverLoad = false; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // shrinkType(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // memoryAlignment(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = true; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // /* * simplify the condition of each branch * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // simplifyControlFlow(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } + flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + simplifyControlFlow(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } // - // legacy::PassManager passManager; - // passManager.add(createCFGSimplificationPass()); - // passManager.add(createInstSimplifyLegacyPass()); - // passManager.add(createGlobalDCEPass()); - // passManager.run(*Mod); + legacy::PassManager passManager; + passManager.add(createCFGSimplificationPass()); + passManager.add(createInstSimplifyLegacyPass()); + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); + + // + // // /* // * remove the functions that are optimized by passes. @@ -1019,14 +1015,14 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // // } // } // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); // Finally, erase old functions eraseOldFunctions(); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 444d6e223..8584fcfc6 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1297,8 +1297,6 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) llvm::errs() << "Finished handling FMul\n"; } - - void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { llvm::errs() << "Handling FDiv\n"; llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; @@ -1321,7 +1319,7 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - // 检查常量:如果其中一个操作数是常量FP,则尝试简化 + // 检查常量:如果其中一个操作数是 ConstantFP,则尝试简化 if (auto rhsConst = dyn_cast(rhs)) { if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) return; @@ -1345,8 +1343,17 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { rhsIsFloat = false; } - // 如果任一操作数是整数常量,则直接用整数除法 + // 如果任一操作数是整数常量,则先检查特殊情况优化: + // 如果右操作数为常量 1,则直接替换为左操作数 if (isa(lhs) || isa(rhs)) { + if (ConstantInt *CI = dyn_cast(rhs)) { + if (CI->isOne()) { + llvm::errs() << "RHS is constant one, replacing division with LHS\n"; + llvmIrInstruction->replaceAllUsesWith(lhs); + llvmIrInstruction->eraseFromParent(); + return; + } + } llvm::errs() << "One of the operands is an integer constant, using division directly\n"; Value *newInst = Builder.CreateSDiv(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); @@ -1367,7 +1374,7 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { // 此时,lhs 和 rhs 均为整数(固定点表示),根据要求: // 除法过程中不需要左移(即不乘 FRAC_BASE),也不在除法后做位移 // 所以直接进行整数除法即可 - llvm::Value *newInst = Builder.CreateSDiv(lhs, rhs); + Value *newInst = Builder.CreateSDiv(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); @@ -1375,6 +1382,82 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { } +//void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { +// llvm::errs() << "Handling FDiv\n"; +// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; +// IRBuilder<> Builder(llvmIrInstruction); +// +// // 如果已有 quantized 元数据,则跳过处理 +// if (llvmIrInstruction->getMetadata("quantized")) { +// llvm::errs() << "Skipping already quantized instruction.\n"; +// return; +// } +// +// // 获取左右操作数 +// Value *lhs = llvmIrInstruction->getOperand(0); +// Value *rhs = llvmIrInstruction->getOperand(1); +// +// llvm::errs() << "LHS: " << *lhs << "\n"; +// llvm::errs() << "RHS: " << *rhs << "\n"; +// +// // 判断操作数是否为浮点型(float 或 double) +// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); +// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); +// +// // 检查常量:如果其中一个操作数是常量FP,则尝试简化 +// if (auto rhsConst = dyn_cast(rhs)) { +// if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) +// return; +// } +// if (auto lhsConst = dyn_cast(lhs)) { +// if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) +// return; +// } +// +// // 如果任一操作数是浮点常量,则通过 simplifyConstant 将其转换为固定点整数 +// if (isa(lhs)) { +// llvm::errs() << "LHS is a floating-point constant, handling it with simplifyConstant\n"; +// simplifyConstant(llvmIrInstruction, quantizedType); +// lhs = llvmIrInstruction->getOperand(0); +// lhsIsFloat = false; // 更新状态,现已转换为固定点整数 +// } +// if (isa(rhs)) { +// llvm::errs() << "RHS is a floating-point constant, handling it with simplifyConstant\n"; +// simplifyConstant(llvmIrInstruction, quantizedType); +// rhs = llvmIrInstruction->getOperand(1); +// rhsIsFloat = false; +// } +// +// // 如果任一操作数是整数常量,则直接用整数除法 +// if (isa(lhs) || isa(rhs)) { +// llvm::errs() << "One of the operands is an integer constant, using division directly\n"; +// Value *newInst = Builder.CreateSDiv(lhs, rhs); +// llvmIrInstruction->replaceAllUsesWith(newInst); +// llvmIrInstruction->eraseFromParent(); +// return; +// } +// +// // 如果任一操作数仍为浮点,则转换为固定点整数 +// if (lhsIsFloat) { +// lhs = Builder.CreateFPToSI(lhs, quantizedType); +// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; +// } +// if (rhsIsFloat) { +// rhs = Builder.CreateFPToSI(rhs, quantizedType); +// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; +// } +// +// // 此时,lhs 和 rhs 均为整数(固定点表示),根据要求: +// // 除法过程中不需要左移(即不乘 FRAC_BASE),也不在除法后做位移 +// // 所以直接进行整数除法即可 +// llvm::Value *newInst = Builder.CreateSDiv(lhs, rhs); +// llvmIrInstruction->replaceAllUsesWith(newInst); +// llvmIrInstruction->eraseFromParent(); +// +// llvm::errs() << "Finished handling FDiv\n"; +//} + + void setQuantizedType(Value * inValue, Type * quantizedType) @@ -1755,6 +1838,8 @@ handleSinCosCall(CallInst * llvmIrCallInstruction, Type * quantizedType, const s // llvmIrCallInstruction->eraseFromParent(); //} + + void // handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) @@ -2069,15 +2154,6 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) } - - - -// Main function to perform LLVM IR auto quantization -//void -//irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector & functionsToInsert, int maxPrecisionBits, int bitWidth, std::vector>> &virtualRegisterVectorRange,bool enableVectorization,bool enableRangeAnalysis) -//void irPassLLVMIRAutoQuantization(State *N, Function &F, std::vector &functionsToInsert, -// int maxPrecisionBits, std::map>> &virtualRegisterVectorRange, -// int bitWidth, bool enableVectorization, bool enableRangeAnalysis) void irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, @@ -2263,6 +2339,9 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect case Instruction::BitCast: case Instruction::Ret: +// handleReturn(cast(llvmIrInstruction), quantizedType); +// break; + case Instruction::Switch: case Instruction::Br: case Instruction::Select: @@ -2292,7 +2371,8 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect case Instruction::InsertValue: case Instruction::LandingPad: case Instruction::Freeze: - break; + + default: break; } From 525cd662c8513979e52d3f6b82fe08cb5ecf4f32 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 14 Feb 2025 13:54:21 +0000 Subject: [PATCH 141/213] update makefile for autoquantization test Addresses #2. --- ...57f139a0634a2a9d3598739471fc1c70f70a4d.txt | 48 +++++++++++++++++++ ...b4d93d4e04855d20909600a0d074ccb2ed4611.txt | 48 +++++++++++++++++++ .../newton/llvm-ir/performance_test/Makefile | 16 ++++--- 3 files changed, 106 insertions(+), 6 deletions(-) create mode 100644 analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt create mode 100644 analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt diff --git a/analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt b/analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt new file mode 100644 index 000000000..9cf776406 --- /dev/null +++ b/analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt @@ -0,0 +1,48 @@ + +changeset: 1733:6b57f139a0634a2a9d3598739471fc1c70f70a4d +char kNewtonVersion[] = "0.3-alpha-1733 (6b57f139a0634a2a9d3598739471fc1c70f70a4d) (build 02-10-2025-21:59-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt b/analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt new file mode 100644 index 000000000..1bbcf0e8f --- /dev/null +++ b/analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt @@ -0,0 +1,48 @@ + +changeset: 1734:8db4d93d4e04855d20909600a0d074ccb2ed4611 +char kNewtonVersion[] = "0.3-alpha-1734 (8db4d93d4e04855d20909600a0d074ccb2ed4611) (build 02-14-2025-13:06-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 8473b8e7a..5f4e1d79b 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -40,10 +40,10 @@ endif # auto-quantization configs, FP hardware by default, for Madgwick only currently # AUTO_QUANT=true make ... -ifdef AUTO_QUANT -DATA_TYPE=INT_DATA_TYPE -else -DATA_TYPE=FP_DATA_TYPE +#ifdef AUTO_QUANT +#DATA_TYPE=INT_DATA_TYPE +#else +#DATA_TYPE=FP_DATA_TYPE # SOFT_FLOAT_LIB=true make ... ifdef SOFT_FLOAT_LIB @@ -54,10 +54,10 @@ CC_FP_FLAG = -msoft-float OPT_FP_FLAG = --float-abi=soft endif -endif + # ENABLE_OVERLOAD=true ENABLE_BUILTIN_ASSUME=true make ... -NEWTON_FLAG=--llvm-ir-liveness-check --llvm-ir-auto-quantization +NEWTON_FLAG=--llvm-ir-liveness-check #NEWTON_FLAG=--llvm-ir-liveness-check ifdef ENABLE_OVERLOAD NEWTON_FLAG+=--llvm-ir-enable-overload @@ -66,6 +66,10 @@ ifdef ENABLE_BUILTIN_ASSUME NEWTON_FLAG+=--llvm-ir-enable-builtin-assume endif +ifdef AUTO_QUANT +NEWTON_FLAG+=--llvm-ir-auto-quantization +endif + max_opt_fn = opt ../$(1).ll $(OPT_FP_FLAG) -O3 -Os -S -o $(OUT_FILE) non_opt_fn = cp ../$(1).ll $(OUT_FILE) necessary_opt_fn = opt ../$(1).ll --simplifycfg --instsimplify -S -o $(OUT_FILE) From 097d5ef1e76af93962500380ad008ec13141c86d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 14 Feb 2025 14:01:08 +0000 Subject: [PATCH 142/213] save some files Addresses #2. --- ...4437405003dfbd61c14ae2d8deac27809467bb.txt | 48 ++ .../newton/llvm-ir/c-files/MadgwickAHRSfix.c | 542 ++---------------- .../newton/llvm-ir/c-files/MadgwickAHRSfix.h | 2 +- 3 files changed, 83 insertions(+), 509 deletions(-) create mode 100644 analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt diff --git a/analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt b/analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt new file mode 100644 index 000000000..67356f859 --- /dev/null +++ b/analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt @@ -0,0 +1,48 @@ + +changeset: 1735:ef4437405003dfbd61c14ae2d8deac27809467bb +char kNewtonVersion[] = "0.3-alpha-1735 (ef4437405003dfbd61c14ae2d8deac27809467bb) (build 02-14-2025-13:54-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c index e4658c016..3272535b3 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c @@ -1,14 +1,7 @@ -#include -#include -// #include "config.h" - -#include "fsl_misc_utilities.h" - - #include "MadgwickAHRSfix.h" -#define sampleFreq 28 // sample frequency in Hz -#define betaDef 0.1f // 2 * proportional gain +#define sampleFreq 28 // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain typedef int32_t bmx055xAcceleration; typedef int32_t bmx055yAcceleration; @@ -20,11 +13,10 @@ typedef int32_t bmx055xMagneto; typedef int32_t bmx055yMagneto; typedef int32_t bmx055zMagneto; -volatile bmx055xAcceleration beta = (uint8_t)(betaDef * FRAC_BASE); // 0.1f // 2 * proportional gain (Kp) -// volatile bmx055xAcceleration q0 = FRAC_BASE, q1 = 0x0, q2 = 0x0, q3 = 0x0; // quaternion of sensor frame relative to auxiliary frame +volatile bmx055xAcceleration beta = (uint8_t)(betaDef*FRAC_BASE); //0.1f // 2 * proportional gain (Kp) +//volatile bmx055xAcceleration q0 = 0.64306622f*FRAC_BASE, q1 = 0.02828862f*FRAC_BASE, +// q2 = -0.00567953f*FRAC_BASE, q3 = -0.76526684f*FRAC_BASE; // quaternion of sensor frame relative to auxiliary frame -volatile bmx055xAcceleration q0 = 0.64306622f * FRAC_BASE, q1 = 0.02828862f * FRAC_BASE, - q2 = -0.00567953f * FRAC_BASE, q3 = -0.76526684f * FRAC_BASE; // m=-7 1/Yest=0.0858 Yest=11.3120 Yest_hex=B50 // m=-6 1/Yest=0.1248 Yest=8.0000 Yest_hex=800 // m=-5 1/Yest=0.1755 Yest=5.6552 Yest_hex=5A8 @@ -57,114 +49,43 @@ volatile bmx055xAcceleration q0 = 0.64306622f * FRAC_BASE, q1 = 0.02828862f * FR // m=22 1/Yest=2048.0000 Yest=0.0000 Yest_hex=0 -//void MadgwickAHRSupdate_float(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, -// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { -// int mag_x = roundf(gx * FRAC_BASE); -// int mag_y = roundf(gy * FRAC_BASE); -// int mag_z = roundf(gz * FRAC_BASE); -// int gyr_x = roundf(ax * FRAC_BASE); -// int gyr_y = roundf(ay * FRAC_BASE); -// int gyr_z = roundf(az * FRAC_BASE); -// int acc_x = roundf(mx * FRAC_BASE); -// int acc_y = roundf(my * FRAC_BASE); -// int acc_z = roundf(mz * FRAC_BASE); -// // Call to update Madgwick's algorithm -// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, acc_x, acc_y, acc_z, mag_x, mag_y, mag_z, (int*)q0_ptr, (int*)q1_ptr, (int*)q2_ptr, (int*)q3_ptr); -// -// //dequantize -// -// float q0_float = (float)(*(int*)q0_ptr) / FRAC_BASE; -// float q1_float = (float)(*(int*)q1_ptr) / FRAC_BASE; -// float q2_float = (float)(*(int*)q2_ptr) / FRAC_BASE; -// float q3_float = (float)(*(int*)q3_ptr) / FRAC_BASE; -//} - -int -frac2dec(int32_t x) { - if (x < 0) { - return -((FRAC_BASE - 1) & (~x + 1)) * (10000 / FRAC_BASE); - } - return ((FRAC_BASE - 1) & x) * (10000 / FRAC_BASE); -} - -float fixed_point_to_float(int q0_value, int FRAC_Q) { - int q0_int_part = q0_value >> FRAC_Q; // 提取整数部分 - int q0_frac_part = q0_value & ((1 << FRAC_Q) - 1); // 提取小数部分 - float result = (float)q0_int_part + (float)q0_frac_part / (1 << FRAC_Q); // 组合整数和小数部分 - return result; -} - - -int32_t +inline __attribute__((always_inline))int32_t mulfix(int32_t x, int32_t y) { - return ((int64_t)x * y) / FRAC_BASE; + // int32_t result; + // int64_t temp; + // temp = (int64_t)x * (int64_t)y; + // temp += K; + // result = round(temp/FRAC_BASE); + // return result; + // return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; + return ((int64_t)x*y)>>FRAC_Q; } /* * Compute square root of x and reciprocal with Goldschmidt's method */ int32_t -sqrt_rsqrt(int32_t x, int recip) -{ - /* assume x>0 */ - // warpPrint("x=%d.%04d\n", DISPLAY_INT(x), DISPLAY_FRAC(x)); - - /* m ranges in -7 -> 23 */ - static int32_t Y_est[31] = - {0xB50, 0x800, 0x5A8, 0x400, 0x2D4, 0x200, 0x16A, 0x100, 0xB5, 0x80, 0x5A, 0x40, 0x2D, 0x20, 0x16, 0x10, 0xB, 0x8, 0x5, 0x4, 0x2, 0x2, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1, 0x1}; - - /* - * Initial estimate is sqrt(2^(|_ log_2(x) _| - FRAC_BITS))*FRAC_BASE - */ - int32_t m = sizeof(int32_t) * 8 - 1 - __builtin_clz(x) - FRAC_Q; - - /* - * - */ - int32_t bi = x; - int32_t Yi = Y_est[m + 7]; - int32_t xi = mulfix(x, Yi); // x*Yi/FRAC_BASE; - int32_t yi = Yi; - - /* 1st iteration */ - bi = mulfix(mulfix(bi, Yi), Yi); - Yi = (3 * FRAC_BASE - bi) / 2; - xi = mulfix(xi, Yi); - yi = mulfix(yi, Yi); - // bi = bi*Yi*Yi/FRAC_BASE/FRAC_BASE; - // Yi = (3*FRAC_BASE - bi)/2; - // xi = xi*Yi/FRAC_BASE; - // yi = yi*Yi/FRAC_BASE; - - // warpPrint("run fixpoint \n"); - - /* 2nd iteration */ - bi = mulfix(mulfix(bi, Yi), Yi); - Yi = (3 * FRAC_BASE - bi) / 2; - xi = mulfix(xi, Yi); - yi = mulfix(yi, Yi); - - // warpPrint("sqrt(x)_2=%d.%04d\n", DISPLAY_INT(xi), DISPLAY_FRAC(xi)); - - /* 3rd iteration */ - bi = mulfix(mulfix(bi, Yi), Yi); - Yi = (3 * FRAC_BASE - bi) / 2; - xi = mulfix(xi, Yi); - yi = mulfix(yi, Yi); - - // warpPrint("sqrt(x)_3=%d.%04d\n", DISPLAY_INT(xi), DISPLAY_FRAC(xi)); - - /* 4th iteration */ - bi = mulfix(mulfix(bi, Yi), Yi); - Yi = (3 * FRAC_BASE - bi) / 2; - xi = mulfix(xi, Yi); - yi = mulfix(yi, Yi); - - // warpPrint("sqrt(x)_4=%d.%04d\n", DISPLAY_INT(xi), DISPLAY_FRAC(xi)); - - return recip ? (yi > 0 ? yi : 1) : (xi > 0 ? xi : 1); +sqrt_rsqrt(int32_t x, int recip) { + if (recip) { + int32_t int_halfx = mulfix(0.5*FRAC_BASE, x); + float fp_y = (float)x/FRAC_BASE; + long i = *(long*)&fp_y; + i = 0x5f3759df - (i>>1); + fp_y = *(float*)&i; + int32_t int_y = fp_y*FRAC_BASE; + int_y = mulfix(int_y, ((int32_t)(1.5f*FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); + return int_y; + // fp_y = fp_y * (1.5f - (halfx * fp_y * fp_y)); + // return fp_y*FRAC_BASE; + } else { + int32_t res = (int32_t)sqrt((double)x)<<(FRAC_Q/2); + if (FRAC_Q%2) + return res*1.414213562; + else + return res; + } } //==================================================================================================== @@ -356,12 +277,6 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR // Normalise quaternion recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); - // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", - // (double)q0/FRAC_BASE, - // (double)q1/FRAC_BASE, - // (double)q2/FRAC_BASE, - // (double)q3/FRAC_BASE, - // (double)recipNorm/FRAC_BASE); q0 = mulfix(q0, recipNorm); q1 = mulfix(q1, recipNorm); q2 = mulfix(q2, recipNorm); @@ -371,18 +286,6 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR *q2_ptr = q2; *q3_ptr = q3; - // printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", - // DISPLAY_INT(q0), DISPLAY_FRAC(q0), - // DISPLAY_INT(q1), DISPLAY_FRAC(q1), - // DISPLAY_INT(q2), DISPLAY_FRAC(q2), - // DISPLAY_INT(q3), DISPLAY_FRAC(q3)); - - // /* 2nd iter normalization */ - // recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); - // q0 = mulfix(q0, recipNorm); - // q1 = mulfix(q1, recipNorm); - // q2 = mulfix(q2, recipNorm); - // q3 = mulfix(q3, recipNorm); } //--------------------------------------------------------------------------------------------------- @@ -485,381 +388,4 @@ MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngul q1_ptr = &q1; q2_ptr = &q2; q3_ptr = &q3; -} - -//TODO: Original code -//#include "MadgwickAHRSfix.h" -// -//#define sampleFreq 28 // sample frequency in Hz -//#define betaDef 0.1f // 2 * proportional gain -// -//typedef int32_t bmx055xAcceleration; -//typedef int32_t bmx055yAcceleration; -//typedef int32_t bmx055zAcceleration; -//typedef int32_t bmx055xAngularRate; -//typedef int32_t bmx055yAngularRate; -//typedef int32_t bmx055zAngularRate; -//typedef int32_t bmx055xMagneto; -//typedef int32_t bmx055yMagneto; -//typedef int32_t bmx055zMagneto; -// -//volatile bmx055xAcceleration beta = (uint8_t)(betaDef*FRAC_BASE); //0.1f // 2 * proportional gain (Kp) -// -// -//int32_t -//mulfix(int32_t x, int32_t y) -//{ -// // int32_t result; -// // int64_t temp; -// // temp = (int64_t)x * (int64_t)y; -// // temp += K; -// // result = round(temp/FRAC_BASE); -// // return result; -// // return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; -// return ((int64_t)x*y)>>FRAC_Q; -//} -// -///* -// * Compute square root of x and reciprocal with Goldschmidt's method -// */ -//int32_t -//sqrt_rsqrt(int32_t x, int recip) { -// if (recip) { -// int32_t int_halfx = mulfix(0.5*FRAC_BASE, x); -// float fp_y = (float)x/FRAC_BASE; -// long i = *(long*)&fp_y; -// i = 0x5f3759df - (i>>1); -// fp_y = *(float*)&i; -// int32_t int_y = fp_y*FRAC_BASE; -// int_y = mulfix(int_y, ((int32_t)(1.5f*FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); -// return int_y; -// // fp_y = fp_y * (1.5f - (halfx * fp_y * fp_y)); -// // return fp_y*FRAC_BASE; -// } else { -// int32_t res = (int32_t)sqrt((double)x)<<(FRAC_Q/2); -// if (FRAC_Q%2) -// return res*1.414213562; -// else -// return res; -// } -//} -// -////==================================================================================================== -//// Functions -// -////--------------------------------------------------------------------------------------------------- -//// AHRS algorithm update -// -//void -//MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, -// bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, -// bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, -// int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { -// -// int32_t q0 = *q0_ptr; -// int32_t q1 = *q1_ptr; -// int32_t q2 = *q2_ptr; -// int32_t q3 = *q3_ptr; -// -// int32_t recipNorm; -// int32_t s0, s1, s2, s3; -// int32_t qDot1, qDot2, qDot3, qDot4; -// int32_t hx, hy; -// int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; -// -// // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) -// if((mx == 0x0) && (my == 0x0) && (mz == 0x0)) { -// MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); -// return; -// } -// -// // Rate of change of quaternion from gyroscope -// qDot1 = (-mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; -// qDot2 = (mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; -// qDot3 = (mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; -// qDot4 = (mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; -// -// // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) -// if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { -// -// // Normalise accelerometer measurement -// recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); -// // printf("1: %f\n", (double)recipNorm/FRAC_BASE); -// ax = mulfix(ax, recipNorm); -// ay = mulfix(ay, recipNorm); -// az = mulfix(az, recipNorm); -// -// // Normalise magnetometer measurement -// recipNorm = sqrt_rsqrt(mulfix(mx, mx) + mulfix(my, my) + mulfix(mz, mz), true); -// mx = mulfix(mx, recipNorm); -// my = mulfix(my, recipNorm); -// mz = mulfix(mz, recipNorm); -// -// // Auxiliary variables to avoid repeated arithmetic -// _2q0mx = 2 * mulfix(q0, mx); -// _2q0my = 2 * mulfix(q0, my); -// _2q0mz = 2 * mulfix(q0, mz); -// _2q1mx = 2 * mulfix(q1, mx); -// _2q0 = 2*q0; -// _2q1 = 2*q1; -// _2q2 = 2*q2; -// _2q3 = 2*q3; -// _2q0q2 = 2 * mulfix(q0, q2); -// _2q2q3 = 2 * mulfix(q2, q3); -// q0q0 = mulfix(q0, q0); -// q0q1 = mulfix(q0, q1); -// q0q2 = mulfix(q0, q2); -// q0q3 = mulfix(q0, q3); -// q1q1 = mulfix(q1, q1); -// q1q2 = mulfix(q1, q2); -// q1q3 = mulfix(q1, q3); -// q2q2 = mulfix(q2, q2); -// q2q3 = mulfix(q2, q3); -// q3q3 = mulfix(q3, q3); -// -// // Reference direction of Earth's magnetic field -// hx = mulfix(mx, q0q0) -// - mulfix(_2q0my, q3) -// + mulfix(_2q0mz, q2) -// + mulfix(mx, q1q1) -// + mulfix(mulfix(_2q1, my), q2) -// + mulfix(mulfix(_2q1, mz), q3) -// - mulfix(mx, q2q2) -// - mulfix(mx, q3q3); -// hy = mulfix(_2q0mx, q3) -// + mulfix(my, q0q0) -// - mulfix(_2q0mz, q1) -// + mulfix(_2q1mx, q2) -// - mulfix(my, q1q1) -// + mulfix(my, q2q2) -// + mulfix(mulfix(_2q2, mz), q3) -// - mulfix(my, q3q3); -// _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); -// _2bz = -mulfix(_2q0mx, q2) -// + mulfix(_2q0my, q1) -// + mulfix(mz, q0q0) -// + mulfix(_2q1mx, q3) -// - mulfix(mz, q1q1) -// + mulfix(mulfix(_2q2, my), q3) -// - mulfix(mz, q2q2) -// + mulfix(mz, q3q3); -// _4bx = 2*_2bx; -// _4bz = 2*_2bz; -// -// // Gradient decent algorithm corrective step -// s0 = - mulfix(_2q2, (2*q1q3 - _2q0q2 - ax)) -// + mulfix(_2q1, (2*q0q1 + _2q2q3 - ay)) -// - mulfix(mulfix(_2bz, q2), ( -// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) -// + mulfix(_2bz, (q1q3 - q0q2)) -// - mx)) -// + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), ( -// mulfix(_2bx, (q1q2 - q0q3)) -// + mulfix(_2bz, (q0q1 + q2q3)) -// - my)) -// + mulfix(mulfix(_2bx, q2), ( -// mulfix(_2bx, (q0q2 + q1q3)) -// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) -// - mz)); -// s1 = mulfix(_2q3, (2*q1q3 - _2q0q2 - ax)) -// + mulfix(_2q0, (2*q0q1 + _2q2q3 - ay)) -// - 4 * mulfix(q1, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) -// + mulfix(mulfix(_2bz, q3), ( -// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) -// + mulfix(_2bz, (q1q3 - q0q2)) -// - mx)) -// + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), ( -// mulfix(_2bx, (q1q2 - q0q3)) -// + mulfix(_2bz, (q0q1 + q2q3)) -// - my)) -// + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), ( -// mulfix(_2bx, (q0q2 + q1q3)) -// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) -// - mz)); -// s2 = - mulfix(_2q0, (2*q1q3 - _2q0q2 - ax)) -// + mulfix(_2q3, (2*q0q1 + _2q2q3 - ay)) -// - 4 * mulfix(q2, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) -// + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), ( -// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) -// + mulfix(_2bz, (q1q3 - q0q2)) -// - mx)) -// + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), ( -// mulfix(_2bx, (q1q2 - q0q3)) -// + mulfix(_2bz, (q0q1 + q2q3)) -// - my)) -// + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), ( -// mulfix(_2bx, (q0q2 + q1q3)) -// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) -// - mz)); -// s3 = mulfix(_2q1, (2*q1q3 - _2q0q2 - ax)) -// + mulfix(_2q2, (2*q0q1 + _2q2q3 - ay)) -// + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), ( -// mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) -// + mulfix(_2bz, (q1q3 - q0q2)) -// - mx)) -// + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), ( -// mulfix(_2bx, (q1q2 - q0q3)) -// + mulfix(_2bz, (q0q1 + q2q3)) -// - my)) -// + mulfix(mulfix(_2bx, q1), ( -// mulfix(_2bx, (q0q2 + q1q3)) -// + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) -// - mz)); -// recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude -// s0 = mulfix(s0, recipNorm); -// s1 = mulfix(s1, recipNorm); -// s2 = mulfix(s2, recipNorm); -// s3 = mulfix(s3, recipNorm); -// -// /* 2nd iter normalizaton */ -// // recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude -// // s0 = mulfix(s0, recipNorm); -// // s1 = mulfix(s1, recipNorm); -// // s2 = mulfix(s2, recipNorm); -// // s3 = mulfix(s3, recipNorm); -// -// // Apply feedback step -// qDot1 -= mulfix(beta, s0); -// qDot2 -= mulfix(beta, s1); -// qDot3 -= mulfix(beta, s2); -// qDot4 -= mulfix(beta, s3); -// } -// -// // Integrate rate of change of quaternion to yield quaternion -// q0 += qDot1 / sampleFreq; -// q1 += qDot2 / sampleFreq; -// q2 += qDot3 / sampleFreq; -// q3 += qDot4 / sampleFreq; -// -// // Normalise quaternion -// recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); -// // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", -// // (double)q0/FRAC_BASE, -// // (double)q1/FRAC_BASE, -// // (double)q2/FRAC_BASE, -// // (double)q3/FRAC_BASE, -// // (double)recipNorm/FRAC_BASE); -// q0 = mulfix(q0, recipNorm); -// q1 = mulfix(q1, recipNorm); -// q2 = mulfix(q2, recipNorm); -// q3 = mulfix(q3, recipNorm); -// *q0_ptr = q0; -// *q1_ptr = q1; -// *q2_ptr = q2; -// *q3_ptr = q3; -// -// // printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", -// // DISPLAY_INT(q0), DISPLAY_FRAC(q0), -// // DISPLAY_INT(q1), DISPLAY_FRAC(q1), -// // DISPLAY_INT(q2), DISPLAY_FRAC(q2), -// // DISPLAY_INT(q3), DISPLAY_FRAC(q3)); -// -// // /* 2nd iter normalization */ -// // recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); -// // q0 = mulfix(q0, recipNorm); -// // q1 = mulfix(q1, recipNorm); -// // q2 = mulfix(q2, recipNorm); -// // q3 = mulfix(q3, recipNorm); -//} -// -////--------------------------------------------------------------------------------------------------- -//// IMU algorithm update -// -//void -//MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, -// bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, -// int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { -// int32_t q0 = *q0_ptr; -// int32_t q1 = *q1_ptr; -// int32_t q2 = *q2_ptr; -// int32_t q3 = *q3_ptr; -// int32_t recipNorm; -// int32_t s0, s1, s2, s3; -// int32_t qDot1, qDot2, qDot3, qDot4; -// int32_t _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; -// -// // Rate of change of quaternion from gyroscope -// qDot1 = (- mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; -// qDot2 = ( mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; -// qDot3 = ( mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; -// qDot4 = ( mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; -// -// // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) -// if(!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { -// -// // Normalise accelerometer measurement -// recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); -// ax = mulfix(ax, recipNorm); -// ay = mulfix(ay, recipNorm); -// az = mulfix(az, recipNorm); -// -// // Auxiliary variables to avoid repeated arithmetic -// _2q0 = 2*q0; -// _2q1 = 2*q1; -// _2q2 = 2*q2; -// _2q3 = 2*q3; -// _4q0 = 4*q0; -// _4q1 = 4*q1; -// _4q2 = 4*q2; -// _8q1 = 8*q1; -// _8q2 = 8*q2; -// q0q0 = mulfix(q0, q0); -// q1q1 = mulfix(q1, q1); -// q2q2 = mulfix(q2, q2); -// q3q3 = mulfix(q3, q3); -// -// // Gradient decent algorithm corrective step -// s0 = mulfix(_4q0, q2q2) -// + mulfix(_2q2, ax) -// + mulfix(_4q0, q1q1) -// - mulfix(_2q1, ay); -// s1 = mulfix(_4q1, q3q3) -// - mulfix(_2q3, ax) -// + 4 * mulfix(q0q0, q1) -// - mulfix(_2q0, ay) -// - _4q1 -// + mulfix(_8q1, q1q1) -// + mulfix(_8q1, q2q2) -// + mulfix(_4q1, az); -// s2 = 4 * mulfix(q0q0, q2) -// + mulfix(_2q0, ax) -// + mulfix(_4q2, q3q3) -// - mulfix(_2q3, ay) -// - _4q2 -// + mulfix(_8q2, q1q1) -// + mulfix(_8q2, q2q2) -// + mulfix(_4q2, az); -// s3 = 4 * mulfix(q1q1, q3) -// - mulfix(_2q1, ax) -// + 4 * mulfix(q2q2, q3) -// - mulfix(_2q2, ay); -// recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude -// s0 = mulfix(s0, recipNorm); -// s1 = mulfix(s1, recipNorm); -// s2 = mulfix(s2, recipNorm); -// s3 = mulfix(s3, recipNorm); -// -// // Apply feedback step -// qDot1 -= mulfix(beta, s0); -// qDot2 -= mulfix(beta, s1); -// qDot3 -= mulfix(beta, s2); -// qDot4 -= mulfix(beta, s3); -// } -// -// // Integrate rate of change of quaternion to yield quaternion -// q0 += qDot1 / sampleFreq; -// q1 += qDot2 / sampleFreq; -// q2 += qDot3 / sampleFreq; -// q3 += qDot4 / sampleFreq; -// -// // Normalise quaternion -// recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); -// q0 = mulfix(q0, recipNorm); -// q1 = mulfix(q1, recipNorm); -// q2 = mulfix(q2, recipNorm); -// q3 = mulfix(q3, recipNorm); -// q0_ptr = &q0; -// q1_ptr = &q1; -// q2_ptr = &q2; -// q3_ptr = &q3; -//} \ No newline at end of file +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h index 95a15345a..717f21ab7 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h @@ -23,7 +23,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -#define FRAC_Q 10 +#define FRAC_Q 16 #define FRAC_BASE (1<>FRAC_Q) + (((uint32_t)_x)>>(sizeof(int32_t)*FRAC_Q-1))) From b4b6d70cf8df7c34458af9d56a129d1bd0bb2889 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 25 Feb 2025 11:33:05 +0000 Subject: [PATCH 143/213] fix phi issue Addresses #2. --- ...5cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt | 48 ++++ .../newton-irPass-LLVMIR-quantization.cpp | 213 +++++++++++------- 2 files changed, 184 insertions(+), 77 deletions(-) create mode 100644 analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt diff --git a/analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt b/analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt new file mode 100644 index 000000000..8cfde0c81 --- /dev/null +++ b/analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt @@ -0,0 +1,48 @@ + +changeset: 1736:525cd662c8513979e52d3f6b82fe08cb5ecf4f32 +char kNewtonVersion[] = "0.3-alpha-1736 (525cd662c8513979e52d3f6b82fe08cb5ecf4f32) (build 02-14-2025-14:01-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 8584fcfc6..a11b1f17b 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -354,7 +354,8 @@ bool shouldProcessFunction(Function &F) { "matrixAdd", "matrixSub", "pzero", - "qzero" + "qzero", + "__ieee754_exp" }; @@ -778,11 +779,20 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) Value * pointerOperand = loadInst->getPointerOperand(); Function * parentFunc = loadInst->getFunction(); +// if (isa(pointerOperand) || +// parentFunc->getName() == "MadgwickAHRSupdateIMU" || +// parentFunc->getName() == "MahonyAHRSupdateIMU" || +// parentFunc->getName() == "pzero" || +// parentFunc->getName() == "qzero") || +// parentFunc->getName() == "ieee754_exp" if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU" || parentFunc->getName() == "MahonyAHRSupdateIMU" || parentFunc->getName() == "pzero" || - parentFunc->getName() == "qzero") + parentFunc->getName() == "qzero" || + parentFunc->getName() == "__ieee754_exp") + // ... + { llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; @@ -990,6 +1000,54 @@ handleFCmp(Instruction * inInstruction, Type * quantizedType) } } +void handleSelect(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling Select\n"; + + // 检查操作数数量 + if (inInstruction->getNumOperands() < 3) { + llvm::errs() << "Error: Select instruction does not have 3 operands!\n"; + return; + } + IRBuilder<> Builder(inInstruction); + + // 获取 select 指令的三个操作数 + // 操作数 0 为条件值,操作数 1 为条件为 true 时的值,操作数 2 为条件为 false 时的值 + Value * condition = inInstruction->getOperand(0); + Value * opTrue = inInstruction->getOperand(1); + Value * opFalse = inInstruction->getOperand(2); + + llvm::errs() << "Original condition: " << *condition << "\n"; + llvm::errs() << "Original true branch: " << *opTrue << "\n"; + llvm::errs() << "Original false branch: " << *opFalse << "\n"; + + // 如果 true 分支的操作数是浮点常量,则进行量化转换 + if (ConstantFP * constFp = dyn_cast(opTrue)) + { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + opTrue = ConstantInt::get(quantizedType, quantizedValue); + } + + // 如果 false 分支的操作数是浮点常量,则进行量化转换 + if (ConstantFP * constFp = dyn_cast(opFalse)) + { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + opFalse = ConstantInt::get(quantizedType, quantizedValue); + } + + // 生成新的 select 指令,保持条件不变,使用转换后的 true 和 false 值 + Value * newInst = Builder.CreateSelect(condition, opTrue, opFalse); + llvm::errs() << "Created new select instruction: " << *newInst << "\n"; + + // 将原来的 select 指令替换成新的 select 指令 + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling Select\n"; +} + void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { @@ -1381,81 +1439,71 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { llvm::errs() << "Finished handling FDiv\n"; } +void handlePhi(Instruction *inInstruction, Type *quantizedType) +{ + llvm::errs() << "Handling PHI\n"; + PHINode *phi = dyn_cast(inInstruction); + if (!phi) { + llvm::errs() << "Error: Instruction is not a PHI node.\n"; + return; + } -//void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { -// llvm::errs() << "Handling FDiv\n"; -// llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; -// IRBuilder<> Builder(llvmIrInstruction); -// -// // 如果已有 quantized 元数据,则跳过处理 -// if (llvmIrInstruction->getMetadata("quantized")) { -// llvm::errs() << "Skipping already quantized instruction.\n"; -// return; -// } -// -// // 获取左右操作数 -// Value *lhs = llvmIrInstruction->getOperand(0); -// Value *rhs = llvmIrInstruction->getOperand(1); -// -// llvm::errs() << "LHS: " << *lhs << "\n"; -// llvm::errs() << "RHS: " << *rhs << "\n"; -// -// // 判断操作数是否为浮点型(float 或 double) -// bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); -// bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); -// -// // 检查常量:如果其中一个操作数是常量FP,则尝试简化 -// if (auto rhsConst = dyn_cast(rhs)) { -// if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) -// return; -// } -// if (auto lhsConst = dyn_cast(lhs)) { -// if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) -// return; -// } -// -// // 如果任一操作数是浮点常量,则通过 simplifyConstant 将其转换为固定点整数 -// if (isa(lhs)) { -// llvm::errs() << "LHS is a floating-point constant, handling it with simplifyConstant\n"; -// simplifyConstant(llvmIrInstruction, quantizedType); -// lhs = llvmIrInstruction->getOperand(0); -// lhsIsFloat = false; // 更新状态,现已转换为固定点整数 -// } -// if (isa(rhs)) { -// llvm::errs() << "RHS is a floating-point constant, handling it with simplifyConstant\n"; -// simplifyConstant(llvmIrInstruction, quantizedType); -// rhs = llvmIrInstruction->getOperand(1); -// rhsIsFloat = false; -// } -// -// // 如果任一操作数是整数常量,则直接用整数除法 -// if (isa(lhs) || isa(rhs)) { -// llvm::errs() << "One of the operands is an integer constant, using division directly\n"; -// Value *newInst = Builder.CreateSDiv(lhs, rhs); -// llvmIrInstruction->replaceAllUsesWith(newInst); -// llvmIrInstruction->eraseFromParent(); -// return; -// } -// -// // 如果任一操作数仍为浮点,则转换为固定点整数 -// if (lhsIsFloat) { -// lhs = Builder.CreateFPToSI(lhs, quantizedType); -// llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; -// } -// if (rhsIsFloat) { -// rhs = Builder.CreateFPToSI(rhs, quantizedType); -// llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; -// } -// -// // 此时,lhs 和 rhs 均为整数(固定点表示),根据要求: -// // 除法过程中不需要左移(即不乘 FRAC_BASE),也不在除法后做位移 -// // 所以直接进行整数除法即可 -// llvm::Value *newInst = Builder.CreateSDiv(lhs, rhs); -// llvmIrInstruction->replaceAllUsesWith(newInst); -// llvmIrInstruction->eraseFromParent(); -// -// llvm::errs() << "Finished handling FDiv\n"; -//} + // 判断是否是指针类型的 PHI 节点 + bool isPtr = phi->getType()->isPointerTy(); + unsigned pointerAddr = 0; + if (isPtr) + pointerAddr = phi->getType()->getPointerAddressSpace(); + + // 新 PHI 节点的类型 + // 如果原来是 pointer 类型,则新类型为 quantizedType->getPointerTo(pointerAddr) + // 否则直接使用 quantizedType + Type *newPhiType = isPtr ? quantizedType->getPointerTo(pointerAddr) : quantizedType; + + // 创建新的 PHI 节点,新节点插入在原 PHI 节点之前 + PHINode *newPhi = PHINode::Create(newPhiType, phi->getNumIncomingValues(), + phi->getName() + ".quantized", phi); + + // 遍历所有入边 + for (unsigned i = 0, e = phi->getNumIncomingValues(); i < e; i++) { + Value *incoming = phi->getIncomingValue(i); + BasicBlock *incomingBB = phi->getIncomingBlock(i); + Value *newVal = nullptr; + + llvm::errs() << "Original PHI incoming value: " << *incoming << "\n"; + + if (!isPtr) { + // 针对非指针情况:如果是浮点常量,直接量化;如果是浮点值,则插入 FPToSI 转换 + if (ConstantFP *constFp = dyn_cast(incoming)) { + float fpVal = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(fpVal * FRAC_BASE)); + newVal = llvm::ConstantInt::get(quantizedType, quantizedValue); + llvm::errs() << "Converted constant: " << *newVal << "\n"; + } else if (incoming->getType()->isFloatingPointTy()) { + IRBuilder<> builder(incomingBB->getTerminator()); + newVal = builder.CreateFPToSI(incoming, quantizedType, incoming->getName() + ".to_int"); + llvm::errs() << "Inserted conversion: " << *newVal << "\n"; + } else { + newVal = incoming; + } + } else { + // 针对指针类型:要求新入边值类型为 newPhiType + if (incoming->getType() != newPhiType) { + IRBuilder<> builder(incomingBB->getTerminator()); + newVal = builder.CreateBitCast(incoming, newPhiType, incoming->getName() + ".cast"); + llvm::errs() << "BitCast pointer: " << *newVal << "\n"; + } else { + newVal = incoming; + } + } + newPhi->addIncoming(newVal, incomingBB); + } + + // 替换所有使用并删除原 PHI 节点 + phi->replaceAllUsesWith(newPhi); + phi->eraseFromParent(); + + llvm::errs() << "Finished handling PHI: " << *newPhi << "\n"; +} @@ -2260,7 +2308,9 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect break; case Instruction::PHI: { - setQuantizedType(llvmIrInstruction, quantizedType); +// setQuantizedType(llvmIrInstruction, quantizedType); + llvm::errs() << "handle phi " << *llvmIrInstruction << "\n"; + handlePhi(llvmIrInstruction, quantizedType); } break; @@ -2344,7 +2394,16 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect case Instruction::Switch: case Instruction::Br: + { + llvm::errs() << "Skipping Br instruction: " << *llvmIrInstruction << "\n"; + break; + } case Instruction::Select: + { + handleSelect(llvmIrInstruction, quantizedType); + break; + + } case Instruction::IndirectBr: case Instruction::Invoke: case Instruction::Resume: From 3ea3381f0e5bba490bdbbfc408a09f136cc9a272 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 26 Feb 2025 13:02:56 +0000 Subject: [PATCH 144/213] fix fmul constant issue Addresses #2. --- ...7d5ef1e76af93962500380ad008ec13141c86d.txt | 48 +++++++++++++++++++ 1 file changed, 48 insertions(+) create mode 100644 analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt diff --git a/analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt b/analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt new file mode 100644 index 000000000..d03925cb9 --- /dev/null +++ b/analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt @@ -0,0 +1,48 @@ + +changeset: 1737:097d5ef1e76af93962500380ad008ec13141c86d +char kNewtonVersion[] = "0.3-alpha-1737 (097d5ef1e76af93962500380ad008ec13141c86d) (build 02-25-2025-11:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 7869325b7fdaab528356887d1d0eb5b0291012db Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 26 Feb 2025 21:49:51 +0000 Subject: [PATCH 145/213] fix internal constant replacement issue Addresses #2. --- ...b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt | 48 ++ .../newton-irPass-LLVMIR-quantization.cpp | 579 +++++++++++------- 2 files changed, 398 insertions(+), 229 deletions(-) create mode 100644 analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt diff --git a/analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt b/analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt new file mode 100644 index 000000000..c0f2e7033 --- /dev/null +++ b/analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt @@ -0,0 +1,48 @@ + +changeset: 1738:b4b6d70cf8df7c34458af9d56a129d1bd0bb2889 +char kNewtonVersion[] = "0.3-alpha-1738 (b4b6d70cf8df7c34458af9d56a129d1bd0bb2889) (build 02-26-2025-13:02-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a11b1f17b..38d519758 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -128,10 +128,8 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +// llvm::Function * +// createFixMul(Module * irModule, Type * quantizedType, std::vector & functionsToInsert) //{ // llvm::errs() << "Entering createFixMul\n"; // @@ -187,7 +185,7 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vectorgetName() << "\n"; // return func; -//} +// } // llvm::Function * @@ -336,27 +334,31 @@ eraseOldFunctions() llvm::errs() << "Exiting eraseOldFunctions\n"; } -bool isWhitelistedGlobal(const std::string &globalName) { +bool +isWhitelistedGlobal(const std::string & globalName) +{ // Define the whitelist of global variables - static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz"}; + static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero"}; return whitelist.find(globalName) != whitelist.end(); } -bool shouldProcessFunction(Function &F) { +bool +shouldProcessFunction(Function & F) +{ // List of function names to process static const std::set targetFunctions = { "sensfusion6UpdateQImpl", "MadgwickAHRSupdate", "MadgwickAHRSupdateIMU", -// "__kernel_sin", -// "__kernel_cos", "matrixMul", "matrixAdd", "matrixSub", "pzero", "qzero", - "__ieee754_exp" - + "pone", + "qone" +// "__ieee754_exp", +// "__ieee754_log", }; // Check if the function name is in the set @@ -364,37 +366,42 @@ bool shouldProcessFunction(Function &F) { } // Helper to create or retrieve the quantized global variable -GlobalVariable *getOrCreateQuantizedGlobal(Module *module, GlobalVariable &globalVar, Type *quantizedType) { +GlobalVariable * +getOrCreateQuantizedGlobal(Module * module, GlobalVariable & globalVar, Type * quantizedType) +{ std::string quantizedName = globalVar.getName().str() + "_quantized"; // Check if the quantized version already exists - if (GlobalVariable *existingGlobal = module->getNamedGlobal(quantizedName)) { + if (GlobalVariable * existingGlobal = module->getNamedGlobal(quantizedName)) + { llvm::errs() << "Quantized global already exists: " << quantizedName << "\n"; return existingGlobal; } -// // Calculate initializer for the quantized global -// llvm::Constant *initializer = nullptr; -// if (llvm::Constant *init = globalVar.getInitializer()) { -// if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { -// double value = constFp->getValueAPF().convertToDouble(); -// int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); -// initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); -// } + // // Calculate initializer for the quantized global + // llvm::Constant *initializer = nullptr; + // if (llvm::Constant *init = globalVar.getInitializer()) { + // if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { + // double value = constFp->getValueAPF().convertToDouble(); + // int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); + // initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); + // } // } // Calculate initializer for the quantized global - llvm::Constant *initializer = nullptr; - if (llvm::Constant *init = globalVar.getInitializer()) { - if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { - double value = constFp->getValueAPF().convertToDouble(); - int64_t quantizedValue = static_cast(round((value * FRAC_BASE))); - initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); - } + llvm::Constant * initializer = nullptr; + if (llvm::Constant * init = globalVar.getInitializer()) + { + if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) + { + double value = constFp->getValueAPF().convertToDouble(); + int64_t quantizedValue = static_cast(round((value * FRAC_BASE))); + initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); } + } // Create the quantized global variable - GlobalVariable *newGlobalVar = new GlobalVariable( + GlobalVariable * newGlobalVar = new GlobalVariable( *module, quantizedType, globalVar.isConstant(), @@ -403,7 +410,8 @@ GlobalVariable *getOrCreateQuantizedGlobal(Module *module, GlobalVariable &globa quantizedName); // Set alignment based on bit width - switch (BIT_WIDTH) { + switch (BIT_WIDTH) + { case 16: newGlobalVar->setAlignment(llvm::MaybeAlign(2)); break; @@ -420,75 +428,105 @@ GlobalVariable *getOrCreateQuantizedGlobal(Module *module, GlobalVariable &globa return newGlobalVar; } - // Helper to create or retrieve the quantized internal constant -GlobalVariable *getOrCreateQuantizedConstant(Module *module, GlobalVariable &origConst, Type *quantizedType) { +// Helper to create or retrieve the quantized internal constant +llvm::GlobalVariable * +getOrCreateQuantizedConstant(llvm::Module *module, + llvm::GlobalVariable &origConst, + llvm::Type *quantizedType) +{ std::string quantizedName = origConst.getName().str() + "_quantized"; - // Check if the quantized version already exists - if (GlobalVariable *existingConst = module->getNamedGlobal(quantizedName)) { - errs() << "Quantized internal constant already exists: " << quantizedName << "\n"; + // 检查量化版本是否已存在 + if (llvm::GlobalVariable *existingConst = module->getNamedGlobal(quantizedName)) + { + llvm::errs() << "Quantized internal constant already exists: " << quantizedName << "\n"; return existingConst; } - // Ensure the original constant has an initializer - if (!origConst.hasInitializer()) { - errs() << "Skipping quantization: constant has no initializer: " << origConst.getName() << "\n"; + // 确保原常量有 initializer + if (!origConst.hasInitializer()) + { + llvm::errs() << "Skipping quantization: constant has no initializer: " << origConst.getName() << "\n"; return nullptr; } llvm::Constant *init = origConst.getInitializer(); - ArrayType *origArrayType = dyn_cast(origConst.getType()->getElementType()); + llvm::ArrayType *origArrayType = llvm::dyn_cast(origConst.getType()->getElementType()); - // Ensure the global variable is an array of floating-point values - if (!origArrayType || (!origArrayType->getArrayElementType()->isFloatTy() && - !origArrayType->getArrayElementType()->isDoubleTy())) { - errs() << "Skipping non-float internal constant: " << origConst.getName() << "\n"; + // 只处理浮点数组 + if (!origArrayType || + (!origArrayType->getArrayElementType()->isFloatTy() && + !origArrayType->getArrayElementType()->isDoubleTy())) + { + llvm::errs() << "Skipping non-float internal constant: " << origConst.getName() << "\n"; return nullptr; } - errs() << "Quantizing internal constant: " << origConst.getName() << "\n"; + llvm::errs() << "Quantizing internal constant: " << origConst.getName() << "\n"; std::vector quantizedValues; - Type *intType = Type::getInt32Ty(module->getContext()); // Use int32 for quantized values + // 使用 int32 表示量化后的值 + llvm::Type *intType = llvm::Type::getInt32Ty(module->getContext()); - // Iterate over array elements and quantize each floating-point value - for (unsigned i = 0; i < origArrayType->getNumElements(); ++i) { - llvm::ConstantFP *fpVal = dyn_cast(init->getAggregateElement(i)); - if (!fpVal) continue; + // 定义阈值 epsilon,调整为 1e-9 以便对极小值直接量化为0 + const double epsilon = 1e-9; - double floatValue = fpVal->getValueAPF().convertToDouble(); - int quantizedValue = static_cast(round(floatValue * FRAC_BASE)); + // 遍历数组元素进行量化 + for (unsigned i = 0; i < origArrayType->getNumElements(); ++i) + { + llvm::ConstantFP *fpVal = llvm::dyn_cast(init->getAggregateElement(i)); + if (!fpVal) + continue; + double floatValue = fpVal->getValueAPF().convertToDouble(); + int quantizedValue = 0; + if (fabs(floatValue) < epsilon) + { + llvm::errs() << "Quantizing value below epsilon: " << floatValue << "\n"; + quantizedValue = 0; + } + else + { + quantizedValue = static_cast(round(floatValue * FRAC_BASE)); + } quantizedValues.push_back(llvm::ConstantInt::get(intType, quantizedValue)); } - // Create a new quantized array type - ArrayType *quantizedArrayType = ArrayType::get(intType, quantizedValues.size()); + // 创建新的数组类型和 initializer + llvm::ArrayType *quantizedArrayType = llvm::ArrayType::get(intType, quantizedValues.size()); llvm::Constant *newInit = llvm::ConstantArray::get(quantizedArrayType, quantizedValues); - // Create the new quantized GlobalVariable - GlobalVariable *quantizedConst = new GlobalVariable( - *module, quantizedArrayType, true, origConst.getLinkage(), - newInit, quantizedName); - - // Set appropriate alignment based on bit width - quantizedConst->setAlignment(llvm::MaybeAlign(4)); // Align to 4 bytes for int32 + // 创建新的全局变量 + llvm::GlobalVariable *quantizedConst = new llvm::GlobalVariable( + *module, + quantizedArrayType, + true, + origConst.getLinkage(), + newInit, + quantizedName); + // 设置对齐(这里使用4字节对齐,适用于 int32) + quantizedConst->setAlignment(llvm::MaybeAlign(4)); quantizedConst->setDSOLocal(true); - errs() << "Created quantized internal constant: " << quantizedName << "\n"; + llvm::errs() << "Created quantized internal constant: " << quantizedName << "\n"; return quantizedConst; } // Helper to replace uses of the original global in whitelisted functions -void replaceUsesInWhitelistedFunctions(GlobalVariable &originalGlobal, GlobalVariable &quantizedGlobal) { - for (auto it = originalGlobal.use_begin(), end = originalGlobal.use_end(); it != end;) { - Use &use = *it++; - if (Instruction *inst = dyn_cast(use.getUser())) { - Function *parentFunc = inst->getFunction(); - if (parentFunc && shouldProcessFunction(*parentFunc)) { +void +replaceUsesInWhitelistedFunctions(GlobalVariable & originalGlobal, GlobalVariable & quantizedGlobal) +{ + for (auto it = originalGlobal.use_begin(), end = originalGlobal.use_end(); it != end;) + { + Use & use = *it++; + if (Instruction * inst = dyn_cast(use.getUser())) + { + Function * parentFunc = inst->getFunction(); + if (parentFunc && shouldProcessFunction(*parentFunc)) + { llvm::errs() << "Replacing use of " << originalGlobal.getName() << " in function: " << parentFunc->getName() << "\n"; use.set(&quantizedGlobal); @@ -496,21 +534,26 @@ void replaceUsesInWhitelistedFunctions(GlobalVariable &originalGlobal, GlobalVar } } } -void updateGlobalVariables(Module *module, Type *quantizedType) { +void +updateGlobalVariables(Module * module, Type * quantizedType) +{ llvm::errs() << "Updating global variables\n"; - for (GlobalVariable &globalVar : module->globals()) { + for (GlobalVariable & globalVar : module->globals()) + { std::string globalName = globalVar.getName().str(); // Skip non-whitelisted globals - if (!isWhitelistedGlobal(globalName)) { + if (!isWhitelistedGlobal(globalName)) + { llvm::errs() << "Skipping global variable not in whitelist: " << globalName << "\n"; continue; } // Process only float or double global variables if (!globalVar.getType()->getElementType()->isFloatTy() && - !globalVar.getType()->getElementType()->isDoubleTy()) { + !globalVar.getType()->getElementType()->isDoubleTy()) + { llvm::errs() << "Skipping non-float global variable: " << globalName << "\n"; continue; } @@ -518,7 +561,7 @@ void updateGlobalVariables(Module *module, Type *quantizedType) { llvm::errs() << "Quantizing global variable: " << globalName << "\n"; // Create or retrieve the quantized version of the global variable - GlobalVariable *quantizedGlobal = getOrCreateQuantizedGlobal(module, globalVar, quantizedType); + GlobalVariable * quantizedGlobal = getOrCreateQuantizedGlobal(module, globalVar, quantizedType); // Replace uses of the original global in whitelisted functions replaceUsesInWhitelistedFunctions(globalVar, *quantizedGlobal); @@ -529,112 +572,62 @@ void updateGlobalVariables(Module *module, Type *quantizedType) { } // 辅助函数:对于 ConstantExpr 类型的使用,如果它是 GEP,则尝试构造新的 constant GEP 表达式 -static void handleConstantExprUse(llvm::ConstantExpr *constExpr, - llvm::GlobalVariable &origConst, - llvm::GlobalVariable &quantizedConst) { +static void +handleConstantExprUse(llvm::ConstantExpr * constExpr, + llvm::GlobalVariable & origConst, + llvm::GlobalVariable & quantizedConst) +{ // 如果 constant-expression 是 getelementptr,则重建一个新的 constant-expression - if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) { + if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) + { llvm::SmallVector Indices; // 从操作数1开始(operand0 是指针) - for (unsigned i = 1, e = constExpr->getNumOperands(); i < e; ++i) { - if (llvm::Constant *C = llvm::dyn_cast(constExpr->getOperand(i))) + for (unsigned i = 1, e = constExpr->getNumOperands(); i < e; ++i) + { + if (llvm::Constant * C = llvm::dyn_cast(constExpr->getOperand(i))) Indices.push_back(C); } // 直接使用量化后的全局变量,不做 bitcast - llvm::Constant *newGEP = llvm::ConstantExpr::getGetElementPtr( + llvm::Constant * newGEP = llvm::ConstantExpr::getGetElementPtr( quantizedConst.getType()->getPointerElementType(), &quantizedConst, Indices); // 用新构造的 constant 替换所有对该 constant-expression 的使用 constExpr->replaceAllUsesWith(newGEP); llvm::errs() << "Replaced constant GEP for " << origConst.getName() << "\n"; - } else { + } + else + { // 对于非 GEP 的 constant-expression,则转换为指令 - llvm::Instruction *insertPt = nullptr; - for (llvm::Use &U : constExpr->uses()) { - if (llvm::Instruction *inst = llvm::dyn_cast(U.getUser())) { + llvm::Instruction * insertPt = nullptr; + for (llvm::Use & U : constExpr->uses()) + { + if (llvm::Instruction * inst = llvm::dyn_cast(U.getUser())) + { insertPt = inst; break; } } - if (insertPt) { - llvm::Instruction *newInst = constExpr->getAsInstruction(); + if (insertPt) + { + llvm::Instruction * newInst = constExpr->getAsInstruction(); newInst->insertBefore(insertPt); constExpr->replaceAllUsesWith(newInst); llvm::errs() << "Converted constant expr to instruction for " << origConst.getName() << "\n"; } } } -//static void handleConstantExprUse(llvm::ConstantExpr *constExpr, -// llvm::GlobalVariable &origConst, -// llvm::GlobalVariable &quantizedConst) { -// // 如果 constant-expression 是 getelementptr,则重建一个新的 constant-expression -// if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) { -// llvm::SmallVector Indices; -// // 从操作数1开始(operand0 是指针) -// for (unsigned i = 1, e = constExpr->getNumOperands(); i < e; ++i) { -// if (llvm::Constant *C = llvm::dyn_cast(constExpr->getOperand(i))) -// Indices.push_back(C); -// } -// // 构造 constant bitcast,将量化后的全局变量从 [N x i32]* 转换为原全局变量的类型(例如 [N x double]*) -// llvm::Constant *bitcasted = llvm::ConstantExpr::getBitCast(&quantizedConst, origConst.getType()); -// // 构造新的 constant GEP 表达式 -// llvm::Constant *newGEP = llvm::ConstantExpr::getGetElementPtr( -// origConst.getType()->getPointerElementType(), bitcasted, Indices); -// // 用新构造的 constant 替换所有对该 constant-expression 的使用 -// constExpr->replaceAllUsesWith(newGEP); -// llvm::errs() << "Replaced constant GEP for " << origConst.getName() << "\n"; -// } else { -// // 对于非 GEP 的 constant-expression,则转换为指令 -// llvm::Instruction *insertPt = nullptr; -// for (llvm::Use &U : constExpr->uses()) { -// if (llvm::Instruction *inst = llvm::dyn_cast(U.getUser())) { -// insertPt = inst; -// break; -// } -// } -// if (insertPt) { -// llvm::Instruction *newInst = constExpr->getAsInstruction(); -// newInst->insertBefore(insertPt); -// constExpr->replaceAllUsesWith(newInst); -// llvm::errs() << "Converted constant expr to instruction for " << origConst.getName() << "\n"; -// } -// } -//} - - -//// 辅助函数:处理函数内部的 GEP 指令(非 constant-expression),即将计算结果转换为常量表达式等价形式 -//static void handleGEPInstruction(GetElementPtrInst *gep, -// GlobalVariable &origConst, -// GlobalVariable &quantizedConst) { -// IRBuilder<> builder(gep); -// // 将量化后的全局变量 bitcast 成原全局变量的类型(例如 [6 x double]*) -// Value *quantizedBitcast = builder.CreateBitCast(&quantizedConst, origConst.getType(), -// quantizedConst.getName() + ".bitcast"); -// // 收集 GEP 的索引(保持原有索引不变) -// SmallVector Indices; -// for (Value *idx : gep->indices()) -// Indices.push_back(idx); -// // 重建 GEP:注意,这里我们希望得到的结果仍然是 double*(与原来的 GEP 相同) -// Value *newGEP = builder.CreateGEP(origConst.getType()->getPointerElementType(), -// quantizedBitcast, Indices, -// gep->getName() + ".quantized_gep"); -// // 如果新得到的 newGEP 类型与原来的不一致,使用 bitcast 调整回来 -// Value *castedGEP = builder.CreateBitCast(newGEP, gep->getType(), -// gep->getName() + ".casted"); -// gep->replaceAllUsesWith(castedGEP); -// gep->eraseFromParent(); -// errs() << "Replaced GEP instruction for " << origConst.getName() << "\n"; -//} -static void handleGEPInstruction(llvm::GetElementPtrInst *gep, - llvm::GlobalVariable &origConst, - llvm::GlobalVariable &quantizedConst) { +static void +handleGEPInstruction(llvm::GetElementPtrInst * gep, + llvm::GlobalVariable & origConst, + llvm::GlobalVariable & quantizedConst) +{ llvm::IRBuilder<> builder(gep); // 直接使用量化后的全局变量,无需 bitcast - llvm::SmallVector Indices; - for (llvm::Value *idx : gep->indices()) + llvm::SmallVector Indices; + for (llvm::Value * idx : gep->indices()) Indices.push_back(idx); // 重建 GEP:注意这里使用 quantizedConst 的类型(例如 [6 x i32]),因此 newGEP 的类型为 i32* - llvm::Value *newGEP = builder.CreateGEP(quantizedConst.getType()->getPointerElementType(), + llvm::Value * newGEP = builder.CreateGEP(quantizedConst.getType()->getPointerElementType(), &quantizedConst, Indices, gep->getName() + ".quantized_gep"); // newGEP 应该直接就是你期望的类型,即 i32* @@ -644,74 +637,147 @@ static void handleGEPInstruction(llvm::GetElementPtrInst *gep, } // 主函数:遍历 origConst 的所有使用,在白名单函数中替换为量化后的全局变量 -void replaceInternalConstantUses(Module *module, - GlobalVariable &origConst, - GlobalVariable &quantizedConst) { - std::vector usesToReplace; - - for (auto it = origConst.use_begin(), end = origConst.use_end(); it != end; ) { - Use &use = *it++; - Value *user = use.getUser(); - - // 如果用户是 ConstantExpr - if (ConstantExpr *constExpr = dyn_cast(user)) { - handleConstantExprUse(constExpr, origConst, quantizedConst); +//void +//replaceInternalConstantUses(Module * module, +// GlobalVariable & origConst, +// GlobalVariable & quantizedConst) +//{ +// std::vector usesToReplace; +// +// for (auto it = origConst.use_begin(), end = origConst.use_end(); it != end;) +// { +// Use & use = *it++; +// Value * user = use.getUser(); +// +// // 如果用户是 ConstantExpr +// if (ConstantExpr * constExpr = dyn_cast(user)) +// { +// handleConstantExprUse(constExpr, origConst, quantizedConst); +// continue; +// } +// +// // 如果用户是指令,且所在函数在白名单中 +// Instruction * inst = dyn_cast(user); +// if (!inst || !inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) +// continue; +// +// // 如果该指令是 GEP,并且结果类型为 double*,则进行替换 +// if (GetElementPtrInst * gep = dyn_cast(inst)) +// { +// if (gep->getResultElementType()->isDoubleTy()) +// { +// handleGEPInstruction(gep, origConst, quantizedConst); +// continue; // 该 GEP 指令已经被替换,不需要再添加到 usesToReplace +// } +// } +// // 其他情况,将该 use 加入待统一替换列表 +// usesToReplace.push_back(&use); +// } +// +// // 对剩余未处理的使用,统一替换为量化后的全局变量 +// for (Use * use : usesToReplace) +// use->set(&quantizedConst); +// +// errs() << "Replaced all uses of " << origConst.getName() +// << " with " << quantizedConst.getName() +// << " in whitelisted functions.\n"; +//} + +void +replaceInternalConstantUses(llvm::Module *module, + llvm::GlobalVariable &origConst, + llvm::GlobalVariable &quantizedConst) +{ + std::vector usesToReplace; + + for (auto it = origConst.use_begin(), end = origConst.use_end(); it != end; ) + { + llvm::Use &use = *it++; + llvm::Value *user = use.getUser(); + + // 如果用户是 ConstantExpr,我们需要检查其最终使用者是否位于白名单函数中 + if (llvm::ConstantExpr *constExpr = llvm::dyn_cast(user)) + { + bool replace = false; + // 遍历 constant expression 的所有使用 + for (auto &CEUse : constExpr->uses()) + { + if (llvm::Instruction *inst = llvm::dyn_cast(CEUse.getUser())) + { + if (inst->getFunction() && shouldProcessFunction(*inst->getFunction())) + { + replace = true; + break; + } + } + } + if (replace) + { + handleConstantExprUse(constExpr, origConst, quantizedConst); + } + // 不论是否替换,继续处理下一个 use continue; } // 如果用户是指令,且所在函数在白名单中 - Instruction *inst = dyn_cast(user); - if (!inst || !inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) - continue; + if (llvm::Instruction *inst = llvm::dyn_cast(user)) + { + if (!inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) + continue; - // 如果该指令是 GEP,并且结果类型为 double*,则进行替换 - if (GetElementPtrInst *gep = dyn_cast(inst)) { - if (gep->getResultElementType()->isDoubleTy()) { - handleGEPInstruction(gep, origConst, quantizedConst); - continue; // 该 GEP 指令已经被替换,不需要再添加到 usesToReplace + // 如果该指令是 GEP 并且结果类型为 double*,则特殊处理 + if (llvm::GetElementPtrInst *gep = llvm::dyn_cast(inst)) + { + if (gep->getResultElementType()->isDoubleTy()) + { + handleGEPInstruction(gep, origConst, quantizedConst); + continue; // 该 GEP 指令已经被替换,不再加入 usesToReplace + } } + // 否则,将该 use 加入待统一替换列表 + usesToReplace.push_back(&use); } - // 其他情况,将该 use 加入待统一替换列表 - usesToReplace.push_back(&use); } // 对剩余未处理的使用,统一替换为量化后的全局变量 - for (Use *use : usesToReplace) + for (llvm::Use *use : usesToReplace) use->set(&quantizedConst); - errs() << "Replaced all uses of " << origConst.getName() - << " with " << quantizedConst.getName() - << " in whitelisted functions.\n"; + llvm::errs() << "Replaced all uses of " << origConst.getName() + << " with " << quantizedConst.getName() + << " in whitelisted functions.\n"; } - - - - -void updateInternalConstants(Module *module, Type *quantizedType) { +void +updateInternalConstants(Module * module, Type * quantizedType) +{ llvm::errs() << "Updating internal constants\n"; - for (GlobalVariable &globalVar : module->globals()) { + for (GlobalVariable & globalVar : module->globals()) + { std::string globalName = globalVar.getName().str(); - if (!globalVar.isConstant() || !globalVar.hasInitializer()) { + if (!globalVar.isConstant() || !globalVar.hasInitializer()) + { llvm::errs() << "Skipping non-constant or uninitialized global: " << globalName << "\n"; continue; } - Type *elementType = globalVar.getType()->getElementType(); + Type * elementType = globalVar.getType()->getElementType(); if (!elementType->isArrayTy() || (!elementType->getArrayElementType()->isFloatTy() && - !elementType->getArrayElementType()->isDoubleTy())) { + !elementType->getArrayElementType()->isDoubleTy())) + { llvm::errs() << "Skipping non-float internal constant: " << globalName << "\n"; continue; } llvm::errs() << "Quantizing internal constant: " << globalName << "\n"; - GlobalVariable *quantizedConst = getOrCreateQuantizedConstant(module, globalVar, quantizedType); + GlobalVariable * quantizedConst = getOrCreateQuantizedConstant(module, globalVar, quantizedType); - if (quantizedConst) { + if (quantizedConst) + { replaceInternalConstantUses(module, globalVar, *quantizedConst); } @@ -719,10 +785,6 @@ void updateInternalConstants(Module *module, Type *quantizedType) { } } - - - - void quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { @@ -740,19 +802,20 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType llvm::errs() << "Replaced load with quantized integer value.\n"; } -void quantizeMatrixFloat(LoadInst *loadInst, IRBuilder<> &Builder, Type *quantizedType, Type *loadedType) +void +quantizeMatrixFloat(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { // Get the pointer operand of the load instruction - Value *pointerOperand = loadInst->getPointerOperand(); - Type *pointerElementType = pointerOperand->getType()->getPointerElementType(); + Value * pointerOperand = loadInst->getPointerOperand(); + Type * pointerElementType = pointerOperand->getType()->getPointerElementType(); if (pointerElementType->isFloatingPointTy()) { llvm::errs() << "Quantizing load from local float pointer: " << *pointerOperand << "\n"; - Value *loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); - Value *scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled"); - Value *quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized"); + Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); + Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled"); + Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized"); loadInst->replaceAllUsesWith(quantizedValue); loadInst->eraseFromParent(); @@ -779,22 +842,24 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) Value * pointerOperand = loadInst->getPointerOperand(); Function * parentFunc = loadInst->getFunction(); -// if (isa(pointerOperand) || -// parentFunc->getName() == "MadgwickAHRSupdateIMU" || -// parentFunc->getName() == "MahonyAHRSupdateIMU" || -// parentFunc->getName() == "pzero" || -// parentFunc->getName() == "qzero") || -// parentFunc->getName() == "ieee754_exp" + // if (isa(pointerOperand) || + // parentFunc->getName() == "MadgwickAHRSupdateIMU" || + // parentFunc->getName() == "MahonyAHRSupdateIMU" || + // parentFunc->getName() == "pzero" || + // parentFunc->getName() == "qzero") || + // parentFunc->getName() == "ieee754_exp" if (isa(pointerOperand) || parentFunc->getName() == "MadgwickAHRSupdateIMU" || parentFunc->getName() == "MahonyAHRSupdateIMU" || parentFunc->getName() == "pzero" || parentFunc->getName() == "qzero" || - parentFunc->getName() == "__ieee754_exp") - // ... + parentFunc->getName() == "pone" || + parentFunc->getName() == "qone" || + parentFunc->getName() == "__ieee754_exp"|| + parentFunc->getName() == "__ieee754_log") + // ... { - llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); llvm::errs() << "New load instruction: " << *newLoadInst << "\n"; @@ -803,17 +868,17 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) } // Quantize local pointers -//#ifndef IS_MATRIX -// else if (!isa(pointerOperand)) -// { -// quantizePointer(loadInst, Builder, quantizedType, loadedType); -// } -//#else -// else if (!isa(pointerOperand)) -// { -// quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); -// } -//#endif + // #ifndef IS_MATRIX + // else if (!isa(pointerOperand)) + // { + // quantizePointer(loadInst, Builder, quantizedType, loadedType); + // } + // #else + // else if (!isa(pointerOperand)) + // { + // quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); + // } + // #endif #ifdef IS_POINTER else if (!isa(pointerOperand)) @@ -827,7 +892,6 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); } #endif - } } } @@ -1000,12 +1064,14 @@ handleFCmp(Instruction * inInstruction, Type * quantizedType) } } -void handleSelect(Instruction * inInstruction, Type * quantizedType) +void +handleSelect(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Handling Select\n"; // 检查操作数数量 - if (inInstruction->getNumOperands() < 3) { + if (inInstruction->getNumOperands() < 3) + { llvm::errs() << "Error: Select instruction does not have 3 operands!\n"; return; } @@ -1014,8 +1080,8 @@ void handleSelect(Instruction * inInstruction, Type * quantizedType) // 获取 select 指令的三个操作数 // 操作数 0 为条件值,操作数 1 为条件为 true 时的值,操作数 2 为条件为 false 时的值 Value * condition = inInstruction->getOperand(0); - Value * opTrue = inInstruction->getOperand(1); - Value * opFalse = inInstruction->getOperand(2); + Value * opTrue = inInstruction->getOperand(1); + Value * opFalse = inInstruction->getOperand(2); llvm::errs() << "Original condition: " << *condition << "\n"; llvm::errs() << "Original true branch: " << *opTrue << "\n"; @@ -1024,17 +1090,17 @@ void handleSelect(Instruction * inInstruction, Type * quantizedType) // 如果 true 分支的操作数是浮点常量,则进行量化转换 if (ConstantFP * constFp = dyn_cast(opTrue)) { - float constValue = constFp->getValueAPF().convertToFloat(); + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - opTrue = ConstantInt::get(quantizedType, quantizedValue); + opTrue = ConstantInt::get(quantizedType, quantizedValue); } // 如果 false 分支的操作数是浮点常量,则进行量化转换 if (ConstantFP * constFp = dyn_cast(opFalse)) { - float constValue = constFp->getValueAPF().convertToFloat(); + float constValue = constFp->getValueAPF().convertToFloat(); int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); - opFalse = ConstantInt::get(quantizedType, quantizedValue); + opFalse = ConstantInt::get(quantizedType, quantizedValue); } // 生成新的 select 指令,保持条件不变,使用转换后的 true 和 false 值 @@ -1180,6 +1246,7 @@ simplifyConstant(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Exiting Simplifying Constant\n"; } } + void simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) { @@ -1505,6 +1572,55 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) llvm::errs() << "Finished handling PHI: " << *newPhi << "\n"; } +void handleFpToSi(Instruction *llvmInst, Type *quantizedType) { + llvm::errs() << "Handling FPToSI\n"; + + // 仅处理 FPToSI 指令 + FPToSIInst *fpToSiInst = dyn_cast(llvmInst); + if (!fpToSiInst) { + llvm::errs() << "Not a FPToSI instruction.\n"; + return; + } + + // 检查是否存在使用该 FPToSI 结果的 SIToFP 指令, + // 即检测模式:%tmp = fptosi double %in to i32 ; %out = sitofp i32 %tmp to double + for (User *U : fpToSiInst->users()) { + if (auto *siToFpInst = dyn_cast(U)) { + llvm::errs() << "Detected fptosi/sitofp sequence:\n"; + llvm::errs() << " FPToSI: " << *fpToSiInst << "\n"; + llvm::errs() << " SIToFP: " << *siToFpInst << "\n"; + + // 假设你希望丢弃的位数为 FRAC_Q(例如 FRAC_Q 表示小数部分位数) + IRBuilder<> Builder(siToFpInst); + + // fpToSiInst 的结果类型是整数,作为固定点值 + Value *intVal = fpToSiInst; + + // 生成算术右移指令,丢弃低 FRAC_Q 位 + Value *truncInst = Builder.CreateAShr( + intVal, + ConstantInt::get(intVal->getType(), FRAC_Q), + "trunc"); + + llvm::errs() << "Replaced sequence with: " << *truncInst << "\n"; + + // 替换所有对 siToFpInst 的使用为 truncInst + siToFpInst->replaceAllUsesWith(truncInst); + + // 删除原来的 FPToSI 和 SIToFP 指令 + siToFpInst->eraseFromParent(); + fpToSiInst->eraseFromParent(); + + return; + } + } + + // 如果没有匹配到这种模式,则可以继续做其他处理(或者直接保留原来的 FPToSI) + llvm::errs() << "No fptosi/sitofp sequence detected. Skipping optimization.\n"; +} + + + void @@ -2147,6 +2263,7 @@ adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { case Instruction::FPToUI: case Instruction::FPToSI: + case Instruction::SIToFP: case Instruction::UIToFP: { @@ -2359,6 +2476,10 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect case Instruction::FPToUI: case Instruction::FPToSI: + { + handleFpToSi(llvmIrInstruction, quantizedType); + break; + } case Instruction::SIToFP: case Instruction::UIToFP: From 885709452237c47c7cbdffaa146be03b2e8860f8 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 27 Feb 2025 21:04:52 +0000 Subject: [PATCH 146/213] save some files Addresses #2. --- ...a3381f0e5bba490bdbbfc408a09f136cc9a272.txt | 48 ++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 150 +++++++++++------- .../newton-irPass-LLVMIR-quantization.cpp | 2 +- 3 files changed, 144 insertions(+), 56 deletions(-) create mode 100644 analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt diff --git a/analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt b/analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt new file mode 100644 index 000000000..d8bfa95b4 --- /dev/null +++ b/analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt @@ -0,0 +1,48 @@ + +changeset: 1739:3ea3381f0e5bba490bdbbfc408a09f136cc9a272 +char kNewtonVersion[] = "0.3-alpha-1739 (3ea3381f0e5bba490bdbbfc408a09f136cc9a272) (build 02-26-2025-21:49-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 74dc585fe..8abe3c1de 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -84,13 +84,17 @@ POSSIBILITY OF SUCH DAMAGE. using namespace llvm; #define FRAC_BASE (1 << maxPrecisionBits) #define BIT_WIDTH 32 +//#define IS_POINTER 1 std::set whitelist = { "MadgwickAHRSupdate", "MahonyAHRSupdate", "sensfusion6UpdateQImpl", "matrixMul", "pzero", - "qzero" + "qzero", + "pone", + "qone", + "__ieee754_exp" }; @@ -299,6 +303,7 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) #if IS_MATRIX handleMatrixStore(storeInst, Builder, maxPrecisionBits); #elif IS_POINTER + llvm::errs() << "Handling pointer store.\n"; handlePointerStore(storeInst, Builder, maxPrecisionBits); #else @@ -590,6 +595,43 @@ dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) dumpedFile.close(); } +//void dumpIR(State *N, std::string fileSuffix, const std::unique_ptr &Mod) { +// StringRef filePath(N->llvmIR); +// std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; +// std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; +// std::string filePathStr = dirPath + fileName; +// filePath = StringRef(filePathStr); +// +// // 输出调试信息:目标 IR 文件路径 +// flexprint(N->Fe, N->Fm, N->Fpinfo, "Dump IR of: %s\n", filePath.str().c_str()); +// llvm::errs() << "DumpIR: File path = " << filePath.str() << "\n"; +// +// // 使用 errorCode 检查创建文件是否成功 +// std::error_code errorCode; +// raw_fd_ostream dumpedFile(filePath, errorCode); +// if (errorCode) { +// // 输出错误信息 +// flexprint(N->Fe, N->Fm, N->Fpinfo, "Error opening file %s: %s\n", filePath.str().c_str(), errorCode.message().c_str()); +// llvm::errs() << "DumpIR: Failed to open file: " << filePath.str() << " (" << errorCode.message() << ")\n"; +// return; +// } else { +// llvm::errs() << "DumpIR: File opened successfully.\n"; +// } +// +// // 写入 IR 并检查写入过程 +// WriteBitcodeToFile(*Mod, dumpedFile); +// dumpedFile.flush(); +// if (dumpedFile.has_error()) { +// llvm::errs() << "DumpIR: Error during WriteBitcodeToFile: " << dumpedFile.error().message() << "\n"; +// flexprint(N->Fe, N->Fm, N->Fpinfo, "Error during WriteBitcodeToFile: %s\n", dumpedFile.error().message().c_str()); +// } else { +// llvm::errs() << "DumpIR: WriteBitcodeToFile completed successfully.\n"; +// } +// +// dumpedFile.close(); +// llvm::errs() << "DumpIR: File closed.\n"; +//} + void mergeBoundInfo(BoundInfo * dst, const BoundInfo * src) { @@ -859,44 +901,42 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl -//#ifdef AUTO_QUANTIZATION -//#endif - /* - * analyze the range of all local variables in each function - * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - std::map callerMap; - callerMap.clear(); - funcBoundInfo.clear(); - bool useOverLoad = false; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - } +// /* +// * analyze the range of all local variables in each function +// * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// std::map callerMap; +// callerMap.clear(); +// funcBoundInfo.clear(); +// bool useOverLoad = false; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } // - flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - shrinkType(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } +// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// shrinkType(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } if (enableQuantization) { @@ -952,28 +992,28 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); // } - /* - * simplify the condition of each branch - * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - simplifyControlFlow(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } - // - legacy::PassManager passManager; - passManager.add(createCFGSimplificationPass()); - passManager.add(createInstSimplifyLegacyPass()); - passManager.add(createGlobalDCEPass()); - passManager.run(*Mod); +// /* +// * simplify the condition of each branch +// * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); +// for (auto & mi : *Mod) +// { +// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); +// if (boundInfoIt != funcBoundInfo.end()) +// { +// simplifyControlFlow(N, boundInfoIt->second, mi); +// } +// // else +// // { +// // assert(false); +// // } +// } +// +// legacy::PassManager passManager; +// passManager.add(createCFGSimplificationPass()); +// passManager.add(createInstSimplifyLegacyPass()); +// passManager.add(createGlobalDCEPass()); +// passManager.run(*Mod); // diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 38d519758..c553624b6 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -338,7 +338,7 @@ bool isWhitelistedGlobal(const std::string & globalName) { // Define the whitelist of global variables - static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero"}; + static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero","q0","q1","q2","q3"}; return whitelist.find(globalName) != whitelist.end(); } From e39ea318616cf24ca79a747f69c9eccac4a2427f Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 27 Feb 2025 22:26:19 +0000 Subject: [PATCH 147/213] add support phi infinite value Addresses #2. --- ...69325b7fdaab528356887d1d0eb5b0291012db.txt | 48 +++++ .../newton-irPass-LLVMIR-quantization.cpp | 184 ++++++++++++++---- 2 files changed, 190 insertions(+), 42 deletions(-) create mode 100644 analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt diff --git a/analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt b/analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt new file mode 100644 index 000000000..efb5dd963 --- /dev/null +++ b/analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt @@ -0,0 +1,48 @@ + +changeset: 1740:7869325b7fdaab528356887d1d0eb5b0291012db +char kNewtonVersion[] = "0.3-alpha-1740 (7869325b7fdaab528356887d1d0eb5b0291012db) (build 02-27-2025-21:04-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index c553624b6..ec054a637 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -10,6 +10,8 @@ #include #include "llvm/IR/Metadata.h" #include "config.h" +#include +#include using namespace llvm; unsigned int FRAC_Q; @@ -356,8 +358,8 @@ shouldProcessFunction(Function & F) "pzero", "qzero", "pone", - "qone" -// "__ieee754_exp", + "qone", + "__ieee754_exp" // "__ieee754_log", }; @@ -1506,8 +1508,8 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { llvm::errs() << "Finished handling FDiv\n"; } -void handlePhi(Instruction *inInstruction, Type *quantizedType) -{ + +void handlePhi(Instruction *inInstruction, Type *quantizedType) { llvm::errs() << "Handling PHI\n"; PHINode *phi = dyn_cast(inInstruction); if (!phi) { @@ -1515,22 +1517,17 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) return; } - // 判断是否是指针类型的 PHI 节点 + // Check if the PHI node is of pointer type. bool isPtr = phi->getType()->isPointerTy(); unsigned pointerAddr = 0; if (isPtr) pointerAddr = phi->getType()->getPointerAddressSpace(); - // 新 PHI 节点的类型 - // 如果原来是 pointer 类型,则新类型为 quantizedType->getPointerTo(pointerAddr) - // 否则直接使用 quantizedType + // Determine new PHI node type. Type *newPhiType = isPtr ? quantizedType->getPointerTo(pointerAddr) : quantizedType; - - // 创建新的 PHI 节点,新节点插入在原 PHI 节点之前 PHINode *newPhi = PHINode::Create(newPhiType, phi->getNumIncomingValues(), - phi->getName() + ".quantized", phi); + phi->getName() + ".quantized", phi); - // 遍历所有入边 for (unsigned i = 0, e = phi->getNumIncomingValues(); i < e; i++) { Value *incoming = phi->getIncomingValue(i); BasicBlock *incomingBB = phi->getIncomingBlock(i); @@ -1539,12 +1536,38 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) llvm::errs() << "Original PHI incoming value: " << *incoming << "\n"; if (!isPtr) { - // 针对非指针情况:如果是浮点常量,直接量化;如果是浮点值,则插入 FPToSI 转换 if (ConstantFP *constFp = dyn_cast(incoming)) { - float fpVal = constFp->getValueAPF().convertToFloat(); - int64_t quantizedValue = static_cast(round(fpVal * FRAC_BASE)); - newVal = llvm::ConstantInt::get(quantizedType, quantizedValue); - llvm::errs() << "Converted constant: " << *newVal << "\n"; + // Check for infinity. + if (constFp->getValueAPF().isInfinity()) { + int64_t maxVal = 0, minVal = 0; + // Choose maximum/minimum based on BIT_WIDTH. + if (BIT_WIDTH == 16) { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } else if (BIT_WIDTH == 32) { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } else if (BIT_WIDTH == 64) { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } else { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } + + if (!constFp->getValueAPF().isNegative()) { + llvm::errs() << "Detected positive infinity, mapping to max integer.\n"; + newVal = llvm::ConstantInt::get(quantizedType, maxVal, true); + } else { + llvm::errs() << "Detected negative infinity, mapping to min integer.\n"; + newVal = llvm::ConstantInt::get(quantizedType, minVal, true); + } + } else { + float fpVal = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(fpVal * FRAC_BASE)); + newVal = llvm::ConstantInt::get(quantizedType, quantizedValue, true); + llvm::errs() << "Converted constant: " << *newVal << "\n"; + } } else if (incoming->getType()->isFloatingPointTy()) { IRBuilder<> builder(incomingBB->getTerminator()); newVal = builder.CreateFPToSI(incoming, quantizedType, incoming->getName() + ".to_int"); @@ -1553,7 +1576,6 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) newVal = incoming; } } else { - // 针对指针类型:要求新入边值类型为 newPhiType if (incoming->getType() != newPhiType) { IRBuilder<> builder(incomingBB->getTerminator()); newVal = builder.CreateBitCast(incoming, newPhiType, incoming->getName() + ".cast"); @@ -1565,64 +1587,142 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) newPhi->addIncoming(newVal, incomingBB); } - // 替换所有使用并删除原 PHI 节点 phi->replaceAllUsesWith(newPhi); phi->eraseFromParent(); - llvm::errs() << "Finished handling PHI: " << *newPhi << "\n"; } + +//void handlePhi(Instruction *inInstruction, Type *quantizedType) +//{ +// llvm::errs() << "Handling PHI\n"; +// PHINode *phi = dyn_cast(inInstruction); +// if (!phi) { +// llvm::errs() << "Error: Instruction is not a PHI node.\n"; +// return; +// } +// +// // 判断是否是指针类型的 PHI 节点 +// bool isPtr = phi->getType()->isPointerTy(); +// unsigned pointerAddr = 0; +// if (isPtr) +// pointerAddr = phi->getType()->getPointerAddressSpace(); +// +// // 新 PHI 节点的类型 +// // 如果原来是 pointer 类型,则新类型为 quantizedType->getPointerTo(pointerAddr) +// // 否则直接使用 quantizedType +// Type *newPhiType = isPtr ? quantizedType->getPointerTo(pointerAddr) : quantizedType; +// +// // 创建新的 PHI 节点,新节点插入在原 PHI 节点之前 +// PHINode *newPhi = PHINode::Create(newPhiType, phi->getNumIncomingValues(), +// phi->getName() + ".quantized", phi); +// +// // 遍历所有入边 +// for (unsigned i = 0, e = phi->getNumIncomingValues(); i < e; i++) { +// Value *incoming = phi->getIncomingValue(i); +// BasicBlock *incomingBB = phi->getIncomingBlock(i); +// Value *newVal = nullptr; +// +// llvm::errs() << "Original PHI incoming value: " << *incoming << "\n"; +// +// if (!isPtr) { +// // 针对非指针情况:如果是浮点常量,直接量化;如果是浮点值,则插入 FPToSI 转换 +// if (ConstantFP *constFp = dyn_cast(incoming)) { +// float fpVal = constFp->getValueAPF().convertToFloat(); +// int64_t quantizedValue = static_cast(round(fpVal * FRAC_BASE)); +// newVal = llvm::ConstantInt::get(quantizedType, quantizedValue); +// llvm::errs() << "Converted constant: " << *newVal << "\n"; +// } else if (incoming->getType()->isFloatingPointTy()) { +// IRBuilder<> builder(incomingBB->getTerminator()); +// newVal = builder.CreateFPToSI(incoming, quantizedType, incoming->getName() + ".to_int"); +// llvm::errs() << "Inserted conversion: " << *newVal << "\n"; +// } else { +// newVal = incoming; +// } +// } else { +// // 针对指针类型:要求新入边值类型为 newPhiType +// if (incoming->getType() != newPhiType) { +// IRBuilder<> builder(incomingBB->getTerminator()); +// newVal = builder.CreateBitCast(incoming, newPhiType, incoming->getName() + ".cast"); +// llvm::errs() << "BitCast pointer: " << *newVal << "\n"; +// } else { +// newVal = incoming; +// } +// } +// newPhi->addIncoming(newVal, incomingBB); +// } +// +// // 替换所有使用并删除原 PHI 节点 +// phi->replaceAllUsesWith(newPhi); +// phi->eraseFromParent(); +// +// llvm::errs() << "Finished handling PHI: " << *newPhi << "\n"; +//} + void handleFpToSi(Instruction *llvmInst, Type *quantizedType) { llvm::errs() << "Handling FPToSI\n"; - // 仅处理 FPToSI 指令 + // Ensure we are processing an FPToSI instruction. FPToSIInst *fpToSiInst = dyn_cast(llvmInst); if (!fpToSiInst) { llvm::errs() << "Not a FPToSI instruction.\n"; return; } - // 检查是否存在使用该 FPToSI 结果的 SIToFP 指令, - // 即检测模式:%tmp = fptosi double %in to i32 ; %out = sitofp i32 %tmp to double + // Check if the operand to FPToSI is already an integer. + Value *inputVal = fpToSiInst->getOperand(0); + if (inputVal->getType()->isIntegerTy()) { + llvm::errs() << "Input of FPToSI is already integer. Replacing redundant conversion.\n"; + + // Set insertion point immediately after the FPToSI instruction. + IRBuilder<> Builder(fpToSiInst->getNextNode()); + + // Directly apply an arithmetic right shift on the input value. + Value *truncInst = Builder.CreateAShr( + inputVal, + ConstantInt::get(inputVal->getType(), FRAC_Q), + "trunc"); + llvm::errs() << "Created ASHR instruction: " << *truncInst << "\n"; + + // Replace all uses of the FPToSI instruction with the new ASHR result. + fpToSiInst->replaceAllUsesWith(truncInst); + fpToSiInst->eraseFromParent(); + return; + } + + // Otherwise, if the input is not already integer, try to match the standard fptosi/sitofp sequence. for (User *U : fpToSiInst->users()) { if (auto *siToFpInst = dyn_cast(U)) { llvm::errs() << "Detected fptosi/sitofp sequence:\n"; llvm::errs() << " FPToSI: " << *fpToSiInst << "\n"; llvm::errs() << " SIToFP: " << *siToFpInst << "\n"; - // 假设你希望丢弃的位数为 FRAC_Q(例如 FRAC_Q 表示小数部分位数) - IRBuilder<> Builder(siToFpInst); - - // fpToSiInst 的结果类型是整数,作为固定点值 - Value *intVal = fpToSiInst; - - // 生成算术右移指令,丢弃低 FRAC_Q 位 + // Use the result of FPToSI (assumed to be the fixed-point representation) directly. + IRBuilder<> Builder(siToFpInst->getNextNode()); Value *truncInst = Builder.CreateAShr( - intVal, - ConstantInt::get(intVal->getType(), FRAC_Q), - "trunc"); + fpToSiInst, + ConstantInt::get(fpToSiInst->getType(), FRAC_Q), + "trunc"); + llvm::errs() << "Created ASHR instruction: " << *truncInst << "\n"; - llvm::errs() << "Replaced sequence with: " << *truncInst << "\n"; - - // 替换所有对 siToFpInst 的使用为 truncInst siToFpInst->replaceAllUsesWith(truncInst); - - // 删除原来的 FPToSI 和 SIToFP 指令 siToFpInst->eraseFromParent(); - fpToSiInst->eraseFromParent(); - + if (fpToSiInst->use_empty()) { + fpToSiInst->eraseFromParent(); + } return; } } - // 如果没有匹配到这种模式,则可以继续做其他处理(或者直接保留原来的 FPToSI) - llvm::errs() << "No fptosi/sitofp sequence detected. Skipping optimization.\n"; + llvm::errs() << "No matching pattern found for FPToSI optimization. Skipping.\n"; } + + void setQuantizedType(Value * inValue, Type * quantizedType) { From 1de2a17c4727c42120c3d791515eabc082b76067 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 3 Mar 2025 21:22:45 +0000 Subject: [PATCH 148/213] update test files Addresses #2. --- .../newton/llvm-ir/c-files/MadgwickAHRS.c | 30 +- .../newton/llvm-ir/c-files/MahonyAHRS.c | 234 +++++----- .../newton/llvm-ir/c-files/MahonyAHRS.h | 6 +- .../llvm-ir/c-files/test_MadgwickAHRS.c | 429 ++++++++++-------- applications/newton/llvm-ir/rms_error.py | 71 ++- applications/newton/llvm-ir/testMadgwick.sh | 74 +-- applications/newton/llvm-ir/testOriginal.sh | 9 +- applications/newton/llvm-ir/test_madgwick.c | 346 -------------- src/newton/Makefile | 26 +- src/newton/config.h | 7 +- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 29 +- .../newton-irPass-LLVMIR-quantization.cpp | 22 +- 12 files changed, 569 insertions(+), 714 deletions(-) delete mode 100644 applications/newton/llvm-ir/test_madgwick.c diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index 90917828e..78c77c190 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -26,6 +26,16 @@ #define sampleFreq 128.0f // sample frequency in Hz #define betaDef 0.1f // 2 * proportional gain +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + #ifndef lowerBound #define lowerBound -16 #endif @@ -65,7 +75,13 @@ float invSqrt(float x) { //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +//void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + +void +MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { float q0 = *q0_ptr; float q1 = *q1_ptr; @@ -74,7 +90,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float #ifdef ASSUME __builtin_assume(ax > lowerBound && ax < upperBound); __builtin_assume(ay > lowerBound && ay < upperBound); - __builtin_assume(az > lowerBound && az < upperBound); + __builtin_assume(az > lowerBound && az < upperBound);b #endif float recipNorm; float s0, s1, s2, s3; @@ -94,6 +110,9 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -135,7 +154,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float // Reference direction of Earth's magnetic field hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; - _2bx = sqrt(hx * hx + hy * hy); + _2bx = sqrtf(hx * hx + hy * hy); _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; _4bx = 2.0f * _2bx; _4bz = 2.0f * _2bz; @@ -189,7 +208,10 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + +void +MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { float q0 = *q0_ptr; float q1 = *q1_ptr; diff --git a/applications/newton/llvm-ir/c-files/MahonyAHRS.c b/applications/newton/llvm-ir/c-files/MahonyAHRS.c index c4cbeb46b..5fb080a89 100644 --- a/applications/newton/llvm-ir/c-files/MahonyAHRS.c +++ b/applications/newton/llvm-ir/c-files/MahonyAHRS.c @@ -1,36 +1,37 @@ //===================================================================================================== -// MadgwickAHRS.c +// MahonyAHRS.c //===================================================================================================== // -// Implementation of Madgwick's IMU and AHRS algorithms. +// Madgwick's implementation of Mayhony's AHRS algorithm. // See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms // -// Date Author Notes +// Date Author Notes // 29/09/2011 SOH Madgwick Initial release // 02/10/2011 SOH Madgwick Optimised for reduced CPU load -// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised // //===================================================================================================== //--------------------------------------------------------------------------------------------------- // Header files -#include "MadgwickAHRS.h" +#include "MahonyAHRS.h" #include //--------------------------------------------------------------------------------------------------- // Definitions -// #define sampleFreq 512.0f // sample frequency in Hz -// #define sampleFreq 100.0f // sample frequency in Hz -#define sampleFreq 28.0f // sample frequency in Hz -#define betaDef 0.1f // 2 * proportional gain +//#define sampleFreq 512.0f // sample frequency in Hz +#define sampleFreq 128.0f // sample frequency in Hz +#define twoKpDef (2.0f * 0.5f) // 2 * proportional gain +#define twoKiDef (2.0f * 0.0f) // 2 * integral gain //--------------------------------------------------------------------------------------------------- // Variable definitions -volatile float beta = betaDef; // 2 * proportional gain (Kp) -volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame +volatile float twoKp = twoKpDef; // 2 * proportional gain (Kp) +volatile float twoKi = twoKiDef; // 2 * integral gain (Ki) +//volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame +volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki //--------------------------------------------------------------------------------------------------- // Function declarations @@ -43,26 +44,27 @@ float invSqrt(float x); //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + + float recipNorm; - float s0, s1, s2, s3; - float qDot1, qDot2, qDot3, qDot4; - float hx, hy; - float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float hx, hy, bx, bz; + float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; + float halfex, halfey, halfez; + float qa, qb, qc; - // warpPrint("In MadgwickAHRSupdate!!\n"); // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { - MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az); + MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); return; } - // Rate of change of quaternion from gyroscope - qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); - qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); - qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); - qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); - // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -79,16 +81,6 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float mz *= recipNorm; // Auxiliary variables to avoid repeated arithmetic - _2q0mx = 2.0f * q0 * mx; - _2q0my = 2.0f * q0 * my; - _2q0mz = 2.0f * q0 * mz; - _2q1mx = 2.0f * q1 * mx; - _2q0 = 2.0f * q0; - _2q1 = 2.0f * q1; - _2q2 = 2.0f * q2; - _2q3 = 2.0f * q3; - _2q0q2 = 2.0f * q0 * q2; - _2q2q3 = 2.0f * q2 * q3; q0q0 = q0 * q0; q0q1 = q0 * q1; q0q2 = q0 * q2; @@ -101,36 +93,56 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float q3q3 = q3 * q3; // Reference direction of Earth's magnetic field - hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; - hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; - _2bx = sqrt(hx * hx + hy * hy); - _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; - _4bx = 2.0f * _2bx; - _4bz = 2.0f * _2bz; - - // Gradient decent algorithm corrective step - s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); - recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude - s0 *= recipNorm; - s1 *= recipNorm; - s2 *= recipNorm; - s3 *= recipNorm; - - // Apply feedback step - qDot1 -= beta * s0; - qDot2 -= beta * s1; - qDot3 -= beta * s2; - qDot4 -= beta * s3; + hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); + hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + bx = sqrtf(hx * hx + hy * hy); + bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; + halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); + halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); + halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); + halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); + halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; } - // Integrate rate of change of quaternion to yield quaternion - q0 += qDot1 * (1.0f / sampleFreq); - q1 += qDot2 * (1.0f / sampleFreq); - q2 += qDot3 * (1.0f / sampleFreq); - q3 += qDot4 * (1.0f / sampleFreq); + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); @@ -138,22 +150,24 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; } //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) { +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; float recipNorm; - float s0, s1, s2, s3; - float qDot1, qDot2, qDot3, qDot4; - float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; - - // Rate of change of quaternion from gyroscope - qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); - qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); - qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); - qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { @@ -164,44 +178,48 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo ay *= recipNorm; az *= recipNorm; - // Auxiliary variables to avoid repeated arithmetic - _2q0 = 2.0f * q0; - _2q1 = 2.0f * q1; - _2q2 = 2.0f * q2; - _2q3 = 2.0f * q3; - _4q0 = 4.0f * q0; - _4q1 = 4.0f * q1; - _4q2 = 4.0f * q2; - _8q1 = 8.0f * q1; - _8q2 = 8.0f * q2; - q0q0 = q0 * q0; - q1q1 = q1 * q1; - q2q2 = q2 * q2; - q3q3 = q3 * q3; - - // Gradient decent algorithm corrective step - s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; - s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; - s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; - s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; - recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude - s0 *= recipNorm; - s1 *= recipNorm; - s2 *= recipNorm; - s3 *= recipNorm; - - // Apply feedback step - qDot1 -= beta * s0; - qDot2 -= beta * s1; - qDot3 -= beta * s2; - qDot4 -= beta * s3; + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = q1 * q3 - q0 * q2; + halfvy = q0 * q1 + q2 * q3; + halfvz = q0 * q0 - 0.5f + q3 * q3; + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; } - // Integrate rate of change of quaternion to yield quaternion - q0 += qDot1 * (1.0f / sampleFreq); - q1 += qDot2 * (1.0f / sampleFreq); - q2 += qDot3 * (1.0f / sampleFreq); - q3 += qDot4 * (1.0f / sampleFreq); + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); diff --git a/applications/newton/llvm-ir/c-files/MahonyAHRS.h b/applications/newton/llvm-ir/c-files/MahonyAHRS.h index 2a288a146..158e366e5 100644 --- a/applications/newton/llvm-ir/c-files/MahonyAHRS.h +++ b/applications/newton/llvm-ir/c-files/MahonyAHRS.h @@ -23,8 +23,10 @@ //--------------------------------------------------------------------------------------------------- // Function declarations -void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); -void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); #endif //===================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c index 0804e4075..15ae55118 100644 --- a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -13,18 +13,19 @@ #include #include #include +#include #include #include #if defined(INT_DATA_TYPE) -extern volatile int32_t q0, q1, q2, q3; -//#include "c-files/MadgwickAHRSfix.h" +extern volatile float q0, q1, q2, q3; +#include "MadgwickAHRS.h" #elif defined(FP_DATA_TYPE) -extern volatile float q0, q1, q2, q3; +extern volatile float q0, q1, q2, q3; #if defined(SOFT_FLOAT_LIB) -#include "c-files/MadgwickAHRS_softfloat.h" +#include "MadgwickAHRS_softfloat.h" #else -#include "c-files/MadgwickAHRS.h" +#include "MadgwickAHRS.h" #endif #else @@ -32,239 +33,309 @@ extern volatile float q0, q1, q2, q3; #endif #define DATA_SIZE 1000 -#define FRAC_Q 10 -#define FRAC_BASE (1<= 1000000000) { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } else { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; } -void printTimeSpec(timespec t, const char* prefix) { - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); } -timespec tic( ) +timespec +tic() { - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; } -timespec toc( timespec* start_time, const char* prefix ) +timespec +toc(timespec * start_time, const char * prefix) { - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - timespec time_consump = diff( *start_time, current_time ); - printTimeSpec(time_consump, prefix ); - *start_time = current_time; - return time_consump; + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; } -int main() { - FILE* fp = fopen("input.csv", "r"); +int +main() +{ + FILE * fp = fopen("input.csv", "r"); - if (!fp) { - printf("Can't open file\n"); - return -1; - } + if (!fp) + { + printf("Can't open file\n"); + return -1; + } - double time[DATA_SIZE]; + double time[DATA_SIZE]; #if defined(INT_DATA_TYPE) - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = (0.64306622f*FRAC_BASE); - q1[i] = (0.02828862f*FRAC_BASE); - q2[i] = (-0.00567953f*FRAC_BASE); - q3[i] = (-0.76526684f*FRAC_BASE); - } - - int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + + // q0[i] = 1.0f; + // q1[i] = 0.0f; + // q2[i] = 0.0f; + // q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + #elif defined(FP_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } - - float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + + // q0[i] = 1.0f; + // q1[i] = 0.0f; + // q2[i] = 0.0f; + // q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; #else #error "Must set data type: FP or INT" #endif - char buffer[1024]; - int row = 0, column = 0; - while (fgets(buffer, 1024, fp)) { - column = 0; - row++; + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; - if (row == 1) - continue; + if (row == 1) + continue; - char* value = strtok(buffer, ", "); + char * value = strtok(buffer, ", "); - while (value) { - switch (column) { - case 0: - time[row-2] = atof(value); - break; - case 1: + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: #if defined(INT_DATA_TYPE) - mag_x[row-2] = round(atof(value)*FRAC_BASE); + + mag_x[row - 2] = atof(value); #elif defined(FP_DATA_TYPE) - mag_x[row-2] = atof(value); + mag_x[row - 2] = atof(value); #endif - break; - case 2: + break; + case 2: #if defined(INT_DATA_TYPE) - mag_y[row-2] = round(atof(value)*FRAC_BASE); + + mag_y[row - 2] = atof(value); #elif defined(FP_DATA_TYPE) - mag_y[row-2] = atof(value); + mag_y[row - 2] = atof(value); #endif - break; - case 3: + break; + case 3: #if defined(INT_DATA_TYPE) - mag_z[row-2] = round(atof(value)*FRAC_BASE); + + mag_z[row - 2] = atof(value); #elif defined(FP_DATA_TYPE) - mag_z[row-2] = atof(value); + mag_z[row - 2] = atof(value); #endif - break; - case 4: + break; + case 4: #if defined(INT_DATA_TYPE) - gyr_x[row-2] = round(atof(value)*FRAC_BASE)*61; + // gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_x[row - 2] = atof(value) * 61; #elif defined(FP_DATA_TYPE) - gyr_x[row-2] = atof(value)*61; + gyr_x[row - 2] = atof(value) * 61; #endif - break; - case 5: + break; + case 5: #if defined(INT_DATA_TYPE) - gyr_y[row-2] = round(atof(value)*FRAC_BASE)*61; + // gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_y[row - 2] = atof(value) * 61; #elif defined(FP_DATA_TYPE) - gyr_y[row-2] = atof(value)*61; + gyr_y[row - 2] = atof(value) * 61; #endif - break; - case 6: + break; + case 6: #if defined(INT_DATA_TYPE) - gyr_z[row-2] = round(atof(value)*FRAC_BASE)*61; + // gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_z[row - 2] = atof(value) * 61; #elif defined(FP_DATA_TYPE) - gyr_z[row-2] = atof(value)*61; + gyr_z[row - 2] = atof(value) * 61; #endif - break; - case 7: + break; + case 7: #if defined(INT_DATA_TYPE) - acc_x[row-2] = round(atof(value)*FRAC_BASE)*2; + // acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; #elif defined(FP_DATA_TYPE) - acc_x[row-2] = atof(value)*2; + acc_x[row - 2] = atof(value) * 2; #endif - break; - case 8: + break; + case 8: #if defined(INT_DATA_TYPE) - acc_y[row-2] = round(atof(value)*FRAC_BASE)*2; + // acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_y[row - 2] = atof(value) * 2; #elif defined(FP_DATA_TYPE) - acc_y[row-2] = atof(value)*2; + acc_y[row - 2] = atof(value) * 2; #endif - break; - case 9: + break; + case 9: #if defined(INT_DATA_TYPE) - acc_z[row-2] = round(atof(value)*FRAC_BASE)*2; + // acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_z[row - 2] = atof(value) * 2; #elif defined(FP_DATA_TYPE) - acc_z[row-2] = atof(value)*2; + acc_z[row - 2] = atof(value) * 2; #endif - break; - default: - break; - } - - value = strtok(NULL, ", "); - column++; - } - } - - fclose(fp); - - u_int64_t time_slots[ITERATION]; - - for (size_t idx = 0; idx < ITERATION; idx++) { - timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - } - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - } - - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } + + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + // unsigned int init[3] = {0}; + // unsigned int start[3] = {0}; + // unsigned int end[3] = {0}; + // unsigned int overhead = 0; + // + // readticks(init, 0); + // readticks(start, 1); + // readticks(end, 1); + // + // overhead = end[0] - start[0]; + // readticks(init, 0); + // readticks(start, 1); + + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + + // end[0] = end[0] - start[0] - overhead; + // printf("clock cycles= %d\n", end[0]); + + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); #if defined(FP_DATA_TYPE) - FILE *fptr = fopen("fp_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - } - fclose(fptr); + FILE * fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); #elif defined(INT_DATA_TYPE) - FILE *fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - // ts, (double)q0[ts]/FRAC_BASE, - // ts, (double)q1[ts]/FRAC_BASE, - // ts, (double)q2[ts]/FRAC_BASE, - // ts, (double)q3[ts]/FRAC_BASE); - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, (double)q0[ts]/FRAC_BASE, - ts, (double)q1[ts]/FRAC_BASE, - ts, (double)q2[ts]/FRAC_BASE, - ts, (double)q3[ts]/FRAC_BASE); - } - fclose(fptr); -// printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", -// DISPLAY_INT(q0), DISPLAY_FRAC(q0), -// DISPLAY_INT(q1), DISPLAY_FRAC(q1), -// DISPLAY_INT(q2), DISPLAY_FRAC(q2), -// DISPLAY_INT(q3), DISPLAY_FRAC(q3)); + FILE * fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + + + #endif - return 0; -} \ No newline at end of file + return 0; +} diff --git a/applications/newton/llvm-ir/rms_error.py b/applications/newton/llvm-ir/rms_error.py index 39391c477..ece1b2aad 100644 --- a/applications/newton/llvm-ir/rms_error.py +++ b/applications/newton/llvm-ir/rms_error.py @@ -1,10 +1,10 @@ import math +import argparse -# Function to parse the data from the files after removing the prefix def parse_data(file_path, prefix_to_remove): """ Reads a file and extracts q0, q1, q2, q3 values from each line after removing the specified prefix. - Returns a list of tuples containing the extracted values. + Returns a list of lists containing the extracted values. """ with open(file_path, 'r') as file: data = [] @@ -18,8 +18,11 @@ def parse_data(file_path, prefix_to_remove): data.append(parsed_values) return data -# Calculate RMS errors for q0, q1, q2, q3 def calculate_rms_errors(fp_data, int_data): + """ + Calculate RMS errors for each quaternion component (q0, q1, q2, q3) and overall RMS. + Returns (rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms). + """ errors_q0 = [] errors_q1 = [] errors_q2 = [] @@ -41,31 +44,68 @@ def calculate_rms_errors(fp_data, int_data): # Accumulate the overall squared error for all components overall_errors.append(q0_error ** 2 + q1_error ** 2 + q2_error ** 2 + q3_error ** 2) + # RMS for each component rms_q0 = math.sqrt(sum(errors_q0) / len(errors_q0)) rms_q1 = math.sqrt(sum(errors_q1) / len(errors_q1)) rms_q2 = math.sqrt(sum(errors_q2) / len(errors_q2)) rms_q3 = math.sqrt(sum(errors_q3) / len(errors_q3)) - # Overall RMS error + # Overall RMS error (combined four components) rms_overall = math.sqrt(sum(overall_errors) / len(overall_errors)) - # Average of RMS values + # Average of RMS values (average of q0, q1, q2, q3 RMS) avg_rms = (rms_q0 + rms_q1 + rms_q2 + rms_q3) / 4 return rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms -# Main function to compute RMS errors +def compute_snr(fp_data, int_data, assume_signal_power=1.0): + """ + Calculate SNR (in dB). Assumes: + 1) The original signal power = assume_signal_power (default 1) + 2) Noise power = (RMS Error)^2 (overall RMS) + """ + _, _, _, _, rms_overall, _ = calculate_rms_errors(fp_data, int_data) + p_noise = rms_overall ** 2 + p_signal = assume_signal_power + if p_noise == 0: + return float('inf') + snr_db = 10 * math.log10(p_signal / p_noise) + return snr_db + +def compute_real_signal_power(fp_data): + """ + If you want to compute the real signal power from FP data. + P_signal = mean( q0^2 + q1^2 + q2^2 + q3^2 ) over the dataset. + """ + total = 0.0 + for fp in fp_data: + q0, q1, q2, q3 = fp + total += (q0**2 + q1**2 + q2**2 + q3**2) + return total / len(fp_data) + def main(): - fp_data = parse_data('fp_result.txt', 'Original: ') - int_data = parse_data('int_result.txt', 'FIX: ') + parser = argparse.ArgumentParser(description="Calculate RMS error and SNR for quaternion data.") + parser.add_argument("--filter", choices=["madgwick", "mahony"], default="madgwick", + help="Select which algorithm's data to process (default: madgwick)") + args = parser.parse_args() + + if args.filter == "madgwick": + fp_data = parse_data('fp_result.txt', 'Original: ') + int_data = parse_data('int_result.txt', 'FIX: ') + else: # mahony + fp_data = parse_data('fp_mahony_result.txt', 'Original: ') + int_data = parse_data('int_mahony_result.txt', 'FIX: ') if len(fp_data) != len(int_data): print("Mismatch in the number of quaternions between files!") return + # Use only the first 50 data lines + fp_data = fp_data[:50] + int_data = int_data[:50] + # Calculate RMS errors rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms = calculate_rms_errors(fp_data, int_data) - print(f"RMS Error (q0): {rms_q0:.6f}") print(f"RMS Error (q1): {rms_q1:.6f}") print(f"RMS Error (q2): {rms_q2:.6f}") @@ -73,6 +113,17 @@ def main(): print(f"Overall RMS Error: {rms_overall:.6f}") print(f"Average of RMS Values: {avg_rms:.6f}") -# Run the main function + # Calculate SNR (assuming original signal power = 1) + snr_db = compute_snr(fp_data, int_data, assume_signal_power=1.0) + print(f"SNR (dB) [Assume P_signal=1]: {snr_db:.2f}") + + # For debugging: print first line error values if needed + first_fp = fp_data[0] + first_int = int_data[0] + first_error = [first_fp[i] - first_int[i] for i in range(4)] + print(f"First line FP: {first_fp}") + print(f"First line INT: {first_int}") + print(f"First line errors (q0, q1, q2, q3): {first_error}") + if __name__ == "__main__": main() diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index 18ed45d36..be6fd2f36 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -1,68 +1,68 @@ #!/bin/bash +# Usage: ./run_algorithm.sh [MadgwickAHRS|MahonyAHRS] +# If no argument is provided, it defaults to MadgwickAHRS -# Get the current user's home directory -USER_HOME=$HOME +# Set the algorithm name based on the first command-line argument, default to MadgwickAHRS +ALGO=${1:-MadgwickAHRS} -FILE_PATH="$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll" +# Validate the input (optional) +if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" ]]; then + echo "Error: Unknown algorithm '$ALGO'. Please use either 'MadgwickAHRS' or 'MahonyAHRS'." + exit 1 +fi - #Step 1: Generate LLVM IR file +echo "Selected algorithm: $ALGO" + +# Step 1: Generate LLVM IR file echo "Step 1: Generate LLVM IR file" -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + "$HOME/CoSense/applications/newton/llvm-ir/c-files/${ALGO}.c" +# Optimize the LLVM IR file with mem2reg +opt "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll # Step 2: Use newton for optimization and quantization echo "Step 2: Use newton for optimization and quantization" -#cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/BMX055.nt +cd "$HOME/CoSense/src/newton" && \ + ./newton-linux-EN --llvm-ir="$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + --llvm-ir-liveness-check --llvm-ir-auto-quantization \ + "$HOME/CoSense/applications/newton/sensors/BMX055.nt" -cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/test.nt -# # Step 3: Convert generated bytecode file to LLVM IR file echo "Step 3: Convert generated bytecode file to LLVM IR file" -#llvm-dis $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.bc -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -cd $HOME/CoSense/applications/newton/llvm-ir/&& -./replace.sh $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll - -#python3 replace.py - +llvm-dis "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.bc" \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" +cd "$HOME/CoSense/applications/newton/llvm-ir" && ./replace.sh "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" -# Step 4: Optimize the generated LLVM IR file +# Step 4: Optimize the generated LLVM IR file further (e.g., inline and mem2reg) echo "Step 4: Optimize the generated LLVM IR file" -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt -inline "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/out.ll" # Step 5: Compile the optimized LLVM IR file to bitcode echo "Step 5: Compile the optimized LLVM IR file to bitcode" -llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc +llvm-as "$HOME/CoSense/applications/newton/llvm-ir/out.ll" -o "$HOME/CoSense/applications/newton/llvm-ir/out.bc" # Step 6: Compile the bitcode file to assembly echo "Step 6: Compile the bitcode file to assembly" -llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s +llc "$HOME/CoSense/applications/newton/llvm-ir/out.bc" -o "$HOME/CoSense/applications/newton/llvm-ir/out.s" -# Step 7: Compile the assembly file to object file +# Step 7: Compile the assembly file to an object file echo "Step 7: Compile the assembly file to object file" -clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o +clang -c "$HOME/CoSense/applications/newton/llvm-ir/out.s" -o "$HOME/CoSense/applications/newton/llvm-ir/out.o" # Step 8: Package the object file into a static library echo "Step 8: Package the object file into a static library" -ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o +ar -rc "$HOME/CoSense/applications/newton/llvm-ir/libout.a" "$HOME/CoSense/applications/newton/llvm-ir/out.o" # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm - -#clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -D -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm - -clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm - - -#clang $HOME/CoSense/applications/newton/llvm-ir/c-files/newtest.c -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/c-files/newtest -lm +clang "$HOME/CoSense/applications/newton/llvm-ir/c-files/test_${ALGO}.c" -D INT_DATA_TYPE -no-pie \ + -L"$HOME/CoSense/applications/newton/llvm-ir" -lout -O3 -Os -g \ + -o "$HOME/CoSense/applications/newton/llvm-ir/main_out" -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" -$HOME/CoSense/applications/newton/llvm-ir/main_out - - -#$HOME/CoSense/applications/newton/llvm-ir/c-files/newtest \ No newline at end of file +"$HOME/CoSense/applications/newton/llvm-ir/main_out" diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh index 8537f2b19..20de5c460 100755 --- a/applications/newton/llvm-ir/testOriginal.sh +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -1,13 +1,10 @@ -USER_HOME=$HOME - - # Step 1: Generate LLVM IR file clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c # Step 4: Optimize the generated LLVM IR file -opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll -#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll # Step 5: Compile the optimized LLVM IR file to bitcode llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc @@ -22,7 +19,7 @@ clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applic ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o # Step 9: Compile the test file and link with the static library -clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm # Step 10: Run the test executable $HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/test_madgwick.c b/applications/newton/llvm-ir/test_madgwick.c deleted file mode 100644 index 53f62921c..000000000 --- a/applications/newton/llvm-ir/test_madgwick.c +++ /dev/null @@ -1,346 +0,0 @@ -/* - * Madgwick test case (run locally) - * Compilation command in applications/newton/llvm-ir/performance_test/Makefile - * - * How to compile and run? - * 1. `make perf_madgwick` FP hardware (by default) - * 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) - * 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) - * 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format - * */ - -#include -#include -#include -#include -#include -#include -#include - -#if defined(INT_DATA_TYPE) -//extern volatile int32_t q0, q1, q2, q3; -extern volatile float q0, q1, q2, q3; -// #include "c-files/MadgwickAHRSfix.h" -#include "c-files/MadgwickAHRS.h" -#elif defined(FP_DATA_TYPE) -extern volatile float q0, q1, q2, q3; -#if defined(SOFT_FLOAT_LIB) -#include "c-files/MadgwickAHRS_softfloat.h" -#else -#include "c-files/MadgwickAHRS.h" -#endif - -#else -#error "Must set data type: FP or INT" -#endif - -#define DATA_SIZE 1000 -#define FRAC_Q 16 -#define FRAC_BASE (1 << FRAC_Q) -#define ITERATION 10 - -/*************************************** - * Timer functions of the test framework - ***************************************/ -// readticks(unsigned int *result, int enabled) -//{ -// struct timeval t; -// unsigned int cc; -// unsigned int val; -// if (!enabled) { -// // program the performance-counter control-register: -// asm volatile("msr pmcr_el0, %0" : : "r" (17)); -// //enable all counters -// asm volatile("msr PMCNTENSET_EL0, %0" : : "r" (0x8000000f)); -// //clear the overflow -// asm volatile("msr PMOVSCLR_EL0, %0" : : "r" (0x8000000f)); -// enabled = 1; -// } -// //read the coutner value -// asm volatile("mrs %0, PMCCNTR_EL0" : "=r" (cc)); -// gettimeofday(&t,(struct timezone *) 0); -// result[0] = cc; -// result[1] = t.tv_usec; -// result[2] = t.tv_sec; -// } - -typedef struct timespec timespec; -timespec -diff(timespec start, timespec end) -{ - timespec temp; - if ((end.tv_nsec - start.tv_nsec) < 0) - { - temp.tv_sec = end.tv_sec - start.tv_sec - 1; - temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; - } - else - { - temp.tv_sec = end.tv_sec - start.tv_sec; - temp.tv_nsec = end.tv_nsec - start.tv_nsec; - } - return temp; -} - -timespec -sum(timespec t1, timespec t2) -{ - timespec temp; - if (t1.tv_nsec + t2.tv_nsec >= 1000000000) - { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } - else - { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; -} - -void -printTimeSpec(timespec t, const char * prefix) -{ - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); -} - -timespec -tic() -{ - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; -} - -timespec -toc(timespec * start_time, const char * prefix) -{ - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - timespec time_consump = diff(*start_time, current_time); - printTimeSpec(time_consump, prefix); - *start_time = current_time; - return time_consump; -} - -int -main() -{ - FILE * fp = fopen("input.csv", "r"); - - if (!fp) - { - printf("Can't open file\n"); - return -1; - } - - double time[DATA_SIZE]; -#if defined(INT_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) - { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } - - float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; - -#elif defined(FP_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) - { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } - - float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; -#else -#error "Must set data type: FP or INT" -#endif - - char buffer[1024]; - int row = 0, column = 0; - while (fgets(buffer, 1024, fp)) - { - column = 0; - row++; - - if (row == 1) - continue; - - char * value = strtok(buffer, ", "); - - while (value) - { - switch (column) - { - case 0: - time[row - 2] = atof(value); - break; - case 1: -#if defined(INT_DATA_TYPE) - - mag_x[row - 2] = atof(value); -#elif defined(FP_DATA_TYPE) - mag_x[row - 2] = atof(value); -#endif - break; - case 2: -#if defined(INT_DATA_TYPE) - - mag_y[row - 2] = atof(value); -#elif defined(FP_DATA_TYPE) - mag_y[row - 2] = atof(value); -#endif - break; - case 3: -#if defined(INT_DATA_TYPE) - - mag_z[row - 2] = atof(value); -#elif defined(FP_DATA_TYPE) - mag_z[row - 2] = atof(value); -#endif - break; - case 4: -#if defined(INT_DATA_TYPE) - //gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; - gyr_x[row - 2] = atof(value) * 61; -#elif defined(FP_DATA_TYPE) - gyr_x[row - 2] = atof(value) * 61; -#endif - break; - case 5: -#if defined(INT_DATA_TYPE) - //gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; - gyr_y[row - 2] = atof(value) * 61; -#elif defined(FP_DATA_TYPE) - gyr_y[row - 2] = atof(value) * 61; -#endif - break; - case 6: -#if defined(INT_DATA_TYPE) - //gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; - gyr_z[row - 2] = atof(value) * 61; -#elif defined(FP_DATA_TYPE) - gyr_z[row - 2] = atof(value) * 61; -#endif - break; - case 7: -#if defined(INT_DATA_TYPE) - //acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; - acc_x[row - 2] = atof(value) * 2; -#elif defined(FP_DATA_TYPE) - acc_x[row - 2] = atof(value) * 2; -#endif - break; - case 8: -#if defined(INT_DATA_TYPE) - //acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; - acc_y[row - 2] = atof(value) * 2; -#elif defined(FP_DATA_TYPE) - acc_y[row - 2] = atof(value) * 2; -#endif - break; - case 9: -#if defined(INT_DATA_TYPE) - //acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; - acc_z[row - 2] = atof(value) * 2; -#elif defined(FP_DATA_TYPE) - acc_z[row - 2] = atof(value) * 2; -#endif - break; - default: - break; - } - - value = strtok(NULL, ", "); - column++; - } - } - - fclose(fp); - - u_int64_t time_slots[ITERATION]; - - for (size_t idx = 0; idx < ITERATION; idx++) - { - // unsigned int init[3] = {0}; - // unsigned int start[3] = {0}; - // unsigned int end[3] = {0}; - // unsigned int overhead = 0; - // - // readticks(init, 0); - // readticks(start, 1); - // readticks(end, 1); - // - // overhead = end[0] - start[0]; - // readticks(init, 0); - // readticks(start, 1); - - timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) - { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - - } - - // end[0] = end[0] - start[0] - overhead; - // printf("clock cycles= %d\n", end[0]); - - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - } - - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) - { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); - -#if defined(FP_DATA_TYPE) - FILE * fptr = fopen("fp_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) - { - fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - } - fclose(fptr); -#elif defined(INT_DATA_TYPE) - FILE * fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) - { - - - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - } - fclose(fptr); - - -// void Mahony::computeAngles() -// { -// roll = atan2f(q0*q1 + q2*q3, 0.5f - q1*q1 - q2*q2); -// pitch = asinf(-2.0f * (q1*q3 - q0*q2)); -// yaw = atan2f(q1*q2 + q0*q3, 0.5f - q2*q2 - q3*q3); -// anglesComputed = 1; -// } - - -#endif - return 0; -} diff --git a/src/newton/Makefile b/src/newton/Makefile index e31a11bb2..f4132047b 100644 --- a/src/newton/Makefile +++ b/src/newton/Makefile @@ -1,5 +1,9 @@ TREEROOT = ../.. +ifndef MAX_PRECISION_BITS +MAX_PRECISION_BITS = 16 +endif + include $(TREEROOT)/config.local PRECOMMITHOOK = precommitStatisticsHook-$(OSTYPE).sh @@ -26,6 +30,11 @@ CXXFLAGS = $(PLATFORM_DBGFLAGS) $(PLATFORM_CFLAGS) $(PLATFORM_DFLAGS) $(PLATFORM CCFLAGS = $(PLATFORM_DBGFLAGS) $(PLATFORM_CFLAGS) $(PLATFORM_DFLAGS) $(PLATFORM_OPTFLAGS) LDFLAGS = $(PLATFORM_DBGFLAGS) -lm $(PLATFORM_LFLAGS) `pkg-config --libs 'libprotobuf-c >= 1.0.0'` +CCFLAGS += -DMAX_PRECISION_BITS=$(MAX_PRECISION_BITS) +CXXFLAGS += -DMAX_PRECISION_BITS=$(MAX_PRECISION_BITS) + + + CCFLAGS+=$(shell $(LLVM_CONFIG) --cflags) LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags) CXXFLAGS+=$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags) -fno-rtti @@ -327,6 +336,10 @@ cgi:lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a $(CGIOBJS) $(CONFIGPATH)/config.$ $(LD) $(LINKDIRS) $(LDFLAGS) $(CGIOBJS) $(LLVMLIBS) $(SYSTEMLIBS) -lflex-$(OSTYPE) $(LINKDIRS) $(LDFLAGS) -o $(CGI_TARGET) -lstdc++ + +maxprec.cfg: + @echo $(MAX_PRECISION_BITS) > maxprec.cfg + # # Objects # @@ -366,18 +379,27 @@ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION): newton-irPass-LLVMIR- $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< -newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-quantization.cpp +newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-quantization.cpp maxprec.cfg $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< -newton-irPass-LLVMIR-memoryAlignment.$(OBJECTEXTENSION): newton-irPass-LLVMIR-memoryAlignment.cpp +newton-irPass-LLVMIR-memoryAlignment.$(OBJECTEXTENSION): newton-irPass-LLVMIR-memoryAlignment.cpp maxprec.cfg $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< + + newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION): newton-irPass-LLVMIR-emitAssume.cpp $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< + +.PHONY: rebuild-quant-opt + +rebuild-quant-opt: + rm -f newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION) newton-irPass-LLVMIR-optimizeByRange.$(OBJECTEXTENSION) + $(MAKE) newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION) newton-irPass-LLVMIR-optimizeByRange.$(OBJECTEXTENSION) + version.c: $(HEADERS) Makefile echo 'char kNewtonVersion[] = "0.3-alpha-'`git rev-list --count HEAD`' ('`git rev-parse HEAD`') (build '`date '+%m-%d-%Y-%H:%M'`-`whoami`@`hostname -s`-`uname -s`-`uname -r`-`uname -m`\)\"\; > version.c diff --git a/src/newton/config.h b/src/newton/config.h index 5eed18ee1..0b6cc9841 100644 --- a/src/newton/config.h +++ b/src/newton/config.h @@ -3,8 +3,13 @@ //#define BIT_WIDTH 32 #define AUTO_QUANTIZATION 1 -#define IS_POINTER 0 +#define IS_POINTER 1 #define IS_MATRIX 0 +#ifndef MAX_PRECISION_BITS +#define MAX_PRECISION_BITS 16 + +//#define maxPrecisionBits MAX_PRECISION_BITS +#endif #endif // CONFIG_H diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 8abe3c1de..031106ff9 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -82,8 +82,11 @@ POSSIBILITY OF SUCH DAMAGE. using namespace llvm; -#define FRAC_BASE (1 << maxPrecisionBits) -#define BIT_WIDTH 32 +//#define FRAC_BASE (1 << maxPrecisionBits) +#define FRAC_BASE (1 << MAX_PRECISION_BITS) + + +#define BIT_WIDTH 16 //#define IS_POINTER 1 std::set whitelist = { "MadgwickAHRSupdate", @@ -803,15 +806,16 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } - int maxPrecisionBits = 0; - for (auto & typePrecisionBit : typePrecisionBits) - { - if (typePrecisionBit.second > maxPrecisionBits) - { - maxPrecisionBits = typePrecisionBit.second; - } - } - +// int maxPrecisionBits = 0; + int maxPrecisionBits = MAX_PRECISION_BITS; +// for (auto & typePrecisionBit : typePrecisionBits) +// { +// if (typePrecisionBit.second > maxPrecisionBits) +// { +// maxPrecisionBits = typePrecisionBit.second; +// } +// } +// flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); flexprint(N->Fe, N->Fm, N->Fpinfo, "bitwidth: %d\n", BIT_WIDTH); @@ -820,7 +824,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl */ // int BIT_WIDTH = 32; // maxPrecisionBits = 16; - maxPrecisionBits = 16; /* * get const global variables @@ -1060,7 +1063,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // * */ // if (useOverLoad) // cleanFunctionMap(Mod, callerMap); -// +//./ // if (useOverLoad) // overloadFunc(Mod, callerMap); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index ec054a637..43e979411 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -17,7 +17,7 @@ using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 32 +#define BIT_WIDTH 16 llvm::Value * performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned int FRAC_Q) @@ -359,7 +359,8 @@ shouldProcessFunction(Function & F) "qzero", "pone", "qone", - "__ieee754_exp" + "__ieee754_exp", + "twofft" // "__ieee754_log", }; @@ -858,7 +859,8 @@ handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) parentFunc->getName() == "pone" || parentFunc->getName() == "qone" || parentFunc->getName() == "__ieee754_exp"|| - parentFunc->getName() == "__ieee754_log") + parentFunc->getName() == "__ieee754_log"|| + parentFunc->getName() == "twofft") // ... { @@ -1517,13 +1519,20 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) { return; } + // If the PHI node is already of an integer type, skip quantization. + if (phi->getType()->isIntegerTy()) { + llvm::errs() << "PHI node already has an integer type, skipping quantization: " << *phi << "\n"; + return; + } + // Check if the PHI node is of pointer type. bool isPtr = phi->getType()->isPointerTy(); unsigned pointerAddr = 0; if (isPtr) pointerAddr = phi->getType()->getPointerAddressSpace(); - // Determine new PHI node type. + // Determine new PHI node type: if pointer, then quantizedType->getPointerTo(pointerAddr); + // otherwise, simply quantizedType. Type *newPhiType = isPtr ? quantizedType->getPointerTo(pointerAddr) : quantizedType; PHINode *newPhi = PHINode::Create(newPhiType, phi->getNumIncomingValues(), phi->getName() + ".quantized", phi); @@ -1556,10 +1565,10 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) { } if (!constFp->getValueAPF().isNegative()) { - llvm::errs() << "Detected positive infinity, mapping to max integer.\n"; + llvm::errs() << "Detected positive infinity, mapping to maximum integer.\n"; newVal = llvm::ConstantInt::get(quantizedType, maxVal, true); } else { - llvm::errs() << "Detected negative infinity, mapping to min integer.\n"; + llvm::errs() << "Detected negative infinity, mapping to minimum integer.\n"; newVal = llvm::ConstantInt::get(quantizedType, minVal, true); } } else { @@ -1593,6 +1602,7 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) { } + //void handlePhi(Instruction *inInstruction, Type *quantizedType) //{ // llvm::errs() << "Handling PHI\n"; From f5c135ed6cb79cffa86391c680b1d4d072e95ff2 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sat, 8 Mar 2025 12:12:59 +0000 Subject: [PATCH 149/213] update SQNR test for Mahony Addresses #2. --- applications/newton/llvm-ir/SQNR.sh | 63 +++++++++++++ applications/newton/llvm-ir/rms_error.py | 91 +++++++++++-------- applications/newton/llvm-ir/testMadgwick.sh | 1 + .../newton-irPass-LLVMIR-quantization.cpp | 9 +- 4 files changed, 124 insertions(+), 40 deletions(-) create mode 100755 applications/newton/llvm-ir/SQNR.sh diff --git a/applications/newton/llvm-ir/SQNR.sh b/applications/newton/llvm-ir/SQNR.sh new file mode 100755 index 000000000..4d35c6894 --- /dev/null +++ b/applications/newton/llvm-ir/SQNR.sh @@ -0,0 +1,63 @@ +#!/bin/bash + + + + +while [[ $# -gt 0 ]]; do + case $1 in + --algo) + if [[ "$2" == "MadgwickAHRS" || "$2" == "MahonyAHRS" ]]; then + ALGO="$2" + shift 2 + else + echo "Error: Invalid algorithm '$2'. Use 'MadgwickAHRS' or 'MahonyAHRS'." + exit 1 + fi + ;; + *) + echo "Usage: $0 [--algo MadgwickAHRS|MahonyAHRS]" + exit 1 + ;; + esac +done + +OUTPUT_FILE="snr_results_${ALGO,,}.txt" + +# Clear old file contents +> "$OUTPUT_FILE" + +# Directory where make is executed (source directory) +MAKE_DIR="/home/xyf/CoSense/src/newton" + +# Current directory (where the test scripts are located) +TEST_DIR=$(pwd) + +# Loop over MAX_PRECISION_BITS values from 13 to 16 +for i in {8..16}; do + echo "---------------------------" | tee -a "$OUTPUT_FILE" + echo "Current compile parameter: MAX_PRECISION_BITS = $i" | tee -a "$OUTPUT_FILE" + + # Switch to the make directory for compilation + pushd "$MAKE_DIR" > /dev/null + # Uncomment the following line if a full clean is needed: + # make clean + # Remove object files for quantization and optimizeByRange to force recompilation + rm -f newton-irPass-LLVMIR-quantization.o newton-irPass-LLVMIR-optimizeByRange.o + # Compile with the specified MAX_PRECISION_BITS value + make MAX_PRECISION_BITS=$i + popd > /dev/null + + # Switch back to the test scripts directory (current directory) + cd "$TEST_DIR" || exit + + # Run the test script + ./testMadgwick.sh "$ALGO" + + # Run the Python script to extract the SNR result (adjust grep keyword if needed) + SNR_LINE=$(python3 rms_error.py --filter "$ALGO" | grep "SQNR (dB)") + echo "MAX_PRECISION_BITS=$i, $SNR_LINE" | tee -a "$OUTPUT_FILE" + + echo "Compilation and test complete, result saved." | tee -a "$OUTPUT_FILE" +done + +echo "All tests completed, results saved in $OUTPUT_FILE." diff --git a/applications/newton/llvm-ir/rms_error.py b/applications/newton/llvm-ir/rms_error.py index ece1b2aad..35ba3f9e5 100644 --- a/applications/newton/llvm-ir/rms_error.py +++ b/applications/newton/llvm-ir/rms_error.py @@ -58,38 +58,46 @@ def calculate_rms_errors(fp_data, int_data): return rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms -def compute_snr(fp_data, int_data, assume_signal_power=1.0): +def compute_signal_power(data): """ - Calculate SNR (in dB). Assumes: - 1) The original signal power = assume_signal_power (default 1) - 2) Noise power = (RMS Error)^2 (overall RMS) + Calculate signal power, P_signal = mean(q0^2 + q1^2 + q2^2 + q3^2) """ - _, _, _, _, rms_overall, _ = calculate_rms_errors(fp_data, int_data) - p_noise = rms_overall ** 2 - p_signal = assume_signal_power - if p_noise == 0: - return float('inf') - snr_db = 10 * math.log10(p_signal / p_noise) - return snr_db + total = 0.0 + for quaternion in data: + q0, q1, q2, q3 = quaternion + total += (q0**2 + q1**2 + q2**2 + q3**2) + return total / len(data) -def compute_real_signal_power(fp_data): +def compute_sqnr(fp_data, int_data): """ - If you want to compute the real signal power from FP data. - P_signal = mean( q0^2 + q1^2 + q2^2 + q3^2 ) over the dataset. + Calculate Signal-to-Quantization-Noise Ratio (SQNR). + Uses floating-point data as the reference signal and quantization error as noise. + SQNR = 10 * log10(signal_power / noise_power) """ - total = 0.0 - for fp in fp_data: - q0, q1, q2, q3 = fp - total += (q0**2 + q1**2 + q2**2 + q3**2) - return total / len(fp_data) + # Calculate noise power (mean squared quantization error) + _, _, _, _, rms_overall, _ = calculate_rms_errors(fp_data, int_data) + noise_power = rms_overall ** 2 + + # Calculate signal power (using floating-point data) + signal_power = compute_signal_power(fp_data) + + # Handle division by zero case + if noise_power == 0: + return float('inf') + + # Calculate SQNR in dB + sqnr_db = 10 * math.log10(signal_power / noise_power) + return sqnr_db def main(): - parser = argparse.ArgumentParser(description="Calculate RMS error and SNR for quaternion data.") - parser.add_argument("--filter", choices=["madgwick", "mahony"], default="madgwick", - help="Select which algorithm's data to process (default: madgwick)") + parser = argparse.ArgumentParser(description="Calculate RMS errors and SQNR for quaternion data.") + parser.add_argument("--filter", choices=["MadgwickAHRS", "MahonyAHRS"], default="MadgwickAHRS", + help="Select which algorithm's data to process (default: MadgwickAHRS)") + parser.add_argument("--limit", type=int, default=1000, + help="Limit the number of data lines to process (default: 1000)") args = parser.parse_args() - if args.filter == "madgwick": + if args.filter == "MadgwickAHRS": fp_data = parse_data('fp_result.txt', 'Original: ') int_data = parse_data('int_result.txt', 'FIX: ') else: # mahony @@ -100,9 +108,12 @@ def main(): print("Mismatch in the number of quaternions between files!") return - # Use only the first 50 data lines - fp_data = fp_data[:50] - int_data = int_data[:50] + # Use specified number of data lines + data_limit = min(args.limit, len(fp_data)) if args.limit > 0 else len(fp_data) + fp_data = fp_data[:data_limit] + int_data = int_data[:data_limit] + + print(f"Processing {len(fp_data)} lines of data...") # Calculate RMS errors rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms = calculate_rms_errors(fp_data, int_data) @@ -113,17 +124,23 @@ def main(): print(f"Overall RMS Error: {rms_overall:.6f}") print(f"Average of RMS Values: {avg_rms:.6f}") - # Calculate SNR (assuming original signal power = 1) - snr_db = compute_snr(fp_data, int_data, assume_signal_power=1.0) - print(f"SNR (dB) [Assume P_signal=1]: {snr_db:.2f}") + # Calculate SQNR + sqnr_db = compute_sqnr(fp_data, int_data) + print(f"SQNR (dB): {sqnr_db:.2f}") + + # Calculate signal power (for debugging) + signal_power = compute_signal_power(fp_data) + print(f"Signal Power: {signal_power:.6f}") - # For debugging: print first line error values if needed - first_fp = fp_data[0] - first_int = int_data[0] - first_error = [first_fp[i] - first_int[i] for i in range(4)] - print(f"First line FP: {first_fp}") - print(f"First line INT: {first_int}") - print(f"First line errors (q0, q1, q2, q3): {first_error}") + # Debug info: print first line error values + if len(fp_data) > 0: + first_fp = fp_data[0] + first_int = int_data[0] + first_error = [first_fp[i] - first_int[i] for i in range(4)] + print("\nDebug Information:") + print(f"First line FP: {first_fp}") + print(f"First line INT: {first_int}") + print(f"First line errors (q0, q1, q2, q3): {first_error}") if __name__ == "__main__": - main() + main() \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index be6fd2f36..c4681c04b 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -4,6 +4,7 @@ # Set the algorithm name based on the first command-line argument, default to MadgwickAHRS ALGO=${1:-MadgwickAHRS} +ALGO=${1:-MahonyAHRS} # Validate the input (optional) if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" ]]; then diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 43e979411..bebbc47a6 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -17,7 +17,7 @@ using namespace llvm; unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) -#define BIT_WIDTH 16 + llvm::Value * performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned int FRAC_Q) @@ -340,8 +340,9 @@ bool isWhitelistedGlobal(const std::string & globalName) { // Define the whitelist of global variables - static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero","q0","q1","q2","q3"}; - return whitelist.find(globalName) != whitelist.end(); +// static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero","q0","q1","q2","q3"}; +// return whitelist.find(globalName) != whitelist.end(); + return true; } bool @@ -352,6 +353,8 @@ shouldProcessFunction(Function & F) "sensfusion6UpdateQImpl", "MadgwickAHRSupdate", "MadgwickAHRSupdateIMU", + "MahonyAHRSupdate", + "MahonyAHRSupdateIMU", "matrixMul", "matrixAdd", "matrixSub", From 189b2e7e3b38992fa89085f297617e6af24702c7 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 23 Mar 2025 12:31:04 +0000 Subject: [PATCH 150/213] update overflow checking Addresses #2. --- ...e2a17c4727c42120c3d791515eabc082b76067.txt | 48 ++++++++ ...5709452237c47c7cbdffaa146be03b2e8860f8.txt | 48 ++++++++ ...9ea318616cf24ca79a747f69c9eccac4a2427f.txt | 48 ++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 114 +++++++++++++++--- .../newton-irPass-LLVMIR-quantization.cpp | 77 +++++------- 5 files changed, 270 insertions(+), 65 deletions(-) create mode 100644 analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt create mode 100644 analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt create mode 100644 analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt diff --git a/analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt b/analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt new file mode 100644 index 000000000..eccdf4985 --- /dev/null +++ b/analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt @@ -0,0 +1,48 @@ + +changeset: 1743:1de2a17c4727c42120c3d791515eabc082b76067 +char kNewtonVersion[] = "0.3-alpha-1743 (1de2a17c4727c42120c3d791515eabc082b76067) (build 03-08-2025-12:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt b/analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt new file mode 100644 index 000000000..7f93435a6 --- /dev/null +++ b/analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt @@ -0,0 +1,48 @@ + +changeset: 1741:885709452237c47c7cbdffaa146be03b2e8860f8 +char kNewtonVersion[] = "0.3-alpha-1741 (885709452237c47c7cbdffaa146be03b2e8860f8) (build 02-27-2025-22:26-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt b/analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt new file mode 100644 index 000000000..36b3ca6e8 --- /dev/null +++ b/analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt @@ -0,0 +1,48 @@ + +changeset: 1742:e39ea318616cf24ca79a747f69c9eccac4a2427f +char kNewtonVersion[] = "0.3-alpha-1742 (e39ea318616cf24ca79a747f69c9eccac4a2427f) (build 03-03-2025-21:22-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 031106ff9..aeb848218 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -85,8 +85,43 @@ using namespace llvm; //#define FRAC_BASE (1 << maxPrecisionBits) #define FRAC_BASE (1 << MAX_PRECISION_BITS) +void checkOverflow(State * N, BoundInfo * boundInfo, int FRAC_Q) { + int maxVal, minVal; + if (BIT_WIDTH == 16) { + maxVal = INT16_MAX; + minVal = INT16_MIN; + } else if (BIT_WIDTH == 32) { + maxVal = INT32_MAX; + minVal = INT32_MIN; + } else { + flexprint(N->Fe, N->Fm, N->Fperr, "Unsupported BIT_WIDTH: %d\n", BIT_WIDTH); + return; + } + + for (const auto& entry : boundInfo->virtualRegisterRange) { + double scaledMin = entry.second.first * FRAC_BASE; + double scaledMax = entry.second.second * FRAC_BASE; + + std::string instStr = "unknown"; + if (Instruction* inst = dyn_cast(entry.first)) { + instStr = inst->getOpcodeName(); + } + + if ((scaledMin > maxVal && scaledMax > maxVal) || (scaledMin < minVal && scaledMax < minVal)) { + flexprint(N->Fe, N->Fm, N->Fperr, + "Definite overflow detected: %s range [%f, %f] when scaled by 2^%d is completely outside int%d bounds\n", + instStr.c_str(), entry.second.first, entry.second.second, + FRAC_Q, BIT_WIDTH); + } + else if (scaledMax > maxVal || scaledMin < minVal) { + flexprint(N->Fe, N->Fm, N->Fperr, + "Possible overflow detected: %s range [%f, %f] when scaled by 2^%d partially exceeds int%d bounds\n", + instStr.c_str(), entry.second.first, entry.second.second, + FRAC_Q, BIT_WIDTH); + } + } +} -#define BIT_WIDTH 16 //#define IS_POINTER 1 std::set whitelist = { "MadgwickAHRSupdate", @@ -907,23 +942,39 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl -// /* -// * analyze the range of all local variables in each function -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// std::map callerMap; -// callerMap.clear(); -// funcBoundInfo.clear(); -// bool useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } + /* + * analyze the range of all local variables in each function + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + std::map callerMap; + callerMap.clear(); + funcBoundInfo.clear(); + bool useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } + + /** + * Check for potential overflows + */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); + for (auto & funcPair : funcBoundInfo) { + checkOverflow(N, funcPair.second, maxPrecisionBits); + } + + + + + + + + // @@ -958,6 +1009,33 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl Mod->getFunctionList().push_front(mi); } } + + +// /* +// * analyze the range of all local variables in each function +// * */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// std::map callerMap; +// callerMap.clear(); +// funcBoundInfo.clear(); +// bool useOverLoad = false; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } +// +// /** +// * Check for potential overflows +// */ +// flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); +// for (auto & funcPair : funcBoundInfo) { +// checkOverflow(N, funcPair.second, maxPrecisionBits); +// } // // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); // for (auto & mi : *Mod) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index bebbc47a6..db5318197 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -642,52 +642,7 @@ handleGEPInstruction(llvm::GetElementPtrInst * gep, llvm::errs() << "Replaced GEP instruction for " << origConst.getName() << "\n"; } -// 主函数:遍历 origConst 的所有使用,在白名单函数中替换为量化后的全局变量 -//void -//replaceInternalConstantUses(Module * module, -// GlobalVariable & origConst, -// GlobalVariable & quantizedConst) -//{ -// std::vector usesToReplace; -// -// for (auto it = origConst.use_begin(), end = origConst.use_end(); it != end;) -// { -// Use & use = *it++; -// Value * user = use.getUser(); -// -// // 如果用户是 ConstantExpr -// if (ConstantExpr * constExpr = dyn_cast(user)) -// { -// handleConstantExprUse(constExpr, origConst, quantizedConst); -// continue; -// } -// -// // 如果用户是指令,且所在函数在白名单中 -// Instruction * inst = dyn_cast(user); -// if (!inst || !inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) -// continue; -// -// // 如果该指令是 GEP,并且结果类型为 double*,则进行替换 -// if (GetElementPtrInst * gep = dyn_cast(inst)) -// { -// if (gep->getResultElementType()->isDoubleTy()) -// { -// handleGEPInstruction(gep, origConst, quantizedConst); -// continue; // 该 GEP 指令已经被替换,不需要再添加到 usesToReplace -// } -// } -// // 其他情况,将该 use 加入待统一替换列表 -// usesToReplace.push_back(&use); -// } -// -// // 对剩余未处理的使用,统一替换为量化后的全局变量 -// for (Use * use : usesToReplace) -// use->set(&quantizedConst); -// -// errs() << "Replaced all uses of " << origConst.getName() -// << " with " << quantizedConst.getName() -// << " in whitelisted functions.\n"; -//} + void replaceInternalConstantUses(llvm::Module *module, @@ -808,6 +763,31 @@ quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType llvm::errs() << "Replaced load with quantized integer value.\n"; } + +//void +//quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +//{ +// Value * pointerOperand = loadInst->getPointerOperand(); +// llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; +// +// +// Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); +// +// +// Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); +// +// +// Value * roundingOffset = ConstantFP::get(loadedType, 0.5); +// Value * roundedValue = Builder.CreateFAdd(scaledValue, roundingOffset, loadInst->getName() + ".rounded_ptr"); +// +// Value * quantizedValue = Builder.CreateFPToSI(roundedValue, quantizedType, loadInst->getName() + ".quantized_ptr"); +// +// loadInst->replaceAllUsesWith(quantizedValue); +// loadInst->eraseFromParent(); +// +// llvm::errs() << "Replaced load with quantized integer value with rounding.\n"; +//} + void quantizeMatrixFloat(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { @@ -1295,7 +1275,9 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct {2.0, {1, false}}, {4.0, {2, false}}, {8.0, {3, false}}, - {1 / 128.0, {7, true}}}; + {1 / 128.0, {7, true}}, + {1 / 256.0, {8, true}}, + {1 / 512.0, {9, true}}}; double value = constFP->getValueAPF().convertToDouble(); auto it = constantsMap.find(value); if (it != constantsMap.end()) @@ -1308,6 +1290,7 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct return false; } + void handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) { From d5b36a40859e2c6368e4ec1c01205b230ed11180 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 23 Mar 2025 22:51:55 +0000 Subject: [PATCH 151/213] update resolution Addresses #2. --- ...c135ed6cb79cffa86391c680b1d4d072e95ff2.txt | 48 ++++++++++++++++ applications/newton/sensors/BMX055.nt | 13 +++-- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 56 ++++++++++++++++++- 3 files changed, 112 insertions(+), 5 deletions(-) create mode 100644 analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt diff --git a/analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt b/analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt new file mode 100644 index 000000000..a9583b42f --- /dev/null +++ b/analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt @@ -0,0 +1,48 @@ + +changeset: 1744:f5c135ed6cb79cffa86391c680b1d4d072e95ff2 +char kNewtonVersion[] = "0.3-alpha-1744 (f5c135ed6cb79cffa86391c680b1d4d072e95ff2) (build 03-23-2025-12:31-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/sensors/BMX055.nt b/applications/newton/sensors/BMX055.nt index 74a342280..08bb8052e 100644 --- a/applications/newton/sensors/BMX055.nt +++ b/applications/newton/sensors/BMX055.nt @@ -161,9 +161,9 @@ bmx055: sensor ( # # Range of Angular Rate can be found in Table 3 on page 14. # - range bmx055xAngularRate == [0 ajf, 2000 ajf], - range bmx055yAngularRate == [0 ajf, 2000 ajf], - range bmx055zAngularRate == [0 ajf, 2000 ajf], + range bmx055xAngularRate == [-2000 ajf, 2000 ajf], + range bmx055yAngularRate == [-2000 ajf, 2000 ajf], + range bmx055zAngularRate == [-2000 ajf, 2000 ajf], # # The operation sequence needed to get a sample @@ -196,7 +196,7 @@ bmx055: sensor ( yAccelLow := read 16r04; bmx055yAcceleration = (yAccelHigh << 4) | (yAccelLow >> 4); # bmx055yAcceleration = yAccelHigh[:4] + yAccelLow[4:]; - }, + },# @sensitivity bmx055xAcceleration = 1024 LSB/g interface bmx055zAcceleration == i2c (address: 16r18) { @@ -391,6 +391,11 @@ bmx055: sensor ( (16, -1) }, + + # @sensitivity bmx055xAcceleration = 1024 LSB/g + + + # # Accuracy settings list for the sensor. This is a list of (accuracy, cost) pairs. # diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index aeb848218..a93defccd 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -79,7 +79,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include - +#include using namespace llvm; //#define FRAC_BASE (1 << maxPrecisionBits) @@ -617,6 +617,11 @@ removeQuantizedSuffixInModule(llvm::Module & M) } } +double computeResolution(Modality *mod) +{ + return (mod->rangeUpperBound - mod->rangeLowerBound) / (1 << mod->precisionBits); +} + void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) { @@ -824,6 +829,8 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeLowerBound: %f\n", currentModality->rangeLowerBound); flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeUpperBound: %f\n", currentModality->rangeUpperBound); typeRange.emplace(currentModality->identifier, std::make_pair(currentModality->rangeLowerBound, currentModality->rangeUpperBound)); + double resolution = computeResolution(currentModality); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tresolution: %.10f\n", resolution); } } @@ -838,6 +845,9 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); + + //resolution + } } @@ -851,6 +861,50 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } // } // + double minResolution = 0.0; + bool isFirstSensor = true; + + for (Modality* currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) { + // Calculate resolution + double resolution = (currentModality->rangeUpperBound - currentModality->rangeLowerBound) / + (1 << currentModality->precisionBits); + + // Store and print + currentModality->resolution = resolution; + + // Initialize or compare for minimum + if (isFirstSensor) { + minResolution = resolution; + isFirstSensor = false; + } else if (resolution < minResolution) { + minResolution = resolution; + } + } + + // Only print if we had at least one sensor + if (!isFirstSensor) { + flexprint(N->Fe, N->Fm, N->Fpinfo, "Minimum resolution across all sensors: %f\n", minResolution); + } else { + flexprint(N->Fe, N->Fm, N->Fpinfo, "No sensors found to calculate minimum resolution\n"); + } + + + + + + + + + + + + + + + + + + flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); flexprint(N->Fe, N->Fm, N->Fpinfo, "bitwidth: %d\n", BIT_WIDTH); From 35547345a1f6a7ae94dc279486ae5a33c33e7b2a Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 27 Mar 2025 20:51:20 +0000 Subject: [PATCH 152/213] update removal of unused constant Addresses #2. --- ...9b2e7e3b38992fa89085f297617e6af24702c7.txt | 48 ++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 746 ++++++++++-------- 2 files changed, 467 insertions(+), 327 deletions(-) create mode 100644 analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt diff --git a/analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt b/analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt new file mode 100644 index 000000000..38de37e8a --- /dev/null +++ b/analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt @@ -0,0 +1,48 @@ + +changeset: 1745:189b2e7e3b38992fa89085f297617e6af24702c7 +char kNewtonVersion[] = "0.3-alpha-1745 (189b2e7e3b38992fa89085f297617e6af24702c7) (build 03-23-2025-22:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index a93defccd..c493b0fdb 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -82,38 +82,49 @@ POSSIBILITY OF SUCH DAMAGE. #include using namespace llvm; -//#define FRAC_BASE (1 << maxPrecisionBits) +// #define FRAC_BASE (1 << maxPrecisionBits) #define FRAC_BASE (1 << MAX_PRECISION_BITS) -void checkOverflow(State * N, BoundInfo * boundInfo, int FRAC_Q) { +void +checkOverflow(State * N, BoundInfo * boundInfo, int FRAC_Q) +{ int maxVal, minVal; - if (BIT_WIDTH == 16) { + if (BIT_WIDTH == 16) + { maxVal = INT16_MAX; minVal = INT16_MIN; - } else if (BIT_WIDTH == 32) { + } + else if (BIT_WIDTH == 32) + { maxVal = INT32_MAX; minVal = INT32_MIN; - } else { + } + else + { flexprint(N->Fe, N->Fm, N->Fperr, "Unsupported BIT_WIDTH: %d\n", BIT_WIDTH); return; } - for (const auto& entry : boundInfo->virtualRegisterRange) { + for (const auto & entry : boundInfo->virtualRegisterRange) + { double scaledMin = entry.second.first * FRAC_BASE; double scaledMax = entry.second.second * FRAC_BASE; std::string instStr = "unknown"; - if (Instruction* inst = dyn_cast(entry.first)) { + if (Instruction * inst = dyn_cast(entry.first)) + { instStr = inst->getOpcodeName(); } - if ((scaledMin > maxVal && scaledMax > maxVal) || (scaledMin < minVal && scaledMax < minVal)) { + if ((scaledMin > maxVal && scaledMax > maxVal) || (scaledMin < minVal && scaledMax < minVal)) + { flexprint(N->Fe, N->Fm, N->Fperr, "Definite overflow detected: %s range [%f, %f] when scaled by 2^%d is completely outside int%d bounds\n", instStr.c_str(), entry.second.first, entry.second.second, FRAC_Q, BIT_WIDTH); } - else if (scaledMax > maxVal || scaledMin < minVal) { + else if (scaledMax > maxVal || scaledMin < minVal) + { flexprint(N->Fe, N->Fm, N->Fperr, "Possible overflow detected: %s range [%f, %f] when scaled by 2^%d partially exceeds int%d bounds\n", instStr.c_str(), entry.second.first, entry.second.second, @@ -122,7 +133,7 @@ void checkOverflow(State * N, BoundInfo * boundInfo, int FRAC_Q) { } } -//#define IS_POINTER 1 +// #define IS_POINTER 1 std::set whitelist = { "MadgwickAHRSupdate", "MahonyAHRSupdate", @@ -132,29 +143,60 @@ std::set whitelist = { "qzero", "pone", "qone", - "__ieee754_exp" -}; + "__ieee754_exp"}; -void handleGlobalStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits) { - auto *pointerOperand = storeInst->getPointerOperand(); +void eraseUnusedConstant(Module &M) { + std::set quantizedGlobals; + + // First pass: collect all _quantized names + for (auto &GV : M.globals()) { + if (GV.getName().endswith("_quantized")) { + std::string baseName = GV.getName().str().substr(0, GV.getName().size() - 10); + quantizedGlobals.insert(baseName); + } + } + // Second pass: delete unused original globals + std::vector toDelete; + for (auto &GV : M.globals()) { + std::string name = GV.getName().str(); + if (GV.use_empty() && quantizedGlobals.count(name)) { + toDelete.push_back(&GV); + } + } + for (auto *GV : toDelete) { + GV->eraseFromParent(); + } +} + + + +void +handleGlobalStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) +{ + auto * pointerOperand = storeInst->getPointerOperand(); // Ensure the operation is on a global variable - if (auto *quantizedGlobalVar = dyn_cast(pointerOperand)) { + if (auto * quantizedGlobalVar = dyn_cast(pointerOperand)) + { llvm::errs() << "Processing quantized global variable: " << quantizedGlobalVar->getName() << "\n"; // Identify the corresponding original global variable (e.g., remove "_quantized" suffix) std::string originalName = quantizedGlobalVar->getName().str(); - if (originalName.size() > 10 && originalName.compare(originalName.size() - 10, 10, "_quantized") == 0) { + if (originalName.size() > 10 && originalName.compare(originalName.size() - 10, 10, "_quantized") == 0) + { originalName = originalName.substr(0, originalName.size() - 10); - } else { + } + else + { llvm::errs() << "Skipping: No matching original global for " << quantizedGlobalVar->getName() << "\n"; return; } // Find the original global variable - GlobalVariable *originalGlobalVar = quantizedGlobalVar->getParent()->getNamedGlobal(originalName); - if (!originalGlobalVar || !originalGlobalVar->getType()->getElementType()->isFloatingPointTy()) { + GlobalVariable * originalGlobalVar = quantizedGlobalVar->getParent()->getNamedGlobal(originalName); + if (!originalGlobalVar || !originalGlobalVar->getType()->getElementType()->isFloatingPointTy()) + { llvm::errs() << "Skipping: Original global variable not found or not floating-point: " << originalName << "\n"; return; } @@ -162,39 +204,38 @@ void handleGlobalStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis llvm::errs() << "Found corresponding original global variable: " << originalGlobalVar->getName() << "\n"; // Check if the previous instruction is `trunc` - Instruction *prevInst = storeInst->getPrevNode(); - if (!prevInst || !isa(prevInst)) { + Instruction * prevInst = storeInst->getPrevNode(); + if (!prevInst || !isa(prevInst)) + { llvm::errs() << "Skipping: Previous instruction is not trunc.\n"; return; } // Load the integer value from the quantized global variable - auto *loadInst = Builder.CreateLoad(quantizedGlobalVar->getType()->getPointerElementType(), quantizedGlobalVar); + auto * loadInst = Builder.CreateLoad(quantizedGlobalVar->getType()->getPointerElementType(), quantizedGlobalVar); // Convert the integer value to a floating-point value - Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); - - + Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); // Perform dequantization - Value *dequantizedValue = Builder.CreateFMul( + Value * dequantizedValue = Builder.CreateFMul( convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE)); // Store the dequantized floating-point value back into the original global variable Builder.CreateStore(dequantizedValue, originalGlobalVar); llvm::errs() << "Dequantized and stored value for original global variable: " << originalGlobalVar->getName() << "\n"; - } else { + } + else + { llvm::errs() << "Pointer operand is not a global variable. Skipping.\n"; } } - - void -handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits) +handlePointerStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) { - auto *pointerOperand = storeInst->getPointerOperand(); + auto * pointerOperand = storeInst->getPointerOperand(); if (!pointerOperand->getType()->getPointerElementType()->isIntegerTy(BIT_WIDTH)) { @@ -202,21 +243,21 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB return; } - auto *loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); if (isa(loadInst->getPointerOperand())) { llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; return; } - Value *convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); - Value *dividedValue = Builder.CreateFMul( + Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); + Value * dividedValue = Builder.CreateFMul( convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE)); - if (auto *bitcastInst = dyn_cast(pointerOperand)) + if (auto * bitcastInst = dyn_cast(pointerOperand)) { - Value *finalStorePtr = nullptr; - bool isValidSource = false; + Value * finalStorePtr = nullptr; + bool isValidSource = false; llvm::errs() << "BIT_WIDTH: " << BIT_WIDTH << "\n"; // Determine the final store pointer based on bit width switch (BIT_WIDTH) @@ -225,12 +266,12 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB case 16: if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) { - auto *i32Ptr = bitcastInst->getOperand(0); - if (auto *floatBitcast = dyn_cast(i32Ptr)) + auto * i32Ptr = bitcastInst->getOperand(0); + if (auto * floatBitcast = dyn_cast(i32Ptr)) { if (floatBitcast->getSrcTy()->getPointerElementType()->isFloatTy()) { - finalStorePtr = floatBitcast->getOperand(0); // Original float* + finalStorePtr = floatBitcast->getOperand(0); // Original float* isValidSource = true; } } @@ -240,7 +281,7 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB case 32: if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) { - finalStorePtr = bitcastInst->getOperand(0); // Original float* + finalStorePtr = bitcastInst->getOperand(0); // Original float* isValidSource = true; } break; @@ -262,26 +303,26 @@ handlePointerStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionB } } - - -void handleMatrixStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecisionBits) { - Value *valueOperand = storeInst->getValueOperand(); - Value *pointerOperand = storeInst->getPointerOperand(); +void +handleMatrixStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) +{ + Value * valueOperand = storeInst->getValueOperand(); + Value * pointerOperand = storeInst->getPointerOperand(); // Ensure the stored value is an integer and the destination is a float pointer - Type *valueType = valueOperand->getType(); - Type *pointerElementType = pointerOperand->getType()->getPointerElementType(); + Type * valueType = valueOperand->getType(); + Type * pointerElementType = pointerOperand->getType()->getPointerElementType(); - if (valueType->isIntegerTy() && pointerElementType->isFloatingPointTy()) { + if (valueType->isIntegerTy() && pointerElementType->isFloatingPointTy()) + { llvm::errs() << "Processing matrix store (quantized to dequantized): " << *storeInst << "\n"; // Convert integer value to floating-point (dequantization step 1) llvm::errs() << "Converting integer to float: " << *valueOperand << "\n"; - Value *convertedFloat = Builder.CreateSIToFP(valueOperand, Type::getFloatTy(storeInst->getContext()), storeInst->getName() + ".dequantized"); - + Value * convertedFloat = Builder.CreateSIToFP(valueOperand, Type::getFloatTy(storeInst->getContext()), storeInst->getName() + ".dequantized"); // Perform dequantization by multiplying by (1 / fracBase) - Value *dequantizedValue = Builder.CreateFMul( + Value * dequantizedValue = Builder.CreateFMul( convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE), storeInst->getName() + ".scaled_back"); // Store the dequantized floating-point value back to the original float memory location @@ -291,38 +332,43 @@ void handleMatrixStore(StoreInst *storeInst, IRBuilder<> &Builder, int maxPrecis // Remove the original store instruction storeInst->eraseFromParent(); - } else { + } + else + { llvm::errs() << "Skipping store: Not storing i32 into float*.\n"; } } -void handleReturnValue(ReturnInst *retInst, int maxPrecisionBits) { +void +handleReturnValue(ReturnInst * retInst, int maxPrecisionBits) +{ // 如果返回指令没有返回值,则不处理 if (!retInst->getReturnValue()) return; - Value *retVal = retInst->getReturnValue(); + Value * retVal = retInst->getReturnValue(); // 如果返回值不是整数类型,则不做反量化处理 - if (!retVal->getType()->isIntegerTy()) { + if (!retVal->getType()->isIntegerTy()) + { errs() << "Return value is not integer type, skipping dequantization.\n"; return; } IRBuilder<> Builder(retInst); - Type *targetType = Type::getDoubleTy(retInst->getContext()); + Type * targetType = Type::getDoubleTy(retInst->getContext()); // 将固定点整数转换为浮点数(double) - Value *fpVal = Builder.CreateSIToFP(retVal, targetType); + Value * fpVal = Builder.CreateSIToFP(retVal, targetType); // 创建常量 1/FRAC_BASE,注意用 llvm::ConstantFP 避免歧义 - llvm::Constant *oneDivFrac = llvm::ConstantFP::get(targetType, 1.0 / FRAC_BASE); + llvm::Constant * oneDivFrac = llvm::ConstantFP::get(targetType, 1.0 / FRAC_BASE); // 计算反量化结果:fpVal * (1/FRAC_BASE) - Value *dequantizedVal = Builder.CreateFMul(fpVal, oneDivFrac); + Value * dequantizedVal = Builder.CreateFMul(fpVal, oneDivFrac); // 构造新的返回指令,返回 dequantizedVal(double 类型) - ReturnInst *newRet = ReturnInst::Create(retInst->getContext(), dequantizedVal, retInst); + ReturnInst * newRet = ReturnInst::Create(retInst->getContext(), dequantizedVal, retInst); // 删除原有返回指令 retInst->eraseFromParent(); @@ -330,14 +376,12 @@ void handleReturnValue(ReturnInst *retInst, int maxPrecisionBits) { errs() << "Replaced return with dequantized value: " << *newRet << "\n"; } - - -void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) +void +dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) { IRBuilder<> Builder(storeInst->getNextNode()); llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; - #if IS_MATRIX handleMatrixStore(storeInst, Builder, maxPrecisionBits); #elif IS_POINTER @@ -347,74 +391,96 @@ void dequantizeResults(StoreInst *storeInst, Function &F, int maxPrecisionBits) #else handleGlobalStore(storeInst, Builder, maxPrecisionBits); #endif - } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// bool enableAutoQuantization = false; -void detectFloatingPointOps(Module &Mod) { - bool hasFloatOps = false; - std::map floatOpCounts; // Map to store the count of floating-point operations per function +void +detectFloatingPointOps(Module & Mod) +{ + bool hasFloatOps = false; + std::map floatOpCounts; // Map to store the count of floating-point operations per function - for (auto &F : Mod) { - int functionFloatOpCount = 0; // Counter for floating-point operations in the current function + for (auto & F : Mod) + { + int functionFloatOpCount = 0; // Counter for floating-point operations in the current function // Analyze function parameters - int paramCount = 0; - int returnCount = 0; - std::vector paramTypes; // To store parameter types - for (auto &Arg : F.args()) { + int paramCount = 0; + int returnCount = 0; + std::vector paramTypes; // To store parameter types + for (auto & Arg : F.args()) + { paramCount++; - if (Arg.getType()->isPointerTy()) { + if (Arg.getType()->isPointerTy()) + { paramTypes.push_back("pointer"); - } else if (Arg.getType()->isFloatingPointTy()) { + } + else if (Arg.getType()->isFloatingPointTy()) + { paramTypes.push_back("floating-point"); - } else if (Arg.getType()->isIntegerTy()) { + } + else if (Arg.getType()->isIntegerTy()) + { paramTypes.push_back("integer"); - } else { + } + else + { paramTypes.push_back("unknown"); } } // Analyze function return type - std::string returnType = "void"; // Default return type - if (!F.getReturnType()->isVoidTy()) { - if (F.getReturnType()->isPointerTy()) { + std::string returnType = "void"; // Default return type + if (!F.getReturnType()->isVoidTy()) + { + if (F.getReturnType()->isPointerTy()) + { returnType = "pointer"; - } else if (F.getReturnType()->isFloatingPointTy()) { + } + else if (F.getReturnType()->isFloatingPointTy()) + { returnType = "floating-point"; - } else if (F.getReturnType()->isIntegerTy()) { + } + else if (F.getReturnType()->isIntegerTy()) + { returnType = "integer"; - } else { + } + else + { returnType = "unknown"; } } - for (auto &BB : F) { - for (auto &I : BB) { + for (auto & BB : F) + { + for (auto & I : BB) + { // Check if the instruction is a floating-point operation if (I.getOpcode() == Instruction::FAdd || I.getOpcode() == Instruction::FMul || I.getOpcode() == Instruction::FSub || - I.getOpcode() == Instruction::FDiv) { + I.getOpcode() == Instruction::FDiv) + { hasFloatOps = true; - functionFloatOpCount++; // Increment the counter for this function -// llvm::errs() << "Detected floating-point operation in function: " -// << F.getName() << " - Instruction: " << I << "\n"; + functionFloatOpCount++; // Increment the counter for this function + // llvm::errs() << "Detected floating-point operation in function: " + // << F.getName() << " - Instruction: " << I << "\n"; } - // Check if the instruction is a return - if (isa(I)) { + if (isa(I)) + { returnCount++; } } } // Store the count for this function - if (functionFloatOpCount > 0) { + if (functionFloatOpCount > 0) + { floatOpCounts[F.getName().str()] = functionFloatOpCount; } @@ -423,36 +489,43 @@ void detectFloatingPointOps(Module &Mod) { llvm::errs() << " Return Type: " << returnType << "\n"; llvm::errs() << " Parameter Count: " << paramCount << "\n"; llvm::errs() << " Parameter Types: "; - for (const auto &type : paramTypes) { + for (const auto & type : paramTypes) + { llvm::errs() << type << " "; } llvm::errs() << "\n"; -//f + // f } // Output the results - if (hasFloatOps) { + if (hasFloatOps) + { llvm::errs() << "Floating-point operations detected in the module.\n"; - for (const auto &entry : floatOpCounts) { + for (const auto & entry : floatOpCounts) + { llvm::errs() << "Function: " << entry.first << " - Floating-point operations: " << entry.second << "\n"; } llvm::errs() << "Enabling Auto-Quantization.\n"; enableAutoQuantization = true; - } else { + } + else + { llvm::errs() << "No floating-point operations detected. Skipping Auto-Quantization.\n"; } } - - -void checkFPUAvailability(Module &Mod) { - bool hasFPU = false; +void +checkFPUAvailability(Module & Mod) +{ + bool hasFPU = false; std::set detectedFeatures; // Iterate over functions to check attributes - for (auto &F : Mod) { - if (F.hasFnAttribute("target-features")) { + for (auto & F : Mod) + { + if (F.hasFnAttribute("target-features")) + { std::string features = F.getFnAttribute("target-features").getValueAsString().str(); detectedFeatures.insert(features); @@ -461,7 +534,8 @@ void checkFPUAvailability(Module &Mod) { features.find("+sse2") != std::string::npos || features.find("+avx") != std::string::npos || features.find("+x87") != std::string::npos || - features.find("+fma") != std::string::npos) { + features.find("+fma") != std::string::npos) + { hasFPU = true; } @@ -469,63 +543,79 @@ void checkFPUAvailability(Module &Mod) { if (features.find("+vfp") != std::string::npos || features.find("+neon") != std::string::npos || features.find("+fp-armv8") != std::string::npos || - features.find("+fp16") != std::string::npos) { + features.find("+fp16") != std::string::npos) + { hasFPU = true; } } } // Print detected target features (only once) - if (!detectedFeatures.empty()) { + if (!detectedFeatures.empty()) + { llvm::errs() << "Target Features: "; - for (const auto &feature : detectedFeatures) { + for (const auto & feature : detectedFeatures) + { llvm::errs() << feature << " "; } llvm::errs() << "\n"; } // Final decision based on FPU detection - if (hasFPU) { + if (hasFPU) + { llvm::errs() << "FPU detected via function attributes. \n"; - } else { + } + else + { llvm::errs() << "No FPU detected. Enabling Auto-Quantization.\n"; enableAutoQuantization = true; } } - /////////////////////////////////////////////////////////////////////////////////////////////////////////////// - // Process functions that are whitelisted for dequantization -void processWhitelistedFunctions(Module &module, const std::set &whitelist, int maxPrecisionBits) { +void +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) +{ // 对于白名单中的函数,我们先遍历,收集所有需要处理的返回指令 - for (Function &F : module) { - if (whitelist.find(F.getName().str()) != whitelist.end()) { + for (Function & F : module) + { + if (whitelist.find(F.getName().str()) != whitelist.end()) + { llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; std::vector retWorkList; // 遍历函数基本块,并将所有返回指令收集到 retWorkList 中 - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { - if (ReturnInst *retInst = dyn_cast(&I)) { + for (BasicBlock & BB : F) + { + for (Instruction & I : BB) + { + if (ReturnInst * retInst = dyn_cast(&I)) + { // 仅处理返回值存在且为整数类型的返回指令 - if (retInst->getReturnValue() && retInst->getReturnValue()->getType()->isIntegerTy()) { + if (retInst->getReturnValue() && retInst->getReturnValue()->getType()->isIntegerTy()) + { retWorkList.push_back(retInst); } } } } // 依次处理收集到的返回指令 - for (ReturnInst *retInst : retWorkList) { + for (ReturnInst * retInst : retWorkList) + { handleReturnValue(retInst, maxPrecisionBits); } // 同时继续处理其他指令,例如 StoreInst(这部分你已有代码) - for (BasicBlock &BB : F) { - for (Instruction &I : BB) { - //llvm::errs() << "Processing instruction: " << I << "\n"; - if (auto *storeInst = dyn_cast(&I)) { + for (BasicBlock & BB : F) + { + for (Instruction & I : BB) + { + // llvm::errs() << "Processing instruction: " << I << "\n"; + if (auto * storeInst = dyn_cast(&I)) + { llvm::errs() << "Found valid StoreInst.\n"; dequantizeResults(storeInst, F, maxPrecisionBits); } @@ -535,9 +625,8 @@ void processWhitelistedFunctions(Module &module, const std::set &wh } } - -//void -//processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) +// void +// processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) //{ // for (auto & F : module) // { @@ -566,7 +655,7 @@ void processWhitelistedFunctions(Module &module, const std::set &wh // } // } // -//} +// } // Function to save the IR of a module to a file void @@ -617,7 +706,8 @@ removeQuantizedSuffixInModule(llvm::Module & M) } } -double computeResolution(Modality *mod) +double +computeResolution(Modality * mod) { return (mod->rangeUpperBound - mod->rangeLowerBound) / (1 << mod->precisionBits); } @@ -638,7 +728,7 @@ dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) dumpedFile.close(); } -//void dumpIR(State *N, std::string fileSuffix, const std::unique_ptr &Mod) { +// void dumpIR(State *N, std::string fileSuffix, const std::unique_ptr &Mod) { // StringRef filePath(N->llvmIR); // std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; // std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; @@ -673,7 +763,7 @@ dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) // // dumpedFile.close(); // llvm::errs() << "DumpIR: File closed.\n"; -//} +// } void mergeBoundInfo(BoundInfo * dst, const BoundInfo * src) @@ -846,25 +936,33 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); - //resolution - + // resolution } } -// int maxPrecisionBits = 0; + // int maxPrecisionBits = 0; + // int maxPrecisionBits = MAX_PRECISION_BITS; + // for (auto & typePrecisionBit : typePrecisionBits) + // { + // if (typePrecisionBit.second > maxPrecisionBits) + // { + // maxPrecisionBits = typePrecisionBit.second; + // } + // } + // + + + // int maxPrecisionBits = 0; int maxPrecisionBits = MAX_PRECISION_BITS; -// for (auto & typePrecisionBit : typePrecisionBits) -// { -// if (typePrecisionBit.second > maxPrecisionBits) -// { -// maxPrecisionBits = typePrecisionBit.second; -// } -// } -// + + /** + * Precision Analysis + */ double minResolution = 0.0; - bool isFirstSensor = true; + bool isFirstSensor = true; - for (Modality* currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) { + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + { // Calculate resolution double resolution = (currentModality->rangeUpperBound - currentModality->rangeLowerBound) / (1 << currentModality->precisionBits); @@ -873,46 +971,55 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl currentModality->resolution = resolution; // Initialize or compare for minimum - if (isFirstSensor) { + if (isFirstSensor) + { minResolution = resolution; isFirstSensor = false; - } else if (resolution < minResolution) { + } + else if (resolution < minResolution) + { minResolution = resolution; } } - // Only print if we had at least one sensor - if (!isFirstSensor) { - flexprint(N->Fe, N->Fm, N->Fpinfo, "Minimum resolution across all sensors: %f\n", minResolution); - } else { - flexprint(N->Fe, N->Fm, N->Fpinfo, "No sensors found to calculate minimum resolution\n"); - } - - - - - - - - - - - - + double fracQ_exact = -log2(minResolution) - 1.0; + int fracQ = (int)ceil(fracQ_exact); + flexprint(N->Fe, N->Fm, N->Fpinfo, "Minimum resolution across all sensors: %f\n", minResolution); + flexprint(N->Fe, N->Fm, N->Fpinfo, "Required FRAC_Q: ceil(-log2(minResolution) - 1) = ceil(%f) = %d\n", + fracQ_exact, fracQ); + /** + * Bitwidth Setting + */ +// int maxPrecisionBits = 0; +// // int maxPrecisionBits = MAX_PRECISION_BITS; +// for (auto & typePrecisionBit : typePrecisionBits) +// { +// if (typePrecisionBit.second > maxPrecisionBits) +// { +// maxPrecisionBits = typePrecisionBit.second; +// } +// } +// +// int BIT_WIDTH = (maxPrecisionBits > 16) ? 32 : 16; +// flexprint(N->Fe, N->Fm, N->Fpinfo, +// "Max precisionBits among sensors: %d → BIT_WIDTH = %d\n", +// maxPrecisionBits, BIT_WIDTH); +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "bitwidth: %d => using %s\n", +// BIT_WIDTH, +// (BIT_WIDTH == 32 ? "int32_t (i32 fix)" : "int16_t (i16 fix)")); - flexprint(N->Fe, N->Fm, N->Fpinfo, "maxPrecisionBits: %d\n", maxPrecisionBits); - flexprint(N->Fe, N->Fm, N->Fpinfo, "bitwidth: %d\n", BIT_WIDTH); /** * Config */ -// int BIT_WIDTH = 32; -// maxPrecisionBits = 16; + // int BIT_WIDTH = 32; + // maxPrecisionBits = 16; /* * get const global variables @@ -991,166 +1098,151 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl detectFloatingPointOps(*Mod); checkFPUAvailability(*Mod); + /* + * analyze the range of all local variables in each function + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + std::map callerMap; + callerMap.clear(); + funcBoundInfo.clear(); + bool useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } + /** + * Check for potential overflows + */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); + for (auto & funcPair : funcBoundInfo) + { + checkOverflow(N, funcPair.second, maxPrecisionBits); + } + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // shrinkType(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } - - - /* - * analyze the range of all local variables in each function - * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - std::map callerMap; - callerMap.clear(); - funcBoundInfo.clear(); - bool useOverLoad = false; + if (enableQuantization) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; + std::vector functionsToInsert; for (auto & mi : *Mod) { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); } - - /** - * Check for potential overflows - */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); - for (auto & funcPair : funcBoundInfo) { - checkOverflow(N, funcPair.second, maxPrecisionBits); - } - - - - - - - - - - - // -// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// shrinkType(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } - - if (enableQuantization) + for (auto mi : functionsToInsert) { - flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - llvm::errs() << "Auto quantization enabled\n"; - std::vector functionsToInsert; - for (auto & mi : *Mod) - { - llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); - - } - for (auto mi : functionsToInsert) - { - Mod->getFunctionList().remove(mi); - Mod->getFunctionList().push_front(mi); - } + Mod->getFunctionList().remove(mi); + Mod->getFunctionList().push_front(mi); } + } - -// /* -// * analyze the range of all local variables in each function -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// std::map callerMap; -// callerMap.clear(); -// funcBoundInfo.clear(); -// bool useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// /** -// * Check for potential overflows -// */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); -// for (auto & funcPair : funcBoundInfo) { -// checkOverflow(N, funcPair.second, maxPrecisionBits); -// } + // /* + // * analyze the range of all local variables in each function + // * */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // std::map callerMap; + // callerMap.clear(); + // funcBoundInfo.clear(); + // bool useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } // -// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// memoryAlignment(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } + // /** + // * Check for potential overflows + // */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); + // for (auto & funcPair : funcBoundInfo) { + // checkOverflow(N, funcPair.second, maxPrecisionBits); + // } // - /* - * remove the functions that are optimized by passes. - * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = true; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } + // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // memoryAlignment(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + /* + * remove the functions that are optimized by passes. + * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = true; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } -// /* -// * simplify the condition of each branch -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); + // /* + // * simplify the condition of each branch + // * */ + // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + // for (auto & mi : *Mod) + // { + // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + // if (boundInfoIt != funcBoundInfo.end()) + // { + // simplifyControlFlow(N, boundInfoIt->second, mi); + // } + // // else + // // { + // // assert(false); + // // } + // } + // + // legacy::PassManager passManager; + // passManager.add(createCFGSimplificationPass()); + // passManager.add(createInstSimplifyLegacyPass()); + // passManager.add(createGlobalDCEPass()); + // passManager.run(*Mod); - // + // // // /* @@ -1190,22 +1282,23 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // // } // } // -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -//./ -// if (useOverLoad) -// overloadFunc(Mod, callerMap); + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + //./ + // if (useOverLoad) + // overloadFunc(Mod, callerMap); // Finally, erase old functions eraseOldFunctions(); - //eraseOldGlobals(); + // eraseOldGlobals(); + eraseUnusedConstant(*Mod); // Perform text replacement to remove "_quantized" suffixes - //removeQuantizedSuffixInModule(*Mod); + // removeQuantizedSuffixInModule(*Mod); // eraseOldInstructions(); @@ -1226,7 +1319,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/sensfusion6_output.ll"); // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); - /* * Dump BC file to a file. * */ From 9088c775663fd78b590e60fae67cc63d0bb2e8f4 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 27 Mar 2025 20:59:46 +0000 Subject: [PATCH 153/213] update comments Addresses #2. --- ...b36a40859e2c6368e4ec1c01205b230ed11180.txt | 48 ++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 17 +- .../newton-irPass-LLVMIR-quantization.cpp | 259 +----------------- 3 files changed, 57 insertions(+), 267 deletions(-) create mode 100644 analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt diff --git a/analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt b/analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt new file mode 100644 index 000000000..20e5c984c --- /dev/null +++ b/analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt @@ -0,0 +1,48 @@ + +changeset: 1746:d5b36a40859e2c6368e4ec1c01205b230ed11180 +char kNewtonVersion[] = "0.3-alpha-1746 (d5b36a40859e2c6368e4ec1c01205b230ed11180) (build 03-27-2025-20:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index c493b0fdb..9c1ddd52b 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -342,13 +342,11 @@ handleMatrixStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecision void handleReturnValue(ReturnInst * retInst, int maxPrecisionBits) { - // 如果返回指令没有返回值,则不处理 if (!retInst->getReturnValue()) return; Value * retVal = retInst->getReturnValue(); - // 如果返回值不是整数类型,则不做反量化处理 if (!retVal->getType()->isIntegerTy()) { errs() << "Return value is not integer type, skipping dequantization.\n"; @@ -358,19 +356,13 @@ handleReturnValue(ReturnInst * retInst, int maxPrecisionBits) IRBuilder<> Builder(retInst); Type * targetType = Type::getDoubleTy(retInst->getContext()); - // 将固定点整数转换为浮点数(double) Value * fpVal = Builder.CreateSIToFP(retVal, targetType); - // 创建常量 1/FRAC_BASE,注意用 llvm::ConstantFP 避免歧义 llvm::Constant * oneDivFrac = llvm::ConstantFP::get(targetType, 1.0 / FRAC_BASE); - // 计算反量化结果:fpVal * (1/FRAC_BASE) Value * dequantizedVal = Builder.CreateFMul(fpVal, oneDivFrac); - - // 构造新的返回指令,返回 dequantizedVal(double 类型) ReturnInst * newRet = ReturnInst::Create(retInst->getContext(), dequantizedVal, retInst); - // 删除原有返回指令 retInst->eraseFromParent(); errs() << "Replaced return with dequantized value: " << *newRet << "\n"; @@ -465,9 +457,7 @@ detectFloatingPointOps(Module & Mod) I.getOpcode() == Instruction::FDiv) { hasFloatOps = true; - functionFloatOpCount++; // Increment the counter for this function - // llvm::errs() << "Detected floating-point operation in function: " - // << F.getName() << " - Instruction: " << I << "\n"; + functionFloatOpCount++; } // Check if the instruction is a return @@ -580,21 +570,18 @@ checkFPUAvailability(Module & Mod) void processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) { - // 对于白名单中的函数,我们先遍历,收集所有需要处理的返回指令 for (Function & F : module) { if (whitelist.find(F.getName().str()) != whitelist.end()) { llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; std::vector retWorkList; - // 遍历函数基本块,并将所有返回指令收集到 retWorkList 中 for (BasicBlock & BB : F) { for (Instruction & I : BB) { if (ReturnInst * retInst = dyn_cast(&I)) { - // 仅处理返回值存在且为整数类型的返回指令 if (retInst->getReturnValue() && retInst->getReturnValue()->getType()->isIntegerTy()) { retWorkList.push_back(retInst); @@ -602,13 +589,11 @@ processWhitelistedFunctions(Module & module, const std::set & white } } } - // 依次处理收集到的返回指令 for (ReturnInst * retInst : retWorkList) { handleReturnValue(retInst, maxPrecisionBits); } - // 同时继续处理其他指令,例如 StoreInst(这部分你已有代码) for (BasicBlock & BB : F) { for (Instruction & I : BB) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index db5318197..084e87f2a 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -128,67 +128,7 @@ createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) -//{ -// llvm::errs() << "Entering createFixMul\n"; -// -// // Check if irModule is valid -// if (!irModule) -// { -// llvm::errs() << "Error: irModule is nullptr\n"; -// return nullptr; -// } -// -// std::string fixmulFuncName = "fixmul"; -// for (auto & function : *irModule) -// { -// if (function.getName() == fixmulFuncName) -// { -// llvm::errs() << "fixmul already exists\n"; -// return &function; -// } -// } -// -// llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); -// llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); -// -// llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "entry", func); -// llvm::IRBuilder<> builder(entryBB); -// builder.SetInsertPoint(entryBB); -// -// // Create fixed-point multiplication instruction -// Type * higherQuantizedType; -// switch (BIT_WIDTH) -// { -// case 8: -// higherQuantizedType = Type::getInt16Ty(irModule->getContext()); -// break; -// case 16: -// higherQuantizedType = Type::getInt32Ty(irModule->getContext()); -// break; -// default: -// higherQuantizedType = Type::getInt64Ty(irModule->getContext()); -// break; -// } -// -// llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); -// llvm::Value * sext1 = builder.CreateSExt(arg1, higherQuantizedType); -// llvm::Function::arg_iterator arg2 = &*(++arg1); -// llvm::Value * sext2 = builder.CreateSExt(arg2, higherQuantizedType); -// llvm::Value * mulInst = builder.CreateNSWMul(sext1, sext2); -// // llvm::Value * mulInst = builder.CreateMul(sext1, sext2); -// llvm::Value * ashrInst = builder.CreateLShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); -// llvm::Value * truncInst = builder.CreateTrunc(ashrInst, quantizedType); -// builder.CreateRet(truncInst); -// -// functionsToInsert.emplace_back(func); -// llvm::errs() << "Created fixmul function: " << func->getName() << "\n"; -// return func; -// } -// llvm::Function * createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector & functionsToInsert) @@ -628,15 +568,12 @@ handleGEPInstruction(llvm::GetElementPtrInst * gep, llvm::GlobalVariable & quantizedConst) { llvm::IRBuilder<> builder(gep); - // 直接使用量化后的全局变量,无需 bitcast llvm::SmallVector Indices; for (llvm::Value * idx : gep->indices()) Indices.push_back(idx); - // 重建 GEP:注意这里使用 quantizedConst 的类型(例如 [6 x i32]),因此 newGEP 的类型为 i32* llvm::Value * newGEP = builder.CreateGEP(quantizedConst.getType()->getPointerElementType(), &quantizedConst, Indices, gep->getName() + ".quantized_gep"); - // newGEP 应该直接就是你期望的类型,即 i32* gep->replaceAllUsesWith(newGEP); gep->eraseFromParent(); llvm::errs() << "Replaced GEP instruction for " << origConst.getName() << "\n"; @@ -656,7 +593,6 @@ replaceInternalConstantUses(llvm::Module *module, llvm::Use &use = *it++; llvm::Value *user = use.getUser(); - // 如果用户是 ConstantExpr,我们需要检查其最终使用者是否位于白名单函数中 if (llvm::ConstantExpr *constExpr = llvm::dyn_cast(user)) { bool replace = false; @@ -676,31 +612,29 @@ replaceInternalConstantUses(llvm::Module *module, { handleConstantExprUse(constExpr, origConst, quantizedConst); } - // 不论是否替换,继续处理下一个 use continue; } - // 如果用户是指令,且所在函数在白名单中 + if (llvm::Instruction *inst = llvm::dyn_cast(user)) { if (!inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) continue; - // 如果该指令是 GEP 并且结果类型为 double*,则特殊处理 + if (llvm::GetElementPtrInst *gep = llvm::dyn_cast(inst)) { if (gep->getResultElementType()->isDoubleTy()) { handleGEPInstruction(gep, origConst, quantizedConst); - continue; // 该 GEP 指令已经被替换,不再加入 usesToReplace + continue; } } - // 否则,将该 use 加入待统一替换列表 + usesToReplace.push_back(&use); } } - // 对剩余未处理的使用,统一替换为量化后的全局变量 for (llvm::Use *use : usesToReplace) use->set(&quantizedConst); @@ -1056,7 +990,6 @@ handleSelect(Instruction * inInstruction, Type * quantizedType) { llvm::errs() << "Handling Select\n"; - // 检查操作数数量 if (inInstruction->getNumOperands() < 3) { llvm::errs() << "Error: Select instruction does not have 3 operands!\n"; @@ -1064,8 +997,6 @@ handleSelect(Instruction * inInstruction, Type * quantizedType) } IRBuilder<> Builder(inInstruction); - // 获取 select 指令的三个操作数 - // 操作数 0 为条件值,操作数 1 为条件为 true 时的值,操作数 2 为条件为 false 时的值 Value * condition = inInstruction->getOperand(0); Value * opTrue = inInstruction->getOperand(1); Value * opFalse = inInstruction->getOperand(2); @@ -1074,7 +1005,6 @@ handleSelect(Instruction * inInstruction, Type * quantizedType) llvm::errs() << "Original true branch: " << *opTrue << "\n"; llvm::errs() << "Original false branch: " << *opFalse << "\n"; - // 如果 true 分支的操作数是浮点常量,则进行量化转换 if (ConstantFP * constFp = dyn_cast(opTrue)) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -1082,7 +1012,6 @@ handleSelect(Instruction * inInstruction, Type * quantizedType) opTrue = ConstantInt::get(quantizedType, quantizedValue); } - // 如果 false 分支的操作数是浮点常量,则进行量化转换 if (ConstantFP * constFp = dyn_cast(opFalse)) { float constValue = constFp->getValueAPF().convertToFloat(); @@ -1090,11 +1019,9 @@ handleSelect(Instruction * inInstruction, Type * quantizedType) opFalse = ConstantInt::get(quantizedType, quantizedValue); } - // 生成新的 select 指令,保持条件不变,使用转换后的 true 和 false 值 Value * newInst = Builder.CreateSelect(condition, opTrue, opFalse); llvm::errs() << "Created new select instruction: " << *newInst << "\n"; - // 将原来的 select 指令替换成新的 select 指令 inInstruction->replaceAllUsesWith(newInst); inInstruction->eraseFromParent(); @@ -1381,34 +1308,15 @@ handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) if (lhsIsInteger && rhsIsInteger) - // fixmul - // { - // llvm::errs() << "Both operands are integers, substituting with fixmul function...\n"; - // llvm::CallInst * callInst = Builder.CreateCall(fixmul, {lhs, rhs}); - // llvmIrInstruction->replaceAllUsesWith(callInst); - // llvmIrInstruction->eraseFromParent(); - // } - // 64bit + + { llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); } - // 32bit - // { - // llvm::errs() << "Both operands are integers, performing multiplication and shifting...\n"; - // - // // Perform multiplication directly - // llvm::Value * mulResult = Builder.CreateMul(lhs, rhs, ""); - // - // // Perform right arithmetic shift - // llvm::Value * shiftResult = Builder.CreateLShr(mulResult, llvm::ConstantInt::get(lhs->getType(), FRAC_Q)); - // - // // Replace all uses of the original instruction with the result of the shift - // llvmIrInstruction->replaceAllUsesWith(shiftResult); - // llvmIrInstruction->eraseFromParent(); - // } + llvm::errs() << "Finished handling FMul\n"; } @@ -1417,24 +1325,20 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; IRBuilder<> Builder(llvmIrInstruction); - // 如果已有 quantized 元数据,则跳过处理 if (llvmIrInstruction->getMetadata("quantized")) { llvm::errs() << "Skipping already quantized instruction.\n"; return; } - // 获取左右操作数 Value *lhs = llvmIrInstruction->getOperand(0); Value *rhs = llvmIrInstruction->getOperand(1); llvm::errs() << "LHS: " << *lhs << "\n"; llvm::errs() << "RHS: " << *rhs << "\n"; - // 判断操作数是否为浮点型(float 或 double) bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); - // 检查常量:如果其中一个操作数是 ConstantFP,则尝试简化 if (auto rhsConst = dyn_cast(rhs)) { if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) return; @@ -1443,13 +1347,11 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) return; } - - // 如果任一操作数是浮点常量,则通过 simplifyConstant 将其转换为固定点整数 if (isa(lhs)) { llvm::errs() << "LHS is a floating-point constant, handling it with simplifyConstant\n"; simplifyConstant(llvmIrInstruction, quantizedType); lhs = llvmIrInstruction->getOperand(0); - lhsIsFloat = false; // 更新状态,现已转换为固定点整数 + lhsIsFloat = false; } if (isa(rhs)) { llvm::errs() << "RHS is a floating-point constant, handling it with simplifyConstant\n"; @@ -1458,25 +1360,6 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { rhsIsFloat = false; } - // 如果任一操作数是整数常量,则先检查特殊情况优化: - // 如果右操作数为常量 1,则直接替换为左操作数 - if (isa(lhs) || isa(rhs)) { - if (ConstantInt *CI = dyn_cast(rhs)) { - if (CI->isOne()) { - llvm::errs() << "RHS is constant one, replacing division with LHS\n"; - llvmIrInstruction->replaceAllUsesWith(lhs); - llvmIrInstruction->eraseFromParent(); - return; - } - } - llvm::errs() << "One of the operands is an integer constant, using division directly\n"; - Value *newInst = Builder.CreateSDiv(lhs, rhs); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->eraseFromParent(); - return; - } - - // 如果任一操作数仍为浮点,则转换为固定点整数 if (lhsIsFloat) { lhs = Builder.CreateFPToSI(lhs, quantizedType); llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; @@ -1486,9 +1369,6 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; } - // 此时,lhs 和 rhs 均为整数(固定点表示),根据要求: - // 除法过程中不需要左移(即不乘 FRAC_BASE),也不在除法后做位移 - // 所以直接进行整数除法即可 Value *newInst = Builder.CreateSDiv(lhs, rhs); llvmIrInstruction->replaceAllUsesWith(newInst); llvmIrInstruction->eraseFromParent(); @@ -1588,73 +1468,6 @@ void handlePhi(Instruction *inInstruction, Type *quantizedType) { } - -//void handlePhi(Instruction *inInstruction, Type *quantizedType) -//{ -// llvm::errs() << "Handling PHI\n"; -// PHINode *phi = dyn_cast(inInstruction); -// if (!phi) { -// llvm::errs() << "Error: Instruction is not a PHI node.\n"; -// return; -// } -// -// // 判断是否是指针类型的 PHI 节点 -// bool isPtr = phi->getType()->isPointerTy(); -// unsigned pointerAddr = 0; -// if (isPtr) -// pointerAddr = phi->getType()->getPointerAddressSpace(); -// -// // 新 PHI 节点的类型 -// // 如果原来是 pointer 类型,则新类型为 quantizedType->getPointerTo(pointerAddr) -// // 否则直接使用 quantizedType -// Type *newPhiType = isPtr ? quantizedType->getPointerTo(pointerAddr) : quantizedType; -// -// // 创建新的 PHI 节点,新节点插入在原 PHI 节点之前 -// PHINode *newPhi = PHINode::Create(newPhiType, phi->getNumIncomingValues(), -// phi->getName() + ".quantized", phi); -// -// // 遍历所有入边 -// for (unsigned i = 0, e = phi->getNumIncomingValues(); i < e; i++) { -// Value *incoming = phi->getIncomingValue(i); -// BasicBlock *incomingBB = phi->getIncomingBlock(i); -// Value *newVal = nullptr; -// -// llvm::errs() << "Original PHI incoming value: " << *incoming << "\n"; -// -// if (!isPtr) { -// // 针对非指针情况:如果是浮点常量,直接量化;如果是浮点值,则插入 FPToSI 转换 -// if (ConstantFP *constFp = dyn_cast(incoming)) { -// float fpVal = constFp->getValueAPF().convertToFloat(); -// int64_t quantizedValue = static_cast(round(fpVal * FRAC_BASE)); -// newVal = llvm::ConstantInt::get(quantizedType, quantizedValue); -// llvm::errs() << "Converted constant: " << *newVal << "\n"; -// } else if (incoming->getType()->isFloatingPointTy()) { -// IRBuilder<> builder(incomingBB->getTerminator()); -// newVal = builder.CreateFPToSI(incoming, quantizedType, incoming->getName() + ".to_int"); -// llvm::errs() << "Inserted conversion: " << *newVal << "\n"; -// } else { -// newVal = incoming; -// } -// } else { -// // 针对指针类型:要求新入边值类型为 newPhiType -// if (incoming->getType() != newPhiType) { -// IRBuilder<> builder(incomingBB->getTerminator()); -// newVal = builder.CreateBitCast(incoming, newPhiType, incoming->getName() + ".cast"); -// llvm::errs() << "BitCast pointer: " << *newVal << "\n"; -// } else { -// newVal = incoming; -// } -// } -// newPhi->addIncoming(newVal, incomingBB); -// } -// -// // 替换所有使用并删除原 PHI 节点 -// phi->replaceAllUsesWith(newPhi); -// phi->eraseFromParent(); -// -// llvm::errs() << "Finished handling PHI: " << *newPhi << "\n"; -//} - void handleFpToSi(Instruction *llvmInst, Type *quantizedType) { llvm::errs() << "Handling FPToSI\n"; @@ -1778,40 +1591,6 @@ handleAlloca(AllocaInst * llvmIrAllocaInstruction, Type * quantizedType) } -void handleGEPForQuantization(GetElementPtrInst *gep, Type *quantizedType) { - // 检查原来的元素类型是否为浮点型 - if (gep->getResultElementType()->isFloatingPointTy()) { - IRBuilder<> Builder(gep); - llvm::errs() << "Handling GEP quantization for: " << *gep << "\n"; - - // 获取原指针操作数 - Value *ptr = gep->getPointerOperand(); - // 如果指针操作数的元素类型不是 quantizedType,则转换 - if (ptr->getType()->getPointerElementType() != quantizedType) { - // 注意:如果 ptr 原本不是 quantizedType*,这里我们进行 bitcast - ptr = Builder.CreateBitCast(ptr, PointerType::getUnqual(quantizedType), - ptr->getName() + ".casted"); - } - - // 收集原来的索引(不包括第一个操作数) - SmallVector Indices; - for (Value *idx : gep->indices()) { - Indices.push_back(idx); - } - - // 创建新的 GEP 指令,元素类型直接使用 quantizedType - Value *newGEP = Builder.CreateGEP(quantizedType, ptr, Indices, gep->getName() + ".quantized"); - - // 替换所有使用,并删除原来的 GEP 指令 - gep->replaceAllUsesWith(newGEP); - gep->eraseFromParent(); - - llvm::errs() << "Replaced GEP with quantized version: " << *newGEP << "\n"; - } else { - llvm::errs() << "GEP element type is not floating-point. No quantization performed for: " - << *gep << "\n"; - } -} void handleStore(Instruction * llvmIrInstruction, Type * quantizedType) @@ -1969,28 +1748,6 @@ handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function llvmIrCallInstruction->eraseFromParent(); } -/* - * // Call sqrt on the floating-point value -llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); -llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); - -// Convert the result back to a fixed-point integer -llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); - -// Perform a left shift to scale the result -llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); - -// Apply compensation if FRAC_Q is odd -llvm::Value * finalRes = shlRes; -if (FRAC_Q % 2 != 0) -{ -llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562); -llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); -llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); -finalRes = builder.CreateFPToSI(compensated, quantizedType); -} - - */ llvm::Value * performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPointValue, Type * quantizedType, int FRAC_Q, LLVMContext & context) From 9ec1c11d31a59c57f04599ecd0f4214c47075f4e Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 2 Apr 2025 18:32:28 +0100 Subject: [PATCH 154/213] update * quantize. --- ...547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 2 +- .../newton-irPass-LLVMIR-quantization.cpp | 12 ++--- 3 files changed, 52 insertions(+), 10 deletions(-) create mode 100644 analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt diff --git a/analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt b/analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt new file mode 100644 index 000000000..41a11c4df --- /dev/null +++ b/analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt @@ -0,0 +1,48 @@ + +changeset: 1747:35547345a1f6a7ae94dc279486ae5a33c33e7b2a +char kNewtonVersion[] = "0.3-alpha-1747 (35547345a1f6a7ae94dc279486ae5a33c33e7b2a) (build 03-27-2025-20:59-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 9c1ddd52b..2e8179cc3 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -389,7 +389,7 @@ dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) bool enableAutoQuantization = false; -void +voidb detectFloatingPointOps(Module & Mod) { bool hasFloatOps = false; diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 084e87f2a..e51501110 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -383,14 +383,14 @@ getOrCreateQuantizedConstant(llvm::Module *module, { std::string quantizedName = origConst.getName().str() + "_quantized"; - // 检查量化版本是否已存在 + if (llvm::GlobalVariable *existingConst = module->getNamedGlobal(quantizedName)) { llvm::errs() << "Quantized internal constant already exists: " << quantizedName << "\n"; return existingConst; } - // 确保原常量有 initializer + if (!origConst.hasInitializer()) { llvm::errs() << "Skipping quantization: constant has no initializer: " << origConst.getName() << "\n"; @@ -400,7 +400,6 @@ getOrCreateQuantizedConstant(llvm::Module *module, llvm::Constant *init = origConst.getInitializer(); llvm::ArrayType *origArrayType = llvm::dyn_cast(origConst.getType()->getElementType()); - // 只处理浮点数组 if (!origArrayType || (!origArrayType->getArrayElementType()->isFloatTy() && !origArrayType->getArrayElementType()->isDoubleTy())) @@ -412,13 +411,11 @@ getOrCreateQuantizedConstant(llvm::Module *module, llvm::errs() << "Quantizing internal constant: " << origConst.getName() << "\n"; std::vector quantizedValues; - // 使用 int32 表示量化后的值 + llvm::Type *intType = llvm::Type::getInt32Ty(module->getContext()); - // 定义阈值 epsilon,调整为 1e-9 以便对极小值直接量化为0 const double epsilon = 1e-9; - // 遍历数组元素进行量化 for (unsigned i = 0; i < origArrayType->getNumElements(); ++i) { llvm::ConstantFP *fpVal = llvm::dyn_cast(init->getAggregateElement(i)); @@ -439,11 +436,9 @@ getOrCreateQuantizedConstant(llvm::Module *module, quantizedValues.push_back(llvm::ConstantInt::get(intType, quantizedValue)); } - // 创建新的数组类型和 initializer llvm::ArrayType *quantizedArrayType = llvm::ArrayType::get(intType, quantizedValues.size()); llvm::Constant *newInit = llvm::ConstantArray::get(quantizedArrayType, quantizedValues); - // 创建新的全局变量 llvm::GlobalVariable *quantizedConst = new llvm::GlobalVariable( *module, quantizedArrayType, @@ -452,7 +447,6 @@ getOrCreateQuantizedConstant(llvm::Module *module, newInit, quantizedName); - // 设置对齐(这里使用4字节对齐,适用于 int32) quantizedConst->setAlignment(llvm::MaybeAlign(4)); quantizedConst->setDSOLocal(true); llvm::errs() << "Created quantized internal constant: " << quantizedName << "\n"; From 133584b2775487f43b4ea4e659e29799241fd93b Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 2 Apr 2025 18:33:01 +0100 Subject: [PATCH 155/213] update * quantize. --- .../9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt | 7 +++++++ src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp | 2 +- 2 files changed, 8 insertions(+), 1 deletion(-) create mode 100644 analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt diff --git a/analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt b/analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt new file mode 100644 index 000000000..86f74c152 --- /dev/null +++ b/analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt @@ -0,0 +1,7 @@ + +changeset: 1748:9088c775663fd78b590e60fae67cc63d0bb2e8f4 +char kNewtonVersion[] = "0.3-alpha-1748 (9088c775663fd78b590e60fae67cc63d0bb2e8f4) (build 04-02-2025-18:32-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 2e8179cc3..9c1ddd52b 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -389,7 +389,7 @@ dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) bool enableAutoQuantization = false; -voidb +void detectFloatingPointOps(Module & Mod) { bool hasFloatOps = false; From 36e47b1b17600eacfd132cde3109992ea24c3c09 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Fri, 25 Apr 2025 20:36:49 +0100 Subject: [PATCH 156/213] update sensfusion6 test files * quantize. --- .../newton/llvm-ir/c-files/sensfusion6.c | 241 ++++++++++++++ .../newton/llvm-ir/c-files/sensfusion6.h | 30 ++ .../newton/llvm-ir/c-files/test_sensfusion6.c | 308 ++++++++++++++++++ .../newton/llvm-ir/testSensfusion6.sh | 68 ++++ 4 files changed, 647 insertions(+) create mode 100644 applications/newton/llvm-ir/c-files/sensfusion6.c create mode 100644 applications/newton/llvm-ir/c-files/sensfusion6.h create mode 100644 applications/newton/llvm-ir/c-files/test_sensfusion6.c create mode 100755 applications/newton/llvm-ir/testSensfusion6.sh diff --git a/applications/newton/llvm-ir/c-files/sensfusion6.c b/applications/newton/llvm-ir/c-files/sensfusion6.c new file mode 100644 index 000000000..5fd54ce60 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/sensfusion6.c @@ -0,0 +1,241 @@ +/** +* || ____ _ __ +* +------+ / __ )(_) /_______________ _____ ___ +* | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ +* +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ +* || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ +* +* Crazyflie Firmware +* +* Copyright (C) 2011-2012 Bitcraze AB +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* +*/ + + + + + +#include +#define CONFIG_IMU_MADGWICK_QUATERNION + +#ifdef CONFIG_IMU_MADGWICK_QUATERNION +#define BETA_DEF 0.01f // 2 * proportional gain +#else // MAHONY_QUATERNION_IMU +#define TWO_KP_DEF (2.0f * 0.4f) // 2 * proportional gain +#define TWO_KI_DEF (2.0f * 0.001f) // 2 * integral gain +#endif + +#ifdef CONFIG_IMU_MADGWICK_QUATERNION +#define dt (1.0f/128.0f); // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain +volatile float beta = betaDef; +#else // MAHONY_QUATERNION_IMU +float twoKp = TWO_KP_DEF; // 2 * proportional gain (Kp) +float twoKi = TWO_KI_DEF; // 2 * integral gain (Ki) +float integralFBx = 0.0f; +float integralFBy = 0.0f; +float integralFBz = 0.0f; // integral error terms scaled by Ki +#endif + +static float invSqrt(float x); + +#ifdef CONFIG_IMU_MADGWICK_QUATERNION +//void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az, float dt,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +{ + float qw = *qw_ptr; + float qx = *qx_ptr; + float qy = *qy_ptr; + float qz = *qz_ptr; + + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2qw, _2qx, _2qy, _2qz, _4qw, _4qx, _4qy ,_8qx, _8qy, qwqw, qxqx, qyqy, qzqz; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-qx * gx - qy * gy - qz * gz); + qDot2 = 0.5f * (qw * gx + qy * gz - qz * gy); + qDot3 = 0.5f * (qw * gy - qx * gz + qz * gx); + qDot4 = 0.5f * (qw * gz + qx * gy - qy * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) + { + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2qw = 2.0f * qw; + _2qx = 2.0f * qx; + _2qy = 2.0f * qy; + _2qz = 2.0f * qz; + _4qw = 4.0f * qw; + _4qx = 4.0f * qx; + _4qy = 4.0f * qy; + _8qx = 8.0f * qx; + _8qy = 8.0f * qy; + qwqw = qw * qw; + qxqx = qx * qx; + qyqy = qy * qy; + qzqz = qz * qz; + + // Gradient decent algorithm corrective step + s0 = _4qw * qyqy + _2qy * ax + _4qw * qxqx - _2qx * ay; + s1 = _4qx * qzqz - _2qz * ax + 4.0f * qwqw * qx - _2qw * ay - _4qx + _8qx * qxqx + _8qx * qyqy + _4qx * az; + s2 = 4.0f * qwqw * qy + _2qw * ax + _4qy * qzqz - _2qz * ay - _4qy + _8qy * qxqx + _8qy * qyqy + _4qy * az; + s3 = 4.0f * qxqx * qz - _2qx * ax + 4.0f * qyqy * qz - _2qy * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + // Integrate rate of change of quaternion to yield quaternion + qw += qDot1 * dt; + qx += qDot2 * dt; + qy += qDot3 * dt; + qz += qDot4 * dt; + + // Normalise quaternion + recipNorm = invSqrt(qw*qw + qx*qx + qy*qy + qz*qz); + qw *= recipNorm; + qx *= recipNorm; + qy *= recipNorm; + qz *= recipNorm; + + // Store results in the output pointers + *qw_ptr = qw; + *qx_ptr = qx; + *qy_ptr = qy; + *qz_ptr = qz; +} +#else +// Madgwick's implementation of Mahony's AHRS algorithm. +// See: http://www.x-io.co.uk/open-source-ahrs-with-x-imu +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +static void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az, float dt, float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +{ + + float qw = *qw_ptr; + float qx = *qx_ptr; + float qy = *qy_ptr; + float qz = *qz_ptr; + + float recipNorm; + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; + + gx = gx * M_PI_F / 180; + gy = gy * M_PI_F / 180; + gz = gz * M_PI_F / 180; + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) + { + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = qx * qz - qw * qy; + halfvy = qw * qx + qy * qz; + halfvz = qw * qw - 0.5f + qz * qz; + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) + { + integralFBx += twoKi * halfex * dt; // integral error scaled by Ki + integralFBy += twoKi * halfey * dt; + integralFBz += twoKi * halfez * dt; + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else + { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * dt); // pre-multiply common factors + gy *= (0.5f * dt); + gz *= (0.5f * dt); + qa = qw; + qb = qx; + qc = qy; + qw += (-qb * gx - qc * gy - qz * gz); + qx += (qa * gx + qc * gz - qz * gy); + qy += (qa * gy - qb * gz + qz * gx); + qz += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(qw * qw + qx * qx + qy * qy + qz * qz); + qw *= recipNorm; + qx *= recipNorm; + qy *= recipNorm; + qz *= recipNorm; + + // Store results in the output pointers + *qw_ptr = qw; + *qx_ptr = qx; + *qy_ptr = qy; + *qz_ptr = qz; + +} +#endif + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root +float invSqrt(float x) +{ + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/sensfusion6.h b/applications/newton/llvm-ir/c-files/sensfusion6.h new file mode 100644 index 000000000..183a6e48c --- /dev/null +++ b/applications/newton/llvm-ir/c-files/sensfusion6.h @@ -0,0 +1,30 @@ +/** +* || ____ _ __ ______ +* +------+ / __ )(_) /_/ ____/_________ _____ ___ +* | 0xBC | / __ / / __/ / / ___/ __ `/_ / / _ \ +* +------+ / /_/ / / /_/ /___ / / / /_/ / / /_/ __/ +* || || /_____/_/\__/\____//_/ \__,_/ /___/\___/ +* +* Crazyflie control firmware +* +* Copyright (C) 2011-2012 Bitcraze AB +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +*/ + +#ifndef SENSORFUSION6_H_ +#define SENSORFUSION6_H_ + +void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr); +#endif /* SENSORFUSION6_H_ */ \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_sensfusion6.c b/applications/newton/llvm-ir/c-files/test_sensfusion6.c new file mode 100644 index 000000000..57610c3ab --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_sensfusion6.c @@ -0,0 +1,308 @@ +/* +* Madgwick test case (run locally) +* Compilation command in applications/newton/llvm-ir/performance_test/Makefile +* +* How to compile and run? +* 1. `make perf_madgwick` FP hardware (by default) +* 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) +* 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) +* 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format +* */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(INT_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#include "sensfusion6.h" +#elif defined(FP_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#if defined(SOFT_FLOAT_LIB) +#include "MadgwickAHRS_softfloat.h" +#else +#include "sensfusion6.h" +#endif + +#else +#error "Must set data type: FP or INT" +#endif + +#define DATA_SIZE 1000 +#define ITERATION 10 + +/*************************************** +* Timer functions of the test framework +***************************************/ + + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} + +int +main() +{ + FILE * fp = fopen("input.csv", "r"); + + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + + double time[DATA_SIZE]; +#if defined(INT_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + +// q0[i] = 1.0f; +// q1[i] = 0.0f; +// q2[i] = 0.0f; +// q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + +#elif defined(FP_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + + // q0[i] = 1.0f; + // q1[i] = 0.0f; + // q2[i] = 0.0f; + // q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; +#else +#error "Must set data type: FP or INT" +#endif + + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: +#if defined(INT_DATA_TYPE) + + mag_x[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_x[row - 2] = atof(value); +#endif + break; + case 2: +#if defined(INT_DATA_TYPE) + + mag_y[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_y[row - 2] = atof(value); +#endif + break; + case 3: +#if defined(INT_DATA_TYPE) + + mag_z[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_z[row - 2] = atof(value); +#endif + break; + case 4: +#if defined(INT_DATA_TYPE) + // gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_x[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_x[row - 2] = atof(value) * 61; +#endif + break; + case 5: +#if defined(INT_DATA_TYPE) + // gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_y[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_y[row - 2] = atof(value) * 61; +#endif + break; + case 6: +#if defined(INT_DATA_TYPE) + // gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_z[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_z[row - 2] = atof(value) * 61; +#endif + break; + case 7: +#if defined(INT_DATA_TYPE) + // acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_x[row - 2] = atof(value) * 2; +#endif + break; + case 8: +#if defined(INT_DATA_TYPE) + // acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_y[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_y[row - 2] = atof(value) * 2; +#endif + break; + case 9: +#if defined(INT_DATA_TYPE) + // acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_z[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_z[row - 2] = atof(value) * 2; +#endif + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } + + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + + + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + sensfusion6UpdateQImpl(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + + + + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); + +#if defined(FP_DATA_TYPE) + FILE * fptr = fopen("fp_sensfusion6_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); +#elif defined(INT_DATA_TYPE) + FILE * fptr = fopen("int_sensfusion6_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + + + +#endif + return 0; +} diff --git a/applications/newton/llvm-ir/testSensfusion6.sh b/applications/newton/llvm-ir/testSensfusion6.sh new file mode 100755 index 000000000..3c50c3cae --- /dev/null +++ b/applications/newton/llvm-ir/testSensfusion6.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Usage: ./run_algorithm.sh [MadgwickAHRS|MahonyAHRS] +# If no argument is provided, it defaults to MadgwickAHRS + +# Set the algorithm name based on the first command-line argument, default to MadgwickAHRS +#ALGO=${1:-MadgwickAHRS} +# +## Validate the input (optional) +#if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" ]]; then +# echo "Error: Unknown algorithm '$ALGO'. Please use either 'MadgwickAHRS' or 'MahonyAHRS'." +# exit 1 +#fi +ALGO="sensfusion6" +echo "Selected algorithm: $ALGO" + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + "$HOME/CoSense/applications/newton/llvm-ir/c-files/${ALGO}.c" + +# Optimize the LLVM IR file with mem2reg +opt "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd "$HOME/CoSense/src/newton" && \ + ./newton-linux-EN --llvm-ir="$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + --llvm-ir-liveness-check --llvm-ir-auto-quantization \ + "$HOME/CoSense/applications/newton/sensors/BMX055.nt" + +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +llvm-dis "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.bc" \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" +cd "$HOME/CoSense/applications/newton/llvm-ir" && ./replace.sh "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" + +# Step 4: Optimize the generated LLVM IR file further +echo "Step 4: Optimize the generated LLVM IR file" +opt -inline "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/out.ll" + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as "$HOME/CoSense/applications/newton/llvm-ir/out.ll" -o "$HOME/CoSense/applications/newton/llvm-ir/out.bc" + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc "$HOME/CoSense/applications/newton/llvm-ir/out.bc" -o "$HOME/CoSense/applications/newton/llvm-ir/out.s" + +# Step 7: Compile the assembly file to an object file +echo "Step 7: Compile the assembly file to object file" +clang -c "$HOME/CoSense/applications/newton/llvm-ir/out.s" -o "$HOME/CoSense/applications/newton/llvm-ir/out.o" + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc "$HOME/CoSense/applications/newton/llvm-ir/libout.a" "$HOME/CoSense/applications/newton/llvm-ir/out.o" + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang "$HOME/CoSense/applications/newton/llvm-ir/c-files/test_${ALGO}.c" -D INT_DATA_TYPE -no-pie \ + -L"$HOME/CoSense/applications/newton/llvm-ir" -lout -O3 -Os -g \ + -o "$HOME/CoSense/applications/newton/llvm-ir/main_out" -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +"$HOME/CoSense/applications/newton/llvm-ir/main_out" \ No newline at end of file From 50e9d59b8be3d568aca2f34f3b2c75dbcc259f75 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 28 Apr 2025 10:47:23 +0100 Subject: [PATCH 157/213] update exploit of sensor info * quantize. --- ...3584b2775487f43b4ea4e659e29799241fd93b.txt | 48 +++++++++++ ...c1c11d31a59c57f04599ecd0f4214c47075f4e.txt | 48 +++++++++++ applications/newton/llvm-ir/m4_fix.sh | 40 --------- applications/newton/llvm-ir/m4_original.sh | 69 --------------- .../newton-irPass-LLVMIR-quantization.cpp | 83 ++++++++++++++----- 5 files changed, 157 insertions(+), 131 deletions(-) create mode 100644 analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt create mode 100644 analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt delete mode 100755 applications/newton/llvm-ir/m4_fix.sh delete mode 100755 applications/newton/llvm-ir/m4_original.sh diff --git a/analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt b/analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt new file mode 100644 index 000000000..7d93af047 --- /dev/null +++ b/analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt @@ -0,0 +1,48 @@ + +changeset: 1750:133584b2775487f43b4ea4e659e29799241fd93b +char kNewtonVersion[] = "0.3-alpha-1750 (133584b2775487f43b4ea4e659e29799241fd93b) (build 04-25-2025-20:36-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt b/analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt new file mode 100644 index 000000000..0b0d9ea2a --- /dev/null +++ b/analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt @@ -0,0 +1,48 @@ + +changeset: 1749:9ec1c11d31a59c57f04599ecd0f4214c47075f4e +char kNewtonVersion[] = "0.3-alpha-1749 (9ec1c11d31a59c57f04599ecd0f4214c47075f4e) (build 04-02-2025-18:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/m4_fix.sh b/applications/newton/llvm-ir/m4_fix.sh deleted file mode 100755 index e0dddb947..000000000 --- a/applications/newton/llvm-ir/m4_fix.sh +++ /dev/null @@ -1,40 +0,0 @@ -#!/bin/bash - -# Get the current user's home directory -USER_HOME=$HOME - -# Step 1: Generate LLVM IR file -echo "Step 1: Generate LLVM IR file" - -clang -g0 -O0 -Xclang -disable-O0-optnone -target armv7e-m -mcpu=cortex-m4 -mfloat-abi=soft -mthumb -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c - -# Step 2: Use newton for optimization and quantization -echo "Step 2: Use newton for optimization and quantization" -cd $USER_HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $USER_HOME/CoSense/applications/newton/sensors/test.nt - -# Step 3: Convert generated bytecode file to LLVM IR file -echo "Step 3: Convert generated bytecode file to LLVM IR file" -#llvm-dis $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -cd $USER_HOME/CoSense/applications/newton/llvm-ir/&& -./replace.sh $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll - -python3 replace.py - -echo "Step 2: Optimize the generated LLVM IR file" - - -opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll - -echo "Step 3: Compile the optimized LLVM IR file to bitcode" - -llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc - -echo "Step 4: Compile the bitcode file to assembly" - -llc -march=arm -mcpu=cortex-m4 -mattr=+thumb,+soft-float $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s - -echo "Step 5: Compile the assembly file to object file" - -#clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o - -clang -target armv7e-m -mcpu=cortex-m4 -mfloat-abi=soft -mthumb -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o \ No newline at end of file diff --git a/applications/newton/llvm-ir/m4_original.sh b/applications/newton/llvm-ir/m4_original.sh deleted file mode 100755 index fac19d4f7..000000000 --- a/applications/newton/llvm-ir/m4_original.sh +++ /dev/null @@ -1,69 +0,0 @@ -#!/bin/bash - -# 定义目标 CPU 变量 -TARGET_CPU="cortex-m4" - -# 定义交叉编译前缀变量 -CROSS_COMPILE_PREFIX="arm-none-eabi-" - -# 正确使用变量来设置目标 CPU 的 clang 命令 -CLANG_CMD="$CROSS_COMPILE_PREFIX clang" - -# 确保 HOME 变量被正确定义 -if [ -z "$HOME" ]; then - HOME=$HOME -fi - -# Step 1: Generate LLVM IR file -echo "Step 1: Generate LLVM IR file" - -clang -g0 -O0 -Xclang -disable-O0-optnone -target armv7e-m -mcpu=cortex-m4 - - - -mthumb -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c - - -echo "Step 2: Optimize the generated LLVM IR file" - -opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll - -echo "Step 3: Compile the optimized LLVM IR file to bitcode" - -llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc - -echo "Step 4: Compile the bitcode file to assembly" - -llc -march=arm -mcpu=cortex-m4 -mattr=+vfp4,+soft-float $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s - -echo "Step 5: Compile the assembly file to object file" - - -clang -target armv7e-m -mcpu=cortex-m4 -mfloat-abi=soft -mthumb -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o - - -echo "Step 6: Package the object file into a static library" - -ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o - -echo "Step 7: Compile the test file and link with the static library" - - -arm-none-eabi-gcc syscalls.c \ - $HOME/CoSense/applications/newton/llvm-ir/c-files/test_m4.c \ - system_stm32f3xx.c \ - -DSTM32F303xC -DFP_DATA_TYPE -mcpu=cortex-m4 -mfloat-abi=soft -mthumb \ - -L$HOME/CoSense/applications/newton/llvm-ir -lout -lm -O3 -Os -g -fno-builtin \ - -o $HOME/CoSense/applications/newton/llvm-ir/main_out.elf - - - - - -# Step 8: Generate HEX or BIN file for flashing -echo "Step 8: Generate HEX and BIN files" -arm-none-eabi-objcopy -O ihex $HOME/CoSense/applications/newton/llvm-ir/main_out.elf $HOME/CoSense/applications/newton/llvm-ir/main_out.hex -arm-none-eabi-objcopy -O binary $HOME/CoSense/applications/newton/llvm-ir/main_out.elf $HOME/CoSense/applications/newton/llvm-ir/main_out.bin -# -#echo "Step 8: Run the test executable" -# -#$HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index e51501110..ce3a801cb 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -286,31 +286,70 @@ isWhitelistedGlobal(const std::string & globalName) } bool -shouldProcessFunction(Function & F) +shouldProcessFunction(Function &F) { - // List of function names to process - static const std::set targetFunctions = { - "sensfusion6UpdateQImpl", - "MadgwickAHRSupdate", - "MadgwickAHRSupdateIMU", - "MahonyAHRSupdate", - "MahonyAHRSupdateIMU", - "matrixMul", - "matrixAdd", - "matrixSub", - "pzero", - "qzero", - "pone", - "qone", - "__ieee754_exp", - "twofft" -// "__ieee754_log", - }; + if (F.isDeclaration()) + return false; + + + for (auto &BB : F) + { + for (auto &I : BB) + { + + if (auto *DbgValue = dyn_cast(&I)) + { + Value *Val = DbgValue->getValue(); + if (!Val) + continue; + if (auto *Arg = dyn_cast(Val)) + { + auto *DIVar = DbgValue->getVariable(); + if (DIVar) + { + std::string typeName = DIVar->getType()->getName().str(); + if (typeName.find("bmx055") != std::string::npos) + { + + return true; + } + } + } + } + } + } - // Check if the function name is in the set - return targetFunctions.find(F.getName().str()) != targetFunctions.end(); + return false; } + + +//bool +//shouldProcessFunction(Function & F) +//{ +// // List of function names to process +// static const std::set targetFunctions = { +// "sensfusion6UpdateQImpl", +// "MadgwickAHRSupdate", +// "MadgwickAHRSupdateIMU", +// "MahonyAHRSupdate", +// "MahonyAHRSupdateIMU", +// "matrixMul", +// "matrixAdd", +// "matrixSub", +// "pzero", +// "qzero", +// "pone", +// "qone", +// "__ieee754_exp", +// "twofft" +//// "__ieee754_log", +// }; +// +// // Check if the function name is in the set +// return targetFunctions.find(F.getName().str()) != targetFunctions.end(); +//} + // Helper to create or retrieve the quantized global variable GlobalVariable * getOrCreateQuantizedGlobal(Module * module, GlobalVariable & globalVar, Type * quantizedType) @@ -590,7 +629,6 @@ replaceInternalConstantUses(llvm::Module *module, if (llvm::ConstantExpr *constExpr = llvm::dyn_cast(user)) { bool replace = false; - // 遍历 constant expression 的所有使用 for (auto &CEUse : constExpr->uses()) { if (llvm::Instruction *inst = llvm::dyn_cast(CEUse.getUser())) @@ -2189,6 +2227,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect return; } + Type * quantizedType; switch (BIT_WIDTH) { From 38e833cce9ad8f877114760a7900fc0a2085e1f9 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 28 Apr 2025 12:46:27 +0100 Subject: [PATCH 158/213] update SQNR test scripts * quantize. --- applications/newton/llvm-ir/SQNR.sh | 38 +++++++++++------------- applications/newton/llvm-ir/rms_error.py | 11 +++++-- 2 files changed, 27 insertions(+), 22 deletions(-) diff --git a/applications/newton/llvm-ir/SQNR.sh b/applications/newton/llvm-ir/SQNR.sh index 4d35c6894..d3c204e68 100755 --- a/applications/newton/llvm-ir/SQNR.sh +++ b/applications/newton/llvm-ir/SQNR.sh @@ -1,21 +1,19 @@ #!/bin/bash - - - +# Parse input argument for algorithm while [[ $# -gt 0 ]]; do case $1 in --algo) - if [[ "$2" == "MadgwickAHRS" || "$2" == "MahonyAHRS" ]]; then + if [[ "$2" == "MadgwickAHRS" || "$2" == "MahonyAHRS" || "$2" == "sensfusion6" ]]; then ALGO="$2" shift 2 else - echo "Error: Invalid algorithm '$2'. Use 'MadgwickAHRS' or 'MahonyAHRS'." + echo "Error: Invalid algorithm '$2'. Use 'MadgwickAHRS', 'MahonyAHRS', or 'sensfusion6'." exit 1 fi ;; *) - echo "Usage: $0 [--algo MadgwickAHRS|MahonyAHRS]" + echo "Usage: $0 [--algo MadgwickAHRS|MahonyAHRS|sensfusion6]" exit 1 ;; esac @@ -32,32 +30,32 @@ MAKE_DIR="/home/xyf/CoSense/src/newton" # Current directory (where the test scripts are located) TEST_DIR=$(pwd) -# Loop over MAX_PRECISION_BITS values from 13 to 16 -for i in {8..16}; do - echo "---------------------------" | tee -a "$OUTPUT_FILE" - echo "Current compile parameter: MAX_PRECISION_BITS = $i" | tee -a "$OUTPUT_FILE" +# Print header into file +echo "| MAX_PRECISION_BITS | SQNR (dB) |" >> "$OUTPUT_FILE" +echo "|--------------------|-----------|" >> "$OUTPUT_FILE" + +# Loop over MAX_PRECISION_BITS values from 8 to 18 +for i in {8..9}; do + echo "Compiling and testing with MAX_PRECISION_BITS = $i ..." # Switch to the make directory for compilation pushd "$MAKE_DIR" > /dev/null - # Uncomment the following line if a full clean is needed: - # make clean - # Remove object files for quantization and optimizeByRange to force recompilation rm -f newton-irPass-LLVMIR-quantization.o newton-irPass-LLVMIR-optimizeByRange.o - # Compile with the specified MAX_PRECISION_BITS value make MAX_PRECISION_BITS=$i popd > /dev/null # Switch back to the test scripts directory (current directory) cd "$TEST_DIR" || exit - # Run the test script + # Run the test ./testMadgwick.sh "$ALGO" - # Run the Python script to extract the SNR result (adjust grep keyword if needed) - SNR_LINE=$(python3 rms_error.py --filter "$ALGO" | grep "SQNR (dB)") - echo "MAX_PRECISION_BITS=$i, $SNR_LINE" | tee -a "$OUTPUT_FILE" + # Extract SNR line + SNR_VALUE=$(python3 rms_error.py --filter "$ALGO" | grep "SQNR (dB)" | awk -F': ' '{print $2}') + echo "MAX_PRECISION_BITS=$i, SQNR (dB)=$SNR_VALUE" - echo "Compilation and test complete, result saved." | tee -a "$OUTPUT_FILE" + # Append as a clean table row + printf "| %-18s | %-9s |\n" "$i" "$SNR_VALUE" >> "$OUTPUT_FILE" done -echo "All tests completed, results saved in $OUTPUT_FILE." +echo "All tests completed. Results saved to $OUTPUT_FILE." diff --git a/applications/newton/llvm-ir/rms_error.py b/applications/newton/llvm-ir/rms_error.py index 35ba3f9e5..200ff819f 100644 --- a/applications/newton/llvm-ir/rms_error.py +++ b/applications/newton/llvm-ir/rms_error.py @@ -91,7 +91,7 @@ def compute_sqnr(fp_data, int_data): def main(): parser = argparse.ArgumentParser(description="Calculate RMS errors and SQNR for quaternion data.") - parser.add_argument("--filter", choices=["MadgwickAHRS", "MahonyAHRS"], default="MadgwickAHRS", + parser.add_argument("--filter", choices=["MadgwickAHRS", "MahonyAHRS", "sensfusion6"], default="MadgwickAHRS", help="Select which algorithm's data to process (default: MadgwickAHRS)") parser.add_argument("--limit", type=int, default=1000, help="Limit the number of data lines to process (default: 1000)") @@ -100,10 +100,17 @@ def main(): if args.filter == "MadgwickAHRS": fp_data = parse_data('fp_result.txt', 'Original: ') int_data = parse_data('int_result.txt', 'FIX: ') - else: # mahony + elif args.filter == "MahonyAHRS": fp_data = parse_data('fp_mahony_result.txt', 'Original: ') int_data = parse_data('int_mahony_result.txt', 'FIX: ') + elif args.filter == "sensfusion6": + fp_data = parse_data('fp_sensfusion6_result.txt', 'Original: ') + int_data = parse_data('int_sensfusion6_result.txt', 'FIX: ') + + else: + raise ValueError(f"Unsupported filter: {args.filter}") + if len(fp_data) != len(int_data): print("Mismatch in the number of quaternions between files!") return From 88c16b3192f29564bfbcd64405a3136719b8ce3c Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 28 Apr 2025 13:17:40 +0100 Subject: [PATCH 159/213] update remove unused constant * quantize. --- ...e47b1b17600eacfd132cde3109992ea24c3c09.txt | 48 ++++++++++++ ...e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt | 48 ++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 75 ++++++++++++++++--- 3 files changed, 162 insertions(+), 9 deletions(-) create mode 100644 analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt create mode 100644 analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt diff --git a/analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt b/analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt new file mode 100644 index 000000000..e4676d9e3 --- /dev/null +++ b/analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt @@ -0,0 +1,48 @@ + +changeset: 1751:36e47b1b17600eacfd132cde3109992ea24c3c09 +char kNewtonVersion[] = "0.3-alpha-1751 (36e47b1b17600eacfd132cde3109992ea24c3c09) (build 04-28-2025-10:47-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt b/analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt new file mode 100644 index 000000000..73eaf0ca0 --- /dev/null +++ b/analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt @@ -0,0 +1,48 @@ + +changeset: 1752:50e9d59b8be3d568aca2f34f3b2c75dbcc259f75 +char kNewtonVersion[] = "0.3-alpha-1752 (50e9d59b8be3d568aca2f34f3b2c75dbcc259f75) (build 04-28-2025-12:46-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 9c1ddd52b..b4303e3c9 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -45,8 +45,7 @@ POSSIBILITY OF SUCH DAMAGE. #include "newton-irPass-LLVMIR-memoryAlignment.h" #include "newton-irPass-LLVMIR-emitAssume.h" -// added code -// #include "myRangeAnalysis.h" + #endif /* __cplusplus */ #include @@ -133,6 +132,39 @@ checkOverflow(State * N, BoundInfo * boundInfo, int FRAC_Q) } } +void +autoWhitelistFunctions(Module &Mod, std::set &whitelist) +{ + for (Function &F : Mod) + { + if (F.isDeclaration()) continue; + + bool hasSensorParams = false; + + for (auto &Arg : F.args()) + { + if (Arg.hasName()) + { + std::string argName = Arg.getName().str(); + // 这里你可以加更丰富的匹配,比如查 typedef 类型名(需要调试信息) + if (argName.find("bmx055") != std::string::npos) + { + hasSensorParams = true; + break; + } + } + } + + if (hasSensorParams) + { + llvm::errs() << "Auto-Whitelisting function based on sensor params: " << F.getName() << "\n"; + whitelist.insert(F.getName().str()); + } + } +} + + + // #define IS_POINTER 1 std::set whitelist = { "MadgwickAHRSupdate", @@ -146,21 +178,44 @@ std::set whitelist = { "__ieee754_exp"}; +//void eraseUnusedConstant(Module &M) { +// std::set quantizedGlobals; +// +// // First pass: collect all _quantized names +// for (auto &GV : M.globals()) { +// if (GV.getName().endswith("_quantized")) { +// std::string baseName = GV.getName().str().substr(0, GV.getName().size() - 10); +// quantizedGlobals.insert(baseName); +// } +// } +// // Second pass: delete unused original globals +// std::vector toDelete; +// for (auto &GV : M.globals()) { +// std::string name = GV.getName().str(); +// if (GV.use_empty() && quantizedGlobals.count(name)) { +// toDelete.push_back(&GV); +// } +// } +// for (auto *GV : toDelete) { +// GV->eraseFromParent(); +// } +//} void eraseUnusedConstant(Module &M) { - std::set quantizedGlobals; - - // First pass: collect all _quantized names + std::set quantizedBaseNames; for (auto &GV : M.globals()) { if (GV.getName().endswith("_quantized")) { - std::string baseName = GV.getName().str().substr(0, GV.getName().size() - 10); - quantizedGlobals.insert(baseName); + std::string baseName = GV.getName().str().substr(0, GV.getName().size() - 10); // remove "_quantized" + quantizedBaseNames.insert(baseName); } } - // Second pass: delete unused original globals std::vector toDelete; for (auto &GV : M.globals()) { std::string name = GV.getName().str(); - if (GV.use_empty() && quantizedGlobals.count(name)) { + + if (GV.use_empty() && quantizedBaseNames.count(name)) { + toDelete.push_back(&GV); + } + if (GV.use_empty() && name.size() > 10 && name.substr(name.size() - 10) == "_quantized") { toDelete.push_back(&GV); } } @@ -171,6 +226,7 @@ void eraseUnusedConstant(Module &M) { + void handleGlobalStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) { @@ -1133,6 +1189,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl for (auto & mi : *Mod) { llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); } for (auto mi : functionsToInsert) From f861cdaa3cd8c3e510c950f11ee35a7d19d8e899 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sat, 3 May 2025 11:43:55 +0100 Subject: [PATCH 160/213] update fpu detection * quantize. --- ...e833cce9ad8f877114760a7900fc0a2085e1f9.txt | 48 +++++++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 68 ++++++++++--------- 2 files changed, 83 insertions(+), 33 deletions(-) create mode 100644 analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt diff --git a/analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt b/analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt new file mode 100644 index 000000000..c2e198c42 --- /dev/null +++ b/analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt @@ -0,0 +1,48 @@ + +changeset: 1753:38e833cce9ad8f877114760a7900fc0a2085e1f9 +char kNewtonVersion[] = "0.3-alpha-1753 (38e833cce9ad8f877114760a7900fc0a2085e1f9) (build 04-28-2025-13:17-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index b4303e3c9..486a43c62 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -178,28 +178,6 @@ std::set whitelist = { "__ieee754_exp"}; -//void eraseUnusedConstant(Module &M) { -// std::set quantizedGlobals; -// -// // First pass: collect all _quantized names -// for (auto &GV : M.globals()) { -// if (GV.getName().endswith("_quantized")) { -// std::string baseName = GV.getName().str().substr(0, GV.getName().size() - 10); -// quantizedGlobals.insert(baseName); -// } -// } -// // Second pass: delete unused original globals -// std::vector toDelete; -// for (auto &GV : M.globals()) { -// std::string name = GV.getName().str(); -// if (GV.use_empty() && quantizedGlobals.count(name)) { -// toDelete.push_back(&GV); -// } -// } -// for (auto *GV : toDelete) { -// GV->eraseFromParent(); -// } -//} void eraseUnusedConstant(Module &M) { std::set quantizedBaseNames; for (auto &GV : M.globals()) { @@ -561,21 +539,20 @@ detectFloatingPointOps(Module & Mod) } } -void -checkFPUAvailability(Module & Mod) +void checkFPUAvailability(Module &Mod) { - bool hasFPU = false; + bool hasFPU = false; std::set detectedFeatures; - // Iterate over functions to check attributes - for (auto & F : Mod) + // 1. Check target-features from function attributes + for (auto &F : Mod) { if (F.hasFnAttribute("target-features")) { std::string features = F.getFnAttribute("target-features").getValueAsString().str(); detectedFeatures.insert(features); - // Check for x86 floating-point features + // x86 FPU features if (features.find("+sse") != std::string::npos || features.find("+sse2") != std::string::npos || features.find("+avx") != std::string::npos || @@ -585,7 +562,7 @@ checkFPUAvailability(Module & Mod) hasFPU = true; } - // Check for ARM floating-point features + // ARM FPU features if (features.find("+vfp") != std::string::npos || features.find("+neon") != std::string::npos || features.find("+fp-armv8") != std::string::npos || @@ -596,21 +573,44 @@ checkFPUAvailability(Module & Mod) } } - // Print detected target features (only once) + // 2. Supplementary check: parse target triple for architecture + std::string triple = Mod.getTargetTriple(); + llvm::errs() << "Target Triple: " << triple << "\n"; + + if (triple.find("armv7e-m") != std::string::npos || + triple.find("armv8-m.main") != std::string::npos || + triple.find("aarch64") != std::string::npos) + { + hasFPU = true; + llvm::errs() << "Triple indicates hardware FPU support.\n"; + } + else if (triple.find("armv6-m") != std::string::npos || + triple.find("armv7-m") != std::string::npos) + { + hasFPU = false; + llvm::errs() << "Triple indicates no FPU support.\n"; + } + else + { + llvm::errs() << "Triple is not recognized. Assuming conservative FPU support = false.\n"; + } + + // 3. (Removed hardcoded CPU name database for better generality) + + // Summary if (!detectedFeatures.empty()) { llvm::errs() << "Target Features: "; - for (const auto & feature : detectedFeatures) + for (const auto &feature : detectedFeatures) { llvm::errs() << feature << " "; } llvm::errs() << "\n"; } - // Final decision based on FPU detection if (hasFPU) { - llvm::errs() << "FPU detected via function attributes. \n"; + llvm::errs() << "FPU detected (from features and/or triple).\n"; } else { @@ -619,6 +619,8 @@ checkFPUAvailability(Module & Mod) } } + + /////////////////////////////////////////////////////////////////////////////////////////////////////////////// // Process functions that are whitelisted for dequantization From a779b37459ec50e499dcc1734d38fe08eb70f4d9 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 5 May 2025 12:53:50 +0100 Subject: [PATCH 161/213] update test scripts * quantize. --- applications/newton/llvm-ir/SQNR.sh | 13 +++--- applications/newton/llvm-ir/plot_sqnr.py | 50 ++++++++++++++++++++++++ 2 files changed, 57 insertions(+), 6 deletions(-) create mode 100644 applications/newton/llvm-ir/plot_sqnr.py diff --git a/applications/newton/llvm-ir/SQNR.sh b/applications/newton/llvm-ir/SQNR.sh index d3c204e68..f6afe2e34 100755 --- a/applications/newton/llvm-ir/SQNR.sh +++ b/applications/newton/llvm-ir/SQNR.sh @@ -19,7 +19,7 @@ while [[ $# -gt 0 ]]; do esac done -OUTPUT_FILE="snr_results_${ALGO,,}.txt" +OUTPUT_FILE="sqnr_results_${ALGO,,}.txt" # Clear old file contents > "$OUTPUT_FILE" @@ -35,7 +35,7 @@ echo "| MAX_PRECISION_BITS | SQNR (dB) |" >> "$OUTPUT_FILE" echo "|--------------------|-----------|" >> "$OUTPUT_FILE" # Loop over MAX_PRECISION_BITS values from 8 to 18 -for i in {8..9}; do +for i in {8..11}; do echo "Compiling and testing with MAX_PRECISION_BITS = $i ..." # Switch to the make directory for compilation @@ -50,12 +50,13 @@ for i in {8..9}; do # Run the test ./testMadgwick.sh "$ALGO" - # Extract SNR line - SNR_VALUE=$(python3 rms_error.py --filter "$ALGO" | grep "SQNR (dB)" | awk -F': ' '{print $2}') - echo "MAX_PRECISION_BITS=$i, SQNR (dB)=$SNR_VALUE" + # Extract sqnr line + sqnr_VALUE=$(python3 rms_error.py --filter "$ALGO" | grep "SQNR (dB)" | awk -F': ' '{print $2}') + echo "MAX_PRECISION_BITS=$i, SQNR (dB)=$sqnr_VALUE" # Append as a clean table row - printf "| %-18s | %-9s |\n" "$i" "$SNR_VALUE" >> "$OUTPUT_FILE" + printf "| %-18s | %-9s |\n" "$i" "$sqnr_VALUE" >> "$OUTPUT_FILE" done echo "All tests completed. Results saved to $OUTPUT_FILE." +python3 plot_sqnr.py "$OUTPUT_FILE" diff --git a/applications/newton/llvm-ir/plot_sqnr.py b/applications/newton/llvm-ir/plot_sqnr.py new file mode 100644 index 000000000..bfc3218b1 --- /dev/null +++ b/applications/newton/llvm-ir/plot_sqnr.py @@ -0,0 +1,50 @@ +import matplotlib.pyplot as plt +import sys +import re +import os + +def parse_sqnr_file(filename): + bits = [] + sqnrs = [] + with open(filename, 'r') as f: + for line in f: + match = re.match(r"\|\s*(\d+)\s*\|\s*([\d.]+)\s*\|", line) + if match: + bits.append(int(match.group(1))) + sqnrs.append(float(match.group(2))) + return bits, sqnrs + +def extract_filter_name(filename): + base = os.path.basename(filename) + if "madgwickahrs" in base: + return "MadgwickAHRS" + elif "mahonyahrs" in base: + return "MahonyAHRS" + elif "sensfusion6" in base: + return "Sensfusion6" + else: + return "Unknown" + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python3 plot_sqnr.py ") + sys.exit(1) + + filename = sys.argv[1] + bits, sqnrs = parse_sqnr_file(filename) + filter_name = extract_filter_name(filename) + + plt.figure() + plt.plot(bits, sqnrs, marker='o') + + plt.xticks(bits) + + plt.title(f'SQNR vs. MAX_PRECISION_BITS for {filter_name} Filter') + plt.xlabel('MAX_PRECISION_BITS (FRAC_Q)') + plt.ylabel('SQNR (dB)') + plt.grid(True) + plt.tight_layout() + + output_img = filename.replace(".txt", ".png") + plt.savefig(output_img) + print(f"Plot saved to {output_img}") From 0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 5 May 2025 15:50:42 +0100 Subject: [PATCH 162/213] update lshr with ashr * quantize. --- ...c16b3192f29564bfbcd64405a3136719b8ce3c.txt | 48 ++++ ...61cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt | 48 ++++ .../newton-irPass-LLVMIR-quantization.cpp | 228 ++++++++++-------- 3 files changed, 225 insertions(+), 99 deletions(-) create mode 100644 analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt create mode 100644 analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt diff --git a/analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt b/analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt new file mode 100644 index 000000000..6f8b853f8 --- /dev/null +++ b/analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt @@ -0,0 +1,48 @@ + +changeset: 1754:88c16b3192f29564bfbcd64405a3136719b8ce3c +char kNewtonVersion[] = "0.3-alpha-1754 (88c16b3192f29564bfbcd64405a3136719b8ce3c) (build 05-03-2025-11:43-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt b/analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt new file mode 100644 index 000000000..d68b09740 --- /dev/null +++ b/analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt @@ -0,0 +1,48 @@ + +changeset: 1755:f861cdaa3cd8c3e510c950f11ee35a7d19d8e899 +char kNewtonVersion[] = "0.3-alpha-1755 (f861cdaa3cd8c3e510c950f11ee35a7d19d8e899) (build 05-05-2025-12:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index ce3a801cb..a1d4d9fd7 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -36,7 +36,8 @@ performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value llvm::Value * mulResult32 = Builder.CreateNSWMul(lhs32, rhs32); // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value * divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); +// llvm::Value * divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult32 = Builder.CreateAShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); // Truncate the result back to 16-bit integer result = Builder.CreateTrunc(divResult32, llvm::Type::getInt16Ty(Builder.getContext())); @@ -52,7 +53,8 @@ performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); // Right shift the result to simulate fixed-point division by FRAC_Q - llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); +// llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult64 = Builder.CreateAShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); // Truncate the 64-bit result back to 32-bit integer result = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); @@ -180,7 +182,8 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< // Step 3: Approximation using magic number llvm::Value * bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); - llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); +// llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); + llvm::Value * shiftedFpX = builder.CreateAShr(bitcastFpX, 1); llvm::Value * magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); approx = builder.CreateSub(magicNumber, shiftedFpX); llvm::Value * approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); @@ -190,17 +193,20 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< // Step 4: Newton-Raphson refinement llvm::Value * sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); llvm::Value * mul1 = builder.CreateMul(sextShiftedX, intApprox); - llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); +// llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); + llvm::Value * mul1Shifted = builder.CreateAShr(mul1, FRAC_Q); llvm::Value * mul2 = builder.CreateMul(mul1Shifted, intApprox); - llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); +// llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); + llvm::Value * mul2Shifted = builder.CreateAShr(mul2, FRAC_Q); int correctionValue = static_cast(1.5f * FRAC_BASE); llvm::Value * correction = builder.CreateSub( llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), correctionValue), mul2Shifted); llvm::Value * finalMul = builder.CreateMul(intApprox, correction); - llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); +// llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); + llvm::Value * finalShifted = builder.CreateAShr(finalMul, FRAC_Q); // Step 5: Truncate the result back to i16 result = builder.CreateTrunc(finalShifted, quantizedType); @@ -210,14 +216,17 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< default: { // Step 1: Shift x to compute %1 (x >> 1) - llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); +// llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + llvm::Value * halfBase = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); // Step 2: Convert x to floating-point and perform the initial approximation fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); fpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); - llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); - i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); +// llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt64Ty(irModule->getContext())); +// i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateAShr(i, 1)); fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); @@ -263,9 +272,12 @@ eraseOldGlobals() std::vector functionsToErase; // Function to actually erase functions after processing + void eraseOldFunctions() { + + llvm::errs() << "Entering eraseOldFunctions\n"; for (auto * func : functionsToErase) { @@ -285,71 +297,71 @@ isWhitelistedGlobal(const std::string & globalName) return true; } -bool -shouldProcessFunction(Function &F) -{ - if (F.isDeclaration()) - return false; +//bool +//shouldProcessFunction(Function &F) +//{ +// if (F.isDeclaration()) +// return false; +// +// +// for (auto &BB : F) +// { +// for (auto &I : BB) +// { +// +// if (auto *DbgValue = dyn_cast(&I)) +// { +// Value *Val = DbgValue->getValue(); +// if (!Val) +// continue; +// if (auto *Arg = dyn_cast(Val)) +// { +// auto *DIVar = DbgValue->getVariable(); +// if (DIVar) +// { +// std::string typeName = DIVar->getType()->getName().str(); +// if (typeName.find("bmx055") != std::string::npos) +// { +// +// return true; +// } +// } +// } +// } +// } +// } +// +// return false; +//} - for (auto &BB : F) - { - for (auto &I : BB) - { - if (auto *DbgValue = dyn_cast(&I)) - { - Value *Val = DbgValue->getValue(); - if (!Val) - continue; - if (auto *Arg = dyn_cast(Val)) - { - auto *DIVar = DbgValue->getVariable(); - if (DIVar) - { - std::string typeName = DIVar->getType()->getName().str(); - if (typeName.find("bmx055") != std::string::npos) - { - - return true; - } - } - } - } - } - } +bool +shouldProcessFunction(Function & F) +{ + // List of function names to process + static const std::set targetFunctions = { + "sensfusion6UpdateQImpl", + "MadgwickAHRSupdate", + "MadgwickAHRSupdateIMU", + "MahonyAHRSupdate", + "MahonyAHRSupdateIMU", + "matrixMul", + "matrixAdd", + "matrixSub", + "pzero", + "qzero", + "pone", + "qone", + "__ieee754_exp", + "twofft" +// "__ieee754_log", + }; - return false; + // Check if the function name is in the set + return targetFunctions.find(F.getName().str()) != targetFunctions.end(); } - - -//bool -//shouldProcessFunction(Function & F) -//{ -// // List of function names to process -// static const std::set targetFunctions = { -// "sensfusion6UpdateQImpl", -// "MadgwickAHRSupdate", -// "MadgwickAHRSupdateIMU", -// "MahonyAHRSupdate", -// "MahonyAHRSupdateIMU", -// "matrixMul", -// "matrixAdd", -// "matrixSub", -// "pzero", -// "qzero", -// "pone", -// "qone", -// "__ieee754_exp", -// "twofft" -//// "__ieee754_log", -// }; -// -// // Check if the function name is in the set -// return targetFunctions.find(F.getName().str()) != targetFunctions.end(); -//} - // Helper to create or retrieve the quantized global variable GlobalVariable * getOrCreateQuantizedGlobal(Module * module, GlobalVariable & globalVar, Type * quantizedType) @@ -712,48 +724,48 @@ updateInternalConstants(Module * module, Type * quantizedType) } } -void -quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) -{ - Value * pointerOperand = loadInst->getPointerOperand(); - llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; - - Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); - - Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); - - Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); - - loadInst->replaceAllUsesWith(quantizedValue); - loadInst->eraseFromParent(); - - llvm::errs() << "Replaced load with quantized integer value.\n"; -} - //void //quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) //{ // Value * pointerOperand = loadInst->getPointerOperand(); // llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; // -// // Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); // -// // Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); // -// -// Value * roundingOffset = ConstantFP::get(loadedType, 0.5); -// Value * roundedValue = Builder.CreateFAdd(scaledValue, roundingOffset, loadInst->getName() + ".rounded_ptr"); -// -// Value * quantizedValue = Builder.CreateFPToSI(roundedValue, quantizedType, loadInst->getName() + ".quantized_ptr"); +// Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); // // loadInst->replaceAllUsesWith(quantizedValue); // loadInst->eraseFromParent(); // -// llvm::errs() << "Replaced load with quantized integer value with rounding.\n"; +// llvm::errs() << "Replaced load with quantized integer value.\n"; //} +void +quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +{ + Value * pointerOperand = loadInst->getPointerOperand(); + llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; + + + Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); + + + Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); + + + Value * roundingOffset = ConstantFP::get(loadedType, 0.5); + Value * roundedValue = Builder.CreateFAdd(scaledValue, roundingOffset, loadInst->getName() + ".rounded_ptr"); + + Value * quantizedValue = Builder.CreateFPToSI(roundedValue, quantizedType, loadInst->getName() + ".quantized_ptr"); + + loadInst->replaceAllUsesWith(quantizedValue); + loadInst->eraseFromParent(); + + llvm::errs() << "Replaced load with quantized integer value with rounding.\n"; +} + void quantizeMatrixFloat(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) { @@ -1234,6 +1246,18 @@ checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruct {2.0, {1, false}}, {4.0, {2, false}}, {8.0, {3, false}}, + {16.0, {4, false}}, + {32.0, {5, false}}, + {64.0, {6, false}}, + {128.0, {7, false}}, + {256.0, {8, false}}, + {512.0, {9, false}}, + {1 / 2.0, {1, true}}, + {1 / 4.0, {2, true}}, + {1 / 8.0, {3, true}}, + {1 / 16.0, {4, true}}, + {1 / 32.0, {5, true}}, + {1 / 64.0, {6, true}}, {1 / 128.0, {7, true}}, {1 / 256.0, {8, true}}, {1 / 512.0, {9, true}}}; @@ -1833,7 +1857,6 @@ performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPoi // Create call to the fixed-point sqrt function //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); - //手动写减少call overhead llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); // No need to apply shl and compensation if it's already done in createFixSqrt llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); @@ -1910,10 +1933,14 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) // Check if the function has no more uses +// functionsToErase.erase( +// std::remove(functionsToErase.begin(), functionsToErase.end(), fixrsqrt), +// functionsToErase.end() +// ); + + if (calledFunction->use_empty()) { - functionsToErase.push_back(calledFunction); // Add it to the list for later erasure + functionsToErase.push_back(calledFunction); } } @@ -1934,7 +1961,6 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) { functionsToErase.push_back(calledFunction); @@ -2280,6 +2306,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect * */ //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); +// functionsToErase.push_back(fixrsqrt); for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { @@ -2445,10 +2472,13 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect } } + + // adaptTypeCast(llvmIrFunction, quantizedType); // Process functions that are whitelisted for dequantization // processWhitelistedFunctions(*module, whitelist); return; } + } \ No newline at end of file From 61e354ebef3180f38512bf461d40fdbfcf75ec0a Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 5 May 2025 15:54:55 +0100 Subject: [PATCH 163/213] save * quantize. --- ...79b37459ec50e499dcc1734d38fe08eb70f4d9.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 2 +- 2 files changed, 49 insertions(+), 1 deletion(-) create mode 100644 analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt diff --git a/analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt b/analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt new file mode 100644 index 000000000..f382e46d4 --- /dev/null +++ b/analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt @@ -0,0 +1,48 @@ + +changeset: 1756:a779b37459ec50e499dcc1734d38fe08eb70f4d9 +char kNewtonVersion[] = "0.3-alpha-1756 (a779b37459ec50e499dcc1734d38fe08eb70f4d9) (build 05-05-2025-15:50-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index a1d4d9fd7..075832513 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -224,7 +224,7 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< fpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); // llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); - llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt64Ty(irModule->getContext())); + llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); // i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateAShr(i, 1)); fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); From b814e9bacbb529d77179ce70115eb96e3390ece8 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 20 May 2025 16:46:44 +0100 Subject: [PATCH 164/213] save before change * quantize. --- src/newton/newton-irPass-LLVMIR-quantization.cpp | 16 ++++++++-------- 1 file changed, 8 insertions(+), 8 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 075832513..ceb85a1e2 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -182,8 +182,8 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< // Step 3: Approximation using magic number llvm::Value * bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); -// llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); - llvm::Value * shiftedFpX = builder.CreateAShr(bitcastFpX, 1); + llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); +// llvm::Value * shiftedFpX = builder.CreateAShr(bitcastFpX, 1); llvm::Value * magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); approx = builder.CreateSub(magicNumber, shiftedFpX); llvm::Value * approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); @@ -193,8 +193,8 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< // Step 4: Newton-Raphson refinement llvm::Value * sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); llvm::Value * mul1 = builder.CreateMul(sextShiftedX, intApprox); -// llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); - llvm::Value * mul1Shifted = builder.CreateAShr(mul1, FRAC_Q); + llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); +// llvm::Value * mul1Shifted = builder.CreateAShr(mul1, FRAC_Q); llvm::Value * mul2 = builder.CreateMul(mul1Shifted, intApprox); // llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); @@ -205,8 +205,8 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), correctionValue), mul2Shifted); llvm::Value * finalMul = builder.CreateMul(intApprox, correction); -// llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); - llvm::Value * finalShifted = builder.CreateAShr(finalMul, FRAC_Q); + llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); +// llvm::Value * finalShifted = builder.CreateAShr(finalMul, FRAC_Q); // Step 5: Truncate the result back to i16 result = builder.CreateTrunc(finalShifted, quantizedType); @@ -216,8 +216,8 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< default: { // Step 1: Shift x to compute %1 (x >> 1) -// llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); - llvm::Value * halfBase = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); + llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); +// llvm::Value * halfBase = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); // Step 2: Convert x to floating-point and perform the initial approximation fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); From 89ede4f688c88b2c08007743b79b087f55648258 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 25 May 2025 13:09:13 +0100 Subject: [PATCH 165/213] update microbenchmark testfile * quantize. --- .../llvm-ir/performance_test/auto_test.cpp | 479 +++++++++++------- 1 file changed, 294 insertions(+), 185 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index b1e4d6f66..b6c43b4c5 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -16,8 +16,9 @@ #include #include #include +#include -const size_t iteration_num = 5; +const size_t iteration_num = 1; struct perfData { int64_t inst_count_avg; @@ -91,7 +92,11 @@ std::pair processDataPerf(const std::string test_case, const s int64_t inst_count, time_consumption; // perf command - std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + //todo Quantization prefix + std::string quant_prefix = (test_case.find("_opt") != std::string::npos) ? "AUTO_QUANT=1 " : ""; + std::string cmd = "bash -c '" + quant_prefix + "ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + std::cout << "[DEBUG] Running command: " << cmd << std::endl; + // std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; int command_return = system(cmd.c_str()); if (command_return != 0) { return std::make_pair(0, 0); @@ -99,7 +104,8 @@ std::pair processDataPerf(const std::string test_case, const s cmd.clear(); cmd = "bash -c 'perf stat -B ./main_out " + params; - cmd += "if=/dev/zero of=/dev/null count=1000000"; +// cmd += "if=/dev/zero of=/dev/null count=1000000"; + cmd += "if=/dev/zero of=/dev/null count=1"; cmd += " 2>&1 | tee tmp.log'"; command_return = system(cmd.c_str()); if (command_return != 0) { @@ -138,7 +144,18 @@ std::pair> processDataTimer(const std::string test_c std::vector function_results; // perf command - std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; + //TODO Addd Quantization prefix +// std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; +// std::string quant_prefix = "AUTO_QUANT=1 "; + +// std::string cmd = "bash -c '" + quant_prefix + "make " + test_case + " >& compile.log'"; +// std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; +// std::cout << "[DEBUG] Running command: " << cmd << std::endl; + std::string quant_prefix = (test_case.find("_opt") != std::string::npos) ? "AUTO_QUANT=1 " : ""; + std::string cmd = "bash -c '" + quant_prefix + "make " + test_case + " >& compile.log'"; + std::cout << "[DEBUG] Running command: " << cmd << std::endl; + + int command_return = system(cmd.c_str()); if (command_return != 0) { return std::make_pair(0, std::vector(0)); @@ -290,184 +307,276 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin return timer_data; } -int main(int argc, char** argv) { - std::vector test_cases{ - "perf_exp", "perf_log", - "perf_acosh", "perf_j0", - "perf_y0", "perf_rem_pio2", "perf_sincosf", - "perf_float64_add", "perf_float64_div", - "perf_float64_mul"}; - - if (argc >= 2) { - test_cases.clear(); - test_cases.emplace_back(argv[1]); - } - - std::ofstream ofs("perf.log"); - if (!ofs.is_open()) { - std::cout << "error opening perf.log"; - return -1; - } - - std::ofstream avg_speedup("average_speedup.log"); - if (!avg_speedup.is_open()) { - std::cout << "error opening perf.log"; - return -1; - } - - std::vector> normalParameters{ - // BMX055 acceleration - {-2, 2}, - {-4, 4}, - {-8, 8}, - {-16, 16}, - // BMX055 gyroscope - {-125, 125}, - // LM35 Centigrade Temperature Sensor - {-40, 110}, - {-55, 150}, - {0, 100}, - {0, 70}, - // LPS25H crazyflie - {260, 1260}, - // MAX31820 1-Wire Ambient Temperature Sensor - {10, 45}, - {-55, 125}, - // DHT11 Humidity Sensor - {20, 80}, - {0, 50}, - // LMP82064 Current Sensor and Voltage Monitor with SPI - {-0.2, 2}, - // PCE-353 LEQ Sound Level Meter - {30, 130}, - // LLS05-A Linear Light Sensor - {1, 200} - }; - - std::vector> trigonometricParams{ - {0, 0.17453292519943295}, // (0, pi/18) - {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) - {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) - {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) - {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) - {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) - {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) - {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) - {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) - {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) - }; - - if (argc == 4) { - normalParameters.clear(); - std::vector input_param{strtod(argv[2], nullptr), strtod(argv[3], nullptr)}; - normalParameters.emplace_back(input_param); - - trigonometricParams.clear(); - trigonometricParams.emplace_back(input_param); - } - - ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; - avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; - - for (size_t case_id = 0; case_id < test_cases.size(); case_id++) { - int avg_inst_speedup = 0; - int avg_time_speedup = 0; - int avg_ir_reduce = 0; - int avg_lib_size_reduce = 0; - const std::vector> parameters = - test_cases[case_id] == "perf_float64_sin" ? trigonometricParams : normalParameters; - for (const auto& p : parameters) { - const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", - "/g' ../../sensors/test.nt", - {p.front(), p.back()}); - const double p1 = p.front() + 0.6; - const double p2 = p.back() + 0.3; - change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); - - // perfData ori_perf_data = recordData(test_cases[case_id], param_str, ofs); - // perfData opt_perf_data = recordData(test_cases[case_id] + "_opt", param_str, ofs); - timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); - timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); - - // check function results - if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), - opt_perf_data.function_results.begin())) { - std::cerr << "result error: " << test_cases[case_id] << " with parameters: " << param_str << - "ori: " << ori_perf_data.function_results[0] << ", opt: " << opt_perf_data.function_results[0] << - std::endl; - } - - // remove element if ori < opt - assert(ori_perf_data.ms_time_consumption.size() == opt_perf_data.ms_time_consumption.size()); - auto itOri = ori_perf_data.ms_time_consumption.begin(); - for (auto itOpt = opt_perf_data.ms_time_consumption.begin(); - itOpt != opt_perf_data.ms_time_consumption.end();) { - if (*itOri < *itOpt) { - // assert(false && "Need to check why this case slow down!!!!!!"); - itOri = ori_perf_data.ms_time_consumption.erase(itOri); - itOpt = opt_perf_data.ms_time_consumption.erase(itOpt); - } else { - itOri++; - itOpt++; - } - } - - int inst_speedup, time_speedup, ir_reduce, lib_size_reduce; - if (ori_perf_data.ms_time_consumption.empty()) { - assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); - inst_speedup = 0; - time_speedup = 0; - } else { - ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), - ori_perf_data.ms_time_consumption.end(), - 0.0) / ori_perf_data.ms_time_consumption.size(); - opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), - opt_perf_data.ms_time_consumption.end(), - 0.0) / opt_perf_data.ms_time_consumption.size(); - - inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) - * 100 / opt_perf_data.inst_count_avg); - time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) - * 100 / opt_perf_data.time_consumption_avg); - } - - if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) { - ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); - lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); - } else { - // assert(false && "Need to check why this case increase size!!!!!!"); - ir_reduce = 0; - lib_size_reduce = 0; - } - ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" - << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; - std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" - << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; - - avg_inst_speedup += inst_speedup; - avg_time_speedup += time_speedup; - avg_ir_reduce += ir_reduce; - avg_lib_size_reduce += lib_size_reduce; - - // reset test.nt - change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", - {p.front(), p.back()}); - change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); - } - avg_inst_speedup = round(avg_inst_speedup / parameters.size()); - avg_time_speedup = round(avg_time_speedup / parameters.size()); - avg_ir_reduce = round(avg_ir_reduce / parameters.size()); - avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); - avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" - << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%" << std::endl; - - if (test_cases[case_id] == "perf_float64_sin") { - // trigonometricParams cannot have extent - break; - } - } - - ofs.close(); - - return 0; -} +int main(int argc, char** argv) +{ + std::vector test_cases{ + // "perf_exp", "perf_log", + // "perf_acosh", "perf_j0", + // "perf_y0", "perf_rem_pio2", "perf_sincosf", + // "perf_float64_add", "perf_float64_div", + // "perf_float64_mul"}; + "perf_j0"}; +// "perf_y0"}; + + if (argc >= 2) + { + test_cases.clear(); + test_cases.emplace_back(argv[1]); + } + + std::ofstream ofs("perf.log"); + if (!ofs.is_open()) + { + std::cout << "error opening perf.log"; + return -1; + } + + std::ofstream avg_speedup("average_speedup.log"); + if (!avg_speedup.is_open()) + { + std::cout << "error opening perf.log"; + return -1; + } + + // std::vector> normalParameters{ + // // BMX055 acceleration + // {-2, 2}, + // {-4, 4}, + // {-8, 8}, + // {-16, 16}, + // // BMX055 gyroscope + // {-125, 125}, + // // LM35 Centigrade Temperature Sensor + // {-40, 110}, + // {-55, 150}, + // {0, 100}, + // {0, 70}, + // // LPS25H + // {260, 1260}, + // // MAX31820 1-Wire Ambient Temperature Sensor + // {10, 45}, + // {-55, 125}, + // // DHT11 Humidity Sensor + // {20, 80}, + // {0, 50}, + // // LMP82064 Current Sensor and Voltage Monitor with SPI + // {-0.2, 2}, + // // PCE-353 LEQ Sound Level Meter + // {30, 130}, + // // LLS05-A Linear Light Sensor + // {1, 200} + // }; + + // param_range, precision_bits, frac_q + std::vector, int, int>> normalParameters = { + // BMX055 acceleration + {{-2, 2}, 12, 9}, + {{-4, 4}, 12, 8}, + {{-8, 8}, 12, 7}, + {{-16, 16}, 12, 6}}; +// +// // BMX055 gyroscope +// {{-125, 125}, 16, 8}, +// +// // LM35 Centigrade Temperature Sensor +// {{-40, 110}, 10, 2}, +// {{-55, 150}, 11, 3}, +// {{0, 100}, 10, 3}, +// {{0, 70}, 10, 3}, +// +// // LPS25H Pressure Sensor +// {{260, 1260}, 14, 4}, +// +// // MAX31820 1-Wire Ambient Temperature Sensor +// {{10, 45}, 12, 6}, +// {{-55, 125}, 11, 3}, +// +// // DHT11 Humidity Sensor +// {{20, 80}, 8, 2}, +// {{0, 50}, 8, 2}, +// +// // LMP82064 Current Sensor and Voltage Monitor with SPI +// {{-0.2, 2}, 14, 12}, +// +// // PCE-353 LEQ Sound Level Meter +// {{30, 130}, 8, 1}, +// +// // LLS05-A Linear Light Sensor +// {{1, 200}, 10, 2}}; + + std::vector> trigonometricParams{ + {0, 0.17453292519943295}, // (0, pi/18) + {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) + {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) + {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) + {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) + {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) + {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) + {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) + {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) + {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) + }; + + if (argc == 4) + { + normalParameters.clear(); + std::vector input_param{strtod(argv[2], nullptr), strtod(argv[3], nullptr)}; + int default_frac_q = 8; + int default_precision_bits = 12; + normalParameters.emplace_back(std::make_tuple(input_param, default_precision_bits, default_frac_q)); + //normalParameters.emplace_back(input_param); + + + trigonometricParams.clear(); + trigonometricParams.emplace_back(input_param); + } + + ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; + avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; + + for (size_t case_id = 0; case_id < test_cases.size(); case_id++) + { + int avg_inst_speedup = 0; + int avg_time_speedup = 0; + int avg_ir_reduce = 0; + int avg_lib_size_reduce = 0; + // const std::vector> parameters = + // test_cases[case_id] == "perf_float64_sin" ? trigonometricParams : normalParameters; + // TODO + const bool is_trig = test_cases[case_id] == "perf_float64_sin"; + // 对于普通测试(非trig),使用 normalParameters + if (!is_trig) + { + for (const auto & entry : normalParameters) + { + const std::vector & range = std::get<0>(entry); + int frac_q = std::get<2>(entry); + + // 自动重编译 newton 以更新 MAX_PRECISION_BITS=frac_q + std::string frac_str = std::to_string(frac_q); + std::string rebuild_cmd = + "bash -c '" + "echo \"[DEBUG] Cleaning object files...\" && " + "rm -f ../../../../src/newton/newton-irPass-LLVMIR-quantization.o ../../../../src/newton/newton-irPass-LLVMIR-optimizeByRange.o && " + "echo \"[DEBUG] Running make with MAX_PRECISION_BITS=" + frac_str + "\" && " + "make -C ../../../../src/newton MAX_PRECISION_BITS=" + frac_str + " BIT_WIDTH=32 > build_" + frac_str + ".log 2>&1'"; + +// "2>&1 | tee build_" + frac_str + ".log'"; +//\\ std::string rebuild_cmd = "make -C ../../../src/newton MAX_PRECISION_BITS=" + std::to_string(frac_q) + " VERBOSE=1 rebuild-quant-opt"; + std::cout << "[INFO] Rebuilding Newton with MAX_PRECISION_BITS = " << frac_q << std::endl; + int ret = system(rebuild_cmd.c_str()); + if (ret != 0) + { + std::cerr << "[ERROR] Failed to rebuild Newton with MAX_PRECISION_BITS=" << frac_q << std::endl; + continue; + } + + // 使用范围构造参数字符串 + const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", "/g' ../../sensors/test.nt", range); + const double p1 = range.front() + 0.6; + const double p2 = range.back() + 0.3; + change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); + + // 后续测试逻辑保持不变 + timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); + timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); + + // check function results + if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), + opt_perf_data.function_results.begin())) + { + std::cerr << "result error: " << test_cases[case_id] << " with parameters: " << param_str << "ori: " << ori_perf_data.function_results[0] << ", opt: " << opt_perf_data.function_results[0] << std::endl; + } + + // remove element if ori < opt + assert(ori_perf_data.ms_time_consumption.size() == opt_perf_data.ms_time_consumption.size()); + auto itOri = ori_perf_data.ms_time_consumption.begin(); + for (auto itOpt = opt_perf_data.ms_time_consumption.begin(); + itOpt != opt_perf_data.ms_time_consumption.end();) + { + if (*itOri < *itOpt) + { + // assert(false && "Need to check why this case slow down!!!!!!"); + itOri = ori_perf_data.ms_time_consumption.erase(itOri); + itOpt = opt_perf_data.ms_time_consumption.erase(itOpt); + } + else + { + itOri++; + itOpt++; + } + } + + int inst_speedup, time_speedup, ir_reduce, lib_size_reduce; + if (ori_perf_data.ms_time_consumption.empty()) + { + assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); + inst_speedup = 0; + time_speedup = 0; + } + else + { + ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), + ori_perf_data.ms_time_consumption.end(), + 0.0) / + ori_perf_data.ms_time_consumption.size(); + opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), + opt_perf_data.ms_time_consumption.end(), + 0.0) / + opt_perf_data.ms_time_consumption.size(); + + inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) * 100 / opt_perf_data.inst_count_avg); + time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) * 100 / opt_perf_data.time_consumption_avg); + } + + if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) + { + ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); + lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); + } + else + { + // assert(false && "Need to check why this case increase size!!!!!!"); + ir_reduce = 0; + lib_size_reduce = 0; + } + ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; + std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; + + avg_inst_speedup += inst_speedup; + avg_time_speedup += time_speedup; + avg_ir_reduce += ir_reduce; + avg_lib_size_reduce += lib_size_reduce; + + // reset test.nt + change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", + // {p.front(), p.back()}); + {range.front(), range.back()}); + change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); + } + size_t count = is_trig ? trigonometricParams.size() : normalParameters.size(); + avg_inst_speedup = round(avg_inst_speedup / count); + avg_time_speedup = round(avg_time_speedup / count); + avg_ir_reduce = round(avg_ir_reduce / count); + avg_lib_size_reduce = round(avg_lib_size_reduce / count); + // avg_inst_speedup = round(avg_inst_speedup / parameters.size()); + // avg_time_speedup = round(avg_time_speedup / parameters.size()); + // avg_ir_reduce = round(avg_ir_reduce / parameters.size()); + // avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); + avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" + << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%" << std::endl; + + if (test_cases[case_id] == "perf_float64_sin") + { + // trigonometricParams cannot have extent + break; + } + } + + ofs.close(); + + return 0; + } +} \ No newline at end of file From a2fb29881175d5696515be66cccf1ea9b095d2be Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 25 May 2025 14:55:33 +0100 Subject: [PATCH 166/213] update optimizeByRange.cpp for microbenchmark * quantize. --- ...91ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt | 48 +++ ...e354ebef3180f38512bf461d40fdbfcf75ec0a.txt | 48 +++ ...14e9bacbb529d77179ce70115eb96e3390ece8.txt | 48 +++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 361 +++++++++--------- 4 files changed, 314 insertions(+), 191 deletions(-) create mode 100644 analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt create mode 100644 analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt create mode 100644 analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt diff --git a/analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt b/analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt new file mode 100644 index 000000000..98edcbba2 --- /dev/null +++ b/analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt @@ -0,0 +1,48 @@ + +changeset: 1757:0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d +char kNewtonVersion[] = "0.3-alpha-1757 (0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d) (build 05-05-2025-15:54-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt b/analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt new file mode 100644 index 000000000..7913d8203 --- /dev/null +++ b/analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt @@ -0,0 +1,48 @@ + +changeset: 1758:61e354ebef3180f38512bf461d40fdbfcf75ec0a +char kNewtonVersion[] = "0.3-alpha-1758 (61e354ebef3180f38512bf461d40fdbfcf75ec0a) (build 05-20-2025-16:46-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt b/analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt new file mode 100644 index 000000000..22bfc8b0b --- /dev/null +++ b/analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt @@ -0,0 +1,48 @@ + +changeset: 1759:b814e9bacbb529d77179ce70115eb96e3390ece8 +char kNewtonVersion[] = "0.3-alpha-1759 (b814e9bacbb529d77179ce70115eb96e3390ece8) (build 05-25-2025-13:09-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 486a43c62..e60cff5ee 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -417,6 +417,7 @@ dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) #else handleGlobalStore(storeInst, Builder, maxPrecisionBits); #endif + } //////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// @@ -668,37 +669,7 @@ processWhitelistedFunctions(Module & module, const std::set & white } } -// void -// processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) -//{ -// for (auto & F : module) -// { -// if (whitelist.find(F.getName().str()) != whitelist.end()) -// { -// llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; -// -// -// -// -// for (auto & B : F) -// { -// for (auto & I : B) -// { -// llvm::errs() << "Processing instruction: " << I << "\n"; -// if (auto * storeInst = dyn_cast(&I)) -// { -// llvm::errs() << "Found valid StoreInst.\n"; -// dequantizeResults(storeInst, F, maxPrecisionBits); -// } -// -// } -// } -// -// -// } -// } -// -// } + // Function to save the IR of a module to a file void @@ -994,8 +965,18 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } // +// int maxPrecisionBits = 0; +// for (auto & typePrecisionBit : typePrecisionBits) +// { +// if (typePrecisionBit.second > maxPrecisionBits) +// { +// maxPrecisionBits = typePrecisionBit.second; +// } +// } +// int MAX_PRECISION_BITS = maxPrecisionBits; + + - // int maxPrecisionBits = 0; int maxPrecisionBits = MAX_PRECISION_BITS; /** @@ -1168,172 +1149,174 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl checkOverflow(N, funcPair.second, maxPrecisionBits); } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // shrinkType(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - if (enableQuantization) + flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + for (auto & mi : *Mod) { - flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - llvm::errs() << "Auto quantization enabled\n"; - std::vector functionsToInsert; - for (auto & mi : *Mod) + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) { - llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + shrinkType(N, boundInfoIt->second, mi); } - for (auto mi : functionsToInsert) + // else + // { + // memoryAlignment + // } + } + +// if (enableQuantization) +// { +// flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); +// llvm::errs() << "Auto quantization enabled\n"; +// std::vector functionsToInsert; +// for (auto & mi : *Mod) +// { +// llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; +// +// irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); +// } +// for (auto mi : functionsToInsert) +// { +// Mod->getFunctionList().remove(mi); +// Mod->getFunctionList().push_front(mi); +// } +// } + + /** + * Check for potential overflows + */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); + for (auto & funcPair : funcBoundInfo) { + checkOverflow(N, funcPair.second, maxPrecisionBits); + } + + flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) { - Mod->getFunctionList().remove(mi); - Mod->getFunctionList().push_front(mi); + memoryAlignment(N, boundInfoIt->second, mi); } + // else + // { + // assert(false); + // } } - // /* - // * analyze the range of all local variables in each function - // * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // std::map callerMap; - // callerMap.clear(); - // funcBoundInfo.clear(); - // bool useOverLoad = false; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // - // /** - // * Check for potential overflows - // */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); - // for (auto & funcPair : funcBoundInfo) { - // checkOverflow(N, funcPair.second, maxPrecisionBits); - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // memoryAlignment(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - /* - * remove the functions that are optimized by passes. - * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = true; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); +// +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = true; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } - // /* - // * simplify the condition of each branch - // * */ - // flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // simplifyControlFlow(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // legacy::PassManager passManager; - // passManager.add(createCFGSimplificationPass()); - // passManager.add(createInstSimplifyLegacyPass()); - // passManager.add(createGlobalDCEPass()); - // passManager.run(*Mod); + /* + * simplify the condition of each branch + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + simplifyControlFlow(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } - // + legacy::PassManager passManager; + passManager.add(createCFGSimplificationPass()); + passManager.add(createInstSimplifyLegacyPass()); + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = false; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); - // for (auto & mi : *Mod) - // { - // auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - // if (boundInfoIt != funcBoundInfo.end()) - // { - // constantSubstitution(N, boundInfoIt->second, mi); - // } - // // else - // // { - // // assert(false); - // // } - // } - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - //./ - // if (useOverLoad) - // overloadFunc(Mod, callerMap); + + + +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); +// +// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); +// callerMap.clear(); +// funcBoundInfo.clear(); +// useOverLoad = false; +// for (auto & mi : *Mod) +// { +// auto boundInfo = new BoundInfo(); +// mergeBoundInfo(boundInfo, globalBoundInfo); +// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); +// funcBoundInfo.emplace(mi.getName().str(), boundInfo); +// std::vector calleeNames; +// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); +// } +// + flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + constantSubstitution(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } +// +// /* +// * remove the functions that are optimized by passes. +// * */ +// if (useOverLoad) +// cleanFunctionMap(Mod, callerMap); +// +// if (useOverLoad) +// overloadFunc(Mod, callerMap); + + + + if (enableQuantization) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; + std::vector functionsToInsert; + for (auto & mi : *Mod) + { + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + } + for (auto mi : functionsToInsert) + { + Mod->getFunctionList().remove(mi); + Mod->getFunctionList().push_front(mi); + } + } // Finally, erase old functions eraseOldFunctions(); @@ -1341,10 +1324,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // eraseOldGlobals(); eraseUnusedConstant(*Mod); - // Perform text replacement to remove "_quantized" suffixes - // removeQuantizedSuffixInModule(*Mod); - // eraseOldInstructions(); processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); @@ -1361,7 +1341,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MahonyAHRS_output.ll"); saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/sensfusion6_output.ll"); - // saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll"); /* * Dump BC file to a file. From a7dd276146c9a4f5d54782096eeee007b12e8fb5 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 25 May 2025 15:38:45 +0100 Subject: [PATCH 167/213] update for microbenmark and Madgwick * quantize. --- ...ede4f688c88b2c08007743b79b087f55648258.txt | 48 +++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 99 +++++++------------ 2 files changed, 86 insertions(+), 61 deletions(-) create mode 100644 analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt diff --git a/analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt b/analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt new file mode 100644 index 000000000..7b9eccd9f --- /dev/null +++ b/analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt @@ -0,0 +1,48 @@ + +changeset: 1760:89ede4f688c88b2c08007743b79b087f55648258 +char kNewtonVersion[] = "0.3-alpha-1760 (89ede4f688c88b2c08007743b79b087f55648258) (build 05-25-2025-14:55-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index e60cff5ee..361a2b2cc 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -982,6 +982,8 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl /** * Precision Analysis */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "Precision Analysis"); + double minResolution = 0.0; bool isFirstSensor = true; @@ -1013,32 +1015,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "Required FRAC_Q: ceil(-log2(minResolution) - 1) = ceil(%f) = %d\n", fracQ_exact, fracQ); - /** - * Bitwidth Setting - */ - -// int maxPrecisionBits = 0; -// // int maxPrecisionBits = MAX_PRECISION_BITS; -// for (auto & typePrecisionBit : typePrecisionBits) -// { -// if (typePrecisionBit.second > maxPrecisionBits) -// { -// maxPrecisionBits = typePrecisionBit.second; -// } -// } -// -// int BIT_WIDTH = (maxPrecisionBits > 16) ? 32 : 16; -// flexprint(N->Fe, N->Fm, N->Fpinfo, -// "Max precisionBits among sensors: %d → BIT_WIDTH = %d\n", -// maxPrecisionBits, BIT_WIDTH); -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "bitwidth: %d => using %s\n", -// BIT_WIDTH, -// (BIT_WIDTH == 32 ? "int32_t (i32 fix)" : "int16_t (i16 fix)")); - - - - /** * Config */ @@ -1164,23 +1140,23 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } } -// if (enableQuantization) -// { -// flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); -// llvm::errs() << "Auto quantization enabled\n"; -// std::vector functionsToInsert; -// for (auto & mi : *Mod) -// { -// llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; -// -// irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); -// } -// for (auto mi : functionsToInsert) -// { -// Mod->getFunctionList().remove(mi); -// Mod->getFunctionList().push_front(mi); -// } -// } + if (enableQuantization) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; + std::vector functionsToInsert; + for (auto & mi : *Mod) + { + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + } + for (auto mi : functionsToInsert) + { + Mod->getFunctionList().remove(mi); + Mod->getFunctionList().push_front(mi); + } + } /** * Check for potential overflows @@ -1300,26 +1276,27 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl - if (enableQuantization) - { - flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - llvm::errs() << "Auto quantization enabled\n"; - std::vector functionsToInsert; - for (auto & mi : *Mod) - { - llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); - } - for (auto mi : functionsToInsert) - { - Mod->getFunctionList().remove(mi); - Mod->getFunctionList().push_front(mi); - } - } +// if (enableQuantization) +// { +// flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); +// llvm::errs() << "Auto quantization enabled\n"; +// std::vector functionsToInsert; +// for (auto & mi : *Mod) +// { +// llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; +// +// irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); +// } +// for (auto mi : functionsToInsert) +// { +// Mod->getFunctionList().remove(mi); +// Mod->getFunctionList().push_front(mi); +// } +// } // Finally, erase old functions - eraseOldFunctions(); + +// eraseOldFunctions(); // eraseOldGlobals(); eraseUnusedConstant(*Mod); From d5dd60931a0d357145a3ad4b6bf1b32886e9a85e Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 25 May 2025 20:27:39 +0100 Subject: [PATCH 168/213] update for integration * quantize. --- ...ton-irPass-LLVMIR-constantSubstitution.cpp | 24 +++++++++ .../newton-irPass-LLVMIR-optimizeByRange.cpp | 51 ------------------- 2 files changed, 24 insertions(+), 51 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp b/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp index 8f72bd51e..fcd1a6651 100644 --- a/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp +++ b/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp @@ -34,6 +34,26 @@ using namespace llvm; extern "C" { + +static bool isFromQuantizedGlobal(Value *V) { + if (auto *loadInst = dyn_cast(V)) { + if (auto *gv = dyn_cast(loadInst->getPointerOperand())) { + if (gv->getName().contains("_quantized")) { + return true; + } + } + } + if (auto *inst = dyn_cast(V)) { + for (auto &op : inst->operands()) { + if (isFromQuantizedGlobal(op)) { + return true; + } + } + } + return false; +} + + /* * Steps of constantSubstitution: * 1. for each instruction (that is the case statement), get the range of current instruction from boundInfo @@ -132,6 +152,10 @@ constantSubstitution(State * N, BoundInfo * boundInfo, llvm::Function & llvmIrFu * */ if (fabs(lowerBound - upperBound) < DBL_EPSILON) { + if (isFromQuantizedGlobal(llvmIrInstruction)) { + break; + } + /* * check the type of instruction * */ diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 361a2b2cc..8b456f4c2 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -687,39 +687,6 @@ saveModuleIR(llvm::Module & M, const std::string & fileName) file.close(); } -void -removeQuantizedSuffixInModule(llvm::Module & M) -{ - for (auto & F : M) - { - if (F.hasName()) - { - std::string FuncName = F.getName().str(); - size_t pos = FuncName.find("_quantized"); - if (pos != std::string::npos) - { - FuncName.erase(pos, 10); - F.setName(FuncName); - } - } - } - - // Remove suffix from global variables - for (auto & G : M.globals()) - { - if (G.hasName()) - { - std::string GlobalName = G.getName().str(); - size_t pos = GlobalName.find("_quantized"); - if (pos != std::string::npos) - { - GlobalName.erase(pos, 10); // Remove "_quantized" - G.setName(GlobalName); - } - } - } -} - double computeResolution(Modality * mod) { @@ -1276,24 +1243,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl -// if (enableQuantization) -// { -// flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); -// llvm::errs() << "Auto quantization enabled\n"; -// std::vector functionsToInsert; -// for (auto & mi : *Mod) -// { -// llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; -// -// irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); -// } -// for (auto mi : functionsToInsert) -// { -// Mod->getFunctionList().remove(mi); -// Mod->getFunctionList().push_front(mi); -// } -// } - // Finally, erase old functions // eraseOldFunctions(); From 9e9f413256b96f30f453566a1b4a64e4d00fb06d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 26 May 2025 22:00:03 +0100 Subject: [PATCH 169/213] update files for microbenchmark test * quantize. --- applications/newton/llvm-ir/SQNR.sh | 7 +- .../newton/llvm-ir/c-files/sensfusion6.c | 15 +- .../newton/llvm-ir/performance_test/Makefile | 5 +- .../llvm-ir/performance_test/auto_test.cpp | 18 +- applications/newton/llvm-ir/testMadgwick.sh | 15 +- applications/newton/sensors/BMX055.nt | 12 +- src/newton/config.h | 7 +- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 254 +++++++++--------- .../newton-irPass-LLVMIR-quantization.cpp | 92 ++++--- .../newton-irPass-LLVMIR-quantization.h | 3 +- 10 files changed, 241 insertions(+), 187 deletions(-) diff --git a/applications/newton/llvm-ir/SQNR.sh b/applications/newton/llvm-ir/SQNR.sh index f6afe2e34..ce2d11133 100755 --- a/applications/newton/llvm-ir/SQNR.sh +++ b/applications/newton/llvm-ir/SQNR.sh @@ -30,12 +30,17 @@ MAKE_DIR="/home/xyf/CoSense/src/newton" # Current directory (where the test scripts are located) TEST_DIR=$(pwd) +#Run fp test +./testOriginal.sh "$ALGO" + # Print header into file echo "| MAX_PRECISION_BITS | SQNR (dB) |" >> "$OUTPUT_FILE" echo "|--------------------|-----------|" >> "$OUTPUT_FILE" + + # Loop over MAX_PRECISION_BITS values from 8 to 18 -for i in {8..11}; do +for i in {12..13}; do echo "Compiling and testing with MAX_PRECISION_BITS = $i ..." # Switch to the make directory for compilation diff --git a/applications/newton/llvm-ir/c-files/sensfusion6.c b/applications/newton/llvm-ir/c-files/sensfusion6.c index 5fd54ce60..24fd45364 100644 --- a/applications/newton/llvm-ir/c-files/sensfusion6.c +++ b/applications/newton/llvm-ir/c-files/sensfusion6.c @@ -30,6 +30,8 @@ #include #define CONFIG_IMU_MADGWICK_QUATERNION +//#define CONFIG_IMU_MAHONY_QUATERNION_IMU +#define dt (1.0f/100.0f) // sample frequency in Hz #ifdef CONFIG_IMU_MADGWICK_QUATERNION #define BETA_DEF 0.01f // 2 * proportional gain @@ -39,10 +41,11 @@ #endif #ifdef CONFIG_IMU_MADGWICK_QUATERNION -#define dt (1.0f/128.0f); // sample frequency in Hz -#define betaDef 0.1f // 2 * proportional gain -volatile float beta = betaDef; + + +volatile float beta = BETA_DEF; #else // MAHONY_QUATERNION_IMU + float twoKp = TWO_KP_DEF; // 2 * proportional gain (Kp) float twoKi = TWO_KI_DEF; // 2 * integral gain (Ki) float integralFBx = 0.0f; @@ -50,6 +53,9 @@ float integralFBy = 0.0f; float integralFBz = 0.0f; // integral error terms scaled by Ki #endif +volatile float M_PI_F = 3.14159265358979323846f; + + static float invSqrt(float x); #ifdef CONFIG_IMU_MADGWICK_QUATERNION @@ -92,6 +98,7 @@ void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, fl _8qx = 8.0f * qx; _8qy = 8.0f * qy; qwqw = qw * qw; + qwqw = qw * qw; qxqx = qx * qx; qyqy = qy * qy; qzqz = qz * qz; @@ -139,7 +146,7 @@ void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, fl // Date Author Notes // 29/09/2011 SOH Madgwick Initial release // 02/10/2011 SOH Madgwick Optimised for reduced CPU load -static void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az, float dt, float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) { float qw = *qw_ptr; diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 5f4e1d79b..688011fa7 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -18,8 +18,8 @@ OUT_OBJ = out.o OUT_LIB = libout.a # DEBUG_MODE=true make ... -ifdef DEBUG_MODE -CC_OPT_LEVEL = -O0 -g +ifdef DEBUG_MODE./ +CC_OPT_LEVEL = -O0 -g0 else CC_OPT_LEVEL = -O3 -Os endif @@ -73,7 +73,6 @@ endif max_opt_fn = opt ../$(1).ll $(OPT_FP_FLAG) -O3 -Os -S -o $(OUT_FILE) non_opt_fn = cp ../$(1).ll $(OUT_FILE) necessary_opt_fn = opt ../$(1).ll --simplifycfg --instsimplify -S -o $(OUT_FILE) - newton_opt_fn = ./newton-linux-EN --llvm-ir=../../applications/newton/llvm-ir/$(1).ll $(NEWTON_FLAG) ../../applications/newton/sensors/$(NT_FILE) compile_main_fn = $(CC) main.c $(TARGET_FLAG) -no-pie -L. -lout -D $(1) $(CC_OPT_LEVEL) -o main_out -lm diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index b6c43b4c5..e0044845f 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -315,8 +315,8 @@ int main(int argc, char** argv) // "perf_y0", "perf_rem_pio2", "perf_sincosf", // "perf_float64_add", "perf_float64_div", // "perf_float64_mul"}; - "perf_j0"}; -// "perf_y0"}; + "perf_j0","perf_y0"}; + if (argc >= 2) { @@ -471,13 +471,11 @@ int main(int argc, char** argv) continue; } - // 使用范围构造参数字符串 const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", "/g' ../../sensors/test.nt", range); const double p1 = range.front() + 0.6; const double p2 = range.back() + 0.3; change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); - // 后续测试逻辑保持不变 timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); @@ -575,8 +573,12 @@ int main(int argc, char** argv) } } - ofs.close(); - - return 0; +// ofs.close(); +// +// return 0; } -} \ No newline at end of file + ofs.close(); + + return 0; +} + diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh index c4681c04b..70e71cdf1 100755 --- a/applications/newton/llvm-ir/testMadgwick.sh +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -4,11 +4,10 @@ # Set the algorithm name based on the first command-line argument, default to MadgwickAHRS ALGO=${1:-MadgwickAHRS} -ALGO=${1:-MahonyAHRS} # Validate the input (optional) -if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" ]]; then - echo "Error: Unknown algorithm '$ALGO'. Please use either 'MadgwickAHRS' or 'MahonyAHRS'." +if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" && "$ALGO" != "sensfusion6" ]]; then + echo "Error: Unknown algorithm '$ALGO'. Please use 'MadgwickAHRS', 'MahonyAHRS', or 'sensfusion6'." exit 1 fi @@ -16,7 +15,7 @@ echo "Selected algorithm: $ALGO" # Step 1: Generate LLVM IR file echo "Step 1: Generate LLVM IR file" -clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra \ +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra \ -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ "$HOME/CoSense/applications/newton/llvm-ir/c-files/${ALGO}.c" @@ -31,13 +30,13 @@ cd "$HOME/CoSense/src/newton" && \ --llvm-ir-liveness-check --llvm-ir-auto-quantization \ "$HOME/CoSense/applications/newton/sensors/BMX055.nt" -# Step 3: Convert generated bytecode file to LLVM IR file +## Step 3: Convert generated bytecode file to LLVM IR file echo "Step 3: Convert generated bytecode file to LLVM IR file" llvm-dis "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.bc" \ -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" cd "$HOME/CoSense/applications/newton/llvm-ir" && ./replace.sh "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" -# Step 4: Optimize the generated LLVM IR file further (e.g., inline and mem2reg) +# Step 4: Optimize the generated LLVM IR file further echo "Step 4: Optimize the generated LLVM IR file" opt -inline "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.ll" --mem2reg -S \ -o "$HOME/CoSense/applications/newton/llvm-ir/out.ll" @@ -60,10 +59,10 @@ ar -rc "$HOME/CoSense/applications/newton/llvm-ir/libout.a" "$HOME/CoSense/appli # Step 9: Compile the test file and link with the static library echo "Step 9: Compile the test file and link with the static library" -clang "$HOME/CoSense/applications/newton/llvm-ir/c-files/test_${ALGO}.c" -D INT_DATA_TYPE -no-pie \ +clang "$HOME/CoSense/applications/newton/llvm-ir/c-files/test_${ALGO}.c" -D INT_DATA_TYPE -no-pie \ -L"$HOME/CoSense/applications/newton/llvm-ir" -lout -O3 -Os -g \ -o "$HOME/CoSense/applications/newton/llvm-ir/main_out" -lm # Step 10: Run the test executable echo "Step 10: Run the test executable" -"$HOME/CoSense/applications/newton/llvm-ir/main_out" +"$HOME/CoSense/applications/newton/llvm-ir/main_out" \ No newline at end of file diff --git a/applications/newton/sensors/BMX055.nt b/applications/newton/sensors/BMX055.nt index 08bb8052e..e03b07cd8 100644 --- a/applications/newton/sensors/BMX055.nt +++ b/applications/newton/sensors/BMX055.nt @@ -142,9 +142,13 @@ bmx055: sensor ( # # Range of Acceleration can be found in Table 2 on page 11. # - range bmx055xAcceleration == [-16 mjf, 16 mjf], - range bmx055yAcceleration == [-16 mjf, 16 mjf], - range bmx055zAcceleration == [-16 mjf, 16 mjf], + #range bmx055xAcceleration == [-16 mjf, 16 mjf], + #range bmx055yAcceleration == [-16 mjf, 16 mjf], + #range bmx055zAcceleration == [-16 mjf, 16 mjf], + + range bmx055xAcceleration == [-2 mjf, 2 mjf], + range bmx055yAcceleration == [-2 mjf, 2 mjf], + range bmx055zAcceleration == [-2 mjf, 2 mjf], # # Range of Temperature can be found in Table 2 on page 11. @@ -196,7 +200,7 @@ bmx055: sensor ( yAccelLow := read 16r04; bmx055yAcceleration = (yAccelHigh << 4) | (yAccelLow >> 4); # bmx055yAcceleration = yAccelHigh[:4] + yAccelLow[4:]; - },# @sensitivity bmx055xAcceleration = 1024 LSB/g + }, interface bmx055zAcceleration == i2c (address: 16r18) { diff --git a/src/newton/config.h b/src/newton/config.h index 0b6cc9841..a61df6b81 100644 --- a/src/newton/config.h +++ b/src/newton/config.h @@ -1,15 +1,16 @@ #ifndef CONFIG_H #define CONFIG_H -//#define BIT_WIDTH 32 +#ifndef BIT_WIDTH +#define BIT_WIDTH 32 +#endif + #define AUTO_QUANTIZATION 1 #define IS_POINTER 1 #define IS_MATRIX 0 #ifndef MAX_PRECISION_BITS #define MAX_PRECISION_BITS 16 - -//#define maxPrecisionBits MAX_PRECISION_BITS #endif #endif // CONFIG_H diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 8b456f4c2..cb8edc9a4 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -3,37 +3,37 @@ Authored 2022. Pei Mu. All rights reserved. -Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions -are met: + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions + are met: -* Redistributions of source code must retain the above -copyright notice, this list of conditions and the following -disclaimer. - - * Redistributions in binary form must reproduce the above + * Redistributions of source code must retain the above copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - -THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS -"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT -LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS -FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, -INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER -CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT -LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN -ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE -POSSIBILITY OF SUCH DAMAGE. - */ + disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS + FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + POSSIBILITY OF SUCH DAMAGE. + */ #ifdef __cplusplus #include "newton-irPass-LLVMIR-rangeAnalysis.h" @@ -80,7 +80,7 @@ POSSIBILITY OF SUCH DAMAGE. #include #include -using namespace llvm; + using namespace llvm; // #define FRAC_BASE (1 << maxPrecisionBits) #define FRAC_BASE (1 << MAX_PRECISION_BITS) @@ -932,15 +932,15 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } // -// int maxPrecisionBits = 0; -// for (auto & typePrecisionBit : typePrecisionBits) -// { -// if (typePrecisionBit.second > maxPrecisionBits) -// { -// maxPrecisionBits = typePrecisionBit.second; -// } -// } -// int MAX_PRECISION_BITS = maxPrecisionBits; + // int maxPrecisionBits = 0; + // for (auto & typePrecisionBit : typePrecisionBits) + // { + // if (typePrecisionBit.second > maxPrecisionBits) + // { + // maxPrecisionBits = typePrecisionBit.second; + // } + // } + // int MAX_PRECISION_BITS = maxPrecisionBits; @@ -1147,113 +1147,121 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } } -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = true; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = true; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } - /* + /* * simplify the condition of each branch * */ - flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); - for (auto & mi : *Mod) - { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - simplifyControlFlow(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } - } + flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + simplifyControlFlow(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } - legacy::PassManager passManager; - passManager.add(createCFGSimplificationPass()); - passManager.add(createInstSimplifyLegacyPass()); - passManager.add(createGlobalDCEPass()); - passManager.run(*Mod); + legacy::PassManager passManager; + passManager.add(createCFGSimplificationPass()); + passManager.add(createInstSimplifyLegacyPass()); + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// - flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); - for (auto & mi : *Mod) + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) { - auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); - if (boundInfoIt != funcBoundInfo.end()) - { - constantSubstitution(N, boundInfoIt->second, mi); - } - // else - // { - // assert(false); - // } + constantSubstitution(N, boundInfoIt->second, mi); } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); + // else + // { + // assert(false); + // } + } + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); // Finally, erase old functions -// eraseOldFunctions(); + // eraseOldFunctions(); // eraseOldGlobals(); eraseUnusedConstant(*Mod); +// eraseOldFunctions(); +// eraseOldFunctions(); processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); +// eraseOldFunctions(); + eraseOldFunctions(*Mod); + + + + const char * homeDir = getenv("HOME"); if (!homeDir) { diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index ceb85a1e2..827662cf3 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -36,7 +36,7 @@ performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value llvm::Value * mulResult32 = Builder.CreateNSWMul(lhs32, rhs32); // Right shift the result to simulate fixed-point division by FRAC_Q -// llvm::Value * divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); + // llvm::Value * divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); llvm::Value * divResult32 = Builder.CreateAShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); // Truncate the result back to 16-bit integer @@ -53,12 +53,13 @@ performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); // Right shift the result to simulate fixed-point division by FRAC_Q -// llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + // llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); llvm::Value * divResult64 = Builder.CreateAShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); // Truncate the 64-bit result back to 32-bit integer result = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); break; + } default: { @@ -183,7 +184,7 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< // Step 3: Approximation using magic number llvm::Value * bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); -// llvm::Value * shiftedFpX = builder.CreateAShr(bitcastFpX, 1); +// llvm::Value * shiftedFpX = builder.CreateAShr(bitcastFpX, 1); llvm::Value * magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); approx = builder.CreateSub(magicNumber, shiftedFpX); llvm::Value * approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); @@ -194,10 +195,10 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< llvm::Value * sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); llvm::Value * mul1 = builder.CreateMul(sextShiftedX, intApprox); llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); -// llvm::Value * mul1Shifted = builder.CreateAShr(mul1, FRAC_Q); +// llvm::Value * mul1Shifted = builder.CreateAShr(mul1, FRAC_Q); llvm::Value * mul2 = builder.CreateMul(mul1Shifted, intApprox); -// llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); + // llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); llvm::Value * mul2Shifted = builder.CreateAShr(mul2, FRAC_Q); int correctionValue = static_cast(1.5f * FRAC_BASE); @@ -206,7 +207,7 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< mul2Shifted); llvm::Value * finalMul = builder.CreateMul(intApprox, correction); llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); -// llvm::Value * finalShifted = builder.CreateAShr(finalMul, FRAC_Q); +// llvm::Value * finalShifted = builder.CreateAShr(finalMul, FRAC_Q); // Step 5: Truncate the result back to i16 result = builder.CreateTrunc(finalShifted, quantizedType); @@ -216,16 +217,16 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< default: { // Step 1: Shift x to compute %1 (x >> 1) - llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); -// llvm::Value * halfBase = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); +// llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + llvm::Value * halfBase = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); // Step 2: Convert x to floating-point and perform the initial approximation fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); fpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); -// llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); + // llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); -// i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + // i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateAShr(i, 1)); fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); @@ -273,27 +274,54 @@ std::vector functionsToErase; // Function to actually erase functions after processing -void -eraseOldFunctions() -{ - +//void +//eraseOldFunctions() +//{ +// +// +// llvm::errs() << "Entering eraseOldFunctions\n"; +// for (auto * func : functionsToErase) +// { +// llvm::errs() << "Erasing old function: " << func->getName() << "\n"; +// func->eraseFromParent(); +// } +// functionsToErase.clear(); +// llvm::errs() << "Exiting eraseOldFunctions\n"; +//} +void eraseOldFunctions(Module &M) { llvm::errs() << "Entering eraseOldFunctions\n"; - for (auto * func : functionsToErase) - { + + std::vector toErase; + for (auto *func : functionsToErase) { + if (!func) continue; + + if (M.getFunction(func->getName()) != func) { + llvm::errs() << "Skipping dangling or already removed function.\n"; + continue; + } + + toErase.push_back(func); + } + + functionsToErase.clear(); + for (Function *func : toErase) { llvm::errs() << "Erasing old function: " << func->getName() << "\n"; func->eraseFromParent(); } - functionsToErase.clear(); llvm::errs() << "Exiting eraseOldFunctions\n"; } + + + + bool isWhitelistedGlobal(const std::string & globalName) { // Define the whitelist of global variables -// static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero","q0","q1","q2","q3"}; -// return whitelist.find(globalName) != whitelist.end(); + // static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero","q0","q1","q2","q3"}; + // return whitelist.find(globalName) != whitelist.end(); return true; } @@ -355,7 +383,7 @@ shouldProcessFunction(Function & F) "qone", "__ieee754_exp", "twofft" -// "__ieee754_log", + // "__ieee754_log", }; // Check if the function name is in the set @@ -425,7 +453,7 @@ getOrCreateQuantizedGlobal(Module * module, GlobalVariable & globalVar, Type * q return newGlobalVar; } -// Helper to create or retrieve the quantized internal constant + // Helper to create or retrieve the quantized internal constant llvm::GlobalVariable * getOrCreateQuantizedConstant(llvm::Module *module, @@ -1843,8 +1871,8 @@ performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPoi return finalRes; } - void - handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); @@ -1861,7 +1889,7 @@ performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPoi // No need to apply shl and compensation if it's already done in createFixSqrt llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); llvmIrCallInstruction->eraseFromParent(); - } +} void handleSinCosCall(CallInst * llvmIrCallInstruction, Type * quantizedType, const std::string & mathFunc) @@ -1933,10 +1961,10 @@ handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vectoruse_empty()) { @@ -2306,7 +2334,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect * */ //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); -// functionsToErase.push_back(fixrsqrt); + // functionsToErase.push_back(fixrsqrt); for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { @@ -2338,7 +2366,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect break; case Instruction::PHI: { -// setQuantizedType(llvmIrInstruction, quantizedType); + // setQuantizedType(llvmIrInstruction, quantizedType); llvm::errs() << "handle phi " << *llvmIrInstruction << "\n"; handlePhi(llvmIrInstruction, quantizedType); } @@ -2423,8 +2451,8 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect case Instruction::BitCast: case Instruction::Ret: -// handleReturn(cast(llvmIrInstruction), quantizedType); -// break; + // handleReturn(cast(llvmIrInstruction), quantizedType); + // break; case Instruction::Switch: case Instruction::Br: diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 97bf6918f..28adbfada 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -59,7 +59,8 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::v extern -void eraseOldFunctions(); +//void eraseOldFunctions(); + void eraseOldFunctions(llvm::Module &M); void eraseOldInstructions(); void eraseOldGlobals(); #ifdef __cplusplus From a8b15350e68d595638e6a7b5918d37fe5209b0d1 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 26 May 2025 22:48:50 +0100 Subject: [PATCH 170/213] update files * quantize. --- src/common/common-data-structures.h | 3 +++ src/newton/newton-irPass-sensors.c | 25 +++++++++++++++++++++++++ 2 files changed, 28 insertions(+) diff --git a/src/common/common-data-structures.h b/src/common/common-data-structures.h index d1e728af8..561ffa946 100644 --- a/src/common/common-data-structures.h +++ b/src/common/common-data-structures.h @@ -664,6 +664,7 @@ typedef enum kNewtonIrNodeType_PruleList, kNewtonIrNodeType_PnewtonDescription, kNewtonIrNodeType_PnumericConst, + kNewtonIrNodeType_PresolutionStatement, /* * Code depends on this bringing up the rear for Newton Productions. @@ -897,6 +898,7 @@ typedef struct Signal Signal; typedef struct Sensor Sensor; typedef struct Modality Modality; + typedef struct NoisyType NoisyType; enum @@ -1011,6 +1013,7 @@ struct Modality { double accuracy; double accuracyCost; // Signal * accuracySignal; + double resolution; SensorInterfaceType interfaceType; /* WiP */ /* Missing register address for modality */ diff --git a/src/newton/newton-irPass-sensors.c b/src/newton/newton-irPass-sensors.c index 9f4db81e7..76280e41b 100644 --- a/src/newton/newton-irPass-sensors.c +++ b/src/newton/newton-irPass-sensors.c @@ -280,6 +280,31 @@ irPassSensorsLoadSensor(State * N, IrNode * sensorNode) } } + /* + Modality resolution + * TODO: Currently only handles one resolution setting per sensor modality. + + + */ + tempNode = NULL; + n = 0; + do + { + tempNode = findNthIrNodeOfType(N, sensorNode, kNewtonIrNodeType_PresolutionStatement, n++); + if (tempNode == NULL) + { + error(N, "no resolution statement found for modality"); + break; + } + } while (strcmp(modality->identifier, tempNode->irLeftChild->tokenString) != 0); + + if (tempNode != NULL) + { + modality->resolution = RLL(tempNode)->value; + } + + + /* * Prepare iterator for next `while` iteration */ From 9b153db99e88be31fbbef7d95651faa8da1e512d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 26 May 2025 22:51:31 +0100 Subject: [PATCH 171/213] update files * quantize. --- src/newton/Makefile | 12 +++++++++++- 1 file changed, 11 insertions(+), 1 deletion(-) diff --git a/src/newton/Makefile b/src/newton/Makefile index f4132047b..a4f119402 100644 --- a/src/newton/Makefile +++ b/src/newton/Makefile @@ -1,7 +1,11 @@ TREEROOT = ../.. ifndef MAX_PRECISION_BITS -MAX_PRECISION_BITS = 16 +MAX_PRECISION_BITS = 13 +endif + +ifndef BIT_WIDTH +BIT_WIDTH = 32 endif include $(TREEROOT)/config.local @@ -30,10 +34,16 @@ CXXFLAGS = $(PLATFORM_DBGFLAGS) $(PLATFORM_CFLAGS) $(PLATFORM_DFLAGS) $(PLATFORM CCFLAGS = $(PLATFORM_DBGFLAGS) $(PLATFORM_CFLAGS) $(PLATFORM_DFLAGS) $(PLATFORM_OPTFLAGS) LDFLAGS = $(PLATFORM_DBGFLAGS) -lm $(PLATFORM_LFLAGS) `pkg-config --libs 'libprotobuf-c >= 1.0.0'` + +#Quantization setting CCFLAGS += -DMAX_PRECISION_BITS=$(MAX_PRECISION_BITS) CXXFLAGS += -DMAX_PRECISION_BITS=$(MAX_PRECISION_BITS) +CCFLAGS += -DBIT_WIDTH=$(BIT_WIDTH) +CXXFLAGS += -DBIT_WIDTH=$(BIT_WIDTH) + + CCFLAGS+=$(shell $(LLVM_CONFIG) --cflags) LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags) From 9d27e49fa335cd09872d71d80e7015edc0309cf7 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 26 May 2025 22:56:42 +0100 Subject: [PATCH 172/213] update * quantize. --- applications/newton/llvm-ir/Makefile | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/applications/newton/llvm-ir/Makefile b/applications/newton/llvm-ir/Makefile index 8d68e6a1c..1e332ff98 100644 --- a/applications/newton/llvm-ir/Makefile +++ b/applications/newton/llvm-ir/Makefile @@ -36,7 +36,7 @@ endif all: default -default: application.ll simple_control_flow.ll inferBound.ll inferBoundControlFlow.ll e_exp.ll sincosf.ll e_log.ll e_acosh.ll e_j0.ll e_y0.ll e_rem_pio2.ll benchmark_suite.ll phi_two_global_arrays.ll func_call.ll test_shift.ll vec_add.ll vec_add_8.ll MadgwickAHRSfix.ll MadgwickAHRS_softfloat.ll MadgwickAHRS.ll arm_sqrt_q15.ll +default: application.ll simple_control_flow.ll inferBound.ll inferBoundControlFlow.ll e_exp.ll sincosf.ll e_log.ll e_acosh.ll e_j0.ll e_j1.ll e_y0.ll e_rem_pio2.ll benchmark_suite.ll phi_two_global_arrays.ll func_call.ll test_shift.ll vec_add.ll vec_add_8.ll MadgwickAHRSfix.ll MadgwickAHRS_softfloat.ll MadgwickAHRS.ll arm_sqrt_q15.ll MadgwickAHRS_softfloat.ll : MadgwickAHRS_softfloat.c @echo Compiling $*.c @@ -47,7 +47,8 @@ MadgwickAHRS_softfloat.ll : MadgwickAHRS_softfloat.c %.ll : %.c @echo Compiling $*.c $(CC) $(TARGET_FLAG) $(CC_FP_FLAG) $(MACRO_FLAG) -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm $(COMMON_FLAGS) -o $@ $< - opt $@ $(OPT_FP_FLAG) --mem2reg --instsimplify -S -o $@ + #opt $@ $(OPT_FP_FLAG) --mem2reg --instsimplify -S -o $@ + opt $@ $(OPT_FP_FLAG) --mem2reg -S -o $@ clean:: $(QUIET)rm -f *.ll *.bc From d1dd2926c54be476cccf4117a73bb751319a0cf8 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 26 May 2025 22:59:48 +0100 Subject: [PATCH 173/213] update * quantize. --- ...153db99e88be31fbbef7d95651faa8da1e512d.txt | 48 +++++++++++++++++++ ...9f413256b96f30f453566a1b4a64e4d00fb06d.txt | 48 +++++++++++++++++++ ...fb29881175d5696515be66cccf1ea9b095d2be.txt | 48 +++++++++++++++++++ ...dd276146c9a4f5d54782096eeee007b12e8fb5.txt | 48 +++++++++++++++++++ ...b15350e68d595638e6a7b5918d37fe5209b0d1.txt | 48 +++++++++++++++++++ ...dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt | 48 +++++++++++++++++++ 6 files changed, 288 insertions(+) create mode 100644 analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt create mode 100644 analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt create mode 100644 analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt create mode 100644 analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt create mode 100644 analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt create mode 100644 analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt diff --git a/analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt b/analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt new file mode 100644 index 000000000..5fd42986b --- /dev/null +++ b/analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt @@ -0,0 +1,48 @@ + +changeset: 1766:9b153db99e88be31fbbef7d95651faa8da1e512d +char kNewtonVersion[] = "0.3-alpha-1766 (9b153db99e88be31fbbef7d95651faa8da1e512d) (build 05-26-2025-22:56-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt b/analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt new file mode 100644 index 000000000..c6f3679cc --- /dev/null +++ b/analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt @@ -0,0 +1,48 @@ + +changeset: 1764:9e9f413256b96f30f453566a1b4a64e4d00fb06d +char kNewtonVersion[] = "0.3-alpha-1764 (9e9f413256b96f30f453566a1b4a64e4d00fb06d) (build 05-26-2025-22:48-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt b/analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt new file mode 100644 index 000000000..787d1493f --- /dev/null +++ b/analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt @@ -0,0 +1,48 @@ + +changeset: 1761:a2fb29881175d5696515be66cccf1ea9b095d2be +char kNewtonVersion[] = "0.3-alpha-1761 (a2fb29881175d5696515be66cccf1ea9b095d2be) (build 05-25-2025-15:38-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt b/analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt new file mode 100644 index 000000000..309cd1c31 --- /dev/null +++ b/analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt @@ -0,0 +1,48 @@ + +changeset: 1762:a7dd276146c9a4f5d54782096eeee007b12e8fb5 +char kNewtonVersion[] = "0.3-alpha-1762 (a7dd276146c9a4f5d54782096eeee007b12e8fb5) (build 05-25-2025-20:27-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt b/analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt new file mode 100644 index 000000000..93ff342ba --- /dev/null +++ b/analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt @@ -0,0 +1,48 @@ + +changeset: 1765:a8b15350e68d595638e6a7b5918d37fe5209b0d1 +char kNewtonVersion[] = "0.3-alpha-1765 (a8b15350e68d595638e6a7b5918d37fe5209b0d1) (build 05-26-2025-22:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt b/analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt new file mode 100644 index 000000000..8ac2e1f17 --- /dev/null +++ b/analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt @@ -0,0 +1,48 @@ + +changeset: 1763:d5dd60931a0d357145a3ad4b6bf1b32886e9a85e +char kNewtonVersion[] = "0.3-alpha-1763 (d5dd60931a0d357145a3ad4b6bf1b32886e9a85e) (build 05-26-2025-22:00-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From 18a293dead7ede85f57ab0abe37fb28880fefc54 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Mon, 26 May 2025 23:51:53 +0100 Subject: [PATCH 174/213] update * quantize. --- applications/newton/llvm-ir/Makefile | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/applications/newton/llvm-ir/Makefile b/applications/newton/llvm-ir/Makefile index 1e332ff98..96b114d1c 100644 --- a/applications/newton/llvm-ir/Makefile +++ b/applications/newton/llvm-ir/Makefile @@ -36,7 +36,7 @@ endif all: default -default: application.ll simple_control_flow.ll inferBound.ll inferBoundControlFlow.ll e_exp.ll sincosf.ll e_log.ll e_acosh.ll e_j0.ll e_j1.ll e_y0.ll e_rem_pio2.ll benchmark_suite.ll phi_two_global_arrays.ll func_call.ll test_shift.ll vec_add.ll vec_add_8.ll MadgwickAHRSfix.ll MadgwickAHRS_softfloat.ll MadgwickAHRS.ll arm_sqrt_q15.ll +default: application.ll simple_control_flow.ll inferBound.ll inferBoundControlFlow.ll e_exp.ll sincosf.ll e_log.ll e_acosh.ll e_j0.ll e_y0.ll e_rem_pio2.ll benchmark_suite.ll phi_two_global_arrays.ll func_call.ll test_shift.ll vec_add.ll vec_add_8.ll MadgwickAHRSfix.ll MadgwickAHRS_softfloat.ll MadgwickAHRS.ll arm_sqrt_q15.ll MadgwickAHRS_softfloat.ll : MadgwickAHRS_softfloat.c @echo Compiling $*.c @@ -47,7 +47,6 @@ MadgwickAHRS_softfloat.ll : MadgwickAHRS_softfloat.c %.ll : %.c @echo Compiling $*.c $(CC) $(TARGET_FLAG) $(CC_FP_FLAG) $(MACRO_FLAG) -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm $(COMMON_FLAGS) -o $@ $< - #opt $@ $(OPT_FP_FLAG) --mem2reg --instsimplify -S -o $@ opt $@ $(OPT_FP_FLAG) --mem2reg -S -o $@ clean:: From 02d48a7bb86d996d9ebb98ad6e499badeabdc76a Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 27 May 2025 00:27:20 +0100 Subject: [PATCH 175/213] update * quantize. --- applications/newton/llvm-ir/performance_test/Makefile | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 688011fa7..94328fcbb 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -19,7 +19,7 @@ OUT_LIB = libout.a # DEBUG_MODE=true make ... ifdef DEBUG_MODE./ -CC_OPT_LEVEL = -O0 -g0 +CC_OPT_LEVEL = -O0 -g else CC_OPT_LEVEL = -O3 -Os endif @@ -73,6 +73,7 @@ endif max_opt_fn = opt ../$(1).ll $(OPT_FP_FLAG) -O3 -Os -S -o $(OUT_FILE) non_opt_fn = cp ../$(1).ll $(OUT_FILE) necessary_opt_fn = opt ../$(1).ll --simplifycfg --instsimplify -S -o $(OUT_FILE) + newton_opt_fn = ./newton-linux-EN --llvm-ir=../../applications/newton/llvm-ir/$(1).ll $(NEWTON_FLAG) ../../applications/newton/sensors/$(NT_FILE) compile_main_fn = $(CC) main.c $(TARGET_FLAG) -no-pie -L. -lout -D $(1) $(CC_OPT_LEVEL) -o main_out -lm From c2ac39ed88098aa56c46f37c41089ff87bcc8f6e Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 27 May 2025 00:38:29 +0100 Subject: [PATCH 176/213] update * quantize. --- .../llvm-ir/performance_test/auto_test.cpp | 60 +++++++++---------- 1 file changed, 30 insertions(+), 30 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index e0044845f..3f5689f3d 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -373,36 +373,36 @@ int main(int argc, char** argv) {{-2, 2}, 12, 9}, {{-4, 4}, 12, 8}, {{-8, 8}, 12, 7}, - {{-16, 16}, 12, 6}}; -// -// // BMX055 gyroscope -// {{-125, 125}, 16, 8}, -// -// // LM35 Centigrade Temperature Sensor -// {{-40, 110}, 10, 2}, -// {{-55, 150}, 11, 3}, -// {{0, 100}, 10, 3}, -// {{0, 70}, 10, 3}, -// -// // LPS25H Pressure Sensor -// {{260, 1260}, 14, 4}, -// -// // MAX31820 1-Wire Ambient Temperature Sensor -// {{10, 45}, 12, 6}, -// {{-55, 125}, 11, 3}, -// -// // DHT11 Humidity Sensor -// {{20, 80}, 8, 2}, -// {{0, 50}, 8, 2}, -// -// // LMP82064 Current Sensor and Voltage Monitor with SPI -// {{-0.2, 2}, 14, 12}, -// -// // PCE-353 LEQ Sound Level Meter -// {{30, 130}, 8, 1}, -// -// // LLS05-A Linear Light Sensor -// {{1, 200}, 10, 2}}; + {{-16, 16}, 12, 6}, + + // BMX055 gyroscope + {{-125, 125}, 16, 8}, + + // LM35 Centigrade Temperature Sensor + {{-40, 110}, 10, 2}, + {{-55, 150}, 11, 3}, + {{0, 100}, 10, 3}, + {{0, 70}, 10, 3}, + + // LPS25H Pressure Sensor + {{260, 1260}, 14, 4}, + + // MAX31820 1-Wire Ambient Temperature Sensor + {{10, 45}, 12, 6}, + {{-55, 125}, 11, 3}, + + // DHT11 Humidity Sensor + {{20, 80}, 8, 2}, + {{0, 50}, 8, 2}, + + // LMP82064 Current Sensor and Voltage Monitor with SPI + {{-0.2, 2}, 14, 12}, + + // PCE-353 LEQ Sound Level Meter + {{30, 130}, 8, 1}, + + // LLS05-A Linear Light Sensor + {{1, 200}, 10, 2}}; std::vector> trigonometricParams{ {0, 0.17453292519943295}, // (0, pi/18) From 545f1ac697cb8cf197689117358f8eed8787877b Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 27 May 2025 17:22:52 +0100 Subject: [PATCH 177/213] update compile time support * quantize. --- .../llvm-ir/performance_test/auto_test.cpp | 83 +++++++++++++++++-- 1 file changed, 74 insertions(+), 9 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index 3f5689f3d..25b7bbc33 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -31,12 +31,56 @@ struct perfData { struct timerData { int64_t inst_count_avg = -1; double time_consumption_avg; + double compile_time_avg; std::vector ms_time_consumption; int64_t ir_lines; int64_t library_size; std::vector function_results; + std::vector compile_time; }; +#define TLOG_TIMESPEC_NSEC_PER_SEC 1000000000 + +typedef struct timespec timespec; + +timespec diff(timespec start, timespec end) { + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec tic() { + timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return t; +} + +timespec toc(timespec* start_time, const char* prefix, bool print) { + timespec end_time; + clock_gettime(CLOCK_REALTIME, &end_time); + timespec diff_time = diff(*start_time, end_time); + *start_time = end_time; + return diff_time; +} + + +double compileTargetCode(const std::string& test_case) { + timespec compile_timer = tic(); + std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; + int ret = system(cmd.c_str()); + timespec compile_time = toc(&compile_timer, "compile time", false); + if (ret != 0) exit(-1); + return compile_time.tv_sec + compile_time.tv_nsec / (double)TLOG_TIMESPEC_NSEC_PER_SEC; +} + + + /* * Get number from: * 36,200,478 instructions @@ -280,6 +324,8 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin timerData timer_data; for (size_t idx = 0; idx < iteration_num; idx++) { + double compile_time = compileTargetCode(test_cases); + timer_data.compile_time.emplace_back(compile_time); const std::pair> data_timer_res = processDataTimer(test_cases, param_str); timer_data.ms_time_consumption.emplace_back(data_timer_res.first); std::copy_if(data_timer_res.second.begin(), data_timer_res.second.end(), @@ -302,7 +348,12 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), timer_data.ms_time_consumption.end(), 0.0) / timer_data.ms_time_consumption.size() - << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size << std::endl; + << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + << "\t" << std::accumulate(timer_data.compile_time.begin(), + timer_data.compile_time.end(), + 0.0) / timer_data.compile_time.size() + << std::endl; return timer_data; } @@ -431,13 +482,15 @@ int main(int argc, char** argv) trigonometricParams.emplace_back(input_param); } - ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; - avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; + + ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; for (size_t case_id = 0; case_id < test_cases.size(); case_id++) { int avg_inst_speedup = 0; int avg_time_speedup = 0; + int avg_compile_time_speedup = 0; int avg_ir_reduce = 0; int avg_lib_size_reduce = 0; // const std::vector> parameters = @@ -505,7 +558,7 @@ int main(int argc, char** argv) } } - int inst_speedup, time_speedup, ir_reduce, lib_size_reduce; + int inst_speedup, time_speedup, ir_reduce, lib_size_reduce, compile_time_speedup; if (ori_perf_data.ms_time_consumption.empty()) { assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); @@ -522,9 +575,17 @@ int main(int argc, char** argv) opt_perf_data.ms_time_consumption.end(), 0.0) / opt_perf_data.ms_time_consumption.size(); + ori_perf_data.compile_time_avg = std::accumulate(ori_perf_data.compile_time.begin(), + ori_perf_data.compile_time.end(), + 0.0) / ori_perf_data.compile_time.size(); + opt_perf_data.compile_time_avg = std::accumulate(opt_perf_data.compile_time.begin(), + opt_perf_data.compile_time.end(), + 0.0) / opt_perf_data.compile_time.size(); inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) * 100 / opt_perf_data.inst_count_avg); time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) * 100 / opt_perf_data.time_consumption_avg); + compile_time_speedup = round((ori_perf_data.compile_time_avg - opt_perf_data.compile_time_avg) + * 100 / opt_perf_data.compile_time_avg); } if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) @@ -539,14 +600,16 @@ int main(int argc, char** argv) lib_size_reduce = 0; } ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" - << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; - std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" - << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup + << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; avg_inst_speedup += inst_speedup; avg_time_speedup += time_speedup; avg_ir_reduce += ir_reduce; avg_lib_size_reduce += lib_size_reduce; + avg_compile_time_speedup += compile_time_speedup; // reset test.nt change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", @@ -559,16 +622,18 @@ int main(int argc, char** argv) avg_time_speedup = round(avg_time_speedup / count); avg_ir_reduce = round(avg_ir_reduce / count); avg_lib_size_reduce = round(avg_lib_size_reduce / count); + avg_compile_time_speedup = round(avg_compile_time_speedup / count); // avg_inst_speedup = round(avg_inst_speedup / parameters.size()); // avg_time_speedup = round(avg_time_speedup / parameters.size()); // avg_ir_reduce = round(avg_ir_reduce / parameters.size()); // avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" - << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%" << std::endl; + << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%\t" + << avg_compile_time_speedup << "%" << std::endl; if (test_cases[case_id] == "perf_float64_sin") { - // trigonometricParams cannot have extent + // trigonometricParams cannot have extention break; } } From 1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 27 May 2025 17:33:26 +0100 Subject: [PATCH 178/213] update compile time * quantize. --- ...d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt | 48 +++++++++++++++++++ ...a293dead7ede85f57ab0abe37fb28880fefc54.txt | 48 +++++++++++++++++++ ...27e49fa335cd09872d71d80e7015edc0309cf7.txt | 48 +++++++++++++++++++ ...ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt | 48 +++++++++++++++++++ ...dd2926c54be476cccf4117a73bb751319a0cf8.txt | 48 +++++++++++++++++++ 5 files changed, 240 insertions(+) create mode 100644 analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt create mode 100644 analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt create mode 100644 analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt create mode 100644 analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt create mode 100644 analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt diff --git a/analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt b/analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt new file mode 100644 index 000000000..680ffbbdd --- /dev/null +++ b/analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt @@ -0,0 +1,48 @@ + +changeset: 1770:02d48a7bb86d996d9ebb98ad6e499badeabdc76a +char kNewtonVersion[] = "0.3-alpha-1770 (02d48a7bb86d996d9ebb98ad6e499badeabdc76a) (build 05-27-2025-00:38-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt b/analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt new file mode 100644 index 000000000..e5967de02 --- /dev/null +++ b/analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt @@ -0,0 +1,48 @@ + +changeset: 1769:18a293dead7ede85f57ab0abe37fb28880fefc54 +char kNewtonVersion[] = "0.3-alpha-1769 (18a293dead7ede85f57ab0abe37fb28880fefc54) (build 05-27-2025-00:27-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt b/analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt new file mode 100644 index 000000000..6c2cbe293 --- /dev/null +++ b/analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt @@ -0,0 +1,48 @@ + +changeset: 1767:9d27e49fa335cd09872d71d80e7015edc0309cf7 +char kNewtonVersion[] = "0.3-alpha-1767 (9d27e49fa335cd09872d71d80e7015edc0309cf7) (build 05-26-2025-22:59-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt b/analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt new file mode 100644 index 000000000..a0f2a3830 --- /dev/null +++ b/analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt @@ -0,0 +1,48 @@ + +changeset: 1771:c2ac39ed88098aa56c46f37c41089ff87bcc8f6e +char kNewtonVersion[] = "0.3-alpha-1771 (c2ac39ed88098aa56c46f37c41089ff87bcc8f6e) (build 05-27-2025-17:22-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt b/analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt new file mode 100644 index 000000000..a07534500 --- /dev/null +++ b/analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt @@ -0,0 +1,48 @@ + +changeset: 1768:d1dd2926c54be476cccf4117a73bb751319a0cf8 +char kNewtonVersion[] = "0.3-alpha-1768 (d1dd2926c54be476cccf4117a73bb751319a0cf8) (build 05-26-2025-23:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + From e832bc92135f328f42b513711082fe155541b256 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 28 May 2025 21:01:09 +0100 Subject: [PATCH 179/213] fix constant issue * quantize. --- ...5f1ac697cb8cf197689117358f8eed8787877b.txt | 48 +++++++++++++++++++ .../newton-irPass-LLVMIR-quantization.cpp | 30 +++++------- 2 files changed, 61 insertions(+), 17 deletions(-) create mode 100644 analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt diff --git a/analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt b/analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt new file mode 100644 index 000000000..4f74cdf03 --- /dev/null +++ b/analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt @@ -0,0 +1,48 @@ + +changeset: 1772:545f1ac697cb8cf197689117358f8eed8787877b +char kNewtonVersion[] = "0.3-alpha-1772 (545f1ac697cb8cf197689117358f8eed8787877b) (build 05-27-2025-17:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 827662cf3..bad060b5f 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -273,22 +273,6 @@ eraseOldGlobals() std::vector functionsToErase; // Function to actually erase functions after processing - -//void -//eraseOldFunctions() -//{ -// -// -// llvm::errs() << "Entering eraseOldFunctions\n"; -// for (auto * func : functionsToErase) -// { -// llvm::errs() << "Erasing old function: " << func->getName() << "\n"; -// func->eraseFromParent(); -// } -// functionsToErase.clear(); -// llvm::errs() << "Exiting eraseOldFunctions\n"; -//} - void eraseOldFunctions(Module &M) { llvm::errs() << "Entering eraseOldFunctions\n"; @@ -1431,6 +1415,17 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) return; } + + // Check if lhs is the constant 1.0 + if (auto lhsConst = dyn_cast(lhs)) { + double val = lhsConst->getValueAPF().convertToDouble(); + if (fabs(val - 1.0) < 1e-6) { + llvm::errs() << "LHS is 1.0, replacing with FRAC_BASE\n"; + lhs = ConstantInt::get(quantizedType, FRAC_BASE, true); + lhsIsFloat = false; + } + } + if (isa(lhs)) { llvm::errs() << "LHS is a floating-point constant, handling it with simplifyConstant\n"; simplifyConstant(llvmIrInstruction, quantizedType); @@ -1444,6 +1439,8 @@ void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { rhsIsFloat = false; } + + if (lhsIsFloat) { lhs = Builder.CreateFPToSI(lhs, quantizedType); llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; @@ -2082,7 +2079,6 @@ quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { usesToReplace.push_back(&use); } - //TODO Check if the argument is a float or double Value * processedArg = &arg; llvm::Instruction * castedFloat = nullptr; if (arg.getType()->isDoubleTy()) { From a90d47861c3c28b9de76f56f756e2ba4eb14e1d5 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 1 Jun 2025 11:39:36 +0100 Subject: [PATCH 180/213] update original cosense wo function overload test * quantize. --- ...32672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt | 48 ++ .../newton/llvm-ir/performance_test/Makefile | 3 + .../performance_test/auto_test_woquant.cpp | 564 ++++++++++++++++++ 3 files changed, 615 insertions(+) create mode 100644 analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt create mode 100644 applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp diff --git a/analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt b/analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt new file mode 100644 index 000000000..8c95fd6a4 --- /dev/null +++ b/analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt @@ -0,0 +1,48 @@ + +changeset: 1773:1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b +char kNewtonVersion[] = "0.3-alpha-1773 (1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b) (build 05-28-2025-21:01-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 94328fcbb..edb89e4f5 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -377,6 +377,9 @@ perf_inferBoundControlFlow_opt: clean make_ll inferBoundControlFlow_opt compile_ auto_test_compile: clang++ auto_test.cpp -g -o auto_test +auto_test_compile_woquant: + clang++ auto_test_woquant.cpp -g -o auto_test_woquant + all: default default: perf_exp perf_exp_opt perf_log perf_log_opt perf_acosh perf_acosh_opt perf_j0 perf_j0_opt perf_y0 perf_y0_opt perf_rem_pio2 perf_rem_pio2_opt perf_sincosf perf_sincosf_opt perf_float64_add perf_float64_add_opt perf_float64_div perf_float64_div_opt perf_float64_mul perf_float64_mul_opt perf_float64_sin perf_float64_sin_opt perf_arm_sqrt_q15 perf_arm_sqrt_q15_opt auto_test_compile diff --git a/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp new file mode 100644 index 000000000..c16283842 --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp @@ -0,0 +1,564 @@ +/* +* Auto test framework of performance and correctness. +* +* Run with: `./auto_test 2> err.log` +* */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const size_t iteration_num = 1; + +struct perfData { + int64_t inst_count_avg; + int64_t time_consumption_avg; + int64_t ir_lines; + int64_t library_size; + std::vector function_results; +}; + +struct timerData { + int64_t inst_count_avg = -1; + double time_consumption_avg; + double compile_time_avg; + std::vector ms_time_consumption; + int64_t ir_lines; + int64_t library_size; + std::vector function_results; + std::vector compile_time; +}; + +#define TLOG_TIMESPEC_NSEC_PER_SEC 1000000000 + +/*************************************** +* Timer functions of the test framework +***************************************/ + +typedef struct timespec timespec; +timespec diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + return temp; +} + +timespec sum(timespec t1, timespec t2) { + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } else { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void printTimeSpec(timespec t, const char* prefix) { + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +timespec tic( ) +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec toc( timespec* start_time, const char* prefix, bool print ) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_diff = diff( *start_time, current_time ); + if (print) + printTimeSpec( time_diff, prefix ); + *start_time = current_time; + return time_diff; +} + +/* +* Get number from: +* 36,200,478 instructions +* 0.013535825 seconds time elapsed +* */ +int64_t getPerfCount(const std::string& string, size_t position) { + std::string substring; + substring = string.substr(0, position); + substring.erase( + std::remove_if(substring.begin(), + substring.end(), + static_cast(&ispunct)), + substring.end()); + substring.erase( + std::remove_if(substring.begin(), + substring.end(), + static_cast(&isspace)), + substring.end()); + return std::stoi(substring); +} + +/* +* Get number from: +* computation delay: 0.001342399 +* */ +double getTimerConsumption(const std::string& string, size_t position) { + std::string substring; + substring = string.substr(position, string.size()); + return std::stod(substring); +} + +/* +* Get number from: +* results: 0.517104 0.809373 0.043233 -0.805564 -0.973201 +* */ +std::vector getFunctionResults(const std::string& string, size_t position) { + std::vector res; + std::stringstream ss; + std::string tmp; + ss << string; + double number; + while (!ss.eof()) { + ss >> tmp; + if (std::stringstream(tmp) >> number) + res.emplace_back(number); + } + return res; +} + +std::pair processDataPerf(const std::string test_case, const std::string params) { + std::string line; + size_t position; + int64_t inst_count, time_consumption; + + // perf command + std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) { + return std::make_pair(0, 0); + } + cmd.clear(); + cmd = "bash -c 'perf stat -B ./main_out " + params; + + cmd += "if=/dev/zero of=/dev/null count=1000000"; + cmd += " 2>&1 | tee tmp.log'"; + command_return = system(cmd.c_str()); + if (command_return != 0) { + return std::make_pair(0, 0); + } + std::ifstream ifs("tmp.log"); + if (!ifs.is_open()) { + std::cout << "error opening tmp.log"; + assert(false); + } + + // process + while (getline(ifs, line)) { + position = line.find("instructions"); + if (position != std::string::npos) { + inst_count = getPerfCount(line, position); + } + position = line.find("seconds time elapsed"); + if (position != std::string::npos) { + time_consumption = getPerfCount(line, position); + continue; + } + } + + // printf("%lu\t%lu\n", inst_count, time_consumption); + + ifs.close(); + + return std::make_pair(inst_count, time_consumption); +} + +double compileTargetCode(const std::string test_case) { + // measure the compile time + timespec compile_timer = tic(); + // perf command + std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; + int command_return = system(cmd.c_str()); + timespec compile_time = toc(&compile_timer, "compile time", false); + if (command_return != 0) { + exit(-1); + } + + return (double)compile_time.tv_sec + (double)compile_time.tv_nsec / TLOG_TIMESPEC_NSEC_PER_SEC; +} + +std::pair> processDataTimer(const std::string test_case, const std::string params) { + std::string line; + size_t position; + double time_consumption; + std::vector function_results; + + // perf command + std::string cmd = "bash -c './main_out " + params; + cmd += " 2>&1 | tee tmp.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) { + return std::make_pair(0, std::vector(0)); + } + std::ifstream ifs("tmp.log"); + if (!ifs.is_open()) { + std::cout << "error opening tmp.log"; + assert(false); + } + + // process + while (getline(ifs, line)) { + std::string key = "computation delay: "; + position = line.find(key); + if (position != std::string::npos) { + time_consumption = getTimerConsumption(line, position+key.size()); + } + key = "results: "; + position = line.find(key); + if (position != std::string::npos) { + function_results = getFunctionResults(line, position+key.size()); + } + } + + // printf("%f\n", time_consumption); + + ifs.close(); + + return std::make_pair(time_consumption, function_results); +} + +std::string change_nt_range(const std::string& cmd1, const std::string& cmd2, const std::vector& params) { + std::string param_str; + std::string change_nt_cmd; + param_str.clear(); + change_nt_cmd.clear(); + change_nt_cmd = cmd1; + // prepare parameters + for (const auto& pp : params) { + param_str += std::to_string(pp) + " "; + change_nt_cmd += std::to_string(pp) + " mjf, "; + } + + change_nt_cmd.erase(change_nt_cmd.end() - 2); + change_nt_cmd += cmd2; + change_nt_cmd = "bash -c \"" + change_nt_cmd + "\""; + system(change_nt_cmd.c_str()); + + return param_str; +} + +int64_t exactNumber() { + std::ifstream ifs("tmp.log"); + if (!ifs.is_open()) { + std::cout << "error opening tmp.log"; + assert(false); + } + + // process + std::string line; + std::getline(ifs, line); + auto it = std::remove_if(line.begin(), line.end(), [](const char &c){ + return !std::isdigit(c); + }); + + line.erase(it, line.end()); + + ifs.close(); + + char* pEnd; + + return std::strtol(line.c_str(), &pEnd, 10); +} + +int64_t getIrLines() { + std::string cmd = "bash -c 'wc -l out.ll >& tmp.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) + return 0; + + return exactNumber(); +} + +int64_t getLibSize() { + std::string cmd = "bash -c 'wc -c libout.a >& tmp.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) + return 0; + + return exactNumber(); +} + +struct perfData recordData(const std::string& test_cases, const std::string& param_str, std::ofstream& ofs) { + perfData perf_data = {0, 0, 0, 0}; + + for (size_t idx = 0; idx < iteration_num; idx++) { + const std::pair inst_time_data = processDataPerf(test_cases, param_str); + perf_data.inst_count_avg += (inst_time_data.first/1000); + perf_data.time_consumption_avg += (inst_time_data.second/1000); + } + perf_data.inst_count_avg /= iteration_num; + perf_data.time_consumption_avg /= iteration_num; + + // check library size + perf_data.ir_lines = getIrLines(); + perf_data.library_size = getLibSize(); + + ofs << test_cases << "\t" << param_str << "\t" << perf_data.inst_count_avg + << "\t" << perf_data.time_consumption_avg << "\t" << perf_data.ir_lines << "\t" << perf_data.library_size << std::endl; + + return perf_data; +} + +struct timerData recordTimerData(const std::string& test_cases, const std::string& param_str, std::ofstream& ofs) { + timerData timer_data; + + for (size_t idx = 0; idx < iteration_num; idx++) { + double compile_time = compileTargetCode(test_cases); + timer_data.compile_time.emplace_back(compile_time); + const std::pair> data_timer_res = processDataTimer(test_cases, param_str); + timer_data.ms_time_consumption.emplace_back(data_timer_res.first); + std::copy_if(data_timer_res.second.begin(), data_timer_res.second.end(), + std::back_inserter(timer_data.function_results), + [test_cases, param_str, timer_data, data_timer_res](double val) { + if (!timer_data.function_results.empty()) { + if (!std::equal(timer_data.function_results.begin(), timer_data.function_results.end(), + data_timer_res.second.begin())) + std::cerr << "result error within iteration: " << test_cases << " with parameters: " << param_str << std::endl; + return false; + } else + return true; + }); + } + // check library size + timer_data.ir_lines = getIrLines(); + timer_data.library_size = getLibSize(); + + ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg + << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), + timer_data.ms_time_consumption.end(), + 0.0) / timer_data.ms_time_consumption.size() + << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + << "\t" << std::accumulate(timer_data.compile_time.begin(), + timer_data.compile_time.end(), + 0.0) / timer_data.compile_time.size() + << std::endl; + + return timer_data; +} + +int main(int argc, char** argv) { +// std::vector test_cases{ +// "perf_exp", "perf_log", +// "perf_acosh", "perf_j0", +// "perf_y0", "perf_rem_pio2", "perf_sincosf", +// "perf_float64_add", "perf_float64_div", +// "perf_float64_mul"}; + std::vector test_cases{ + // "perf_exp", "perf_log", + // "perf_acosh", "perf_j0", + // "perf_y0", "perf_rem_pio2", "perf_sincosf", + // "perf_float64_add", "perf_float64_div", + // "perf_float64_mul"}; + "perf_j0","perf_y0"}; + + if (argc >= 2) { + test_cases.clear(); + test_cases.emplace_back(argv[1]); + } + + std::ofstream ofs("perf.log"); + if (!ofs.is_open()) { + std::cout << "error opening perf.log"; + return -1; + } + + std::ofstream avg_speedup("average_speedup.log"); + if (!avg_speedup.is_open()) { + std::cout << "error opening perf.log"; + return -1; + } + + std::vector> normalParameters{ + // BMX055 acceleration + {-2, 2}, + {-4, 4}, + {-8, 8}, + {-16, 16}, + // BMX055 gyroscope + {-125, 125}, + // LM35 Centigrade Temperature Sensor + {-40, 110}, + {-55, 150}, + {0, 100}, + {0, 70}, + // LPS25H crazyflie + {260, 1260}, + // MAX31820 1-Wire Ambient Temperature Sensor + {10, 45}, + {-55, 125}, + // DHT11 Humidity Sensor + {20, 80}, + {0, 50}, + // LMP82064 Current Sensor and Voltage Monitor with SPI + {-0.2, 2}, + // PCE-353 LEQ Sound Level Meter + {30, 130}, + // LLS05-A Linear Light Sensor + {1, 200} + }; + + std::vector> trigonometricParams{ + {0, 0.17453292519943295}, // (0, pi/18) + {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) + {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) + {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) + {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) + {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) + {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) + {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) + {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) + {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) + }; + + if (argc == 4) { + normalParameters.clear(); + std::vector input_param{strtod(argv[2], nullptr), strtod(argv[3], nullptr)}; + normalParameters.emplace_back(input_param); + + trigonometricParams.clear(); + trigonometricParams.emplace_back(input_param); + } + + ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + + for (size_t case_id = 0; case_id < test_cases.size(); case_id++) { + int avg_inst_speedup = 0; + int avg_time_speedup = 0; + int avg_compile_time_speedup = 0; + int avg_ir_reduce = 0; + int avg_lib_size_reduce = 0; + const std::vector> parameters = + test_cases[case_id] == "perf_float64_sin" ? trigonometricParams : normalParameters; + for (const auto& p : parameters) { + const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", + "/g' ../../sensors/test.nt", + {p.front(), p.back()}); + const double p1 = p.front() + 0.6; + const double p2 = p.back() + 0.3; + change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); + + // perfData ori_perf_data = recordData(test_cases[case_id], param_str, ofs); + // perfData opt_perf_data = recordData(test_cases[case_id] + "_opt", param_str, ofs); + timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); + timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); + + // check function results + if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), + opt_perf_data.function_results.begin())) { + std::cerr << "result error: " << test_cases[case_id] << " with parameters: " << param_str << + "ori: " << ori_perf_data.function_results[0] << ", opt: " << opt_perf_data.function_results[0] << + std::endl; + } + + // remove element if ori < opt + assert(ori_perf_data.ms_time_consumption.size() == opt_perf_data.ms_time_consumption.size()); + auto itOri = ori_perf_data.ms_time_consumption.begin(); + for (auto itOpt = opt_perf_data.ms_time_consumption.begin(); + itOpt != opt_perf_data.ms_time_consumption.end();) { + if (*itOri < *itOpt) { + // assert(false && "Need to check why this case slow down!!!!!!"); + itOri = ori_perf_data.ms_time_consumption.erase(itOri); + itOpt = opt_perf_data.ms_time_consumption.erase(itOpt); + } else { + itOri++; + itOpt++; + } + } + + int inst_speedup, time_speedup, ir_reduce, lib_size_reduce, compile_time_speedup; + if (ori_perf_data.ms_time_consumption.empty()) { + assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); + inst_speedup = 0; + time_speedup = 0; + } else { + ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), + ori_perf_data.ms_time_consumption.end(), + 0.0) / ori_perf_data.ms_time_consumption.size(); + opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), + opt_perf_data.ms_time_consumption.end(), + 0.0) / opt_perf_data.ms_time_consumption.size(); + + ori_perf_data.compile_time_avg = std::accumulate(ori_perf_data.compile_time.begin(), + ori_perf_data.compile_time.end(), + 0.0) / ori_perf_data.compile_time.size(); + opt_perf_data.compile_time_avg = std::accumulate(opt_perf_data.compile_time.begin(), + opt_perf_data.compile_time.end(), + 0.0) / opt_perf_data.compile_time.size(); + + inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) + * 100 / opt_perf_data.inst_count_avg); + time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) + * 100 / opt_perf_data.time_consumption_avg); + compile_time_speedup = round((ori_perf_data.compile_time_avg - opt_perf_data.compile_time_avg) + * 100 / opt_perf_data.compile_time_avg); + } + + if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) { + ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); + lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); + } else { + // assert(false && "Need to check why this case increase size!!!!!!"); + ir_reduce = 0; + lib_size_reduce = 0; + } + ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup + << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + + avg_inst_speedup += inst_speedup; + avg_time_speedup += time_speedup; + avg_ir_reduce += ir_reduce; + avg_lib_size_reduce += lib_size_reduce; + avg_compile_time_speedup += compile_time_speedup; + + // reset test.nt + change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", + {p.front(), p.back()}); + change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); + } + avg_inst_speedup = round(avg_inst_speedup / parameters.size()); + avg_time_speedup = round(avg_time_speedup / parameters.size()); + avg_ir_reduce = round(avg_ir_reduce / parameters.size()); + avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); + avg_compile_time_speedup = round(avg_compile_time_speedup / parameters.size()); + avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" + << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%\t" + << avg_compile_time_speedup << "%" << std::endl; + + if (test_cases[case_id] == "perf_float64_sin") { + // trigonometricParams cannot have extention + break; + } + } + + ofs.close(); + + return 0; +} \ No newline at end of file From c94d1d15ceb2199297b2c9f581e06da7c6765f57 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Tue, 3 Jun 2025 16:40:46 +0100 Subject: [PATCH 181/213] update new script for quant and w/o quant comparision * quantize. --- .../llvm-ir/performance_test/quant_bar.py | 209 ++++++++++++++++++ 1 file changed, 209 insertions(+) create mode 100644 applications/newton/llvm-ir/performance_test/quant_bar.py diff --git a/applications/newton/llvm-ir/performance_test/quant_bar.py b/applications/newton/llvm-ir/performance_test/quant_bar.py new file mode 100644 index 000000000..3a1ae32dc --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/quant_bar.py @@ -0,0 +1,209 @@ +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +# Helper function to parse the text data from file content +def parse_perf_data(file_content, quantization_type): + data = [] + current_benchmark_key = None + lines = file_content.strip().split('\n') + + # Skip header line if present + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + + for line in lines: + parts = line.split('\t') + if not parts or len(parts) < 2: # Basic check for enough parts + if line.strip(): + print(f"Skipping line due to insufficient parts: '{line[:60]}...'") + continue + + test_case = parts[0].strip() + + if test_case.startswith("perf_j0"): + current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): + current_benchmark_key = "y0" + elif test_case == "speed up after optimization": + if current_benchmark_key is None: + print(f"Skipping 'speed up' line due to missing benchmark context: '{line[:60]}...'") + continue + + try: + # Ensure enough parts for "speed up after optimization" line + if len(parts) < 6: + print(f"Skipping 'speed up' line due to insufficient data columns: '{line[:60]}...'") + continue + + params_str = parts[1].strip() + param_values = params_str.split() + if len(param_values) < 2: + print(f"Skipping 'speed up' line due to invalid params format: '{line[:60]}...'") + continue + # Format params for display: "[val1, val2]" + formatted_params = f"[{float(param_values[0]):.1f}, {float(param_values[1]):.1f}]" + + # Time consumption speedup is at index 3 (4th item based on 0-indexing) + time_speedup_pct = float(parts[3].replace('%', '')) + # Library size reduction is at index 5 (6th item based on 0-indexing) + library_size_reduction_pct = float(parts[5].replace('%', '')) + + # Speedup factor: 1 + (percentage / 100) + # e.g., 4% speedup -> 1.04x; 0% speedup -> 1.00x + speedup_factor = 1 + (time_speedup_pct / 100.0) + + data.append({ + "benchmark": current_benchmark_key, + "params_raw": params_str, # For sorting + "params_formatted": formatted_params, # For display + "quant_type": quantization_type, + "speedup_factor": speedup_factor, + "library_size_reduction_pct": library_size_reduction_pct + }) + except (IndexError, ValueError) as e: + print(f"Error parsing 'speed up' data line: '{line[:60]}...' due to: {e}") + continue + return data + +# --- Main script execution --- +file_path_wo_quant = "perf_woquant.log" +file_path_w_quant = "perf_quant.log" +all_data_collected = [] + +# Process data without quantization +try: + with open(file_path_wo_quant, 'r', encoding='utf-8') as f: + perf_woquant_content_from_file = f.read() + data_wo_quant_parsed = parse_perf_data(perf_woquant_content_from_file, "w/o Auto Quantization") + all_data_collected.extend(data_wo_quant_parsed) +except FileNotFoundError: + print(f"Error: File not found '{file_path_wo_quant}'. Please ensure it's in the correct location.") + exit() +except Exception as e: + print(f"Error reading or parsing '{file_path_wo_quant}': {e}") + exit() + +# Process data with quantization +try: + with open(file_path_w_quant, 'r', encoding='utf-8') as f: + perf_quant_content_from_file = f.read() + data_w_quant_parsed = parse_perf_data(perf_quant_content_from_file, "with Auto Quantization") + all_data_collected.extend(data_w_quant_parsed) +except FileNotFoundError: + print(f"Error: File not found '{file_path_w_quant}'. Please ensure it's in the correct location.") + exit() +except Exception as e: + print(f"Error reading or parsing '{file_path_w_quant}': {e}") + exit() + +if not all_data_collected: + print("No data was successfully parsed from the files. Exiting.") + exit() + +df_performance = pd.DataFrame(all_data_collected) + +if df_performance.empty: + print("DataFrame is empty after parsing. No plots will be generated. Check parsing logs if any.") + exit() + +# Function to create and save grouped bar charts +def create_and_save_plot(df_benchmark_data, benchmark_id, metric_column, y_axis_label, plot_title_suffix, output_filename): + if df_benchmark_data.empty: + print(f"No data available for {benchmark_id} {plot_title_suffix.lower()}, skipping plot.") + return + + try: + pivot_table_df = df_benchmark_data.pivot_table(index="params_formatted", + columns="quant_type", + values=metric_column) + except Exception as e: + print(f"Could not create pivot table for {benchmark_id} {plot_title_suffix.lower()}: {e}") + return + + # Define the desired order of columns for plotting + ordered_quant_types = [qt for qt in ["w/o Auto Quantization", "with Auto Quantization"] if qt in pivot_table_df.columns] + + if not ordered_quant_types: + print(f"Pivot table for {benchmark_id} {plot_title_suffix.lower()} has no quantization type columns. Skipping.") + return + pivot_table_df = pivot_table_df[ordered_quant_types] + + # Sort index (params_formatted) based on the numerical values of params_raw for consistent plotting order + unique_params_temp_df = df_benchmark_data[['params_raw', 'params_formatted']].drop_duplicates().copy() + unique_params_temp_df['sort_key_internal'] = unique_params_temp_df['params_raw'].apply(lambda x: tuple(map(float, x.split()))) + unique_params_temp_df = unique_params_temp_df.sort_values('sort_key_internal') + final_ordered_params = unique_params_temp_df['params_formatted'].tolist() + + pivot_table_df = pivot_table_df.reindex(final_ordered_params).dropna(axis=0, how='all') + + if pivot_table_df.empty: + print(f"Pivot table became empty after reindexing for {benchmark_id} {plot_title_suffix.lower()}. Skipping.") + return + + num_param_groups = len(pivot_table_df.index) + bar_item_width = 0.35 # Width of individual bars + x_positions = np.arange(num_param_groups) # Base positions for groups + + # Determine overall figure width dynamically + fig_width = max(12, num_param_groups * 0.7) + fig, ax = plt.subplots(figsize=(fig_width, 7)) + + # Plotting bars for each quantization type + num_quant_types = len(ordered_quant_types) + for i, quant_type in enumerate(ordered_quant_types): + # Calculate offset for each bar within a group to achieve grouped bar chart + offset = (i - (num_quant_types - 1) / 2) * bar_item_width + ax.bar(x_positions + offset, pivot_table_df[quant_type].fillna(0), bar_item_width, + label=quant_type, color=['#C3B1E1', '#6A0DAD'][i % 2]) # Light purple, Dark purple + + ax.set_xlabel("Parameters (Range)") + ax.set_ylabel(y_axis_label) + ax.set_title(f"{benchmark_id.upper()} {plot_title_suffix}") + ax.set_xticks(x_positions) + ax.set_xticklabels(pivot_table_df.index, rotation=45, ha="right") + ax.legend(title="Quantization Type") + ax.grid(True, linestyle='--', alpha=0.7, axis='y') # Horizontal grid lines only + + # Adjust y-axis limits for better visualization + if "Speedup Factor" in y_axis_label: + ax.axhline(1.0, color='grey', linestyle='-.', linewidth=0.8) # Reference line at 1.0x speedup + min_data_val = pivot_table_df[ordered_quant_types].min().min() + max_data_val = pivot_table_df[ordered_quant_types].max().max() + if pd.notna(min_data_val) and pd.notna(max_data_val): + ax.set_ylim(bottom=min(0.9, min_data_val - 0.05), top=max_data_val + 0.05) + elif "Reduction (%)" in y_axis_label: + min_data_val = pivot_table_df[ordered_quant_types].min().min() + max_data_val = pivot_table_df[ordered_quant_types].max().max() + if pd.notna(min_data_val) and pd.notna(max_data_val): + ax.set_ylim(bottom=min(0, min_data_val - 5), top=max_data_val + 5) # Give some padding + + plt.tight_layout() # Adjust layout to prevent labels from overlapping + plt.savefig(output_filename) + print(f"Plot saved: {output_filename}") + plt.close(fig) # Close the figure to free memory + + +# --- Generate and Save Plots --- + +# For e_j0 benchmark +df_j0_data = df_performance[df_performance["benchmark"] == "j0"].copy() +if not df_j0_data.empty: + create_and_save_plot(df_j0_data, "e_j0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_j0_speedup_comparison.png") + create_and_save_plot(df_j0_data, "e_j0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_j0_size_reduction_comparison.png") +else: + print("No data processed for e_j0 benchmark.") + +# For e_y0 benchmark +df_y0_data = df_performance[df_performance["benchmark"] == "y0"].copy() +if not df_y0_data.empty: + create_and_save_plot(df_y0_data, "e_y0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_y0_speedup_comparison.png") + create_and_save_plot(df_y0_data, "e_y0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_y0_size_reduction_comparison.png") +else: + print("No data processed for e_y0 benchmark.") + +print("--- Plot generation complete ---") \ No newline at end of file From ed6c34949316c9546034ef67390543a4a725a64b Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 4 Jun 2025 20:20:59 +0100 Subject: [PATCH 182/213] update precisionbits in perf.log * quantize. --- ...0d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt | 48 +++++++++++++++++++ .../llvm-ir/performance_test/auto_test.cpp | 24 +++++----- 2 files changed, 59 insertions(+), 13 deletions(-) create mode 100644 analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt diff --git a/analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt b/analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt new file mode 100644 index 000000000..887f84963 --- /dev/null +++ b/analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt @@ -0,0 +1,48 @@ + +changeset: 1775:a90d47861c3c28b9de76f56f756e2ba4eb14e1d5 +char kNewtonVersion[] = "0.3-alpha-1775 (a90d47861c3c28b9de76f56f756e2ba4eb14e1d5) (build 06-03-2025-16:40-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index 25b7bbc33..3329bdd84 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -317,10 +317,12 @@ struct perfData recordData(const std::string& test_cases, const std::string& par ofs << test_cases << "\t" << param_str << "\t" << perf_data.inst_count_avg << "\t" << perf_data.time_consumption_avg << "\t" << perf_data.ir_lines << "\t" << perf_data.library_size << std::endl; + + return perf_data; } -struct timerData recordTimerData(const std::string& test_cases, const std::string& param_str, std::ofstream& ofs) { +struct timerData recordTimerData(const std::string& test_cases, const std::string& param_str, int precision_bits, std::ofstream& ofs){ timerData timer_data; for (size_t idx = 0; idx < iteration_num; idx++) { @@ -344,15 +346,11 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin timer_data.ir_lines = getIrLines(); timer_data.library_size = getLibSize(); - ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg - << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), - timer_data.ms_time_consumption.end(), - 0.0) / timer_data.ms_time_consumption.size() - << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size - << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size - << "\t" << std::accumulate(timer_data.compile_time.begin(), - timer_data.compile_time.end(), - 0.0) / timer_data.compile_time.size() +ofs << test_cases << "\t" << param_str << "\t" << precision_bits << "\t" + << timer_data.inst_count_avg << "\t" + << std::accumulate(timer_data.ms_time_consumption.begin(), timer_data.ms_time_consumption.end(), 0.0) / timer_data.ms_time_consumption.size() << "\t" + << timer_data.ir_lines << "\t" << timer_data.library_size << "\t" + << std::accumulate(timer_data.compile_time.begin(), timer_data.compile_time.end(), 0.0) / timer_data.compile_time.size() << std::endl; return timer_data; @@ -483,7 +481,7 @@ int main(int argc, char** argv) } - ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + ofs << "test case\tparam\tprecision_bits\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; for (size_t case_id = 0; case_id < test_cases.size(); case_id++) @@ -529,8 +527,8 @@ int main(int argc, char** argv) const double p2 = range.back() + 0.3; change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); - timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); - timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); + timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str,std::get<1>(entry), ofs); + timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, std::get<1>(entry), ofs); // check function results if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), From c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 4 Jun 2025 20:22:36 +0100 Subject: [PATCH 183/213] rename perf.log to perf_quant.log * quantize. --- ...4d1d15ceb2199297b2c9f581e06da7c6765f57.txt | 48 +++++++++++++++++++ .../llvm-ir/performance_test/auto_test.cpp | 6 +-- 2 files changed, 51 insertions(+), 3 deletions(-) create mode 100644 analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt diff --git a/analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt b/analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt new file mode 100644 index 000000000..e1c0dfe77 --- /dev/null +++ b/analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt @@ -0,0 +1,48 @@ + +changeset: 1776:c94d1d15ceb2199297b2c9f581e06da7c6765f57 +char kNewtonVersion[] = "0.3-alpha-1776 (c94d1d15ceb2199297b2c9f581e06da7c6765f57) (build 06-04-2025-20:21-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index 3329bdd84..091c7ea97 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -373,17 +373,17 @@ int main(int argc, char** argv) test_cases.emplace_back(argv[1]); } - std::ofstream ofs("perf.log"); + std::ofstream ofs("perf_quant.log"); if (!ofs.is_open()) { - std::cout << "error opening perf.log"; + std::cout << "error opening perf_quant.log"; return -1; } std::ofstream avg_speedup("average_speedup.log"); if (!avg_speedup.is_open()) { - std::cout << "error opening perf.log"; + std::cout << "error opening perf_quant.log"; return -1; } From e22e6908d8904f59081e7b1adbef130bdbf0c3ac Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 4 Jun 2025 20:59:42 +0100 Subject: [PATCH 184/213] update bar_plot * quantize. --- ...6c34949316c9546034ef67390543a4a725a64b.txt | 48 ++ .../llvm-ir/performance_test/bar_plot.py | 505 ++++++++++-------- 2 files changed, 337 insertions(+), 216 deletions(-) create mode 100644 analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt diff --git a/analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt b/analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt new file mode 100644 index 000000000..b00b47056 --- /dev/null +++ b/analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt @@ -0,0 +1,48 @@ + +changeset: 1777:ed6c34949316c9546034ef67390543a4a725a64b +char kNewtonVersion[] = "0.3-alpha-1777 (ed6c34949316c9546034ef67390543a4a725a64b) (build 06-04-2025-20:22-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/bar_plot.py b/applications/newton/llvm-ir/performance_test/bar_plot.py index e4d06cef4..d2ed97057 100644 --- a/applications/newton/llvm-ir/performance_test/bar_plot.py +++ b/applications/newton/llvm-ir/performance_test/bar_plot.py @@ -1,220 +1,293 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd -import os - -breakdown = False - - -def bar_plot_groups(labels, - y1, - y2, - y1_label, - y2_label, - ylabel, - title, - save_fig=''): - """ - A simple wrapper for matplotlib's barplot. - - :param labels: - :param y1: - :param y2: - :param ylabel: - :param xlabel: - :param save_fig: - :return: - """ - x = np.arange(len(labels)) # the label locations - width = 0.35 # the width of the bars - - plt.rc('text', usetex=True) - plt.rc('font', family='serif') - plt.rc('font', size=12) - - fig, ax = plt.subplots() - ax.bar(x[1:len(y1)], y1[1:], width, label=y1_label, color='#c2a5cf') - ax.bar(x, y2, width, label=y2_label, color='#7b3294') - - # Add some text for labels, title and custom x-axis tick labels, etc. - ax.set_ylabel(ylabel) - ax.set_title(title) - ax.set_xticks(x) - ax.set_xticklabels(labels) - - plt.xticks(rotation=45) - ax.yaxis.grid() # horizontal lines - - fig.tight_layout() - - if len(save_fig): - plt.savefig(save_fig) - else: - plt.show() - - -def bar_plot_pairs(labels, - y1, - y2, - y1_label, - y2_label, - ylabel, - title, - save_fig=''): - """ - A simple wrapper for matplotlib's barplot. - - :param labels: - :param y1: - :param y2: - :param ylabel: - :param xlabel: - :param save_fig: - :return: - """ - x = np.arange(len(labels)) # the label locations - width = 0.35 # the width of the bars - - plt.rc('text', usetex=True) - plt.rc('font', family='serif') - plt.rc('font', size=12) - - fig, ax = plt.subplots() - - if breakdown: - ax.bar(x[0:4] - width / 2, y1[0:4], width, label=y1_label+"w/o Overload", color='#fdb863') - ax.bar(x[0:4] + width / 2, y2[0:4], width, label=y2_label+"w/o Overload", color='#e66101') - ax.bar(x[4:] - width / 2, y1[4:], width, label=y1_label+"w/ Overload", color='#c2a4cf') - ax.bar(x[4:] + width / 2, y2[4:], width, label=y2_label+"w/ Overload", color='#7b3294') - - # Add some text for labels, title and custom x-axis tick labels, etc. - if ylabel == 'Speedup': - y_axis_length = [0.9, 2.4] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0]+0.1, y_axis_length[1]+0.1, 0.2)) - elif ylabel == 'Size reduction (\%)': - y_axis_length = [2, 13] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0], y_axis_length[1], 2)) - else: - ax.bar(x - width / 2, y1, width, label=y1_label, color='#c2a4cf') - ax.bar(x + width / 2, y2, width, label=y2_label, color='#7b3294') - - # Add some text for labels, title and custom x-axis tick labels, etc. - if ylabel == 'Speedup': - y_axis_length = [1, 2.1] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0], y_axis_length[1], 0.2)) - elif ylabel == 'Size reduction (\%)': - y_axis_length = [-5, 36] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0], y_axis_length[1], 5)) - - ax.set_ylabel(ylabel) - ax.set_title(title) - ax.set_xticks(x) - ax.set_xticklabels(labels) - ax.legend(prop={'size': 9}) - fig.set_figheight(3) - - plt.xticks(rotation=45) - ax.yaxis.grid() # horizontal lines - - fig.tight_layout() - - if len(save_fig) > 0: - plt.savefig(save_fig) - - -if __name__ == '__main__': - if breakdown: - df = pd.read_excel('breakdown_performance.xlsx', - sheet_name='speedup', - nrows=5, - usecols='C:F', - engine='openpyxl') - df.rename(columns={ - "type compression": "Type\nCompression", - "branch elimination": "Condition\nSimplification", - "constant substitution": "Constant\nSubstitution", - "overall": "Overall" - }, - inplace=True) - print(df) - - labels = list(df.columns.values) - labels = labels + labels - - y1 = list(df.iloc[0]) + list(df.iloc[1]) - y2 = list(df.iloc[2]) + list(df.iloc[3]) - - bar_plot_pairs(labels, y1, y2, 'arm', 'x86', 'Speedup', - 'Speedup breakdown - fp_add [0,200]', 'breakdown_speedup.png') - - df = pd.read_excel('breakdown_performance.xlsx', - sheet_name='size', - nrows=5, - usecols='C:F', - engine='openpyxl') - df.rename(columns={ - "type compression": "Type\nCompression", - "branch elimination": "Condition\nSimplification", - "constant substitution": "Constant\nSubstitution", - "overall": "Overall" - }, - inplace=True) - print(df) - - labels = list(df.columns.values) - labels = labels + labels - - y1 = list(map(lambda x: 100*x, list(df.iloc[0]) + list(df.iloc[1]))) - y2 = list(map(lambda x: 100*x, list(df.iloc[2]) + list(df.iloc[3]))) - - bar_plot_pairs(labels, y1, y2, 'arm', 'x86', 'Size reduction (\%)', - 'Size reduction breakdown - fp_add [0,200]', 'breakdown_size.png') - - # bar_plot_groups(labels, y1, y2, 'w/o Function Overload', - # 'w/ Function Overload', 'Speedup', - # 'Performance breakdown of CoSense optimizations', - # 'speedup_breakdown.png') + +# Function to build a map of (benchmark, params_raw) -> precision_bits +# by parsing the primary data lines of the quant_file content. +def build_precision_map_from_quant_file(file_content): + precision_map = {} + current_benchmark_key = None + lines = file_content.strip().split('\n') + + header_skipped = False + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + header_skipped = True + + # parts[0]=test_case, parts[1]=param, parts[2]=precision_bits + precision_bits_col_index = 2 + # Minimum parts needed: test_case, param, precision_bits ... + min_parts_for_precision_line = precision_bits_col_index + 1 + + for line in lines: + parts = line.split('\t') + if not parts: continue + + test_case = parts[0].strip() + + # Only process primary data lines, not "speed up..." lines + if test_case.startswith("perf_j0"): + current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): + current_benchmark_key = "y0" + else: # Skip "speed up..." lines or other unrecognized lines + continue + + if current_benchmark_key and len(parts) > min_parts_for_precision_line: + params_str = parts[1].strip() + # Validate params_str before splitting + if len(params_str.split()) >= 2: + try: + precision_val = parts[precision_bits_col_index].strip() + # Ensure precision_val is not empty and is a valid integer + if precision_val: + int(precision_val) # Test conversion + map_key = (current_benchmark_key, params_str) + if map_key not in precision_map: # Store first encountered for a given param + precision_map[map_key] = precision_val + # else: + # print(f"Debug: Duplicate param '{params_str}' for benchmark '{current_benchmark_key}' in precision map building. Using first value.") + except (IndexError, ValueError) as e: + print(f"Warning: Could not parse precision_bits from line: '{line[:80]}...' Error: {e}") + # else: + # print(f"Debug: Invalid params_str '{params_str}' for precision map building from line: '{line[:80]}...'") + + if not precision_map: + print("Warning: Precision map is empty. Check 'perf_quant.log' format and 'precision_bits_col_index'.") + # else: + # print(f"Debug: Precision map built with {len(precision_map)} entries.") + # for k, v in list(precision_map.items())[:5]: print(f" Map sample: {k} -> {v}") + + return precision_map + +# Helper function to parse the "speed up after optimization" lines +def parse_summary_perf_data(file_content, quantization_type): + data = [] + current_benchmark_key = None + lines = file_content.strip().split('\n') + + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + + for line in lines: + parts = line.split('\t') + if not parts: continue + + test_case = parts[0].strip() + + if test_case.startswith("perf_j0"): + current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): + current_benchmark_key = "y0" + elif test_case == "speed up after optimization": + if current_benchmark_key is None: + print(f"Skipping 'speed up' line (no benchmark context): '{line[:60]}...'") + continue + + # Standard "speed up after optimization" line structure: + # test case(0)|param(1)|instr%(2)|time%(3)|ir%(4)|lib_size%(5)|compile_time%(6) + if len(parts) < 7: + print(f"Skipping 'speed up' line (insufficient columns): '{line[:60]}...'") + continue + + try: + params_str = parts[1].strip() + param_values = params_str.split() + if len(param_values) < 2: + print(f"Skipping 'speed up' line (invalid params format): '{line[:60]}...'") + continue + formatted_params = f"[{float(param_values[0]):.1f}, {float(param_values[1]):.1f}]" + + time_speedup_pct = float(parts[3].replace('%', '')) + library_size_reduction_pct = float(parts[5].replace('%', '')) + speedup_factor = 1 + (time_speedup_pct / 100.0) + + entry = { + "benchmark": current_benchmark_key, + "params_raw": params_str, + "params_formatted": formatted_params, + "quant_type": quantization_type, + "speedup_factor": speedup_factor, + "library_size_reduction_pct": library_size_reduction_pct, + "precision_bits": None # Will be filled in later using the map + } + data.append(entry) + except (IndexError, ValueError) as e: + print(f"Error parsing 'speed up' data line: '{line[:60]}...' due to: {e}") + continue + return data + +# --- Main script execution --- +file_path_wo_quant = "perf_woquant.log" +file_path_w_quant = "perf_quant.log" + +# 1. Build precision_bits map from perf_quant.log +quant_precision_map = {} +try: + with open(file_path_w_quant, 'r', encoding='utf-8') as f: + perf_quant_content_for_map = f.read() + quant_precision_map = build_precision_map_from_quant_file(perf_quant_content_for_map) +except FileNotFoundError: + print(f"Error: File not found '{file_path_w_quant}'. Precision bits cannot be mapped.") + # Continue without it, precision_bits will remain None for all entries +except Exception as e: + print(f"Error building precision map from '{file_path_w_quant}': {e}") + + +# 2. Parse summary performance data for both files +all_data_collected = [] +try: + with open(file_path_wo_quant, 'r', encoding='utf-8') as f: + content = f.read() + parsed_data = parse_summary_perf_data(content, "w/o Auto Quantization") + all_data_collected.extend(parsed_data) +except FileNotFoundError: + print(f"Warning: File not found '{file_path_wo_quant}'.") +except Exception as e: + print(f"Error reading or parsing '{file_path_wo_quant}': {e}") + +try: + with open(file_path_w_quant, 'r', encoding='utf-8') as f: + content = f.read() + # Note: perf_quant.log's "speed up..." lines are parsed like wo_quant's here + parsed_data = parse_summary_perf_data(content, "with Auto Quantization") + all_data_collected.extend(parsed_data) +except FileNotFoundError: + print(f"Warning: File not found '{file_path_w_quant}'.") +except Exception as e: + print(f"Error reading or parsing '{file_path_w_quant}': {e}") + + +if not all_data_collected: + print("No summary data was successfully parsed from any file. Exiting.") + exit() + +# 3. Add precision_bits to all entries using the map +for item in all_data_collected: + key = (item['benchmark'], item['params_raw']) + item['precision_bits'] = quant_precision_map.get(key) + +df_performance = pd.DataFrame(all_data_collected) + +if df_performance.empty: + print("DataFrame is empty after parsing and mapping. No plots will be generated.") + exit() + +# 4. Create the 'range_over_precision_bits' column for X-axis display +def create_range_precision_label(row): + if pd.notna(row['precision_bits']) and str(row['precision_bits']).strip(): + return f"{row['params_formatted']}/{row['precision_bits']}" + return row['params_formatted'] # Fallback if no precision_bits + +df_performance['range_over_precision_bits'] = df_performance.apply(create_range_precision_label, axis=1) + +# Function to create and save grouped bar charts (largely same as before) +def create_and_save_plot(df_benchmark_data, benchmark_id, metric_column, y_axis_label, plot_title_suffix, output_filename): + if df_benchmark_data.empty: + print(f"No data available for {benchmark_id} {plot_title_suffix.lower()}, skipping plot.") + return + + if 'range_over_precision_bits' not in df_benchmark_data.columns: + print(f"Critical error: 'range_over_precision_bits' column missing in data for {benchmark_id}. Skipping plot.") + return + + try: + pivot_table_df = df_benchmark_data.pivot_table(index="range_over_precision_bits", + columns="quant_type", + values=metric_column) + except Exception as e: + print(f"Could not create pivot table for {benchmark_id} {plot_title_suffix.lower()}: {e}") + return + + ordered_quant_types = [qt for qt in ["w/o Auto Quantization", "with Auto Quantization"] if qt in pivot_table_df.columns] + + if not ordered_quant_types: + print(f"Pivot table for {benchmark_id} {plot_title_suffix.lower()} has no quantization type columns (e.g. all NaNs for this metric). Skipping.") + # print(pivot_table_df.head()) # For debugging + return + pivot_table_df = pivot_table_df[ordered_quant_types] + + temp_sort_df = df_benchmark_data[['params_raw', 'range_over_precision_bits']].drop_duplicates().copy() + temp_sort_df['sort_key_params_numeric'] = temp_sort_df['params_raw'].apply(lambda x: tuple(map(float, x.split()))) + temp_sort_df = temp_sort_df.sort_values(by='sort_key_params_numeric') + final_ordered_x_labels = temp_sort_df['range_over_precision_bits'].unique().tolist() + + pivot_table_df = pivot_table_df.reindex(final_ordered_x_labels).dropna(axis=0, how='all') + + if pivot_table_df.empty: + print(f"Pivot table became empty after reindexing for {benchmark_id} {plot_title_suffix.lower()}. Skipping.") + return + + num_param_groups = len(pivot_table_df.index) + bar_item_width = 0.35 + x_positions = np.arange(num_param_groups) + + fig_width = max(12, num_param_groups * 0.8) + fig, ax = plt.subplots(figsize=(fig_width, 7)) + + num_quant_types = len(ordered_quant_types) + for i, quant_type in enumerate(ordered_quant_types): + offset = (i - (num_quant_types - 1) / 2) * bar_item_width + # Ensure data for the bar is numeric, default to 0 if NaN + bar_data = pd.to_numeric(pivot_table_df[quant_type], errors='coerce').fillna(0) + ax.bar(x_positions + offset, bar_data, bar_item_width, + label=quant_type, color=['#C3B1E1', '#6A0DAD'][i % 2]) + + ax.set_xlabel("Range/PrecisionBits") + ax.set_ylabel(y_axis_label) + ax.set_title(f"{benchmark_id.upper()} {plot_title_suffix}") + ax.set_xticks(x_positions) + ax.set_xticklabels(pivot_table_df.index, rotation=45, ha="right") + ax.legend(title="Quantization Type") + ax.grid(True, linestyle='--', alpha=0.7, axis='y') + + # Dynamically set Y limits based on actual plotted data + all_bar_values = [] + for qt in ordered_quant_types: + all_bar_values.extend(pd.to_numeric(pivot_table_df[qt], errors='coerce').fillna(0).tolist()) + + if not all_bar_values: # Should not happen if pivot_table_df is not empty + min_data_val, max_data_val = (0,1) if "Speedup" in y_axis_label else (0,100) else: - # X86 - df = pd.read_excel('overall_speedup_microbenchmarks.xlsx', - sheet_name='x86', - nrows=10, - header=None, - names=['benchmark', 'speedup', 'size'], - usecols='A,B,D', - engine='openpyxl') - df['benchmark'] = df['benchmark'].apply( - lambda x: x.replace(' with time speed up', '')) - - labels = list(df['benchmark'].values) - x86_speedup = list(df['speedup'].values) - x86_size = list(df['size'].values) - x86_speedup = list(map(lambda x: float(x.strip('%'))/100 + 1.0, x86_speedup)) - x86_size = list(map(lambda x: float(x.strip('%')), x86_size)) - - # ARM - df = pd.read_excel('overall_speedup_microbenchmarks.xlsx', - sheet_name='arm64', - nrows=10, - header=None, - names=['benchmark', 'speedup', 'size'], - usecols='A,B,D', - engine='openpyxl') - - arm_speedup = list(df['speedup'].values) - arm_size = list(df['size'].values) - arm_speedup = list(map(lambda x: float(x.strip('%'))/100 + 1.0, arm_speedup)) - arm_size = list(map(lambda x: float(x.strip('%')), arm_size)) - - bar_plot_pairs(labels, arm_speedup, x86_speedup, 'arm', 'x86', 'Speedup', - 'Speedup in microbenchmarks', 'speedup_microbenchmarks.png') - - bar_plot_pairs(labels, arm_size, x86_size, 'arm', 'x86', - 'Size reduction (\%)', 'Library size reduction in microbenchmarks', - 'size_reduction_microbenchmarks.png') - - os.system("mv *.png fig/.") \ No newline at end of file + min_data_val = min(all_bar_values) + max_data_val = max(all_bar_values) + + if "Speedup Factor" in y_axis_label: + ax.axhline(1.0, color='grey', linestyle='-.', linewidth=0.8) + y_bottom = min(0.9, min_data_val - 0.05) if min_data_val < 5 else 0.9 # Ensure 0.9 is a potential floor + y_top = max_data_val + 0.05 + if y_bottom >= y_top : y_bottom = y_top - 0.1 # Ensure valid range + ax.set_ylim(bottom=y_bottom, top=y_top) + + elif "Reduction (%)" in y_axis_label: + y_bottom = min(0, min_data_val - 5) if min_data_val < 100 else 0 + y_top = max_data_val + 5 + if y_bottom >= y_top : y_bottom = y_top - 10 + ax.set_ylim(bottom=y_bottom, top=y_top) + + + plt.tight_layout() + plt.savefig(output_filename) + print(f"Plot saved: {output_filename}") + plt.close(fig) + +# --- Generate and Save Plots --- +df_j0_data = df_performance[df_performance["benchmark"] == "j0"].copy() +if not df_j0_data.empty: + create_and_save_plot(df_j0_data, "e_j0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_j0_speedup_comparison.png") + create_and_save_plot(df_j0_data, "e_j0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_j0_size_reduction_comparison.png") +else: + print("No data processed for e_j0 benchmark.") + +df_y0_data = df_performance[df_performance["benchmark"] == "y0"].copy() +if not df_y0_data.empty: + create_and_save_plot(df_y0_data, "e_y0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_y0_speedup_comparison.png") + create_and_save_plot(df_y0_data, "e_y0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_y0_size_reduction_comparison.png") +else: + print("No data processed for e_y0 benchmark.") + +print("--- Plot generation complete ---") \ No newline at end of file From 9c4975268fa3dac9e85718f0d4660c8d615e3e5c Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Wed, 4 Jun 2025 23:24:24 +0100 Subject: [PATCH 185/213] update testfile * quantize. --- .../newton/llvm-ir/performance_test/auto_test.cpp | 2 +- .../newton/llvm-ir/performance_test/auto_test_woquant.cpp | 8 ++++---- 2 files changed, 5 insertions(+), 5 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index 091c7ea97..96202c8d3 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -380,7 +380,7 @@ int main(int argc, char** argv) return -1; } - std::ofstream avg_speedup("average_speedup.log"); + std::ofstream avg_speedup("average_speedup_quant.log"); if (!avg_speedup.is_open()) { std::cout << "error opening perf_quant.log"; diff --git a/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp index c16283842..481031320 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp @@ -380,15 +380,15 @@ int main(int argc, char** argv) { test_cases.emplace_back(argv[1]); } - std::ofstream ofs("perf.log"); + std::ofstream ofs("perf_woquant.log"); if (!ofs.is_open()) { - std::cout << "error opening perf.log"; + std::cout << "error opening perf_woquant.log"; return -1; } - std::ofstream avg_speedup("average_speedup.log"); + std::ofstream avg_speedup("average_speedup_woquant.log"); if (!avg_speedup.is_open()) { - std::cout << "error opening perf.log"; + std::cout << "error opening perf_woquant.log"; return -1; } From d499d17a5eff6b5cc57009c00e0701c496c388f1 Mon Sep 17 00:00:00 2001 From: Alex Xia <49825906+xyf2002@users.noreply.github.com> Date: Thu, 5 Jun 2025 15:05:49 +0100 Subject: [PATCH 186/213] Quantize (#3) * attempt to modify quantizePredict * dev2. * attempt to modify quantizePredict * dev2. * attempt to modify quantizePredict * dev2. * attempt to modify quantizePredict * dev2. * attempt to modify quantizePredict * dev2. * attempt to modify quantizePredict * dev2. * update quantization.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update optimizeByRange.cpp * dev2. * update Makefile * dev2. * update more test log and save IR function * dev2. * update more test log and save IR function * dev2. * solved store and load type issue * dev2. * solved store and load type issue . * dev2. * try to quantize return type but have some problem * dev2. * add more debug information * dev2. * Solve the problem of duplicate quantization * dev2. * add fixmul to the list of the functions to skip * dev2. * add earse old funtion * dev2. * fix fmul issue * dev2. * fix fneg issue * dev2. * add quantize global variable * dev2. * fix some issue * dev2. * fix function name issue * dev2. * update makefile * dev2. * update MadwickAHRS.c to have more print logs * dev2. * add dequantization pass * dev2. * update test files * dev2. * big update * dev2. * fix fdiv issue * dev2. * save before handle constant operation * dev2. * solve constant issue * dev2. * save before handle more constant issue * dev2. * save before change * dev2. * update some test files * dev2. * update script * dev2. * fix some issue * dev2. * made some changes * dev2. * fix sqrt issue * dev2. * fix more constant issue * dev2. * fix void signature issue * dev2. * fix some add and sub constant issue * dev2. * add fixsqrt * dev2. * comtinue fix some constant issue * dev2. * update makefile to original one * dev2. * save before change * dev2. * save before handle Nan issue * dev2. * add more test files * dev2. * save before change * dev2. * update some test files * dev2. * update testfile * dev2. * continue update testfiles * dev2. * update Original c file * dev2. * update Original c file * dev2. * update test script * dev2. * update replace script * dev2. * update optimizeByRange.cpp * dev2. * update some test files * dev2. * update some test files * dev2. * add input.csv * dev2. * update test script * dev2. * update test script * dev2. * update add ,sub with nsw * dev2. * update mul nsw * dev2. * update mul nsw * dev2. * update some fmul case * dev2. * update Makefile * dev2. * update test script * dev2. * update test file * dev2. * save before change * dev2. * update fixrsqrt * dev2. * update test scripts * dev2. * small update on fixrsqrt * dev2. * add more ashr * dev2. * update i32 fixmul and fixsqrt * dev2. * update fixmul * dev2. * add input.csv * dev2. * update i32 fixmul and fixsqrt * dev2. * add support for get precisionBits of different sensor * dev2. * add support for quantization and fixrsqrt replacement * dev2. * calucate Mean error and max error for diffferent FRAC_Q * dev2. * update gloabal version MadgwickAHRS * dev2. * 9/27 save before change * dev2. * calucate Mean error and max error for diffferent FRAC_Q * dev2. * made some changes and use i64 mul * dev2. * made some changes and use i64 mul * dev2. * use i64 mul * dev2. * calucate Mean error and max error for diffferent FRAC_Q * dev2. * update frequency constant substitution * dev2. * change back to fixmul * dev2. * save before change * dev2. * big update Addresses #1. * fix bitcast pointer issue Addresses #1. * fix several bugs, save before change Addresses #1. * save before change Addresses #1. * solved quantization and dequantization issue Addresses #1. * fix some hard code issue Addresses #1. * inline fixmul and fixsqrt Addresses #1. * env used for test diff fracq Addresses #1. * update support fpr Mahoney Addresses #1. * have some bug, save before roll back Addresses #1. * fix diff frac_q env inline bug Addresses #1. * fix bug of Mahony Addresses #1. * try to support i16 fix version Addresses #1. * fixrsqrt support i16 now Addresses #1. * update fast quantize and dequantize Addresses #1. * update test scriptes for euler_angle and rms error Addresses #1. * fix i16 bitcast issue Addresses #1. * update config of bitWidth Addresses #1. * add rms error analysis of quaternions results Addresses #1. * try to add support for vectorization Addresses #1. * test Addresses #2. * add support for sin and cos Addresses #2. * update rounding for gloabl variable to improve precision Addresses #2. * try to add support for rangeAnalysis Addresses #2. * save env for 11/23 perf test Addresses #2. * Add support for global variables Addresses #2. * Add support for LTO across multiple file Addresses #2. * change to use config file to organize quantization Addresses #2. * try to support martrix, but have store type issue Addresses #2. * update support for internal constant Addresses #2. * use auto-quantiztion flag to control Addresses #2. * use config file to control BIT_WIDTH Addresses #2. * refine code Addresses #2. * add static analysis to decide auto-quantization Addresses #2. * update target featture fpu checking Addresses #2. * try to add support for e_y0 and e_j0 benchmark Addresses #2. * add dequantization support for return value Addresses #2. * save before change Addresses #2. * update makefile for autoquantization test Addresses #2. * save some files Addresses #2. * fix phi issue Addresses #2. * fix fmul constant issue Addresses #2. * fix internal constant replacement issue Addresses #2. * save some files Addresses #2. * add support phi infinite value Addresses #2. * update test files Addresses #2. * update SQNR test for Mahony Addresses #2. * update overflow checking Addresses #2. * update resolution Addresses #2. * update removal of unused constant Addresses #2. * update comments Addresses #2. * update * quantize. * update * quantize. * update sensfusion6 test files * quantize. * update exploit of sensor info * quantize. * update SQNR test scripts * quantize. * update remove unused constant * quantize. * update fpu detection * quantize. * update test scripts * quantize. * update lshr with ashr * quantize. * save * quantize. * save before change * quantize. * update microbenchmark testfile * quantize. * update optimizeByRange.cpp for microbenchmark * quantize. * update for microbenmark and Madgwick * quantize. * update for integration * quantize. * update files for microbenchmark test * quantize. * update files * quantize. * update files * quantize. * update * quantize. * update * quantize. * update * quantize. * update * quantize. * update * quantize. * update compile time support * quantize. * update compile time * quantize. * fix constant issue * quantize. * update original cosense wo function overload test * quantize. * update new script for quant and w/o quant comparision * quantize. * update precisionbits in perf.log * quantize. * rename perf.log to perf_quant.log * quantize. * update bar_plot * quantize. * update testfile * quantize. --------- Co-authored-by: Wzw2000426 --- ...67a50a9ae7f94b11f311a9a771c92ab245c54c.txt | 48 + ...d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt | 48 + ...212c7b5b21a987cda580414026d64e97a3ebf5.txt | 48 + ...5ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt | 48 + ...7273f277374e0717f7025a54ac65f8874e5123.txt | 48 + ...6f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt | 48 + ...7d5ef1e76af93962500380ad008ec13141c86d.txt | 48 + ...91ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt | 48 + ...5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt | 48 + ...74e165e743d19e41942ad7778f7a9f6d76ceec.txt | 48 + ...8c66f5f08f625687e7b85387c410cc14fc2e8a.txt | 7 + ...60ebed7ea85969a8ea83c441a96d30d756ebc6.txt | 48 + ...d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt | 48 + ...3584b2775487f43b4ea4e659e29799241fd93b.txt | 48 + ...01b11217ed30b60e34051bffa718f2bbb8c551.txt | 48 + ...d2e5a37fd670198ad313cd3adca507275521ba.txt | 48 + ...d65a4be09b5334ba88b40e1451c37217ba3971.txt | 48 + ...88a178980fdedaaec03ec6d9876db3a96aed46.txt | 48 + ...7de14acdebe0b852e417df322fa55174bec455.txt | 48 + ...32672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt | 48 + ...9b2e7e3b38992fa89085f297617e6af24702c7.txt | 48 + ...a293dead7ede85f57ab0abe37fb28880fefc54.txt | 48 + ...e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt | 7 + ...8eb5741532f733eafc4bfcb93801bfdc260f25.txt | 7 + ...e2a17c4727c42120c3d791515eabc082b76067.txt | 48 + ...43b79ea3957b991e9baf2ad059f6488e687ea1.txt | 48 + ...24d4e633cfb39b0cd4cb845a10b6113ae2a536.txt | 7 + ...3a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt | 48 + ...cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt | 48 + ...d764c13c3991109adcd534a7a8a5fe25e8154d.txt | 48 + ...e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt | 7 + ...56f8cead1efca3bcef193fe76a2d9a3dac4463.txt | 7 + ...00557e8f76980e0bd2de795d669f5f24606104.txt | 48 + ...e0b2082ea8d7560ef1776838debac2236b308b.txt | 48 + ...260ad3f710627bd8d432d40a897b7a5239516e.txt | 48 + ...547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt | 48 + ...5f482bc78076ef3d9b200117d948b899bba585.txt | 48 + ...a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt | 48 + ...e47b1b17600eacfd132cde3109992ea24c3c09.txt | 48 + ...5a659df702ad3153897f1b8036b4fbbeccc7de.txt | 48 + ...f720e35e3cfdae69167d152d6069da2199b66c.txt | 48 + ...a2089f18acf70a8f55e8433f7d03bc9f992471.txt | 48 + ...e833cce9ad8f877114760a7900fc0a2085e1f9.txt | 48 + ...074aa0fa141b085ce178b6316724d3fcea382f.txt | 48 + ...a3381f0e5bba490bdbbfc408a09f136cc9a272.txt | 48 + ...6938a71d4d9f26283a2a7f3a9b4b5011591d18.txt | 48 + ...c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt | 48 + ...a23acc2f56fd83f793d72017f63d0798febe2c.txt | 48 + ...649b87d74ec0d3c5b7fb1809137bdb38c34682.txt | 48 + ...4908d1897c17fa4e3a54e78371a6e5268c5d46.txt | 48 + ...8a1e350786d8527b315bc202ea91f5b6df797c.txt | 48 + ...a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt | 48 + ...72375b9c565d124523fd22cda7382fe4dd7614.txt | 48 + ...8766310a7770aba17a61bebddf161d7c15081e.txt | 48 + ...e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt | 48 + ...5cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt | 48 + ...5e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt | 48 + ...5f1ac697cb8cf197689117358f8eed8787877b.txt | 48 + ...9d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt | 48 + ...838c712a9bd377eb39365bb131d8ec2fd7baed.txt | 48 + ...e354ebef3180f38512bf461d40fdbfcf75ec0a.txt | 48 + ...5b2689bdc004f9c12d3f827b1313550af254ba.txt | 48 + ...71b9a2ea9c18664318972c2e53e9808f432731.txt | 48 + ...65fa4b66d77af92c57dbcaa570565ea1256778.txt | 48 + ...b3928eeefd23988db60d9401e77c3c1a7e836f.txt | 48 + ...57f139a0634a2a9d3598739471fc1c70f70a4d.txt | 48 + ...a839299d6958488ea11f28a6d4b76d3d5496b0.txt | 48 + ...dee7a34daf3be6125212d01c917f40dc474170.txt | 48 + ...36179b480bc297449afd40c11c44e3243df14d.txt | 48 + ...611897711928b9b7418dc487a7cb63b3716a7a.txt | 48 + ...65d908550d4cb4b411d40aaaf27bd642595550.txt | 48 + ...8ee98b027ece52ff06fb53330989a230eb16d2.txt | 48 + ...4c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt | 7 + ...8ed32c397fc92a7229a442d6d70dfdf8cea747.txt | 48 + ...69325b7fdaab528356887d1d0eb5b0291012db.txt | 48 + ...bf84ffed653927aa206d3bfcd3fe4573b67469.txt | 48 + ...dceef32dea94e7110bde8ccc86621a2b98ffe6.txt | 48 + ...ecf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt | 48 + ...3b089be089313d9b71f9bc677d13b14fabf31e.txt | 48 + ...a2b0f7f25b26215f1edaea1aaf0d327901191c.txt | 48 + ...31c4f461edb6b6e796ca8432ee18a0a301b39a.txt | 48 + ...3a92ff7b0711fd22760220b158845ba2e57720.txt | 7 + ...379969b25b3b3eb107838ab34ad222c3d40663.txt | 48 + ...5709452237c47c7cbdffaa146be03b2e8860f8.txt | 48 + ...c16b3192f29564bfbcd64405a3136719b8ce3c.txt | 48 + ...ede4f688c88b2c08007743b79b087f55648258.txt | 48 + ...0f1a3d9caa6662cb7f24cca1a942003db0156d.txt | 48 + ...4742f73e761c5f138aad89d27caed039565d1a.txt | 7 + ...aeff3e473a731bc92f6f2ce2410f4180b5c163.txt | 48 + ...b4d93d4e04855d20909600a0d074ccb2ed4611.txt | 48 + ...3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt | 48 + ...8315709192bf3eda7c9bc2be1346910b1b517e.txt | 48 + ...88c775663fd78b590e60fae67cc63d0bb2e8f4.txt | 7 + ...2e4aa42d6bb0caf0244616d3a8febb79a02072.txt | 48 + ...4fc8b6effd6620455fb3b51fcd181ff5eee72e.txt | 48 + ...ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt | 48 + ...09b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt | 48 + ...7e5888fd2234f368f99e40a827f00580284241.txt | 48 + ...89e5bca53bce0f88c5325b33ebb7453d53171e.txt | 48 + ...02f825b35cc1885256ad00679db4fbf23a3c9d.txt | 48 + ...153db99e88be31fbbef7d95651faa8da1e512d.txt | 48 + ...27e49fa335cd09872d71d80e7015edc0309cf7.txt | 48 + ...aab14572516b4d365bfcbc003379e336a79c0b.txt | 48 + ...9f413256b96f30f453566a1b4a64e4d00fb06d.txt | 48 + ...bc2e5cb76f71b0fc93523430b9867c72237e64.txt | 7 + ...c1c11d31a59c57f04599ecd0f4214c47075f4e.txt | 48 + ...fb29881175d5696515be66cccf1ea9b095d2be.txt | 48 + ...1bf1c6058a1292ee80592733c87130294252fe.txt | 48 + ...4086b2fe57ccd9a3dff399376cf66a9c753ba4.txt | 48 + ...79b37459ec50e499dcc1734d38fe08eb70f4d9.txt | 48 + ...a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt | 48 + ...dd276146c9a4f5d54782096eeee007b12e8fb5.txt | 48 + ...b15350e68d595638e6a7b5918d37fe5209b0d1.txt | 48 + ...0d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt | 48 + ...88e2d6c3c11fb59f0d4c70b57122722b202729.txt | 48 + ...cb7dec677f4a49de71e626ed0cdc36cf466d75.txt | 7 + ...df5af7de9ae29844dedac1e8068cda313132c3.txt | 48 + ...03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt | 48 + ...1bad9ede204cbcaf22ae227319f98635350c8f.txt | 48 + ...7b00410dd70f1ef49b8cee3eda71abda55a094.txt | 7 + ...d45eb3919365d2234c69fe3c822ca0463bb16d.txt | 48 + ...f066c5238870fe52ca924795148f82bcf76a81.txt | 48 + ...3ceaf79588bd25a04d012ea9f62f69b3147f65.txt | 48 + ...b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt | 48 + ...5f1b260332cbb3a377993270710c055cab07a5.txt | 7 + ...14e9bacbb529d77179ce70115eb96e3390ece8.txt | 48 + ...30bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt | 48 + ...a911370c42ab769501a69e1de57f832a4c8c58.txt | 48 + ...90e2b7968179440b55e72e170c6d618d09ceea.txt | 7 + ...ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt | 48 + ...c03fce474fe62166871bd538e0a8e900045acf.txt | 48 + ...18f4ab5295f304cc643e21aa7a82b6e30ba21e.txt | 48 + ...b170bb8c985e6abee8cdadc4591c25d59bffe8.txt | 48 + ...c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt | 48 + ...f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt | 48 + ...4d1d15ceb2199297b2c9f581e06da7c6765f57.txt | 48 + ...a6c556fd19e0898078bf99d105835d6b817c32.txt | 48 + ...aa642317262daf0bb3b5daf0e16f47fa54df42.txt | 48 + ...1b27c2e423a4822996d07a827190a205ac647a.txt | 48 + ...0042da8466e5dc21a11a2cb96c3d21c1071f09.txt | 48 + ...58d0a4397166e63b19ece97c4a95ddb1c51315.txt | 48 + ...5d558addacea96e02861eb25ec57a34d279064.txt | 48 + ...dd2926c54be476cccf4117a73bb751319a0cf8.txt | 48 + ...23aa59ba738ab8a2f71713088a49f77b2d758e.txt | 48 + ...306a61229c3d7d83d74c7d1a2face3c51fcc70.txt | 48 + ...64b2e035986d58715081e661615d0c8f2fa6a7.txt | 48 + ...b36a40859e2c6368e4ec1c01205b230ed11180.txt | 48 + ...dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt | 48 + ...05b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt | 48 + ...1802d1174f865bc470418ea84dae9c3d2df382.txt | 7 + ...5c826fb59c52307c5b9b3dec1480b538830566.txt | 48 + ...7f5a7f5cb056f7d01fde115d43244ba0075468.txt | 7 + ...6f66a92942bd703e2354b002aea625268138ab.txt | 7 + ...9ea318616cf24ca79a747f69c9eccac4a2427f.txt | 48 + ...8c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt | 48 + ...06af854809196eb5688ca158e41cfbf5cde2db.txt | 48 + ...70cd3b992411d872bc7946f0e905acdffceee2.txt | 48 + ...ecc97f8a487a9fb9366d286e1cea515c914a98.txt | 48 + ...60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt | 48 + ...9b06c559c03438402efadb55b5d5d1162fdcfc.txt | 7 + ...6c34949316c9546034ef67390543a4a725a64b.txt | 48 + ...1432ef7fa123208205b693f08767e1bbcbb98e.txt | 48 + ...4437405003dfbd61c14ae2d8deac27809467bb.txt | 48 + ...644445572b5322612e2f10dd972b71fa46505a.txt | 48 + ...4c0396e8c254265bcc9a7c702cab9c88df00d6.txt | 48 + ...bcd551dea1c1a407d13ef2c5b6de248077c09c.txt | 48 + ...c135ed6cb79cffa86391c680b1d4d072e95ff2.txt | 48 + ...61cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt | 48 + ...cc3b3e46d9a72bf92ab99f199db18b1105927c.txt | 48 + ...87f8d40d678f18669d957fcb15f65fdc51c1bb.txt | 7 + ...b71f9a689fdb93526a37f32bb00684de2ab1fc.txt | 48 + ...0d35fca0fc55e00c998de3cf02138684d02c1e.txt | 48 + ...1fbfff733526eaef18b487fbc61675aed1ce90.txt | 7 + ...815c6275474688987f46fc84e177dfe1553a1b.txt | 48 + .../llvm-ir/CHStone_test/float64_div.ll | 2148 +++ .../llvm-ir/CHStone_test/float64_mul.ll | 1597 ++ .../newton/llvm-ir/Include/stm32f303xc.h | 13485 ++++++++++++++++ .../newton/llvm-ir/Include/stm32f3xx.h | 280 + .../newton/llvm-ir/Include/system_stm32f3xx.h | 104 + applications/newton/llvm-ir/LTO/LTO.sh | 92 + .../newton/llvm-ir/LTO/MadgwickAHRS.h | 29 + .../newton/llvm-ir/LTO/MadgwickAHRSupdate.c | 122 + .../newton/llvm-ir/LTO/MadgwickAHRSupdate.h | 29 + applications/newton/llvm-ir/LTO/MadgwickIMU.c | 84 + applications/newton/llvm-ir/LTO/MadgwickIMU.h | 20 + .../newton/llvm-ir/LTO/MadgwickMath.c | 12 + .../newton/llvm-ir/LTO/MadgwickMath.h | 7 + applications/newton/llvm-ir/MahonyAHRS.sh | 59 + .../newton/llvm-ir/MahonyAHRS_original.sh | 0 applications/newton/llvm-ir/Makefile | 4 +- applications/newton/llvm-ir/SQNR.sh | 67 + applications/newton/llvm-ir/build_and_run.sh | 44 + .../newton/llvm-ir/c-files/Kalman.cpp | 104 + applications/newton/llvm-ir/c-files/Kalman.h | 59 + .../newton/llvm-ir/c-files/MadgwickAHRS.c | 111 +- .../newton/llvm-ir/c-files/MadgwickAHRS.h | 6 +- .../newton/llvm-ir/c-files/MadgwickAHRSDiff.c | 295 + .../newton/llvm-ir/c-files/MadgwickAHRSDiff.h | 40 + .../newton/llvm-ir/c-files/MadgwickAHRSfix.c | 324 +- .../newton/llvm-ir/c-files/MadgwickAHRSfix.h | 38 +- .../llvm-ir/c-files/MadgwickAHRSfixi8.c | 183 + .../llvm-ir/c-files/MadgwickAHRSfixi8.h | 54 + .../newton/llvm-ir/c-files/Madgwick_global.c | 234 + .../newton/llvm-ir/c-files/Madgwick_global.h | 31 + .../newton/llvm-ir/c-files/MahonyAHRS.c | 248 + .../newton/llvm-ir/c-files/MahonyAHRS.h | 34 + applications/newton/llvm-ir/c-files/fft | Bin 0 -> 16400 bytes applications/newton/llvm-ir/c-files/fft.c | 94 + .../newton/llvm-ir/c-files/fixmul_test.c | 18 + .../newton/llvm-ir/c-files/float_mul_test.c | 16 + .../c-files/floating_point_operations.c | 451 + .../c-files/floating_point_operations.h | 23 + .../newton/llvm-ir/c-files/kalman_core.c | 812 + .../newton/llvm-ir/c-files/kalman_core.h | 170 + .../newton/llvm-ir/c-files/sensfusion6.c | 248 + .../newton/llvm-ir/c-files/sensfusion6.h | 30 + .../newton/llvm-ir/c-files/test_Kalman.c | 27 + .../llvm-ir/c-files/test_MadgwickAHRS.c | 341 + .../llvm-ir/c-files/test_MadgwickAHRSfix.c | 421 + .../newton/llvm-ir/c-files/test_Original.c | 393 + .../newton/llvm-ir/c-files/test_fixmul.c | 91 + .../c-files/test_floating_point_operations.c | 264 + .../newton/llvm-ir/c-files/test_sensfusion6.c | 308 + .../newton/llvm-ir/euler_angle_error.py | 75 + applications/newton/llvm-ir/fft.sh | 28 + applications/newton/llvm-ir/fracq.py | 59 + applications/newton/llvm-ir/input.csv | 1001 ++ applications/newton/llvm-ir/kalman.sh | 28 + .../newton/llvm-ir/original_compile.sh | 27 + .../newton/llvm-ir/performance_test/Makefile | 34 +- .../llvm-ir/performance_test/auto_test.cpp | 554 +- .../performance_test/auto_test_woquant.cpp | 564 + .../llvm-ir/performance_test/bar_plot.py | 505 +- .../newton/llvm-ir/performance_test/main.c | 2 +- .../llvm-ir/performance_test/quant_bar.py | 209 + applications/newton/llvm-ir/plot_sqnr.py | 50 + applications/newton/llvm-ir/replace.py | 34 + applications/newton/llvm-ir/replace.sh | 32 + applications/newton/llvm-ir/rms_error.py | 153 + applications/newton/llvm-ir/run.sh | 31 + applications/newton/llvm-ir/stm32f303xc.h | 13485 ++++++++++++++++ applications/newton/llvm-ir/syscalls.c | 60 + .../newton/llvm-ir/system_stm32f3xx.c | 287 + applications/newton/llvm-ir/test.sh | 27 + applications/newton/llvm-ir/testMadgwick.sh | 68 + .../newton/llvm-ir/testMadgwickfix.sh | 38 + applications/newton/llvm-ir/testOriginal.sh | 25 + .../newton/llvm-ir/testSensfusion6.sh | 68 + applications/newton/llvm-ir/test_madgwick.c | 269 - applications/newton/sensors/BMX055.nt | 35 +- src/common/common-data-structures.h | 3 + src/newton/Makefile | 54 +- src/newton/Range.h | 65 + src/newton/SimplePass.cpp | 5 + src/newton/SimplePass.h | 33 + src/newton/config.h | 16 + src/newton/myRangeAnalysis.cpp | 921 ++ src/newton/myRangeAnalysis.h | 12 + ...ton-irPass-LLVMIR-constantSubstitution.cpp | 24 + .../newton-irPass-LLVMIR-dequantization.h | 26 + .../newton-irPass-LLVMIR-optimizeByRange.cpp | 1136 +- .../newton-irPass-LLVMIR-optimizeByRange.h | 66 +- .../newton-irPass-LLVMIR-quantization.cpp | 3109 +++- .../newton-irPass-LLVMIR-quantization.h | 84 +- src/newton/newton-irPass-sensors.c | 25 + src/newton/runPass.cpp | 45 + 266 files changed, 52695 insertions(+), 1819 deletions(-) create mode 100644 analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt create mode 100644 analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt create mode 100644 analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt create mode 100644 analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt create mode 100644 analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt create mode 100644 analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt create mode 100644 analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt create mode 100644 analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt create mode 100644 analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt create mode 100644 analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt create mode 100644 analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt create mode 100644 analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt create mode 100644 analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt create mode 100644 analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt create mode 100644 analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt create mode 100644 analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt create mode 100644 analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt create mode 100644 analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt create mode 100644 analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt create mode 100644 analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt create mode 100644 analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt create mode 100644 analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt create mode 100644 analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt create mode 100644 analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt create mode 100644 analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt create mode 100644 analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt create mode 100644 analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt create mode 100644 analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt create mode 100644 analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt create mode 100644 analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt create mode 100644 analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt create mode 100644 analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt create mode 100644 analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt create mode 100644 analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt create mode 100644 analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt create mode 100644 analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt create mode 100644 analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt create mode 100644 analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt create mode 100644 analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt create mode 100644 analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt create mode 100644 analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt create mode 100644 analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt create mode 100644 analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt create mode 100644 analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt create mode 100644 analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt create mode 100644 analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt create mode 100644 analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt create mode 100644 analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt create mode 100644 analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt create mode 100644 analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt create mode 100644 analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt create mode 100644 analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt create mode 100644 analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt create mode 100644 analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt create mode 100644 analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt create mode 100644 analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt create mode 100644 analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt create mode 100644 analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt create mode 100644 analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt create mode 100644 analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt create mode 100644 analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt create mode 100644 analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt create mode 100644 analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt create mode 100644 analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt create mode 100644 analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt create mode 100644 analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt create mode 100644 analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt create mode 100644 analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt create mode 100644 analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt create mode 100644 analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt create mode 100644 analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt create mode 100644 analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt create mode 100644 analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt create mode 100644 analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt create mode 100644 analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt create mode 100644 analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt create mode 100644 analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt create mode 100644 analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt create mode 100644 analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt create mode 100644 analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt create mode 100644 analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt create mode 100644 analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt create mode 100644 analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt create mode 100644 analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt create mode 100644 analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt create mode 100644 analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt create mode 100644 analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt create mode 100644 analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt create mode 100644 analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt create mode 100644 analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt create mode 100644 analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt create mode 100644 analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt create mode 100644 analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt create mode 100644 analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt create mode 100644 analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt create mode 100644 analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt create mode 100644 analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt create mode 100644 analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt create mode 100644 analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt create mode 100644 analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt create mode 100644 analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt create mode 100644 analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt create mode 100644 analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt create mode 100644 analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt create mode 100644 analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt create mode 100644 analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt create mode 100644 analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt create mode 100644 analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt create mode 100644 analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt create mode 100644 analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt create mode 100644 analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt create mode 100644 analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt create mode 100644 analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt create mode 100644 analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt create mode 100644 analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt create mode 100644 analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt create mode 100644 analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt create mode 100644 analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt create mode 100644 analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt create mode 100644 analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt create mode 100644 analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt create mode 100644 analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt create mode 100644 analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt create mode 100644 analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt create mode 100644 analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt create mode 100644 analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt create mode 100644 analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt create mode 100644 analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt create mode 100644 analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt create mode 100644 analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt create mode 100644 analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt create mode 100644 analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt create mode 100644 analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt create mode 100644 analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt create mode 100644 analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt create mode 100644 analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt create mode 100644 analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt create mode 100644 analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt create mode 100644 analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt create mode 100644 analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt create mode 100644 analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt create mode 100644 analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt create mode 100644 analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt create mode 100644 analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt create mode 100644 analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt create mode 100644 analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt create mode 100644 analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt create mode 100644 analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt create mode 100644 analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt create mode 100644 analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt create mode 100644 analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt create mode 100644 analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt create mode 100644 analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt create mode 100644 analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt create mode 100644 analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt create mode 100644 analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt create mode 100644 analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt create mode 100644 analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt create mode 100644 analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt create mode 100644 analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt create mode 100644 analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt create mode 100644 analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt create mode 100644 analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt create mode 100644 analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt create mode 100644 analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt create mode 100644 analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt create mode 100644 analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt create mode 100644 analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt create mode 100644 analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt create mode 100644 analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt create mode 100644 analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt create mode 100644 analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt create mode 100644 analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt create mode 100644 analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt create mode 100644 applications/newton/llvm-ir/CHStone_test/float64_div.ll create mode 100644 applications/newton/llvm-ir/CHStone_test/float64_mul.ll create mode 100644 applications/newton/llvm-ir/Include/stm32f303xc.h create mode 100644 applications/newton/llvm-ir/Include/stm32f3xx.h create mode 100644 applications/newton/llvm-ir/Include/system_stm32f3xx.h create mode 100755 applications/newton/llvm-ir/LTO/LTO.sh create mode 100644 applications/newton/llvm-ir/LTO/MadgwickAHRS.h create mode 100644 applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c create mode 100644 applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h create mode 100644 applications/newton/llvm-ir/LTO/MadgwickIMU.c create mode 100644 applications/newton/llvm-ir/LTO/MadgwickIMU.h create mode 100644 applications/newton/llvm-ir/LTO/MadgwickMath.c create mode 100644 applications/newton/llvm-ir/LTO/MadgwickMath.h create mode 100755 applications/newton/llvm-ir/MahonyAHRS.sh create mode 100644 applications/newton/llvm-ir/MahonyAHRS_original.sh create mode 100755 applications/newton/llvm-ir/SQNR.sh create mode 100755 applications/newton/llvm-ir/build_and_run.sh create mode 100644 applications/newton/llvm-ir/c-files/Kalman.cpp create mode 100644 applications/newton/llvm-ir/c-files/Kalman.h create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c create mode 100644 applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h create mode 100644 applications/newton/llvm-ir/c-files/Madgwick_global.c create mode 100644 applications/newton/llvm-ir/c-files/Madgwick_global.h create mode 100644 applications/newton/llvm-ir/c-files/MahonyAHRS.c create mode 100644 applications/newton/llvm-ir/c-files/MahonyAHRS.h create mode 100755 applications/newton/llvm-ir/c-files/fft create mode 100644 applications/newton/llvm-ir/c-files/fft.c create mode 100644 applications/newton/llvm-ir/c-files/fixmul_test.c create mode 100644 applications/newton/llvm-ir/c-files/float_mul_test.c create mode 100644 applications/newton/llvm-ir/c-files/floating_point_operations.c create mode 100644 applications/newton/llvm-ir/c-files/floating_point_operations.h create mode 100644 applications/newton/llvm-ir/c-files/kalman_core.c create mode 100644 applications/newton/llvm-ir/c-files/kalman_core.h create mode 100644 applications/newton/llvm-ir/c-files/sensfusion6.c create mode 100644 applications/newton/llvm-ir/c-files/sensfusion6.h create mode 100644 applications/newton/llvm-ir/c-files/test_Kalman.c create mode 100644 applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c create mode 100644 applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c create mode 100644 applications/newton/llvm-ir/c-files/test_Original.c create mode 100644 applications/newton/llvm-ir/c-files/test_fixmul.c create mode 100644 applications/newton/llvm-ir/c-files/test_floating_point_operations.c create mode 100644 applications/newton/llvm-ir/c-files/test_sensfusion6.c create mode 100644 applications/newton/llvm-ir/euler_angle_error.py create mode 100755 applications/newton/llvm-ir/fft.sh create mode 100644 applications/newton/llvm-ir/fracq.py create mode 100644 applications/newton/llvm-ir/input.csv create mode 100755 applications/newton/llvm-ir/kalman.sh create mode 100755 applications/newton/llvm-ir/original_compile.sh create mode 100644 applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp create mode 100644 applications/newton/llvm-ir/performance_test/quant_bar.py create mode 100644 applications/newton/llvm-ir/plot_sqnr.py create mode 100644 applications/newton/llvm-ir/replace.py create mode 100755 applications/newton/llvm-ir/replace.sh create mode 100644 applications/newton/llvm-ir/rms_error.py create mode 100755 applications/newton/llvm-ir/run.sh create mode 100644 applications/newton/llvm-ir/stm32f303xc.h create mode 100644 applications/newton/llvm-ir/syscalls.c create mode 100644 applications/newton/llvm-ir/system_stm32f3xx.c create mode 100755 applications/newton/llvm-ir/test.sh create mode 100755 applications/newton/llvm-ir/testMadgwick.sh create mode 100755 applications/newton/llvm-ir/testMadgwickfix.sh create mode 100755 applications/newton/llvm-ir/testOriginal.sh create mode 100755 applications/newton/llvm-ir/testSensfusion6.sh delete mode 100644 applications/newton/llvm-ir/test_madgwick.c create mode 100644 src/newton/Range.h create mode 100644 src/newton/SimplePass.cpp create mode 100644 src/newton/SimplePass.h create mode 100644 src/newton/config.h create mode 100644 src/newton/myRangeAnalysis.cpp create mode 100644 src/newton/myRangeAnalysis.h create mode 100644 src/newton/newton-irPass-LLVMIR-dequantization.h create mode 100644 src/newton/runPass.cpp diff --git a/analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt b/analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt new file mode 100644 index 000000000..95e7259e8 --- /dev/null +++ b/analysis/statistics/0067a50a9ae7f94b11f311a9a771c92ab245c54c.txt @@ -0,0 +1,48 @@ + +changeset: 1708:0067a50a9ae7f94b11f311a9a771c92ab245c54c +char kNewtonVersion[] = "0.3-alpha-1708 (0067a50a9ae7f94b11f311a9a771c92ab245c54c) (build 11-13-2024-20:39-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt b/analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt new file mode 100644 index 000000000..680ffbbdd --- /dev/null +++ b/analysis/statistics/02d48a7bb86d996d9ebb98ad6e499badeabdc76a.txt @@ -0,0 +1,48 @@ + +changeset: 1770:02d48a7bb86d996d9ebb98ad6e499badeabdc76a +char kNewtonVersion[] = "0.3-alpha-1770 (02d48a7bb86d996d9ebb98ad6e499badeabdc76a) (build 05-27-2025-00:38-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt b/analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt new file mode 100644 index 000000000..5103a87b9 --- /dev/null +++ b/analysis/statistics/03212c7b5b21a987cda580414026d64e97a3ebf5.txt @@ -0,0 +1,48 @@ + +changeset: 1647:03212c7b5b21a987cda580414026d64e97a3ebf5 +char kNewtonVersion[] = "0.3-alpha-1647 (03212c7b5b21a987cda580414026d64e97a3ebf5) (build 08-09-2024-22:22-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt b/analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt new file mode 100644 index 000000000..d98e9a8df --- /dev/null +++ b/analysis/statistics/035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8.txt @@ -0,0 +1,48 @@ + +changeset: 1670:035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8 +char kNewtonVersion[] = "0.3-alpha-1670 (035ce93afdb34d4e6bfc3b6f2765cd3921b6f2f8) (build 08-27-2024-13:52-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt b/analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt new file mode 100644 index 000000000..56e57764b --- /dev/null +++ b/analysis/statistics/037273f277374e0717f7025a54ac65f8874e5123.txt @@ -0,0 +1,48 @@ + +changeset: 1718:037273f277374e0717f7025a54ac65f8874e5123 +char kNewtonVersion[] = "0.3-alpha-1718 (037273f277374e0717f7025a54ac65f8874e5123) (build 11-22-2024-10:39-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt b/analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt new file mode 100644 index 000000000..b4496c781 --- /dev/null +++ b/analysis/statistics/066f6fdad1a8d2e1d066728dcfa388e3dea8628e.txt @@ -0,0 +1,48 @@ + +changeset: 1621:066f6fdad1a8d2e1d066728dcfa388e3dea8628e +char kNewtonVersion[] = "0.3-alpha-1621 (066f6fdad1a8d2e1d066728dcfa388e3dea8628e) (build 07-09-2024-20:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt b/analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt new file mode 100644 index 000000000..d03925cb9 --- /dev/null +++ b/analysis/statistics/097d5ef1e76af93962500380ad008ec13141c86d.txt @@ -0,0 +1,48 @@ + +changeset: 1737:097d5ef1e76af93962500380ad008ec13141c86d +char kNewtonVersion[] = "0.3-alpha-1737 (097d5ef1e76af93962500380ad008ec13141c86d) (build 02-25-2025-11:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt b/analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt new file mode 100644 index 000000000..98edcbba2 --- /dev/null +++ b/analysis/statistics/0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d.txt @@ -0,0 +1,48 @@ + +changeset: 1757:0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d +char kNewtonVersion[] = "0.3-alpha-1757 (0991ab3d819f2ffbb47d4cb40f8e3e2bc428a83d) (build 05-05-2025-15:54-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt b/analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt new file mode 100644 index 000000000..b2f47712d --- /dev/null +++ b/analysis/statistics/0b5aaf8e012c49df771e7f337ec6b6d74a6c5166.txt @@ -0,0 +1,48 @@ + +changeset: 1729:0b5aaf8e012c49df771e7f337ec6b6d74a6c5166 +char kNewtonVersion[] = "0.3-alpha-1729 (0b5aaf8e012c49df771e7f337ec6b6d74a6c5166) (build 02-05-2025-13:16-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt b/analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt new file mode 100644 index 000000000..b1763d988 --- /dev/null +++ b/analysis/statistics/0e74e165e743d19e41942ad7778f7a9f6d76ceec.txt @@ -0,0 +1,48 @@ + +changeset: 1600:0e74e165e743d19e41942ad7778f7a9f6d76ceec +char kNewtonVersion[] = "0.3-alpha-1600 (0e74e165e743d19e41942ad7778f7a9f6d76ceec) (build 06-09-2024-11:40-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt b/analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt new file mode 100644 index 000000000..03410cc22 --- /dev/null +++ b/analysis/statistics/0f8c66f5f08f625687e7b85387c410cc14fc2e8a.txt @@ -0,0 +1,7 @@ + +changeset: 1606:0f8c66f5f08f625687e7b85387c410cc14fc2e8a +char kNewtonVersion[] = "0.3-alpha-1606 (0f8c66f5f08f625687e7b85387c410cc14fc2e8a) (build 06-17-2024-14:32-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt b/analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt new file mode 100644 index 000000000..e37cbb806 --- /dev/null +++ b/analysis/statistics/1060ebed7ea85969a8ea83c441a96d30d756ebc6.txt @@ -0,0 +1,48 @@ + +changeset: 1641:1060ebed7ea85969a8ea83c441a96d30d756ebc6 +char kNewtonVersion[] = "0.3-alpha-1641 (1060ebed7ea85969a8ea83c441a96d30d756ebc6) (build 07-27-2024-10:35-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt b/analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt new file mode 100644 index 000000000..7b522f36b --- /dev/null +++ b/analysis/statistics/12d68d7c7d92a4b59d5d116fbc576b285e4e255e.txt @@ -0,0 +1,48 @@ + +changeset: 1678:12d68d7c7d92a4b59d5d116fbc576b285e4e255e +char kNewtonVersion[] = "0.3-alpha-1678 (12d68d7c7d92a4b59d5d116fbc576b285e4e255e) (build 08-29-2024-18:29-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt b/analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt new file mode 100644 index 000000000..7d93af047 --- /dev/null +++ b/analysis/statistics/133584b2775487f43b4ea4e659e29799241fd93b.txt @@ -0,0 +1,48 @@ + +changeset: 1750:133584b2775487f43b4ea4e659e29799241fd93b +char kNewtonVersion[] = "0.3-alpha-1750 (133584b2775487f43b4ea4e659e29799241fd93b) (build 04-25-2025-20:36-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt b/analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt new file mode 100644 index 000000000..a9fbb0e45 --- /dev/null +++ b/analysis/statistics/1401b11217ed30b60e34051bffa718f2bbb8c551.txt @@ -0,0 +1,48 @@ + +changeset: 1691:1401b11217ed30b60e34051bffa718f2bbb8c551 +char kNewtonVersion[] = "0.3-alpha-1691 (1401b11217ed30b60e34051bffa718f2bbb8c551) (build 09-29-2024-22:47-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt b/analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt new file mode 100644 index 000000000..b02a63f08 --- /dev/null +++ b/analysis/statistics/14d2e5a37fd670198ad313cd3adca507275521ba.txt @@ -0,0 +1,48 @@ + +changeset: 1689:14d2e5a37fd670198ad313cd3adca507275521ba +char kNewtonVersion[] = "0.3-alpha-1689 (14d2e5a37fd670198ad313cd3adca507275521ba) (build 09-29-2024-22:44-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt b/analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt new file mode 100644 index 000000000..e6c77574d --- /dev/null +++ b/analysis/statistics/14d65a4be09b5334ba88b40e1451c37217ba3971.txt @@ -0,0 +1,48 @@ + +changeset: 1704:14d65a4be09b5334ba88b40e1451c37217ba3971 +char kNewtonVersion[] = "0.3-alpha-1704 (14d65a4be09b5334ba88b40e1451c37217ba3971) (build 10-09-2024-16:36-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt b/analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt new file mode 100644 index 000000000..863cdad12 --- /dev/null +++ b/analysis/statistics/1588a178980fdedaaec03ec6d9876db3a96aed46.txt @@ -0,0 +1,48 @@ + +changeset: 1650:1588a178980fdedaaec03ec6d9876db3a96aed46 +char kNewtonVersion[] = "0.3-alpha-1650 (1588a178980fdedaaec03ec6d9876db3a96aed46) (build 08-13-2024-23:18-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt b/analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt new file mode 100644 index 000000000..d833b1d7c --- /dev/null +++ b/analysis/statistics/177de14acdebe0b852e417df322fa55174bec455.txt @@ -0,0 +1,48 @@ + +changeset: 1722:177de14acdebe0b852e417df322fa55174bec455 +char kNewtonVersion[] = "0.3-alpha-1722 (177de14acdebe0b852e417df322fa55174bec455) (build 01-16-2025-19:18-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt b/analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt new file mode 100644 index 000000000..8c95fd6a4 --- /dev/null +++ b/analysis/statistics/1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b.txt @@ -0,0 +1,48 @@ + +changeset: 1773:1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b +char kNewtonVersion[] = "0.3-alpha-1773 (1832672f492a8f7a5cd1c50b3d8c75e66d7a8a2b) (build 05-28-2025-21:01-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt b/analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt new file mode 100644 index 000000000..38de37e8a --- /dev/null +++ b/analysis/statistics/189b2e7e3b38992fa89085f297617e6af24702c7.txt @@ -0,0 +1,48 @@ + +changeset: 1745:189b2e7e3b38992fa89085f297617e6af24702c7 +char kNewtonVersion[] = "0.3-alpha-1745 (189b2e7e3b38992fa89085f297617e6af24702c7) (build 03-23-2025-22:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt b/analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt new file mode 100644 index 000000000..e5967de02 --- /dev/null +++ b/analysis/statistics/18a293dead7ede85f57ab0abe37fb28880fefc54.txt @@ -0,0 +1,48 @@ + +changeset: 1769:18a293dead7ede85f57ab0abe37fb28880fefc54 +char kNewtonVersion[] = "0.3-alpha-1769 (18a293dead7ede85f57ab0abe37fb28880fefc54) (build 05-27-2025-00:27-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt b/analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt new file mode 100644 index 000000000..06e0e47d0 --- /dev/null +++ b/analysis/statistics/19e714f0f985ac7d8b0ef5e53b76767c9cd3854e.txt @@ -0,0 +1,7 @@ + +changeset: 1607:19e714f0f985ac7d8b0ef5e53b76767c9cd3854e +char kNewtonVersion[] = "0.3-alpha-1607 (19e714f0f985ac7d8b0ef5e53b76767c9cd3854e) (build 06-17-2024-14:41-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt b/analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt new file mode 100644 index 000000000..605be0497 --- /dev/null +++ b/analysis/statistics/1d8eb5741532f733eafc4bfcb93801bfdc260f25.txt @@ -0,0 +1,7 @@ + +changeset: 1608:1d8eb5741532f733eafc4bfcb93801bfdc260f25 +char kNewtonVersion[] = "0.3-alpha-1608 (1d8eb5741532f733eafc4bfcb93801bfdc260f25) (build 06-17-2024-14:42-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt b/analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt new file mode 100644 index 000000000..eccdf4985 --- /dev/null +++ b/analysis/statistics/1de2a17c4727c42120c3d791515eabc082b76067.txt @@ -0,0 +1,48 @@ + +changeset: 1743:1de2a17c4727c42120c3d791515eabc082b76067 +char kNewtonVersion[] = "0.3-alpha-1743 (1de2a17c4727c42120c3d791515eabc082b76067) (build 03-08-2025-12:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt b/analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt new file mode 100644 index 000000000..6078e1019 --- /dev/null +++ b/analysis/statistics/1e43b79ea3957b991e9baf2ad059f6488e687ea1.txt @@ -0,0 +1,48 @@ + +changeset: 1645:1e43b79ea3957b991e9baf2ad059f6488e687ea1 +char kNewtonVersion[] = "0.3-alpha-1645 (1e43b79ea3957b991e9baf2ad059f6488e687ea1) (build 08-05-2024-15:52-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt b/analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt new file mode 100644 index 000000000..7224b8f67 --- /dev/null +++ b/analysis/statistics/2124d4e633cfb39b0cd4cb845a10b6113ae2a536.txt @@ -0,0 +1,7 @@ + +changeset: 1604:2124d4e633cfb39b0cd4cb845a10b6113ae2a536 +char kNewtonVersion[] = "0.3-alpha-1604 (2124d4e633cfb39b0cd4cb845a10b6113ae2a536) (build 06-17-2024-14:18-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt b/analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt new file mode 100644 index 000000000..4eed7dd8e --- /dev/null +++ b/analysis/statistics/223a41a57e9c7de1fc56eb069a7f0f64e632dec4.txt @@ -0,0 +1,48 @@ + +changeset: 1633:223a41a57e9c7de1fc56eb069a7f0f64e632dec4 +char kNewtonVersion[] = "0.3-alpha-1633 (223a41a57e9c7de1fc56eb069a7f0f64e632dec4) (build 07-23-2024-17:28-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt b/analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt new file mode 100644 index 000000000..fccc0b750 --- /dev/null +++ b/analysis/statistics/22cb3a8af33722eaeb5c723fb55dd826c3b8d13c.txt @@ -0,0 +1,48 @@ + +changeset: 1618:22cb3a8af33722eaeb5c723fb55dd826c3b8d13c +char kNewtonVersion[] = "0.3-alpha-1618 (22cb3a8af33722eaeb5c723fb55dd826c3b8d13c) (build 07-06-2024-21:36-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt b/analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt new file mode 100644 index 000000000..360cf534b --- /dev/null +++ b/analysis/statistics/27d764c13c3991109adcd534a7a8a5fe25e8154d.txt @@ -0,0 +1,48 @@ + +changeset: 1648:27d764c13c3991109adcd534a7a8a5fe25e8154d +char kNewtonVersion[] = "0.3-alpha-1648 (27d764c13c3991109adcd534a7a8a5fe25e8154d) (build 08-10-2024-22:32-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt b/analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt new file mode 100644 index 000000000..520011c41 --- /dev/null +++ b/analysis/statistics/27e6a6612f474a6aa090e43f29b9313f0ef7e8e2.txt @@ -0,0 +1,7 @@ + +changeset: 1613:27e6a6612f474a6aa090e43f29b9313f0ef7e8e2 +char kNewtonVersion[] = "0.3-alpha-1613 (27e6a6612f474a6aa090e43f29b9313f0ef7e8e2) (build 06-17-2024-15:04-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt b/analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt new file mode 100644 index 000000000..27336608b --- /dev/null +++ b/analysis/statistics/2a56f8cead1efca3bcef193fe76a2d9a3dac4463.txt @@ -0,0 +1,7 @@ + +changeset: 1595:2a56f8cead1efca3bcef193fe76a2d9a3dac4463 +char kNewtonVersion[] = "0.3-alpha-1595 (2a56f8cead1efca3bcef193fe76a2d9a3dac4463) (build 06-09-2024-11:20-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt b/analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt new file mode 100644 index 000000000..ca3a2074b --- /dev/null +++ b/analysis/statistics/2b00557e8f76980e0bd2de795d669f5f24606104.txt @@ -0,0 +1,48 @@ + +changeset: 1695:2b00557e8f76980e0bd2de795d669f5f24606104 +char kNewtonVersion[] = "0.3-alpha-1695 (2b00557e8f76980e0bd2de795d669f5f24606104) (build 09-30-2024-11:49-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt b/analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt new file mode 100644 index 000000000..d5d534b86 --- /dev/null +++ b/analysis/statistics/2de0b2082ea8d7560ef1776838debac2236b308b.txt @@ -0,0 +1,48 @@ + +changeset: 1632:2de0b2082ea8d7560ef1776838debac2236b308b +char kNewtonVersion[] = "0.3-alpha-1632 (2de0b2082ea8d7560ef1776838debac2236b308b) (build 07-18-2024-15:49-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt b/analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt new file mode 100644 index 000000000..0f26162a7 --- /dev/null +++ b/analysis/statistics/2f260ad3f710627bd8d432d40a897b7a5239516e.txt @@ -0,0 +1,48 @@ + +changeset: 1655:2f260ad3f710627bd8d432d40a897b7a5239516e +char kNewtonVersion[] = "0.3-alpha-1655 (2f260ad3f710627bd8d432d40a897b7a5239516e) (build 08-19-2024-21:59-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt b/analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt new file mode 100644 index 000000000..41a11c4df --- /dev/null +++ b/analysis/statistics/35547345a1f6a7ae94dc279486ae5a33c33e7b2a.txt @@ -0,0 +1,48 @@ + +changeset: 1747:35547345a1f6a7ae94dc279486ae5a33c33e7b2a +char kNewtonVersion[] = "0.3-alpha-1747 (35547345a1f6a7ae94dc279486ae5a33c33e7b2a) (build 03-27-2025-20:59-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt b/analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt new file mode 100644 index 000000000..a63b397ad --- /dev/null +++ b/analysis/statistics/365f482bc78076ef3d9b200117d948b899bba585.txt @@ -0,0 +1,48 @@ + +changeset: 1625:365f482bc78076ef3d9b200117d948b899bba585 +char kNewtonVersion[] = "0.3-alpha-1625 (365f482bc78076ef3d9b200117d948b899bba585) (build 07-12-2024-12:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt b/analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt new file mode 100644 index 000000000..5c2cafe47 --- /dev/null +++ b/analysis/statistics/36a536429b6d7ffbe2f5a3e51cb07392c966f76f.txt @@ -0,0 +1,48 @@ + +changeset: 1679:36a536429b6d7ffbe2f5a3e51cb07392c966f76f +char kNewtonVersion[] = "0.3-alpha-1679 (36a536429b6d7ffbe2f5a3e51cb07392c966f76f) (build 09-02-2024-10:39-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt b/analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt new file mode 100644 index 000000000..e4676d9e3 --- /dev/null +++ b/analysis/statistics/36e47b1b17600eacfd132cde3109992ea24c3c09.txt @@ -0,0 +1,48 @@ + +changeset: 1751:36e47b1b17600eacfd132cde3109992ea24c3c09 +char kNewtonVersion[] = "0.3-alpha-1751 (36e47b1b17600eacfd132cde3109992ea24c3c09) (build 04-28-2025-10:47-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt b/analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt new file mode 100644 index 000000000..f12df194f --- /dev/null +++ b/analysis/statistics/375a659df702ad3153897f1b8036b4fbbeccc7de.txt @@ -0,0 +1,48 @@ + +changeset: 1663:375a659df702ad3153897f1b8036b4fbbeccc7de +char kNewtonVersion[] = "0.3-alpha-1663 (375a659df702ad3153897f1b8036b4fbbeccc7de) (build 08-26-2024-13:24-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt b/analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt new file mode 100644 index 000000000..c418c1322 --- /dev/null +++ b/analysis/statistics/37f720e35e3cfdae69167d152d6069da2199b66c.txt @@ -0,0 +1,48 @@ + +changeset: 1643:37f720e35e3cfdae69167d152d6069da2199b66c +char kNewtonVersion[] = "0.3-alpha-1643 (37f720e35e3cfdae69167d152d6069da2199b66c) (build 08-03-2024-12:21-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt b/analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt new file mode 100644 index 000000000..87986b421 --- /dev/null +++ b/analysis/statistics/38a2089f18acf70a8f55e8433f7d03bc9f992471.txt @@ -0,0 +1,48 @@ + +changeset: 1713:38a2089f18acf70a8f55e8433f7d03bc9f992471 +char kNewtonVersion[] = "0.3-alpha-1713 (38a2089f18acf70a8f55e8433f7d03bc9f992471) (build 11-17-2024-11:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt b/analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt new file mode 100644 index 000000000..c2e198c42 --- /dev/null +++ b/analysis/statistics/38e833cce9ad8f877114760a7900fc0a2085e1f9.txt @@ -0,0 +1,48 @@ + +changeset: 1753:38e833cce9ad8f877114760a7900fc0a2085e1f9 +char kNewtonVersion[] = "0.3-alpha-1753 (38e833cce9ad8f877114760a7900fc0a2085e1f9) (build 04-28-2025-13:17-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt b/analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt new file mode 100644 index 000000000..6f0841dc5 --- /dev/null +++ b/analysis/statistics/3d074aa0fa141b085ce178b6316724d3fcea382f.txt @@ -0,0 +1,48 @@ + +changeset: 1693:3d074aa0fa141b085ce178b6316724d3fcea382f +char kNewtonVersion[] = "0.3-alpha-1693 (3d074aa0fa141b085ce178b6316724d3fcea382f) (build 09-30-2024-10:44-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt b/analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt new file mode 100644 index 000000000..d8bfa95b4 --- /dev/null +++ b/analysis/statistics/3ea3381f0e5bba490bdbbfc408a09f136cc9a272.txt @@ -0,0 +1,48 @@ + +changeset: 1739:3ea3381f0e5bba490bdbbfc408a09f136cc9a272 +char kNewtonVersion[] = "0.3-alpha-1739 (3ea3381f0e5bba490bdbbfc408a09f136cc9a272) (build 02-26-2025-21:49-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt b/analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt new file mode 100644 index 000000000..fac9d0862 --- /dev/null +++ b/analysis/statistics/406938a71d4d9f26283a2a7f3a9b4b5011591d18.txt @@ -0,0 +1,48 @@ + +changeset: 1697:406938a71d4d9f26283a2a7f3a9b4b5011591d18 +char kNewtonVersion[] = "0.3-alpha-1697 (406938a71d4d9f26283a2a7f3a9b4b5011591d18) (build 10-05-2024-12:46-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt b/analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt new file mode 100644 index 000000000..deb1c65f0 --- /dev/null +++ b/analysis/statistics/41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a.txt @@ -0,0 +1,48 @@ + +changeset: 1709:41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a +char kNewtonVersion[] = "0.3-alpha-1709 (41c017603db3a1a8560cb2c56ccdb8beb9ad4d4a) (build 11-15-2024-22:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt b/analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt new file mode 100644 index 000000000..ebf84d579 --- /dev/null +++ b/analysis/statistics/43a23acc2f56fd83f793d72017f63d0798febe2c.txt @@ -0,0 +1,48 @@ + +changeset: 1710:43a23acc2f56fd83f793d72017f63d0798febe2c +char kNewtonVersion[] = "0.3-alpha-1710 (43a23acc2f56fd83f793d72017f63d0798febe2c) (build 11-16-2024-12:52-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt b/analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt new file mode 100644 index 000000000..feba021d7 --- /dev/null +++ b/analysis/statistics/47649b87d74ec0d3c5b7fb1809137bdb38c34682.txt @@ -0,0 +1,48 @@ + +changeset: 1703:47649b87d74ec0d3c5b7fb1809137bdb38c34682 +char kNewtonVersion[] = "0.3-alpha-1703 (47649b87d74ec0d3c5b7fb1809137bdb38c34682) (build 10-09-2024-15:50-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt b/analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt new file mode 100644 index 000000000..f15251c9d --- /dev/null +++ b/analysis/statistics/484908d1897c17fa4e3a54e78371a6e5268c5d46.txt @@ -0,0 +1,48 @@ + +changeset: 1652:484908d1897c17fa4e3a54e78371a6e5268c5d46 +char kNewtonVersion[] = "0.3-alpha-1652 (484908d1897c17fa4e3a54e78371a6e5268c5d46) (build 08-16-2024-15:56-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt b/analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt new file mode 100644 index 000000000..fc8b19b89 --- /dev/null +++ b/analysis/statistics/498a1e350786d8527b315bc202ea91f5b6df797c.txt @@ -0,0 +1,48 @@ + +changeset: 1724:498a1e350786d8527b315bc202ea91f5b6df797c +char kNewtonVersion[] = "0.3-alpha-1724 (498a1e350786d8527b315bc202ea91f5b6df797c) (build 02-03-2025-15:00-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt b/analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt new file mode 100644 index 000000000..63546089a --- /dev/null +++ b/analysis/statistics/49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0.txt @@ -0,0 +1,48 @@ + +changeset: 1659:49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0 +char kNewtonVersion[] = "0.3-alpha-1659 (49a8ea5c099b12d8e1c83ca9dde5ea9af7b40ad0) (build 08-19-2024-22:47-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt b/analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt new file mode 100644 index 000000000..ee5365648 --- /dev/null +++ b/analysis/statistics/4c72375b9c565d124523fd22cda7382fe4dd7614.txt @@ -0,0 +1,48 @@ + +changeset: 1624:4c72375b9c565d124523fd22cda7382fe4dd7614 +char kNewtonVersion[] = "0.3-alpha-1624 (4c72375b9c565d124523fd22cda7382fe4dd7614) (build 07-11-2024-18:58-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt b/analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt new file mode 100644 index 000000000..d1a04ea0b --- /dev/null +++ b/analysis/statistics/4d8766310a7770aba17a61bebddf161d7c15081e.txt @@ -0,0 +1,48 @@ + +changeset: 1699:4d8766310a7770aba17a61bebddf161d7c15081e +char kNewtonVersion[] = "0.3-alpha-1699 (4d8766310a7770aba17a61bebddf161d7c15081e) (build 10-07-2024-11:31-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt b/analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt new file mode 100644 index 000000000..73eaf0ca0 --- /dev/null +++ b/analysis/statistics/50e9d59b8be3d568aca2f34f3b2c75dbcc259f75.txt @@ -0,0 +1,48 @@ + +changeset: 1752:50e9d59b8be3d568aca2f34f3b2c75dbcc259f75 +char kNewtonVersion[] = "0.3-alpha-1752 (50e9d59b8be3d568aca2f34f3b2c75dbcc259f75) (build 04-28-2025-12:46-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt b/analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt new file mode 100644 index 000000000..8cfde0c81 --- /dev/null +++ b/analysis/statistics/525cd662c8513979e52d3f6b82fe08cb5ecf4f32.txt @@ -0,0 +1,48 @@ + +changeset: 1736:525cd662c8513979e52d3f6b82fe08cb5ecf4f32 +char kNewtonVersion[] = "0.3-alpha-1736 (525cd662c8513979e52d3f6b82fe08cb5ecf4f32) (build 02-14-2025-14:01-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt b/analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt new file mode 100644 index 000000000..af5377cee --- /dev/null +++ b/analysis/statistics/545e000a370b93eb4c2db46cce5f4d3e8a1d81b2.txt @@ -0,0 +1,48 @@ + +changeset: 1686:545e000a370b93eb4c2db46cce5f4d3e8a1d81b2 +char kNewtonVersion[] = "0.3-alpha-1686 (545e000a370b93eb4c2db46cce5f4d3e8a1d81b2) (build 09-26-2024-20:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt b/analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt new file mode 100644 index 000000000..4f74cdf03 --- /dev/null +++ b/analysis/statistics/545f1ac697cb8cf197689117358f8eed8787877b.txt @@ -0,0 +1,48 @@ + +changeset: 1772:545f1ac697cb8cf197689117358f8eed8787877b +char kNewtonVersion[] = "0.3-alpha-1772 (545f1ac697cb8cf197689117358f8eed8787877b) (build 05-27-2025-17:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt b/analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt new file mode 100644 index 000000000..d06264c82 --- /dev/null +++ b/analysis/statistics/549d564728d0ccca2e7a79b3ab4387cd5c68e43a.txt @@ -0,0 +1,48 @@ + +changeset: 1617:549d564728d0ccca2e7a79b3ab4387cd5c68e43a +char kNewtonVersion[] = "0.3-alpha-1617 (549d564728d0ccca2e7a79b3ab4387cd5c68e43a) (build 06-26-2024-20:11-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt b/analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt new file mode 100644 index 000000000..a588874a8 --- /dev/null +++ b/analysis/statistics/59838c712a9bd377eb39365bb131d8ec2fd7baed.txt @@ -0,0 +1,48 @@ + +changeset: 1664:59838c712a9bd377eb39365bb131d8ec2fd7baed +char kNewtonVersion[] = "0.3-alpha-1664 (59838c712a9bd377eb39365bb131d8ec2fd7baed) (build 08-26-2024-13:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt b/analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt new file mode 100644 index 000000000..7913d8203 --- /dev/null +++ b/analysis/statistics/61e354ebef3180f38512bf461d40fdbfcf75ec0a.txt @@ -0,0 +1,48 @@ + +changeset: 1758:61e354ebef3180f38512bf461d40fdbfcf75ec0a +char kNewtonVersion[] = "0.3-alpha-1758 (61e354ebef3180f38512bf461d40fdbfcf75ec0a) (build 05-20-2025-16:46-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt b/analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt new file mode 100644 index 000000000..d2545cb63 --- /dev/null +++ b/analysis/statistics/675b2689bdc004f9c12d3f827b1313550af254ba.txt @@ -0,0 +1,48 @@ + +changeset: 1636:675b2689bdc004f9c12d3f827b1313550af254ba +char kNewtonVersion[] = "0.3-alpha-1636 (675b2689bdc004f9c12d3f827b1313550af254ba) (build 07-24-2024-09:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt b/analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt new file mode 100644 index 000000000..150baaf88 --- /dev/null +++ b/analysis/statistics/6771b9a2ea9c18664318972c2e53e9808f432731.txt @@ -0,0 +1,48 @@ + +changeset: 1694:6771b9a2ea9c18664318972c2e53e9808f432731 +char kNewtonVersion[] = "0.3-alpha-1694 (6771b9a2ea9c18664318972c2e53e9808f432731) (build 09-30-2024-10:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt b/analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt new file mode 100644 index 000000000..95a54b545 --- /dev/null +++ b/analysis/statistics/6865fa4b66d77af92c57dbcaa570565ea1256778.txt @@ -0,0 +1,48 @@ + +changeset: 1688:6865fa4b66d77af92c57dbcaa570565ea1256778 +char kNewtonVersion[] = "0.3-alpha-1688 (6865fa4b66d77af92c57dbcaa570565ea1256778) (build 09-27-2024-11:13-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt b/analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt new file mode 100644 index 000000000..a7f3e886a --- /dev/null +++ b/analysis/statistics/6ab3928eeefd23988db60d9401e77c3c1a7e836f.txt @@ -0,0 +1,48 @@ + +changeset: 1714:6ab3928eeefd23988db60d9401e77c3c1a7e836f +char kNewtonVersion[] = "0.3-alpha-1714 (6ab3928eeefd23988db60d9401e77c3c1a7e836f) (build 11-17-2024-12:55-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt b/analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt new file mode 100644 index 000000000..9cf776406 --- /dev/null +++ b/analysis/statistics/6b57f139a0634a2a9d3598739471fc1c70f70a4d.txt @@ -0,0 +1,48 @@ + +changeset: 1733:6b57f139a0634a2a9d3598739471fc1c70f70a4d +char kNewtonVersion[] = "0.3-alpha-1733 (6b57f139a0634a2a9d3598739471fc1c70f70a4d) (build 02-10-2025-21:59-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt b/analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt new file mode 100644 index 000000000..d7d3d8895 --- /dev/null +++ b/analysis/statistics/6ca839299d6958488ea11f28a6d4b76d3d5496b0.txt @@ -0,0 +1,48 @@ + +changeset: 1666:6ca839299d6958488ea11f28a6d4b76d3d5496b0 +char kNewtonVersion[] = "0.3-alpha-1666 (6ca839299d6958488ea11f28a6d4b76d3d5496b0) (build 08-26-2024-14:04-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt b/analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt new file mode 100644 index 000000000..135d808ed --- /dev/null +++ b/analysis/statistics/6edee7a34daf3be6125212d01c917f40dc474170.txt @@ -0,0 +1,48 @@ + +changeset: 1616:6edee7a34daf3be6125212d01c917f40dc474170 +char kNewtonVersion[] = "0.3-alpha-1616 (6edee7a34daf3be6125212d01c917f40dc474170) (build 06-26-2024-17:45-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt b/analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt new file mode 100644 index 000000000..c1494df3b --- /dev/null +++ b/analysis/statistics/6f36179b480bc297449afd40c11c44e3243df14d.txt @@ -0,0 +1,48 @@ + +changeset: 1638:6f36179b480bc297449afd40c11c44e3243df14d +char kNewtonVersion[] = "0.3-alpha-1638 (6f36179b480bc297449afd40c11c44e3243df14d) (build 07-24-2024-16:45-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt b/analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt new file mode 100644 index 000000000..7ca810f04 --- /dev/null +++ b/analysis/statistics/6f611897711928b9b7418dc487a7cb63b3716a7a.txt @@ -0,0 +1,48 @@ + +changeset: 1623:6f611897711928b9b7418dc487a7cb63b3716a7a +char kNewtonVersion[] = "0.3-alpha-1623 (6f611897711928b9b7418dc487a7cb63b3716a7a) (build 07-10-2024-12:43-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt b/analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt new file mode 100644 index 000000000..3401f3bd5 --- /dev/null +++ b/analysis/statistics/7065d908550d4cb4b411d40aaaf27bd642595550.txt @@ -0,0 +1,48 @@ + +changeset: 1687:7065d908550d4cb4b411d40aaaf27bd642595550 +char kNewtonVersion[] = "0.3-alpha-1687 (7065d908550d4cb4b411d40aaaf27bd642595550) (build 09-27-2024-10:21-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt b/analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt new file mode 100644 index 000000000..b37c076b7 --- /dev/null +++ b/analysis/statistics/728ee98b027ece52ff06fb53330989a230eb16d2.txt @@ -0,0 +1,48 @@ + +changeset: 1646:728ee98b027ece52ff06fb53330989a230eb16d2 +char kNewtonVersion[] = "0.3-alpha-1646 (728ee98b027ece52ff06fb53330989a230eb16d2) (build 08-05-2024-21:15-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt b/analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt new file mode 100644 index 000000000..af93ba3eb --- /dev/null +++ b/analysis/statistics/734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5.txt @@ -0,0 +1,7 @@ + +changeset: 1609:734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5 +char kNewtonVersion[] = "0.3-alpha-1609 (734c297bb6d7d3eb51df66ce6d4d88eaf12ed5b5) (build 06-17-2024-14:48-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt b/analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt new file mode 100644 index 000000000..0b2286165 --- /dev/null +++ b/analysis/statistics/778ed32c397fc92a7229a442d6d70dfdf8cea747.txt @@ -0,0 +1,48 @@ + +changeset: 1639:778ed32c397fc92a7229a442d6d70dfdf8cea747 +char kNewtonVersion[] = "0.3-alpha-1639 (778ed32c397fc92a7229a442d6d70dfdf8cea747) (build 07-24-2024-16:52-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt b/analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt new file mode 100644 index 000000000..efb5dd963 --- /dev/null +++ b/analysis/statistics/7869325b7fdaab528356887d1d0eb5b0291012db.txt @@ -0,0 +1,48 @@ + +changeset: 1740:7869325b7fdaab528356887d1d0eb5b0291012db +char kNewtonVersion[] = "0.3-alpha-1740 (7869325b7fdaab528356887d1d0eb5b0291012db) (build 02-27-2025-21:04-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt b/analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt new file mode 100644 index 000000000..e9094dedd --- /dev/null +++ b/analysis/statistics/7bbf84ffed653927aa206d3bfcd3fe4573b67469.txt @@ -0,0 +1,48 @@ + +changeset: 1635:7bbf84ffed653927aa206d3bfcd3fe4573b67469 +char kNewtonVersion[] = "0.3-alpha-1635 (7bbf84ffed653927aa206d3bfcd3fe4573b67469) (build 07-23-2024-22:10-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt b/analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt new file mode 100644 index 000000000..fbdc7ab18 --- /dev/null +++ b/analysis/statistics/7cdceef32dea94e7110bde8ccc86621a2b98ffe6.txt @@ -0,0 +1,48 @@ + +changeset: 1696:7cdceef32dea94e7110bde8ccc86621a2b98ffe6 +char kNewtonVersion[] = "0.3-alpha-1696 (7cdceef32dea94e7110bde8ccc86621a2b98ffe6) (build 10-04-2024-12:54-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt b/analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt new file mode 100644 index 000000000..83bcb972c --- /dev/null +++ b/analysis/statistics/7decf9cf0ed772dcd4e13ee57eb9052ae420f8de.txt @@ -0,0 +1,48 @@ + +changeset: 1620:7decf9cf0ed772dcd4e13ee57eb9052ae420f8de +char kNewtonVersion[] = "0.3-alpha-1620 (7decf9cf0ed772dcd4e13ee57eb9052ae420f8de) (build 07-08-2024-23:21-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt b/analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt new file mode 100644 index 000000000..d9a24de16 --- /dev/null +++ b/analysis/statistics/813b089be089313d9b71f9bc677d13b14fabf31e.txt @@ -0,0 +1,48 @@ + +changeset: 1701:813b089be089313d9b71f9bc677d13b14fabf31e +char kNewtonVersion[] = "0.3-alpha-1701 (813b089be089313d9b71f9bc677d13b14fabf31e) (build 10-07-2024-18:35-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt b/analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt new file mode 100644 index 000000000..7804f9673 --- /dev/null +++ b/analysis/statistics/82a2b0f7f25b26215f1edaea1aaf0d327901191c.txt @@ -0,0 +1,48 @@ + +changeset: 1630:82a2b0f7f25b26215f1edaea1aaf0d327901191c +char kNewtonVersion[] = "0.3-alpha-1630 (82a2b0f7f25b26215f1edaea1aaf0d327901191c) (build 07-17-2024-22:20-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt b/analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt new file mode 100644 index 000000000..aab128afb --- /dev/null +++ b/analysis/statistics/8531c4f461edb6b6e796ca8432ee18a0a301b39a.txt @@ -0,0 +1,48 @@ + +changeset: 1700:8531c4f461edb6b6e796ca8432ee18a0a301b39a +char kNewtonVersion[] = "0.3-alpha-1700 (8531c4f461edb6b6e796ca8432ee18a0a301b39a) (build 10-07-2024-16:26-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt b/analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt new file mode 100644 index 000000000..e6fcb5da6 --- /dev/null +++ b/analysis/statistics/853a92ff7b0711fd22760220b158845ba2e57720.txt @@ -0,0 +1,7 @@ + +changeset: 1598:853a92ff7b0711fd22760220b158845ba2e57720 +char kNewtonVersion[] = "0.3-alpha-1598 (853a92ff7b0711fd22760220b158845ba2e57720) (build 06-09-2024-11:32-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt b/analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt new file mode 100644 index 000000000..d241511e7 --- /dev/null +++ b/analysis/statistics/88379969b25b3b3eb107838ab34ad222c3d40663.txt @@ -0,0 +1,48 @@ + +changeset: 1601:88379969b25b3b3eb107838ab34ad222c3d40663 +char kNewtonVersion[] = "0.3-alpha-1601 (88379969b25b3b3eb107838ab34ad222c3d40663) (build 06-16-2024-13:14-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt b/analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt new file mode 100644 index 000000000..7f93435a6 --- /dev/null +++ b/analysis/statistics/885709452237c47c7cbdffaa146be03b2e8860f8.txt @@ -0,0 +1,48 @@ + +changeset: 1741:885709452237c47c7cbdffaa146be03b2e8860f8 +char kNewtonVersion[] = "0.3-alpha-1741 (885709452237c47c7cbdffaa146be03b2e8860f8) (build 02-27-2025-22:26-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt b/analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt new file mode 100644 index 000000000..6f8b853f8 --- /dev/null +++ b/analysis/statistics/88c16b3192f29564bfbcd64405a3136719b8ce3c.txt @@ -0,0 +1,48 @@ + +changeset: 1754:88c16b3192f29564bfbcd64405a3136719b8ce3c +char kNewtonVersion[] = "0.3-alpha-1754 (88c16b3192f29564bfbcd64405a3136719b8ce3c) (build 05-03-2025-11:43-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt b/analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt new file mode 100644 index 000000000..7b9eccd9f --- /dev/null +++ b/analysis/statistics/89ede4f688c88b2c08007743b79b087f55648258.txt @@ -0,0 +1,48 @@ + +changeset: 1760:89ede4f688c88b2c08007743b79b087f55648258 +char kNewtonVersion[] = "0.3-alpha-1760 (89ede4f688c88b2c08007743b79b087f55648258) (build 05-25-2025-14:55-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt b/analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt new file mode 100644 index 000000000..a6eee71fc --- /dev/null +++ b/analysis/statistics/8b0f1a3d9caa6662cb7f24cca1a942003db0156d.txt @@ -0,0 +1,48 @@ + +changeset: 1640:8b0f1a3d9caa6662cb7f24cca1a942003db0156d +char kNewtonVersion[] = "0.3-alpha-1640 (8b0f1a3d9caa6662cb7f24cca1a942003db0156d) (build 07-26-2024-13:07-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt b/analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt new file mode 100644 index 000000000..f59a13774 --- /dev/null +++ b/analysis/statistics/8b4742f73e761c5f138aad89d27caed039565d1a.txt @@ -0,0 +1,7 @@ + +changeset: 1599:8b4742f73e761c5f138aad89d27caed039565d1a +char kNewtonVersion[] = "0.3-alpha-1599 (8b4742f73e761c5f138aad89d27caed039565d1a) (build 06-09-2024-11:37-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt b/analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt new file mode 100644 index 000000000..a77433838 --- /dev/null +++ b/analysis/statistics/8caeff3e473a731bc92f6f2ce2410f4180b5c163.txt @@ -0,0 +1,48 @@ + +changeset: 1637:8caeff3e473a731bc92f6f2ce2410f4180b5c163 +char kNewtonVersion[] = "0.3-alpha-1637 (8caeff3e473a731bc92f6f2ce2410f4180b5c163) (build 07-24-2024-14:28-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt b/analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt new file mode 100644 index 000000000..1bbcf0e8f --- /dev/null +++ b/analysis/statistics/8db4d93d4e04855d20909600a0d074ccb2ed4611.txt @@ -0,0 +1,48 @@ + +changeset: 1734:8db4d93d4e04855d20909600a0d074ccb2ed4611 +char kNewtonVersion[] = "0.3-alpha-1734 (8db4d93d4e04855d20909600a0d074ccb2ed4611) (build 02-14-2025-13:06-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt b/analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt new file mode 100644 index 000000000..bb5bd57e2 --- /dev/null +++ b/analysis/statistics/8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5.txt @@ -0,0 +1,48 @@ + +changeset: 1644:8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5 +char kNewtonVersion[] = "0.3-alpha-1644 (8e3c2ce635e38a9639dcc04fc1a0a082b83d8dd5) (build 08-04-2024-23:16-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt b/analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt new file mode 100644 index 000000000..2ce031b7b --- /dev/null +++ b/analysis/statistics/8e8315709192bf3eda7c9bc2be1346910b1b517e.txt @@ -0,0 +1,48 @@ + +changeset: 1642:8e8315709192bf3eda7c9bc2be1346910b1b517e +char kNewtonVersion[] = "0.3-alpha-1642 (8e8315709192bf3eda7c9bc2be1346910b1b517e) (build 08-01-2024-14:25-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt b/analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt new file mode 100644 index 000000000..86f74c152 --- /dev/null +++ b/analysis/statistics/9088c775663fd78b590e60fae67cc63d0bb2e8f4.txt @@ -0,0 +1,7 @@ + +changeset: 1748:9088c775663fd78b590e60fae67cc63d0bb2e8f4 +char kNewtonVersion[] = "0.3-alpha-1748 (9088c775663fd78b590e60fae67cc63d0bb2e8f4) (build 04-02-2025-18:32-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt b/analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt new file mode 100644 index 000000000..ab712c97f --- /dev/null +++ b/analysis/statistics/922e4aa42d6bb0caf0244616d3a8febb79a02072.txt @@ -0,0 +1,48 @@ + +changeset: 1732:922e4aa42d6bb0caf0244616d3a8febb79a02072 +char kNewtonVersion[] = "0.3-alpha-1732 (922e4aa42d6bb0caf0244616d3a8febb79a02072) (build 02-10-2025-21:18-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt b/analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt new file mode 100644 index 000000000..18ab1ddfd --- /dev/null +++ b/analysis/statistics/954fc8b6effd6620455fb3b51fcd181ff5eee72e.txt @@ -0,0 +1,48 @@ + +changeset: 1669:954fc8b6effd6620455fb3b51fcd181ff5eee72e +char kNewtonVersion[] = "0.3-alpha-1669 (954fc8b6effd6620455fb3b51fcd181ff5eee72e) (build 08-27-2024-13:50-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt b/analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt new file mode 100644 index 000000000..8c6d092e3 --- /dev/null +++ b/analysis/statistics/96ca7a1ae1f0cf3c422c9fc8c560f39b24617284.txt @@ -0,0 +1,48 @@ + +changeset: 1657:96ca7a1ae1f0cf3c422c9fc8c560f39b24617284 +char kNewtonVersion[] = "0.3-alpha-1657 (96ca7a1ae1f0cf3c422c9fc8c560f39b24617284) (build 08-19-2024-22:43-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt b/analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt new file mode 100644 index 000000000..33381e2c0 --- /dev/null +++ b/analysis/statistics/9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0.txt @@ -0,0 +1,48 @@ + +changeset: 1721:9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0 +char kNewtonVersion[] = "0.3-alpha-1721 (9709b4d4f4be62a2ee3ca42eebcf2c24d2a41df0) (build 11-23-2024-11:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt b/analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt new file mode 100644 index 000000000..eefbeee5d --- /dev/null +++ b/analysis/statistics/9a7e5888fd2234f368f99e40a827f00580284241.txt @@ -0,0 +1,48 @@ + +changeset: 1698:9a7e5888fd2234f368f99e40a827f00580284241 +char kNewtonVersion[] = "0.3-alpha-1698 (9a7e5888fd2234f368f99e40a827f00580284241) (build 10-05-2024-14:17-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt b/analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt new file mode 100644 index 000000000..44d89999c --- /dev/null +++ b/analysis/statistics/9a89e5bca53bce0f88c5325b33ebb7453d53171e.txt @@ -0,0 +1,48 @@ + +changeset: 1690:9a89e5bca53bce0f88c5325b33ebb7453d53171e +char kNewtonVersion[] = "0.3-alpha-1690 (9a89e5bca53bce0f88c5325b33ebb7453d53171e) (build 09-29-2024-22:45-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt b/analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt new file mode 100644 index 000000000..40ef56884 --- /dev/null +++ b/analysis/statistics/9b02f825b35cc1885256ad00679db4fbf23a3c9d.txt @@ -0,0 +1,48 @@ + +changeset: 1725:9b02f825b35cc1885256ad00679db4fbf23a3c9d +char kNewtonVersion[] = "0.3-alpha-1725 (9b02f825b35cc1885256ad00679db4fbf23a3c9d) (build 02-04-2025-10:03-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt b/analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt new file mode 100644 index 000000000..5fd42986b --- /dev/null +++ b/analysis/statistics/9b153db99e88be31fbbef7d95651faa8da1e512d.txt @@ -0,0 +1,48 @@ + +changeset: 1766:9b153db99e88be31fbbef7d95651faa8da1e512d +char kNewtonVersion[] = "0.3-alpha-1766 (9b153db99e88be31fbbef7d95651faa8da1e512d) (build 05-26-2025-22:56-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt b/analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt new file mode 100644 index 000000000..6c2cbe293 --- /dev/null +++ b/analysis/statistics/9d27e49fa335cd09872d71d80e7015edc0309cf7.txt @@ -0,0 +1,48 @@ + +changeset: 1767:9d27e49fa335cd09872d71d80e7015edc0309cf7 +char kNewtonVersion[] = "0.3-alpha-1767 (9d27e49fa335cd09872d71d80e7015edc0309cf7) (build 05-26-2025-22:59-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt b/analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt new file mode 100644 index 000000000..cfe3f099a --- /dev/null +++ b/analysis/statistics/9daab14572516b4d365bfcbc003379e336a79c0b.txt @@ -0,0 +1,48 @@ + +changeset: 1627:9daab14572516b4d365bfcbc003379e336a79c0b +char kNewtonVersion[] = "0.3-alpha-1627 (9daab14572516b4d365bfcbc003379e336a79c0b) (build 07-12-2024-22:17-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt b/analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt new file mode 100644 index 000000000..c6f3679cc --- /dev/null +++ b/analysis/statistics/9e9f413256b96f30f453566a1b4a64e4d00fb06d.txt @@ -0,0 +1,48 @@ + +changeset: 1764:9e9f413256b96f30f453566a1b4a64e4d00fb06d +char kNewtonVersion[] = "0.3-alpha-1764 (9e9f413256b96f30f453566a1b4a64e4d00fb06d) (build 05-26-2025-22:48-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt b/analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt new file mode 100644 index 000000000..1e9b1ed9a --- /dev/null +++ b/analysis/statistics/9ebc2e5cb76f71b0fc93523430b9867c72237e64.txt @@ -0,0 +1,7 @@ + +changeset: 1602:9ebc2e5cb76f71b0fc93523430b9867c72237e64 +char kNewtonVersion[] = "0.3-alpha-1602 (9ebc2e5cb76f71b0fc93523430b9867c72237e64) (build 06-17-2024-13:09-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt b/analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt new file mode 100644 index 000000000..0b0d9ea2a --- /dev/null +++ b/analysis/statistics/9ec1c11d31a59c57f04599ecd0f4214c47075f4e.txt @@ -0,0 +1,48 @@ + +changeset: 1749:9ec1c11d31a59c57f04599ecd0f4214c47075f4e +char kNewtonVersion[] = "0.3-alpha-1749 (9ec1c11d31a59c57f04599ecd0f4214c47075f4e) (build 04-02-2025-18:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt b/analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt new file mode 100644 index 000000000..787d1493f --- /dev/null +++ b/analysis/statistics/a2fb29881175d5696515be66cccf1ea9b095d2be.txt @@ -0,0 +1,48 @@ + +changeset: 1761:a2fb29881175d5696515be66cccf1ea9b095d2be +char kNewtonVersion[] = "0.3-alpha-1761 (a2fb29881175d5696515be66cccf1ea9b095d2be) (build 05-25-2025-15:38-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt b/analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt new file mode 100644 index 000000000..01aad8836 --- /dev/null +++ b/analysis/statistics/a51bf1c6058a1292ee80592733c87130294252fe.txt @@ -0,0 +1,48 @@ + +changeset: 1707:a51bf1c6058a1292ee80592733c87130294252fe +char kNewtonVersion[] = "0.3-alpha-1707 (a51bf1c6058a1292ee80592733c87130294252fe) (build 11-13-2024-19:10-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt b/analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt new file mode 100644 index 000000000..188c9a6c7 --- /dev/null +++ b/analysis/statistics/a64086b2fe57ccd9a3dff399376cf66a9c753ba4.txt @@ -0,0 +1,48 @@ + +changeset: 1680:a64086b2fe57ccd9a3dff399376cf66a9c753ba4 +char kNewtonVersion[] = "0.3-alpha-1680 (a64086b2fe57ccd9a3dff399376cf66a9c753ba4) (build 09-10-2024-09:59-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt b/analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt new file mode 100644 index 000000000..f382e46d4 --- /dev/null +++ b/analysis/statistics/a779b37459ec50e499dcc1734d38fe08eb70f4d9.txt @@ -0,0 +1,48 @@ + +changeset: 1756:a779b37459ec50e499dcc1734d38fe08eb70f4d9 +char kNewtonVersion[] = "0.3-alpha-1756 (a779b37459ec50e499dcc1734d38fe08eb70f4d9) (build 05-05-2025-15:50-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt b/analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt new file mode 100644 index 000000000..42df43cdb --- /dev/null +++ b/analysis/statistics/a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9.txt @@ -0,0 +1,48 @@ + +changeset: 1662:a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9 +char kNewtonVersion[] = "0.3-alpha-1662 (a7a1ded43d7c9fcc09c2aee5b78b59713b3ebff9) (build 08-19-2024-23:05-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt b/analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt new file mode 100644 index 000000000..309cd1c31 --- /dev/null +++ b/analysis/statistics/a7dd276146c9a4f5d54782096eeee007b12e8fb5.txt @@ -0,0 +1,48 @@ + +changeset: 1762:a7dd276146c9a4f5d54782096eeee007b12e8fb5 +char kNewtonVersion[] = "0.3-alpha-1762 (a7dd276146c9a4f5d54782096eeee007b12e8fb5) (build 05-25-2025-20:27-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt b/analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt new file mode 100644 index 000000000..93ff342ba --- /dev/null +++ b/analysis/statistics/a8b15350e68d595638e6a7b5918d37fe5209b0d1.txt @@ -0,0 +1,48 @@ + +changeset: 1765:a8b15350e68d595638e6a7b5918d37fe5209b0d1 +char kNewtonVersion[] = "0.3-alpha-1765 (a8b15350e68d595638e6a7b5918d37fe5209b0d1) (build 05-26-2025-22:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt b/analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt new file mode 100644 index 000000000..887f84963 --- /dev/null +++ b/analysis/statistics/a90d47861c3c28b9de76f56f756e2ba4eb14e1d5.txt @@ -0,0 +1,48 @@ + +changeset: 1775:a90d47861c3c28b9de76f56f756e2ba4eb14e1d5 +char kNewtonVersion[] = "0.3-alpha-1775 (a90d47861c3c28b9de76f56f756e2ba4eb14e1d5) (build 06-03-2025-16:40-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt b/analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt new file mode 100644 index 000000000..b7a9ab2f2 --- /dev/null +++ b/analysis/statistics/aa88e2d6c3c11fb59f0d4c70b57122722b202729.txt @@ -0,0 +1,48 @@ + +changeset: 1654:aa88e2d6c3c11fb59f0d4c70b57122722b202729 +char kNewtonVersion[] = "0.3-alpha-1654 (aa88e2d6c3c11fb59f0d4c70b57122722b202729) (build 08-18-2024-11:20-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt b/analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt new file mode 100644 index 000000000..8f2da32dd --- /dev/null +++ b/analysis/statistics/abcb7dec677f4a49de71e626ed0cdc36cf466d75.txt @@ -0,0 +1,7 @@ + +changeset: 1605:abcb7dec677f4a49de71e626ed0cdc36cf466d75 +char kNewtonVersion[] = "0.3-alpha-1605 (abcb7dec677f4a49de71e626ed0cdc36cf466d75) (build 06-17-2024-14:22-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt b/analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt new file mode 100644 index 000000000..662a1cc0f --- /dev/null +++ b/analysis/statistics/abdf5af7de9ae29844dedac1e8068cda313132c3.txt @@ -0,0 +1,48 @@ + +changeset: 1702:abdf5af7de9ae29844dedac1e8068cda313132c3 +char kNewtonVersion[] = "0.3-alpha-1702 (abdf5af7de9ae29844dedac1e8068cda313132c3) (build 10-08-2024-14:35-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt b/analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt new file mode 100644 index 000000000..0bb256ec9 --- /dev/null +++ b/analysis/statistics/ac03ffeef2b0b3d7ea4a525f7c685ea227b45896.txt @@ -0,0 +1,48 @@ + +changeset: 1731:ac03ffeef2b0b3d7ea4a525f7c685ea227b45896 +char kNewtonVersion[] = "0.3-alpha-1731 (ac03ffeef2b0b3d7ea4a525f7c685ea227b45896) (build 02-08-2025-11:37-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt b/analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt new file mode 100644 index 000000000..3efd28155 --- /dev/null +++ b/analysis/statistics/ac1bad9ede204cbcaf22ae227319f98635350c8f.txt @@ -0,0 +1,48 @@ + +changeset: 1674:ac1bad9ede204cbcaf22ae227319f98635350c8f +char kNewtonVersion[] = "0.3-alpha-1674 (ac1bad9ede204cbcaf22ae227319f98635350c8f) (build 08-27-2024-21:46-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt b/analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt new file mode 100644 index 000000000..f4e21638e --- /dev/null +++ b/analysis/statistics/ac7b00410dd70f1ef49b8cee3eda71abda55a094.txt @@ -0,0 +1,7 @@ + +changeset: 1716:ac7b00410dd70f1ef49b8cee3eda71abda55a094 +char kNewtonVersion[] = "0.3-alpha-1716 (ac7b00410dd70f1ef49b8cee3eda71abda55a094) (build 11-21-2024-17:14-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt b/analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt new file mode 100644 index 000000000..e263f7b3c --- /dev/null +++ b/analysis/statistics/acd45eb3919365d2234c69fe3c822ca0463bb16d.txt @@ -0,0 +1,48 @@ + +changeset: 1653:acd45eb3919365d2234c69fe3c822ca0463bb16d +char kNewtonVersion[] = "0.3-alpha-1653 (acd45eb3919365d2234c69fe3c822ca0463bb16d) (build 08-17-2024-13:33-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt b/analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt new file mode 100644 index 000000000..6d749d501 --- /dev/null +++ b/analysis/statistics/acf066c5238870fe52ca924795148f82bcf76a81.txt @@ -0,0 +1,48 @@ + +changeset: 1720:acf066c5238870fe52ca924795148f82bcf76a81 +char kNewtonVersion[] = "0.3-alpha-1720 (acf066c5238870fe52ca924795148f82bcf76a81) (build 11-22-2024-23:08-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt b/analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt new file mode 100644 index 000000000..8dae3ab97 --- /dev/null +++ b/analysis/statistics/ad3ceaf79588bd25a04d012ea9f62f69b3147f65.txt @@ -0,0 +1,48 @@ + +changeset: 1634:ad3ceaf79588bd25a04d012ea9f62f69b3147f65 +char kNewtonVersion[] = "0.3-alpha-1634 (ad3ceaf79588bd25a04d012ea9f62f69b3147f65) (build 07-23-2024-17:30-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt b/analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt new file mode 100644 index 000000000..c0f2e7033 --- /dev/null +++ b/analysis/statistics/b4b6d70cf8df7c34458af9d56a129d1bd0bb2889.txt @@ -0,0 +1,48 @@ + +changeset: 1738:b4b6d70cf8df7c34458af9d56a129d1bd0bb2889 +char kNewtonVersion[] = "0.3-alpha-1738 (b4b6d70cf8df7c34458af9d56a129d1bd0bb2889) (build 02-26-2025-13:02-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt b/analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt new file mode 100644 index 000000000..70fb1c7b3 --- /dev/null +++ b/analysis/statistics/b65f1b260332cbb3a377993270710c055cab07a5.txt @@ -0,0 +1,7 @@ + +changeset: 1610:b65f1b260332cbb3a377993270710c055cab07a5 +char kNewtonVersion[] = "0.3-alpha-1610 (b65f1b260332cbb3a377993270710c055cab07a5) (build 06-17-2024-14:50-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt b/analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt new file mode 100644 index 000000000..22bfc8b0b --- /dev/null +++ b/analysis/statistics/b814e9bacbb529d77179ce70115eb96e3390ece8.txt @@ -0,0 +1,48 @@ + +changeset: 1759:b814e9bacbb529d77179ce70115eb96e3390ece8 +char kNewtonVersion[] = "0.3-alpha-1759 (b814e9bacbb529d77179ce70115eb96e3390ece8) (build 05-25-2025-13:09-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt b/analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt new file mode 100644 index 000000000..6ef19480f --- /dev/null +++ b/analysis/statistics/b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc.txt @@ -0,0 +1,48 @@ + +changeset: 1626:b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc +char kNewtonVersion[] = "0.3-alpha-1626 (b930bdb60a0e225db2395dd1d92a5b5a9d80a1cc) (build 07-12-2024-12:55-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt b/analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt new file mode 100644 index 000000000..93e96b960 --- /dev/null +++ b/analysis/statistics/bca911370c42ab769501a69e1de57f832a4c8c58.txt @@ -0,0 +1,48 @@ + +changeset: 1681:bca911370c42ab769501a69e1de57f832a4c8c58 +char kNewtonVersion[] = "0.3-alpha-1681 (bca911370c42ab769501a69e1de57f832a4c8c58) (build 09-14-2024-17:49-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt b/analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt new file mode 100644 index 000000000..78e87ec88 --- /dev/null +++ b/analysis/statistics/bf90e2b7968179440b55e72e170c6d618d09ceea.txt @@ -0,0 +1,7 @@ + +changeset: 1596:bf90e2b7968179440b55e72e170c6d618d09ceea +char kNewtonVersion[] = "0.3-alpha-1596 (bf90e2b7968179440b55e72e170c6d618d09ceea) (build 06-09-2024-11:27-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt b/analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt new file mode 100644 index 000000000..a0f2a3830 --- /dev/null +++ b/analysis/statistics/c2ac39ed88098aa56c46f37c41089ff87bcc8f6e.txt @@ -0,0 +1,48 @@ + +changeset: 1771:c2ac39ed88098aa56c46f37c41089ff87bcc8f6e +char kNewtonVersion[] = "0.3-alpha-1771 (c2ac39ed88098aa56c46f37c41089ff87bcc8f6e) (build 05-27-2025-17:22-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt b/analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt new file mode 100644 index 000000000..94c487a51 --- /dev/null +++ b/analysis/statistics/c2c03fce474fe62166871bd538e0a8e900045acf.txt @@ -0,0 +1,48 @@ + +changeset: 1719:c2c03fce474fe62166871bd538e0a8e900045acf +char kNewtonVersion[] = "0.3-alpha-1719 (c2c03fce474fe62166871bd538e0a8e900045acf) (build 11-22-2024-21:14-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt b/analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt new file mode 100644 index 000000000..d0d2e411b --- /dev/null +++ b/analysis/statistics/c418f4ab5295f304cc643e21aa7a82b6e30ba21e.txt @@ -0,0 +1,48 @@ + +changeset: 1667:c418f4ab5295f304cc643e21aa7a82b6e30ba21e +char kNewtonVersion[] = "0.3-alpha-1667 (c418f4ab5295f304cc643e21aa7a82b6e30ba21e) (build 08-26-2024-14:06-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt b/analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt new file mode 100644 index 000000000..217a24e27 --- /dev/null +++ b/analysis/statistics/c4b170bb8c985e6abee8cdadc4591c25d59bffe8.txt @@ -0,0 +1,48 @@ + +changeset: 1660:c4b170bb8c985e6abee8cdadc4591c25d59bffe8 +char kNewtonVersion[] = "0.3-alpha-1660 (c4b170bb8c985e6abee8cdadc4591c25d59bffe8) (build 08-19-2024-22:54-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt b/analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt new file mode 100644 index 000000000..212410fe2 --- /dev/null +++ b/analysis/statistics/c4c45e21839a99f707dcde8a2fae9fd2cc5254f2.txt @@ -0,0 +1,48 @@ + +changeset: 1665:c4c45e21839a99f707dcde8a2fae9fd2cc5254f2 +char kNewtonVersion[] = "0.3-alpha-1665 (c4c45e21839a99f707dcde8a2fae9fd2cc5254f2) (build 08-26-2024-13:49-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt b/analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt new file mode 100644 index 000000000..237f2970a --- /dev/null +++ b/analysis/statistics/c4f46e8a0edb511b8ee8b4fb950213ba443d1c78.txt @@ -0,0 +1,48 @@ + +changeset: 1672:c4f46e8a0edb511b8ee8b4fb950213ba443d1c78 +char kNewtonVersion[] = "0.3-alpha-1672 (c4f46e8a0edb511b8ee8b4fb950213ba443d1c78) (build 08-27-2024-16:29-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt b/analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt new file mode 100644 index 000000000..e1c0dfe77 --- /dev/null +++ b/analysis/statistics/c94d1d15ceb2199297b2c9f581e06da7c6765f57.txt @@ -0,0 +1,48 @@ + +changeset: 1776:c94d1d15ceb2199297b2c9f581e06da7c6765f57 +char kNewtonVersion[] = "0.3-alpha-1776 (c94d1d15ceb2199297b2c9f581e06da7c6765f57) (build 06-04-2025-20:21-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt b/analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt new file mode 100644 index 000000000..e7229105f --- /dev/null +++ b/analysis/statistics/caa6c556fd19e0898078bf99d105835d6b817c32.txt @@ -0,0 +1,48 @@ + +changeset: 1614:caa6c556fd19e0898078bf99d105835d6b817c32 +char kNewtonVersion[] = "0.3-alpha-1614 (caa6c556fd19e0898078bf99d105835d6b817c32) (build 06-17-2024-15:06-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt b/analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt new file mode 100644 index 000000000..3040b25ab --- /dev/null +++ b/analysis/statistics/cbaa642317262daf0bb3b5daf0e16f47fa54df42.txt @@ -0,0 +1,48 @@ + +changeset: 1711:cbaa642317262daf0bb3b5daf0e16f47fa54df42 +char kNewtonVersion[] = "0.3-alpha-1711 (cbaa642317262daf0bb3b5daf0e16f47fa54df42) (build 11-16-2024-13:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt b/analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt new file mode 100644 index 000000000..ec0881b8e --- /dev/null +++ b/analysis/statistics/cc1b27c2e423a4822996d07a827190a205ac647a.txt @@ -0,0 +1,48 @@ + +changeset: 1651:cc1b27c2e423a4822996d07a827190a205ac647a +char kNewtonVersion[] = "0.3-alpha-1651 (cc1b27c2e423a4822996d07a827190a205ac647a) (build 08-14-2024-22:52-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt b/analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt new file mode 100644 index 000000000..9c178a33a --- /dev/null +++ b/analysis/statistics/d10042da8466e5dc21a11a2cb96c3d21c1071f09.txt @@ -0,0 +1,48 @@ + +changeset: 1685:d10042da8466e5dc21a11a2cb96c3d21c1071f09 +char kNewtonVersion[] = "0.3-alpha-1685 (d10042da8466e5dc21a11a2cb96c3d21c1071f09) (build 09-25-2024-11:15-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt b/analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt new file mode 100644 index 000000000..307dd99f8 --- /dev/null +++ b/analysis/statistics/d158d0a4397166e63b19ece97c4a95ddb1c51315.txt @@ -0,0 +1,48 @@ + +changeset: 1692:d158d0a4397166e63b19ece97c4a95ddb1c51315 +char kNewtonVersion[] = "0.3-alpha-1692 (d158d0a4397166e63b19ece97c4a95ddb1c51315) (build 09-29-2024-22:50-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt b/analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt new file mode 100644 index 000000000..726fc02f9 --- /dev/null +++ b/analysis/statistics/d15d558addacea96e02861eb25ec57a34d279064.txt @@ -0,0 +1,48 @@ + +changeset: 1671:d15d558addacea96e02861eb25ec57a34d279064 +char kNewtonVersion[] = "0.3-alpha-1671 (d15d558addacea96e02861eb25ec57a34d279064) (build 08-27-2024-14:57-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt b/analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt new file mode 100644 index 000000000..a07534500 --- /dev/null +++ b/analysis/statistics/d1dd2926c54be476cccf4117a73bb751319a0cf8.txt @@ -0,0 +1,48 @@ + +changeset: 1768:d1dd2926c54be476cccf4117a73bb751319a0cf8 +char kNewtonVersion[] = "0.3-alpha-1768 (d1dd2926c54be476cccf4117a73bb751319a0cf8) (build 05-26-2025-23:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt b/analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt new file mode 100644 index 000000000..ed9314506 --- /dev/null +++ b/analysis/statistics/d323aa59ba738ab8a2f71713088a49f77b2d758e.txt @@ -0,0 +1,48 @@ + +changeset: 1673:d323aa59ba738ab8a2f71713088a49f77b2d758e +char kNewtonVersion[] = "0.3-alpha-1673 (d323aa59ba738ab8a2f71713088a49f77b2d758e) (build 08-27-2024-21:39-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt b/analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt new file mode 100644 index 000000000..1042212f9 --- /dev/null +++ b/analysis/statistics/d3306a61229c3d7d83d74c7d1a2face3c51fcc70.txt @@ -0,0 +1,48 @@ + +changeset: 1615:d3306a61229c3d7d83d74c7d1a2face3c51fcc70 +char kNewtonVersion[] = "0.3-alpha-1615 (d3306a61229c3d7d83d74c7d1a2face3c51fcc70) (build 06-24-2024-20:13-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt b/analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt new file mode 100644 index 000000000..45eda60e8 --- /dev/null +++ b/analysis/statistics/d364b2e035986d58715081e661615d0c8f2fa6a7.txt @@ -0,0 +1,48 @@ + +changeset: 1706:d364b2e035986d58715081e661615d0c8f2fa6a7 +char kNewtonVersion[] = "0.3-alpha-1706 (d364b2e035986d58715081e661615d0c8f2fa6a7) (build 11-12-2024-14:35-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt b/analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt new file mode 100644 index 000000000..20e5c984c --- /dev/null +++ b/analysis/statistics/d5b36a40859e2c6368e4ec1c01205b230ed11180.txt @@ -0,0 +1,48 @@ + +changeset: 1746:d5b36a40859e2c6368e4ec1c01205b230ed11180 +char kNewtonVersion[] = "0.3-alpha-1746 (d5b36a40859e2c6368e4ec1c01205b230ed11180) (build 03-27-2025-20:51-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt b/analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt new file mode 100644 index 000000000..8ac2e1f17 --- /dev/null +++ b/analysis/statistics/d5dd60931a0d357145a3ad4b6bf1b32886e9a85e.txt @@ -0,0 +1,48 @@ + +changeset: 1763:d5dd60931a0d357145a3ad4b6bf1b32886e9a85e +char kNewtonVersion[] = "0.3-alpha-1763 (d5dd60931a0d357145a3ad4b6bf1b32886e9a85e) (build 05-26-2025-22:00-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt b/analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt new file mode 100644 index 000000000..94eb2e034 --- /dev/null +++ b/analysis/statistics/d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669.txt @@ -0,0 +1,48 @@ + +changeset: 1676:d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669 +char kNewtonVersion[] = "0.3-alpha-1676 (d605b1fa68dd7ccdc0baeb1334b77e5cbee6a669) (build 08-28-2024-17:36-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt b/analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt new file mode 100644 index 000000000..2960e9f5c --- /dev/null +++ b/analysis/statistics/d61802d1174f865bc470418ea84dae9c3d2df382.txt @@ -0,0 +1,7 @@ + +changeset: 1597:d61802d1174f865bc470418ea84dae9c3d2df382 +char kNewtonVersion[] = "0.3-alpha-1597 (d61802d1174f865bc470418ea84dae9c3d2df382) (build 06-09-2024-11:30-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt b/analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt new file mode 100644 index 000000000..83b1c055b --- /dev/null +++ b/analysis/statistics/d75c826fb59c52307c5b9b3dec1480b538830566.txt @@ -0,0 +1,48 @@ + +changeset: 1661:d75c826fb59c52307c5b9b3dec1480b538830566 +char kNewtonVersion[] = "0.3-alpha-1661 (d75c826fb59c52307c5b9b3dec1480b538830566) (build 08-19-2024-22:58-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt b/analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt new file mode 100644 index 000000000..9bd16dba7 --- /dev/null +++ b/analysis/statistics/db7f5a7f5cb056f7d01fde115d43244ba0075468.txt @@ -0,0 +1,7 @@ + +changeset: 1717:db7f5a7f5cb056f7d01fde115d43244ba0075468 +char kNewtonVersion[] = "0.3-alpha-1717 (db7f5a7f5cb056f7d01fde115d43244ba0075468) (build 11-21-2024-17:45-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt b/analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt new file mode 100644 index 000000000..6035f920c --- /dev/null +++ b/analysis/statistics/e26f66a92942bd703e2354b002aea625268138ab.txt @@ -0,0 +1,7 @@ + +changeset: 1611:e26f66a92942bd703e2354b002aea625268138ab +char kNewtonVersion[] = "0.3-alpha-1611 (e26f66a92942bd703e2354b002aea625268138ab) (build 06-17-2024-14:59-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt b/analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt new file mode 100644 index 000000000..36b3ca6e8 --- /dev/null +++ b/analysis/statistics/e39ea318616cf24ca79a747f69c9eccac4a2427f.txt @@ -0,0 +1,48 @@ + +changeset: 1742:e39ea318616cf24ca79a747f69c9eccac4a2427f +char kNewtonVersion[] = "0.3-alpha-1742 (e39ea318616cf24ca79a747f69c9eccac4a2427f) (build 03-03-2025-21:22-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt b/analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt new file mode 100644 index 000000000..ad8654baf --- /dev/null +++ b/analysis/statistics/e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3.txt @@ -0,0 +1,48 @@ + +changeset: 1712:e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3 +char kNewtonVersion[] = "0.3-alpha-1712 (e48c1ad5d595b2ceefc27ee7f46d045b14b71ff3) (build 11-16-2024-23:20-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt b/analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt new file mode 100644 index 000000000..242f5c819 --- /dev/null +++ b/analysis/statistics/e706af854809196eb5688ca158e41cfbf5cde2db.txt @@ -0,0 +1,48 @@ + +changeset: 1677:e706af854809196eb5688ca158e41cfbf5cde2db +char kNewtonVersion[] = "0.3-alpha-1677 (e706af854809196eb5688ca158e41cfbf5cde2db) (build 08-28-2024-18:11-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt b/analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt new file mode 100644 index 000000000..a64f6541a --- /dev/null +++ b/analysis/statistics/e770cd3b992411d872bc7946f0e905acdffceee2.txt @@ -0,0 +1,48 @@ + +changeset: 1619:e770cd3b992411d872bc7946f0e905acdffceee2 +char kNewtonVersion[] = "0.3-alpha-1619 (e770cd3b992411d872bc7946f0e905acdffceee2) (build 07-06-2024-21:43-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt b/analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt new file mode 100644 index 000000000..a6d65bc17 --- /dev/null +++ b/analysis/statistics/e9ecc97f8a487a9fb9366d286e1cea515c914a98.txt @@ -0,0 +1,48 @@ + +changeset: 1726:e9ecc97f8a487a9fb9366d286e1cea515c914a98 +char kNewtonVersion[] = "0.3-alpha-1726 (e9ecc97f8a487a9fb9366d286e1cea515c914a98) (build 02-04-2025-11:57-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt b/analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt new file mode 100644 index 000000000..3b5acafff --- /dev/null +++ b/analysis/statistics/ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58.txt @@ -0,0 +1,48 @@ + +changeset: 1668:ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58 +char kNewtonVersion[] = "0.3-alpha-1668 (ea60de1650fe88e2fdf2fabe5ba9a18ef9610f58) (build 08-27-2024-13:06-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt b/analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt new file mode 100644 index 000000000..87acf67b0 --- /dev/null +++ b/analysis/statistics/ec9b06c559c03438402efadb55b5d5d1162fdcfc.txt @@ -0,0 +1,7 @@ + +changeset: 1603:ec9b06c559c03438402efadb55b5d5d1162fdcfc +char kNewtonVersion[] = "0.3-alpha-1603 (ec9b06c559c03438402efadb55b5d5d1162fdcfc) (build 06-17-2024-14:03-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt b/analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt new file mode 100644 index 000000000..b00b47056 --- /dev/null +++ b/analysis/statistics/ed6c34949316c9546034ef67390543a4a725a64b.txt @@ -0,0 +1,48 @@ + +changeset: 1777:ed6c34949316c9546034ef67390543a4a725a64b +char kNewtonVersion[] = "0.3-alpha-1777 (ed6c34949316c9546034ef67390543a4a725a64b) (build 06-04-2025-20:22-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt b/analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt new file mode 100644 index 000000000..3460d0b77 --- /dev/null +++ b/analysis/statistics/ef1432ef7fa123208205b693f08767e1bbcbb98e.txt @@ -0,0 +1,48 @@ + +changeset: 1705:ef1432ef7fa123208205b693f08767e1bbcbb98e +char kNewtonVersion[] = "0.3-alpha-1705 (ef1432ef7fa123208205b693f08767e1bbcbb98e) (build 11-11-2024-12:40-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt b/analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt new file mode 100644 index 000000000..67356f859 --- /dev/null +++ b/analysis/statistics/ef4437405003dfbd61c14ae2d8deac27809467bb.txt @@ -0,0 +1,48 @@ + +changeset: 1735:ef4437405003dfbd61c14ae2d8deac27809467bb +char kNewtonVersion[] = "0.3-alpha-1735 (ef4437405003dfbd61c14ae2d8deac27809467bb) (build 02-14-2025-13:54-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt b/analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt new file mode 100644 index 000000000..be86f9458 --- /dev/null +++ b/analysis/statistics/f1644445572b5322612e2f10dd972b71fa46505a.txt @@ -0,0 +1,48 @@ + +changeset: 1656:f1644445572b5322612e2f10dd972b71fa46505a +char kNewtonVersion[] = "0.3-alpha-1656 (f1644445572b5322612e2f10dd972b71fa46505a) (build 08-19-2024-22:30-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt b/analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt new file mode 100644 index 000000000..729ce6157 --- /dev/null +++ b/analysis/statistics/f34c0396e8c254265bcc9a7c702cab9c88df00d6.txt @@ -0,0 +1,48 @@ + +changeset: 1728:f34c0396e8c254265bcc9a7c702cab9c88df00d6 +char kNewtonVersion[] = "0.3-alpha-1728 (f34c0396e8c254265bcc9a7c702cab9c88df00d6) (build 02-05-2025-12:04-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt b/analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt new file mode 100644 index 000000000..a74fe599f --- /dev/null +++ b/analysis/statistics/f5bcd551dea1c1a407d13ef2c5b6de248077c09c.txt @@ -0,0 +1,48 @@ + +changeset: 1727:f5bcd551dea1c1a407d13ef2c5b6de248077c09c +char kNewtonVersion[] = "0.3-alpha-1727 (f5bcd551dea1c1a407d13ef2c5b6de248077c09c) (build 02-05-2025-11:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt b/analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt new file mode 100644 index 000000000..a9583b42f --- /dev/null +++ b/analysis/statistics/f5c135ed6cb79cffa86391c680b1d4d072e95ff2.txt @@ -0,0 +1,48 @@ + +changeset: 1744:f5c135ed6cb79cffa86391c680b1d4d072e95ff2 +char kNewtonVersion[] = "0.3-alpha-1744 (f5c135ed6cb79cffa86391c680b1d4d072e95ff2) (build 03-23-2025-12:31-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt b/analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt new file mode 100644 index 000000000..d68b09740 --- /dev/null +++ b/analysis/statistics/f861cdaa3cd8c3e510c950f11ee35a7d19d8e899.txt @@ -0,0 +1,48 @@ + +changeset: 1755:f861cdaa3cd8c3e510c950f11ee35a7d19d8e899 +char kNewtonVersion[] = "0.3-alpha-1755 (f861cdaa3cd8c3e510c950f11ee35a7d19d8e899) (build 05-05-2025-12:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt b/analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt new file mode 100644 index 000000000..ce9d5b05d --- /dev/null +++ b/analysis/statistics/f9cc3b3e46d9a72bf92ab99f199db18b1105927c.txt @@ -0,0 +1,48 @@ + +changeset: 1730:f9cc3b3e46d9a72bf92ab99f199db18b1105927c +char kNewtonVersion[] = "0.3-alpha-1730 (f9cc3b3e46d9a72bf92ab99f199db18b1105927c) (build 02-06-2025-22:53-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt b/analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt new file mode 100644 index 000000000..a34af663a --- /dev/null +++ b/analysis/statistics/fc87f8d40d678f18669d957fcb15f65fdc51c1bb.txt @@ -0,0 +1,7 @@ + +changeset: 1612:fc87f8d40d678f18669d957fcb15f65fdc51c1bb +char kNewtonVersion[] = "0.3-alpha-1612 (fc87f8d40d678f18669d957fcb15f65fdc51c1bb) (build 06-17-2024-15:00-xyf@jige-Linux-5.15.133.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt b/analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt new file mode 100644 index 000000000..05f85f7a1 --- /dev/null +++ b/analysis/statistics/fcb71f9a689fdb93526a37f32bb00684de2ab1fc.txt @@ -0,0 +1,48 @@ + +changeset: 1715:fcb71f9a689fdb93526a37f32bb00684de2ab1fc +char kNewtonVersion[] = "0.3-alpha-1715 (fcb71f9a689fdb93526a37f32bb00684de2ab1fc) (build 11-18-2024-20:49-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt b/analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt new file mode 100644 index 000000000..f703a4994 --- /dev/null +++ b/analysis/statistics/fd0d35fca0fc55e00c998de3cf02138684d02c1e.txt @@ -0,0 +1,48 @@ + +changeset: 1675:fd0d35fca0fc55e00c998de3cf02138684d02c1e +char kNewtonVersion[] = "0.3-alpha-1675 (fd0d35fca0fc55e00c998de3cf02138684d02c1e) (build 08-28-2024-10:34-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt b/analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt new file mode 100644 index 000000000..a93ff5292 --- /dev/null +++ b/analysis/statistics/fe1fbfff733526eaef18b487fbc61675aed1ce90.txt @@ -0,0 +1,7 @@ + +changeset: 1723:fe1fbfff733526eaef18b487fbc61675aed1ce90 +char kNewtonVersion[] = "0.3-alpha-1723 (fe1fbfff733526eaef18b487fbc61675aed1ce90) (build 01-26-2025-11:33-xyf@jige-Linux-5.15.153.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt diff --git a/analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt b/analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt new file mode 100644 index 000000000..9b28df523 --- /dev/null +++ b/analysis/statistics/fe815c6275474688987f46fc84e177dfe1553a1b.txt @@ -0,0 +1,48 @@ + +changeset: 1658:fe815c6275474688987f46fc84e177dfe1553a1b +char kNewtonVersion[] = "0.3-alpha-1658 (fe815c6275474688987f46fc84e177dfe1553a1b) (build 08-19-2024-22:46-xyf@jige-Linux-6.6.36.3-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/CHStone_test/float64_div.ll b/applications/newton/llvm-ir/CHStone_test/float64_div.ll new file mode 100644 index 000000000..c66752f3c --- /dev/null +++ b/applications/newton/llvm-ir/CHStone_test/float64_div.ll @@ -0,0 +1,2148 @@ +; ModuleID = 'float64_div.ll' +source_filename = "dfdiv/float64_div.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@float_rounding_mode = dso_local global i32 0, align 4, !dbg !0 +@float_exception_flags = dso_local global i32 0, align 4, !dbg !14 +@_ZZL19countLeadingZeros32jE21countLeadingZerosHigh = internal constant <{ [128 x i32], [128 x i32] }> <{ [128 x i32] [i32 8, i32 7, i32 6, i32 6, i32 5, i32 5, i32 5, i32 5, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1], [128 x i32] zeroinitializer }>, align 16, !dbg !18 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z19shift64RightJammingyiPy(i64 %0, i32 %1, i64* %2) #0 !dbg !278 { + call void @llvm.dbg.value(metadata i64 %0, metadata !283, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i32 %1, metadata !285, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i64* %2, metadata !286, metadata !DIExpression()), !dbg !284 + %4 = icmp eq i32 %1, 0, !dbg !287 + br i1 %4, label %5, label %6, !dbg !289 + +5: ; preds = %3 + call void @llvm.dbg.value(metadata i64 %0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22, !dbg !291 + +6: ; preds = %3 + %7 = icmp slt i32 %1, 64, !dbg !293 + br i1 %7, label %8, label %18, !dbg !295 + +8: ; preds = %6 + %9 = zext i32 %1 to i64, !dbg !296 + %10 = lshr i64 %0, %9, !dbg !296 + %11 = sub nsw i32 0, %1, !dbg !298 + %12 = and i32 %11, 63, !dbg !299 + %13 = zext i32 %12 to i64, !dbg !300 + %14 = shl i64 %0, %13, !dbg !300 + %15 = icmp ne i64 %14, 0, !dbg !301 + %16 = zext i1 %15 to i64, !dbg !302 + %17 = or i64 %10, %16, !dbg !303 + call void @llvm.dbg.value(metadata i64 %17, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21, !dbg !304 + +18: ; preds = %6 + %19 = icmp ne i64 %0, 0, !dbg !305 + %20 = zext i1 %19 to i64, !dbg !307 + call void @llvm.dbg.value(metadata i64 %20, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21 + +21: ; preds = %18, %8 + %.0 = phi i64 [ %17, %8 ], [ %20, %18 ], !dbg !308 + call void @llvm.dbg.value(metadata i64 %.0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22 + +22: ; preds = %21, %5 + %.1 = phi i64 [ %0, %5 ], [ %.0, %21 ], !dbg !309 + call void @llvm.dbg.value(metadata i64 %.1, metadata !290, metadata !DIExpression()), !dbg !284 + store i64 %.1, i64* %2, align 8, !dbg !310 + ret void, !dbg !311 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z6add128yyyyPyS_(i64 %0, i64 %1, i64 %2, i64 %3, i64* %4, i64* %5) #0 !dbg !312 { + call void @llvm.dbg.value(metadata i64 %0, metadata !315, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %1, metadata !317, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %2, metadata !318, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %3, metadata !319, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %4, metadata !320, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %5, metadata !321, metadata !DIExpression()), !dbg !316 + %7 = add i64 %1, %3, !dbg !322 + call void @llvm.dbg.value(metadata i64 %7, metadata !323, metadata !DIExpression()), !dbg !316 + store i64 %7, i64* %5, align 8, !dbg !324 + %8 = add i64 %0, %2, !dbg !325 + %9 = icmp ult i64 %7, %1, !dbg !326 + %10 = zext i1 %9 to i64, !dbg !327 + %11 = add i64 %8, %10, !dbg !328 + store i64 %11, i64* %4, align 8, !dbg !329 + ret void, !dbg !330 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z6sub128yyyyPyS_(i64 %0, i64 %1, i64 %2, i64 %3, i64* %4, i64* %5) #0 !dbg !331 { + call void @llvm.dbg.value(metadata i64 %0, metadata !332, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64 %1, metadata !334, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64 %2, metadata !335, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64 %3, metadata !336, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64* %4, metadata !337, metadata !DIExpression()), !dbg !333 + call void @llvm.dbg.value(metadata i64* %5, metadata !338, metadata !DIExpression()), !dbg !333 + %7 = sub i64 %1, %3, !dbg !339 + store i64 %7, i64* %5, align 8, !dbg !340 + %8 = sub i64 %0, %2, !dbg !341 + %9 = icmp ult i64 %1, %3, !dbg !342 + %10 = zext i1 %9 to i64, !dbg !343 + %11 = sub i64 %8, %10, !dbg !344 + store i64 %11, i64* %4, align 8, !dbg !345 + ret void, !dbg !346 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z10mul64To128yyPyS_(i64 %0, i64 %1, i64* %2, i64* %3) #0 !dbg !347 { + call void @llvm.dbg.value(metadata i64 %0, metadata !350, metadata !DIExpression()), !dbg !351 + call void @llvm.dbg.value(metadata i64 %1, metadata !352, metadata !DIExpression()), !dbg !351 + call void @llvm.dbg.value(metadata i64* %2, metadata !353, metadata !DIExpression()), !dbg !351 + call void @llvm.dbg.value(metadata i64* %3, metadata !354, metadata !DIExpression()), !dbg !351 + %5 = trunc i64 %0 to i32, !dbg !355 + call void @llvm.dbg.value(metadata i32 %5, metadata !356, metadata !DIExpression()), !dbg !351 + %6 = lshr i64 %0, 32, !dbg !357 + %7 = trunc i64 %6 to i32, !dbg !358 + call void @llvm.dbg.value(metadata i32 %7, metadata !359, metadata !DIExpression()), !dbg !351 + %8 = trunc i64 %1 to i32, !dbg !360 + call void @llvm.dbg.value(metadata i32 %8, metadata !361, metadata !DIExpression()), !dbg !351 + %9 = lshr i64 %1, 32, !dbg !362 + %10 = trunc i64 %9 to i32, !dbg !363 + call void @llvm.dbg.value(metadata i32 %10, metadata !364, metadata !DIExpression()), !dbg !351 + %11 = zext i32 %5 to i64, !dbg !365 + %12 = zext i32 %8 to i64, !dbg !366 + %13 = mul i64 %11, %12, !dbg !367 + call void @llvm.dbg.value(metadata i64 %13, metadata !368, metadata !DIExpression()), !dbg !351 + %14 = zext i32 %5 to i64, !dbg !369 + %15 = zext i32 %10 to i64, !dbg !370 + %16 = mul i64 %14, %15, !dbg !371 + call void @llvm.dbg.value(metadata i64 %16, metadata !372, metadata !DIExpression()), !dbg !351 + %17 = zext i32 %7 to i64, !dbg !373 + %18 = zext i32 %8 to i64, !dbg !374 + %19 = mul i64 %17, %18, !dbg !375 + call void @llvm.dbg.value(metadata i64 %19, metadata !376, metadata !DIExpression()), !dbg !351 + %20 = zext i32 %7 to i64, !dbg !377 + %21 = zext i32 %10 to i64, !dbg !378 + %22 = mul i64 %20, %21, !dbg !379 + call void @llvm.dbg.value(metadata i64 %22, metadata !380, metadata !DIExpression()), !dbg !351 + %23 = add i64 %16, %19, !dbg !381 + call void @llvm.dbg.value(metadata i64 %23, metadata !372, metadata !DIExpression()), !dbg !351 + %24 = icmp ult i64 %23, %19, !dbg !382 + %25 = zext i1 %24 to i64, !dbg !383 + %26 = shl i64 %25, 32, !dbg !384 + %27 = lshr i64 %23, 32, !dbg !385 + %28 = add i64 %26, %27, !dbg !386 + %29 = add i64 %22, %28, !dbg !387 + call void @llvm.dbg.value(metadata i64 %29, metadata !380, metadata !DIExpression()), !dbg !351 + %30 = shl i64 %23, 32, !dbg !388 + call void @llvm.dbg.value(metadata i64 %30, metadata !372, metadata !DIExpression()), !dbg !351 + %31 = add i64 %13, %30, !dbg !389 + call void @llvm.dbg.value(metadata i64 %31, metadata !368, metadata !DIExpression()), !dbg !351 + %32 = icmp ult i64 %31, %30, !dbg !390 + %33 = zext i1 %32 to i64, !dbg !391 + %34 = add i64 %29, %33, !dbg !392 + call void @llvm.dbg.value(metadata i64 %34, metadata !380, metadata !DIExpression()), !dbg !351 + store i64 %31, i64* %3, align 8, !dbg !393 + store i64 %34, i64* %2, align 8, !dbg !394 + ret void, !dbg !395 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z11float_raisei(i32 %0) #0 !dbg !396 { + call void @llvm.dbg.value(metadata i32 %0, metadata !400, metadata !DIExpression()), !dbg !401 + %2 = load i32, i32* @float_exception_flags, align 4, !dbg !402 + %3 = or i32 %2, %0, !dbg !402 + store i32 %3, i32* @float_exception_flags, align 4, !dbg !402 + ret void, !dbg !403 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z14float64_is_nany(i64 %0) #0 !dbg !404 { + call void @llvm.dbg.value(metadata i64 %0, metadata !410, metadata !DIExpression()), !dbg !411 + %2 = shl i64 %0, 1, !dbg !412 + %3 = icmp ult i64 -9007199254740992, %2, !dbg !413 + %4 = zext i1 %3 to i32, !dbg !414 + ret i32 %4, !dbg !415 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z24float64_is_signaling_nany(i64 %0) #0 !dbg !416 { + call void @llvm.dbg.value(metadata i64 %0, metadata !417, metadata !DIExpression()), !dbg !418 + %2 = lshr i64 %0, 51, !dbg !419 + %3 = and i64 %2, 4095, !dbg !420 + %4 = icmp eq i64 %3, 4094, !dbg !421 + br i1 %4, label %5, label %8, !dbg !422 + +5: ; preds = %1 + %6 = and i64 %0, 2251799813685247, !dbg !423 + %7 = icmp ne i64 %6, 0, !dbg !424 + br label %8 + +8: ; preds = %5, %1 + %9 = phi i1 [ false, %1 ], [ %7, %5 ], !dbg !418 + %10 = zext i1 %9 to i32, !dbg !425 + ret i32 %10, !dbg !426 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @extractFloat64Frac(i64 %0) #2 !dbg !427 { + call void @llvm.dbg.value(metadata i64 %0, metadata !430, metadata !DIExpression()), !dbg !431 + %2 = and i64 %0, 4503599627370495, !dbg !432 + ret i64 %2, !dbg !433 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Exp(i64 %0) #2 !dbg !434 { + call void @llvm.dbg.value(metadata i64 %0, metadata !437, metadata !DIExpression()), !dbg !438 + %2 = lshr i64 %0, 52, !dbg !439 + %3 = and i64 %2, 2047, !dbg !440 + %4 = trunc i64 %3 to i32, !dbg !441 + ret i32 %4, !dbg !442 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Sign(i64 %0) #2 !dbg !443 { + call void @llvm.dbg.value(metadata i64 %0, metadata !444, metadata !DIExpression()), !dbg !445 + %2 = lshr i64 %0, 63, !dbg !446 + %3 = trunc i64 %2 to i32, !dbg !447 + ret i32 %3, !dbg !448 +} + +; Function Attrs: alwaysinline mustprogress uwtable +define dso_local void @_Z25normalizeFloat64SubnormalyPiPy(i64 %0, i32* %1, i64* %2) #3 !dbg !449 { + call void @llvm.dbg.value(metadata i64 %0, metadata !453, metadata !DIExpression()), !dbg !454 + call void @llvm.dbg.value(metadata i32* %1, metadata !455, metadata !DIExpression()), !dbg !454 + call void @llvm.dbg.value(metadata i64* %2, metadata !456, metadata !DIExpression()), !dbg !454 + %4 = call i32 @_ZL19countLeadingZeros64y(i64 %0), !dbg !457 + %5 = sub nsw i32 %4, 11, !dbg !458 + call void @llvm.dbg.value(metadata i32 %5, metadata !459, metadata !DIExpression()), !dbg !454 + %6 = zext i32 %5 to i64, !dbg !460 + %7 = shl i64 %0, %6, !dbg !460 + store i64 %7, i64* %2, align 8, !dbg !461 + %8 = sub nsw i32 1, %5, !dbg !462 + store i32 %8, i32* %1, align 4, !dbg !463 + ret void, !dbg !464 +} + +; Function Attrs: mustprogress noinline uwtable +define internal i32 @_ZL19countLeadingZeros64y(i64 %0) #4 !dbg !465 { + call void @llvm.dbg.value(metadata i64 %0, metadata !468, metadata !DIExpression()), !dbg !469 + call void @llvm.dbg.value(metadata i32 0, metadata !470, metadata !DIExpression()), !dbg !469 + %2 = icmp ult i64 %0, 4294967296, !dbg !471 + br i1 %2, label %3, label %5, !dbg !473 + +3: ; preds = %1 + %4 = add nsw i32 0, 32, !dbg !474 + call void @llvm.dbg.value(metadata i32 %4, metadata !470, metadata !DIExpression()), !dbg !469 + br label %7, !dbg !476 + +5: ; preds = %1 + %6 = lshr i64 %0, 32, !dbg !477 + call void @llvm.dbg.value(metadata i64 %6, metadata !468, metadata !DIExpression()), !dbg !469 + br label %7 + +7: ; preds = %5, %3 + %.01 = phi i32 [ %4, %3 ], [ 0, %5 ], !dbg !469 + %.0 = phi i64 [ %0, %3 ], [ %6, %5 ] + call void @llvm.dbg.value(metadata i64 %.0, metadata !468, metadata !DIExpression()), !dbg !469 + call void @llvm.dbg.value(metadata i32 %.01, metadata !470, metadata !DIExpression()), !dbg !469 + %8 = trunc i64 %.0 to i32, !dbg !479 + %9 = call i32 @_ZL19countLeadingZeros32j(i32 %8), !dbg !480 + %10 = add nsw i32 %.01, %9, !dbg !481 + call void @llvm.dbg.value(metadata i32 %10, metadata !470, metadata !DIExpression()), !dbg !469 + ret i32 %10, !dbg !482 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @packFloat64(i32 %0, i32 %1, i64 %2) #2 !dbg !483 { + call void @llvm.dbg.value(metadata i32 %0, metadata !486, metadata !DIExpression()), !dbg !487 + call void @llvm.dbg.value(metadata i32 %1, metadata !488, metadata !DIExpression()), !dbg !487 + call void @llvm.dbg.value(metadata i64 %2, metadata !489, metadata !DIExpression()), !dbg !487 + %4 = sext i32 %0 to i64, !dbg !490 + %5 = shl i64 %4, 63, !dbg !491 + %6 = sext i32 %1 to i64, !dbg !492 + %7 = shl i64 %6, 52, !dbg !493 + %8 = add i64 %5, %7, !dbg !494 + %9 = add i64 %8, %2, !dbg !495 + ret i64 %9, !dbg !496 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @roundAndPackFloat64(i32 %0, i32 %1, i64 %2) #2 !dbg !497 { + %4 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i32 %0, metadata !498, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 %1, metadata !500, metadata !DIExpression()), !dbg !499 + store i64 %2, i64* %4, align 8 + call void @llvm.dbg.declare(metadata i64* %4, metadata !501, metadata !DIExpression()), !dbg !502 + %5 = load i32, i32* @float_rounding_mode, align 4, !dbg !503 + call void @llvm.dbg.value(metadata i32 %5, metadata !504, metadata !DIExpression()), !dbg !499 + %6 = icmp eq i32 %5, 0, !dbg !505 + %7 = zext i1 %6 to i32, !dbg !506 + call void @llvm.dbg.value(metadata i32 %7, metadata !507, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 512, metadata !508, metadata !DIExpression()), !dbg !499 + %8 = icmp ne i32 %7, 0, !dbg !509 + br i1 %8, label %24, label %9, !dbg !511 + +9: ; preds = %3 + %10 = icmp eq i32 %5, 1, !dbg !512 + br i1 %10, label %11, label %12, !dbg !515 + +11: ; preds = %9 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !499 + br label %23, !dbg !516 + +12: ; preds = %9 + call void @llvm.dbg.value(metadata i32 1023, metadata !508, metadata !DIExpression()), !dbg !499 + %13 = icmp ne i32 %0, 0, !dbg !518 + br i1 %13, label %14, label %18, !dbg !521 + +14: ; preds = %12 + %15 = icmp eq i32 %5, 2, !dbg !522 + br i1 %15, label %16, label %17, !dbg !525 + +16: ; preds = %14 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !499 + br label %17, !dbg !526 + +17: ; preds = %16, %14 + %.01 = phi i32 [ 0, %16 ], [ 1023, %14 ], !dbg !527 + call void @llvm.dbg.value(metadata i32 %.01, metadata !508, metadata !DIExpression()), !dbg !499 + br label %22, !dbg !528 + +18: ; preds = %12 + %19 = icmp eq i32 %5, 3, !dbg !529 + br i1 %19, label %20, label %21, !dbg !532 + +20: ; preds = %18 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !499 + br label %21, !dbg !533 + +21: ; preds = %20, %18 + %.12 = phi i32 [ 0, %20 ], [ 1023, %18 ], !dbg !527 + call void @llvm.dbg.value(metadata i32 %.12, metadata !508, metadata !DIExpression()), !dbg !499 + br label %22 + +22: ; preds = %21, %17 + %.2 = phi i32 [ %.01, %17 ], [ %.12, %21 ], !dbg !534 + call void @llvm.dbg.value(metadata i32 %.2, metadata !508, metadata !DIExpression()), !dbg !499 + br label %23 + +23: ; preds = %22, %11 + %.3 = phi i32 [ 0, %11 ], [ %.2, %22 ], !dbg !535 + call void @llvm.dbg.value(metadata i32 %.3, metadata !508, metadata !DIExpression()), !dbg !499 + br label %24, !dbg !536 + +24: ; preds = %23, %3 + %.4 = phi i32 [ 512, %3 ], [ %.3, %23 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.4, metadata !508, metadata !DIExpression()), !dbg !499 + %25 = load i64, i64* %4, align 8, !dbg !537 + %26 = and i64 %25, 1023, !dbg !538 + %27 = trunc i64 %26 to i32, !dbg !537 + call void @llvm.dbg.value(metadata i32 %27, metadata !539, metadata !DIExpression()), !dbg !499 + %28 = trunc i32 %1 to i16, !dbg !540 + %29 = zext i16 %28 to i32, !dbg !542 + %30 = icmp sle i32 2045, %29, !dbg !543 + br i1 %30, label %31, label %64, !dbg !544 + +31: ; preds = %24 + %32 = icmp slt i32 2045, %1, !dbg !545 + br i1 %32, label %40, label %33, !dbg !548 + +33: ; preds = %31 + %34 = icmp eq i32 %1, 2045, !dbg !549 + br i1 %34, label %35, label %50, !dbg !550 + +35: ; preds = %33 + %36 = load i64, i64* %4, align 8, !dbg !551 + %37 = sext i32 %.4 to i64, !dbg !552 + %38 = add i64 %36, %37, !dbg !553 + %39 = icmp slt i64 %38, 0, !dbg !554 + br i1 %39, label %40, label %50, !dbg !555 + +40: ; preds = %35, %31 + call void @_Z11float_raisei(i32 9), !dbg !556 + call void @llvm.dbg.value(metadata i32 %0, metadata !486, metadata !DIExpression()), !dbg !558 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !558 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !558 + %41 = sext i32 %0 to i64, !dbg !560 + %42 = shl i64 %41, 63, !dbg !561 + %43 = sext i32 2047 to i64, !dbg !562 + %44 = shl i64 %43, 52, !dbg !563 + %45 = add i64 %42, %44, !dbg !564 + %46 = add i64 %45, 0, !dbg !565 + %47 = icmp eq i32 %.4, 0, !dbg !566 + %48 = zext i1 %47 to i64, !dbg !567 + %49 = sub i64 %46, %48, !dbg !568 + br label %93, !dbg !569 + +50: ; preds = %35, %33 + %51 = icmp slt i32 %1, 0, !dbg !570 + br i1 %51, label %52, label %63, !dbg !572 + +52: ; preds = %50 + call void @llvm.dbg.value(metadata i32 1, metadata !573, metadata !DIExpression()), !dbg !499 + %53 = load i64, i64* %4, align 8, !dbg !574 + %54 = sub nsw i32 0, %1, !dbg !576 + call void @_Z19shift64RightJammingyiPy(i64 %53, i32 %54, i64* %4), !dbg !577 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !499 + %55 = load i64, i64* %4, align 8, !dbg !578 + %56 = and i64 %55, 1023, !dbg !579 + %57 = trunc i64 %56 to i32, !dbg !578 + call void @llvm.dbg.value(metadata i32 %57, metadata !539, metadata !DIExpression()), !dbg !499 + %58 = icmp ne i32 1, 0, !dbg !580 + br i1 %58, label %59, label %62, !dbg !582 + +59: ; preds = %52 + %60 = icmp ne i32 %57, 0, !dbg !583 + br i1 %60, label %61, label %62, !dbg !584 + +61: ; preds = %59 + call void @_Z11float_raisei(i32 4), !dbg !585 + br label %62, !dbg !585 + +62: ; preds = %61, %59, %52 + br label %63, !dbg !586 + +63: ; preds = %62, %50 + %.03 = phi i32 [ 0, %62 ], [ %1, %50 ] + %.0 = phi i32 [ %57, %62 ], [ %27, %50 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.0, metadata !539, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 %.03, metadata !500, metadata !DIExpression()), !dbg !499 + br label %64, !dbg !587 + +64: ; preds = %63, %24 + %.14 = phi i32 [ %.03, %63 ], [ %1, %24 ] + %.1 = phi i32 [ %.0, %63 ], [ %27, %24 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.1, metadata !539, metadata !DIExpression()), !dbg !499 + call void @llvm.dbg.value(metadata i32 %.14, metadata !500, metadata !DIExpression()), !dbg !499 + %65 = icmp ne i32 %.1, 0, !dbg !588 + br i1 %65, label %66, label %69, !dbg !590 + +66: ; preds = %64 + %67 = load i32, i32* @float_exception_flags, align 4, !dbg !591 + %68 = or i32 %67, 1, !dbg !591 + store i32 %68, i32* @float_exception_flags, align 4, !dbg !591 + br label %69, !dbg !592 + +69: ; preds = %66, %64 + %70 = load i64, i64* %4, align 8, !dbg !593 + %71 = sext i32 %.4 to i64, !dbg !594 + %72 = add i64 %70, %71, !dbg !595 + %73 = lshr i64 %72, 10, !dbg !596 + store i64 %73, i64* %4, align 8, !dbg !597 + %74 = xor i32 %.1, 512, !dbg !598 + %75 = icmp eq i32 %74, 0, !dbg !599 + %76 = zext i1 %75 to i32, !dbg !600 + %77 = and i32 %76, %7, !dbg !601 + %78 = xor i32 %77, -1, !dbg !602 + %79 = sext i32 %78 to i64, !dbg !602 + %80 = load i64, i64* %4, align 8, !dbg !603 + %81 = and i64 %80, %79, !dbg !603 + store i64 %81, i64* %4, align 8, !dbg !603 + %82 = load i64, i64* %4, align 8, !dbg !604 + %83 = icmp eq i64 %82, 0, !dbg !606 + br i1 %83, label %84, label %85, !dbg !607 + +84: ; preds = %69 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !499 + br label %85, !dbg !608 + +85: ; preds = %84, %69 + %.25 = phi i32 [ 0, %84 ], [ %.14, %69 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.25, metadata !500, metadata !DIExpression()), !dbg !499 + %86 = load i64, i64* %4, align 8, !dbg !609 + call void @llvm.dbg.value(metadata i32 %0, metadata !486, metadata !DIExpression()), !dbg !610 + call void @llvm.dbg.value(metadata i32 %.25, metadata !488, metadata !DIExpression()), !dbg !610 + call void @llvm.dbg.value(metadata i64 %86, metadata !489, metadata !DIExpression()), !dbg !610 + %87 = sext i32 %0 to i64, !dbg !612 + %88 = shl i64 %87, 63, !dbg !613 + %89 = sext i32 %.25 to i64, !dbg !614 + %90 = shl i64 %89, 52, !dbg !615 + %91 = add i64 %88, %90, !dbg !616 + %92 = add i64 %91, %86, !dbg !617 + br label %93, !dbg !618 + +93: ; preds = %85, %40 + %.06 = phi i64 [ %49, %40 ], [ %92, %85 ], !dbg !499 + ret i64 %.06, !dbg !619 +} + +; Function Attrs: mustprogress noinline uwtable +define dso_local i64 @float64_div(i64 %0, i64 %1) #4 !dbg !620 { + %3 = alloca i64, align 8 + %4 = alloca i64, align 8 + %5 = alloca i64, align 8 + %6 = alloca i64, align 8 + %7 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i64 %0, metadata !625, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !627, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.declare(metadata i64* %4, metadata !628, metadata !DIExpression()), !dbg !629 + call void @llvm.dbg.declare(metadata i64* %5, metadata !630, metadata !DIExpression()), !dbg !631 + call void @llvm.dbg.declare(metadata i64* %6, metadata !632, metadata !DIExpression()), !dbg !633 + call void @llvm.dbg.declare(metadata i64* %7, metadata !634, metadata !DIExpression()), !dbg !635 + call void @llvm.dbg.value(metadata i64 %0, metadata !430, metadata !DIExpression()), !dbg !636 + %8 = and i64 %0, 4503599627370495, !dbg !638 + call void @llvm.dbg.value(metadata i64 %8, metadata !639, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %0, metadata !437, metadata !DIExpression()), !dbg !640 + %9 = lshr i64 %0, 52, !dbg !642 + %10 = and i64 %9, 2047, !dbg !643 + %11 = trunc i64 %10 to i32, !dbg !644 + call void @llvm.dbg.value(metadata i32 %11, metadata !645, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %0, metadata !444, metadata !DIExpression()), !dbg !646 + %12 = lshr i64 %0, 63, !dbg !648 + %13 = trunc i64 %12 to i32, !dbg !649 + call void @llvm.dbg.value(metadata i32 %13, metadata !650, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !430, metadata !DIExpression()), !dbg !651 + %14 = and i64 %1, 4503599627370495, !dbg !653 + call void @llvm.dbg.value(metadata i64 %14, metadata !654, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !437, metadata !DIExpression()), !dbg !655 + %15 = lshr i64 %1, 52, !dbg !657 + %16 = and i64 %15, 2047, !dbg !658 + %17 = trunc i64 %16 to i32, !dbg !659 + call void @llvm.dbg.value(metadata i32 %17, metadata !660, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %1, metadata !444, metadata !DIExpression()), !dbg !661 + %18 = lshr i64 %1, 63, !dbg !663 + %19 = trunc i64 %18 to i32, !dbg !664 + call void @llvm.dbg.value(metadata i32 %19, metadata !665, metadata !DIExpression()), !dbg !626 + %20 = xor i32 %13, %19, !dbg !666 + call void @llvm.dbg.value(metadata i32 %20, metadata !667, metadata !DIExpression()), !dbg !626 + %21 = icmp eq i32 %11, 2047, !dbg !668 + br i1 %21, label %22, label %40, !dbg !670 + +22: ; preds = %2 + %23 = icmp ne i64 %8, 0, !dbg !671 + br i1 %23, label %24, label %26, !dbg !674 + +24: ; preds = %22 + %25 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !675 + br label %213, !dbg !676 + +26: ; preds = %22 + %27 = icmp eq i32 %17, 2047, !dbg !677 + br i1 %27, label %28, label %33, !dbg !679 + +28: ; preds = %26 + %29 = icmp ne i64 %14, 0, !dbg !680 + br i1 %29, label %30, label %32, !dbg !683 + +30: ; preds = %28 + %31 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !684 + br label %213, !dbg !685 + +32: ; preds = %28 + call void @_Z11float_raisei(i32 16), !dbg !686 + br label %213, !dbg !687 + +33: ; preds = %26 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !688 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !688 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !688 + %34 = sext i32 %20 to i64, !dbg !690 + %35 = shl i64 %34, 63, !dbg !691 + %36 = sext i32 2047 to i64, !dbg !692 + %37 = shl i64 %36, 52, !dbg !693 + %38 = add i64 %35, %37, !dbg !694 + %39 = add i64 %38, 0, !dbg !695 + br label %213, !dbg !696 + +40: ; preds = %2 + %41 = icmp eq i32 %17, 2047, !dbg !697 + br i1 %41, label %42, label %53, !dbg !699 + +42: ; preds = %40 + %43 = icmp ne i64 %14, 0, !dbg !700 + br i1 %43, label %44, label %46, !dbg !703 + +44: ; preds = %42 + %45 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !704 + br label %213, !dbg !705 + +46: ; preds = %42 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !706 + call void @llvm.dbg.value(metadata i32 0, metadata !488, metadata !DIExpression()), !dbg !706 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !706 + %47 = sext i32 %20 to i64, !dbg !708 + %48 = shl i64 %47, 63, !dbg !709 + %49 = sext i32 0 to i64, !dbg !710 + %50 = shl i64 %49, 52, !dbg !711 + %51 = add i64 %48, %50, !dbg !712 + %52 = add i64 %51, 0, !dbg !713 + br label %213, !dbg !714 + +53: ; preds = %40 + %54 = icmp eq i32 %17, 0, !dbg !715 + br i1 %54, label %55, label %75, !dbg !717 + +55: ; preds = %53 + %56 = icmp eq i64 %14, 0, !dbg !718 + br i1 %56, label %57, label %69, !dbg !721 + +57: ; preds = %55 + %58 = sext i32 %11 to i64, !dbg !722 + %59 = or i64 %58, %8, !dbg !725 + %60 = icmp eq i64 %59, 0, !dbg !726 + br i1 %60, label %61, label %62, !dbg !727 + +61: ; preds = %57 + call void @_Z11float_raisei(i32 16), !dbg !728 + br label %213, !dbg !730 + +62: ; preds = %57 + call void @_Z11float_raisei(i32 2), !dbg !731 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !732 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !732 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !732 + %63 = sext i32 %20 to i64, !dbg !734 + %64 = shl i64 %63, 63, !dbg !735 + %65 = sext i32 2047 to i64, !dbg !736 + %66 = shl i64 %65, 52, !dbg !737 + %67 = add i64 %64, %66, !dbg !738 + %68 = add i64 %67, 0, !dbg !739 + br label %213, !dbg !740 + +69: ; preds = %55 + call void @llvm.dbg.value(metadata i64 %14, metadata !453, metadata !DIExpression()), !dbg !741 + call void @llvm.dbg.value(metadata i32* undef, metadata !455, metadata !DIExpression()), !dbg !741 + call void @llvm.dbg.value(metadata i64* undef, metadata !456, metadata !DIExpression()), !dbg !741 + %70 = call i32 @_ZL19countLeadingZeros64y(i64 %14), !dbg !743 + %71 = sub nsw i32 %70, 11, !dbg !744 + call void @llvm.dbg.value(metadata i32 %71, metadata !459, metadata !DIExpression()), !dbg !741 + %72 = zext i32 %71 to i64, !dbg !745 + %73 = shl i64 %14, %72, !dbg !745 + call void @llvm.dbg.value(metadata i64 %73, metadata !654, metadata !DIExpression()), !dbg !626 + %74 = sub nsw i32 1, %71, !dbg !746 + call void @llvm.dbg.value(metadata i32 %74, metadata !660, metadata !DIExpression()), !dbg !626 + br label %75, !dbg !747 + +75: ; preds = %69, %53 + %.015 = phi i64 [ %73, %69 ], [ %14, %53 ], !dbg !626 + %.012 = phi i32 [ %74, %69 ], [ %17, %53 ], !dbg !626 + call void @llvm.dbg.value(metadata i32 %.012, metadata !660, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %.015, metadata !654, metadata !DIExpression()), !dbg !626 + %76 = icmp eq i32 %11, 0, !dbg !748 + br i1 %76, label %77, label %92, !dbg !750 + +77: ; preds = %75 + %78 = icmp eq i64 %8, 0, !dbg !751 + br i1 %78, label %79, label %86, !dbg !754 + +79: ; preds = %77 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !755 + call void @llvm.dbg.value(metadata i32 0, metadata !488, metadata !DIExpression()), !dbg !755 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !755 + %80 = sext i32 %20 to i64, !dbg !757 + %81 = shl i64 %80, 63, !dbg !758 + %82 = sext i32 0 to i64, !dbg !759 + %83 = shl i64 %82, 52, !dbg !760 + %84 = add i64 %81, %83, !dbg !761 + %85 = add i64 %84, 0, !dbg !762 + br label %213, !dbg !763 + +86: ; preds = %77 + call void @llvm.dbg.value(metadata i64 %8, metadata !453, metadata !DIExpression()), !dbg !764 + call void @llvm.dbg.value(metadata i32* undef, metadata !455, metadata !DIExpression()), !dbg !764 + call void @llvm.dbg.value(metadata i64* undef, metadata !456, metadata !DIExpression()), !dbg !764 + %87 = call i32 @_ZL19countLeadingZeros64y(i64 %8), !dbg !766 + %88 = sub nsw i32 %87, 11, !dbg !767 + call void @llvm.dbg.value(metadata i32 %88, metadata !459, metadata !DIExpression()), !dbg !764 + %89 = zext i32 %88 to i64, !dbg !768 + %90 = shl i64 %8, %89, !dbg !768 + call void @llvm.dbg.value(metadata i64 %90, metadata !639, metadata !DIExpression()), !dbg !626 + %91 = sub nsw i32 1, %88, !dbg !769 + call void @llvm.dbg.value(metadata i32 %91, metadata !645, metadata !DIExpression()), !dbg !626 + br label %92, !dbg !770 + +92: ; preds = %86, %75 + %.013 = phi i64 [ %90, %86 ], [ %8, %75 ], !dbg !626 + %.011 = phi i32 [ %91, %86 ], [ %11, %75 ], !dbg !626 + call void @llvm.dbg.value(metadata i32 %.011, metadata !645, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i64 %.013, metadata !639, metadata !DIExpression()), !dbg !626 + %93 = sub nsw i32 %.011, %.012, !dbg !771 + %94 = add nsw i32 %93, 1021, !dbg !772 + call void @llvm.dbg.value(metadata i32 %94, metadata !773, metadata !DIExpression()), !dbg !626 + %95 = or i64 %.013, 4503599627370496, !dbg !774 + %96 = shl i64 %95, 10, !dbg !775 + call void @llvm.dbg.value(metadata i64 %96, metadata !639, metadata !DIExpression()), !dbg !626 + %97 = or i64 %.015, 4503599627370496, !dbg !776 + %98 = shl i64 %97, 11, !dbg !777 + call void @llvm.dbg.value(metadata i64 %98, metadata !654, metadata !DIExpression()), !dbg !626 + %99 = add i64 %96, %96, !dbg !778 + %100 = icmp ule i64 %98, %99, !dbg !780 + br i1 %100, label %101, label %104, !dbg !781 + +101: ; preds = %92 + %102 = lshr i64 %96, 1, !dbg !782 + call void @llvm.dbg.value(metadata i64 %102, metadata !639, metadata !DIExpression()), !dbg !626 + %103 = add nsw i32 %94, 1, !dbg !784 + call void @llvm.dbg.value(metadata i32 %103, metadata !773, metadata !DIExpression()), !dbg !626 + br label %104, !dbg !785 + +104: ; preds = %101, %92 + %.114 = phi i64 [ %102, %101 ], [ %96, %92 ], !dbg !626 + %.01 = phi i32 [ %103, %101 ], [ %94, %92 ], !dbg !626 + call void @llvm.dbg.value(metadata i64 %.114, metadata !639, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i32 %.01, metadata !773, metadata !DIExpression()), !dbg !626 + %105 = call i64 @_ZL18estimateDiv128To64yyy(i64 %.114, i64 0, i64 %98), !dbg !786 + call void @llvm.dbg.value(metadata i64 %105, metadata !787, metadata !DIExpression()), !dbg !626 + %106 = and i64 %105, 511, !dbg !788 + %107 = icmp ule i64 %106, 2, !dbg !790 + br i1 %107, label %108, label %123, !dbg !791 + +108: ; preds = %104 + call void @_Z10mul64To128yyPyS_(i64 %98, i64 %105, i64* %6, i64* %7), !dbg !792 + %109 = load i64, i64* %6, align 8, !dbg !794 + %110 = load i64, i64* %7, align 8, !dbg !795 + call void @_Z6sub128yyyyPyS_(i64 %.114, i64 0, i64 %109, i64 %110, i64* %4, i64* %5), !dbg !796 + br label %111, !dbg !797 + +111: ; preds = %114, %108 + %.0 = phi i64 [ %105, %108 ], [ %115, %114 ], !dbg !626 + call void @llvm.dbg.value(metadata i64 %.0, metadata !787, metadata !DIExpression()), !dbg !626 + %112 = load i64, i64* %4, align 8, !dbg !798 + %113 = icmp slt i64 %112, 0, !dbg !799 + br i1 %113, label %114, label %118, !dbg !797 + +114: ; preds = %111 + %115 = add i64 %.0, -1, !dbg !800 + call void @llvm.dbg.value(metadata i64 %115, metadata !787, metadata !DIExpression()), !dbg !626 + %116 = load i64, i64* %4, align 8, !dbg !802 + %117 = load i64, i64* %5, align 8, !dbg !803 + call void @_Z6add128yyyyPyS_(i64 %116, i64 %117, i64 0, i64 %98, i64* %4, i64* %5), !dbg !804 + br label %111, !dbg !797, !llvm.loop !805 + +118: ; preds = %111 + %119 = load i64, i64* %5, align 8, !dbg !808 + %120 = icmp ne i64 %119, 0, !dbg !809 + %121 = zext i1 %120 to i64, !dbg !810 + %122 = or i64 %.0, %121, !dbg !811 + call void @llvm.dbg.value(metadata i64 %122, metadata !787, metadata !DIExpression()), !dbg !626 + br label %123, !dbg !812 + +123: ; preds = %118, %104 + %.1 = phi i64 [ %122, %118 ], [ %105, %104 ], !dbg !626 + call void @llvm.dbg.value(metadata i64 %.1, metadata !787, metadata !DIExpression()), !dbg !626 + call void @llvm.dbg.value(metadata i32 %20, metadata !498, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 %.01, metadata !500, metadata !DIExpression()), !dbg !813 + store i64 %.1, i64* %3, align 8 + call void @llvm.dbg.declare(metadata i64* %3, metadata !501, metadata !DIExpression()) #5, !dbg !815 + %124 = load i32, i32* @float_rounding_mode, align 4, !dbg !816 + call void @llvm.dbg.value(metadata i32 %124, metadata !504, metadata !DIExpression()), !dbg !813 + %125 = icmp eq i32 %124, 0, !dbg !817 + %126 = zext i1 %125 to i32, !dbg !818 + call void @llvm.dbg.value(metadata i32 %126, metadata !507, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 512, metadata !508, metadata !DIExpression()), !dbg !813 + %127 = icmp ne i32 %126, 0, !dbg !819 + br i1 %127, label %143, label %128, !dbg !820 + +128: ; preds = %123 + %129 = icmp eq i32 %124, 1, !dbg !821 + br i1 %129, label %130, label %131, !dbg !822 + +130: ; preds = %128 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !813 + br label %142, !dbg !823 + +131: ; preds = %128 + call void @llvm.dbg.value(metadata i32 1023, metadata !508, metadata !DIExpression()), !dbg !813 + %132 = icmp ne i32 %20, 0, !dbg !824 + br i1 %132, label %133, label %137, !dbg !825 + +133: ; preds = %131 + %134 = icmp eq i32 %124, 2, !dbg !826 + br i1 %134, label %135, label %136, !dbg !827 + +135: ; preds = %133 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !813 + br label %136, !dbg !828 + +136: ; preds = %135, %133 + %.05 = phi i32 [ 0, %135 ], [ 1023, %133 ], !dbg !829 + call void @llvm.dbg.value(metadata i32 %.05, metadata !508, metadata !DIExpression()), !dbg !813 + br label %141, !dbg !830 + +137: ; preds = %131 + %138 = icmp eq i32 %124, 3, !dbg !831 + br i1 %138, label %139, label %140, !dbg !832 + +139: ; preds = %137 + call void @llvm.dbg.value(metadata i32 0, metadata !508, metadata !DIExpression()), !dbg !813 + br label %140, !dbg !833 + +140: ; preds = %139, %137 + %.16 = phi i32 [ 0, %139 ], [ 1023, %137 ], !dbg !829 + call void @llvm.dbg.value(metadata i32 %.16, metadata !508, metadata !DIExpression()), !dbg !813 + br label %141 + +141: ; preds = %140, %136 + %.2 = phi i32 [ %.05, %136 ], [ %.16, %140 ], !dbg !834 + call void @llvm.dbg.value(metadata i32 %.2, metadata !508, metadata !DIExpression()), !dbg !813 + br label %142 + +142: ; preds = %141, %130 + %.3 = phi i32 [ 0, %130 ], [ %.2, %141 ], !dbg !835 + call void @llvm.dbg.value(metadata i32 %.3, metadata !508, metadata !DIExpression()), !dbg !813 + br label %143, !dbg !836 + +143: ; preds = %142, %123 + %.4 = phi i32 [ 512, %123 ], [ %.3, %142 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.4, metadata !508, metadata !DIExpression()), !dbg !813 + %144 = load i64, i64* %3, align 8, !dbg !837 + %145 = and i64 %144, 1023, !dbg !838 + %146 = trunc i64 %145 to i32, !dbg !837 + call void @llvm.dbg.value(metadata i32 %146, metadata !539, metadata !DIExpression()), !dbg !813 + %147 = trunc i32 %.01 to i16, !dbg !839 + %148 = zext i16 %147 to i32, !dbg !840 + %149 = icmp sle i32 2045, %148, !dbg !841 + br i1 %149, label %150, label %183, !dbg !842 + +150: ; preds = %143 + %151 = icmp slt i32 2045, %.01, !dbg !843 + br i1 %151, label %159, label %152, !dbg !844 + +152: ; preds = %150 + %153 = icmp eq i32 %.01, 2045, !dbg !845 + br i1 %153, label %154, label %169, !dbg !846 + +154: ; preds = %152 + %155 = load i64, i64* %3, align 8, !dbg !847 + %156 = sext i32 %.4 to i64, !dbg !848 + %157 = add i64 %155, %156, !dbg !849 + %158 = icmp slt i64 %157, 0, !dbg !850 + br i1 %158, label %159, label %169, !dbg !851 + +159: ; preds = %154, %150 + call void @_Z11float_raisei(i32 9) #5, !dbg !852 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !853 + call void @llvm.dbg.value(metadata i32 2047, metadata !488, metadata !DIExpression()), !dbg !853 + call void @llvm.dbg.value(metadata i64 0, metadata !489, metadata !DIExpression()), !dbg !853 + %160 = sext i32 %20 to i64, !dbg !855 + %161 = shl i64 %160, 63, !dbg !856 + %162 = sext i32 2047 to i64, !dbg !857 + %163 = shl i64 %162, 52, !dbg !858 + %164 = add i64 %161, %163, !dbg !859 + %165 = add i64 %164, 0, !dbg !860 + %166 = icmp eq i32 %.4, 0, !dbg !861 + %167 = zext i1 %166 to i64, !dbg !862 + %168 = sub i64 %165, %167, !dbg !863 + br label %212, !dbg !864 + +169: ; preds = %154, %152 + %170 = icmp slt i32 %.01, 0, !dbg !865 + br i1 %170, label %171, label %182, !dbg !866 + +171: ; preds = %169 + call void @llvm.dbg.value(metadata i32 1, metadata !573, metadata !DIExpression()), !dbg !813 + %172 = load i64, i64* %3, align 8, !dbg !867 + %173 = sub nsw i32 0, %.01, !dbg !868 + call void @_Z19shift64RightJammingyiPy(i64 %172, i32 %173, i64* %3) #5, !dbg !869 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !813 + %174 = load i64, i64* %3, align 8, !dbg !870 + %175 = and i64 %174, 1023, !dbg !871 + %176 = trunc i64 %175 to i32, !dbg !870 + call void @llvm.dbg.value(metadata i32 %176, metadata !539, metadata !DIExpression()), !dbg !813 + %177 = icmp ne i32 1, 0, !dbg !872 + br i1 %177, label %178, label %181, !dbg !873 + +178: ; preds = %171 + %179 = icmp ne i32 %176, 0, !dbg !874 + br i1 %179, label %180, label %181, !dbg !875 + +180: ; preds = %178 + call void @_Z11float_raisei(i32 4) #5, !dbg !876 + br label %181, !dbg !876 + +181: ; preds = %180, %178, %171 + br label %182, !dbg !877 + +182: ; preds = %181, %169 + %.07 = phi i32 [ 0, %181 ], [ %.01, %169 ] + %.03 = phi i32 [ %176, %181 ], [ %146, %169 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.03, metadata !539, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 %.07, metadata !500, metadata !DIExpression()), !dbg !813 + br label %183, !dbg !878 + +183: ; preds = %182, %143 + %.18 = phi i32 [ %.07, %182 ], [ %.01, %143 ] + %.14 = phi i32 [ %.03, %182 ], [ %146, %143 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.14, metadata !539, metadata !DIExpression()), !dbg !813 + call void @llvm.dbg.value(metadata i32 %.18, metadata !500, metadata !DIExpression()), !dbg !813 + %184 = icmp ne i32 %.14, 0, !dbg !879 + br i1 %184, label %185, label %188, !dbg !880 + +185: ; preds = %183 + %186 = load i32, i32* @float_exception_flags, align 4, !dbg !881 + %187 = or i32 %186, 1, !dbg !881 + store i32 %187, i32* @float_exception_flags, align 4, !dbg !881 + br label %188, !dbg !882 + +188: ; preds = %185, %183 + %189 = load i64, i64* %3, align 8, !dbg !883 + %190 = sext i32 %.4 to i64, !dbg !884 + %191 = add i64 %189, %190, !dbg !885 + %192 = lshr i64 %191, 10, !dbg !886 + store i64 %192, i64* %3, align 8, !dbg !887 + %193 = xor i32 %.14, 512, !dbg !888 + %194 = icmp eq i32 %193, 0, !dbg !889 + %195 = zext i1 %194 to i32, !dbg !890 + %196 = and i32 %195, %126, !dbg !891 + %197 = xor i32 %196, -1, !dbg !892 + %198 = sext i32 %197 to i64, !dbg !892 + %199 = load i64, i64* %3, align 8, !dbg !893 + %200 = and i64 %199, %198, !dbg !893 + store i64 %200, i64* %3, align 8, !dbg !893 + %201 = load i64, i64* %3, align 8, !dbg !894 + %202 = icmp eq i64 %201, 0, !dbg !895 + br i1 %202, label %203, label %204, !dbg !896 + +203: ; preds = %188 + call void @llvm.dbg.value(metadata i32 0, metadata !500, metadata !DIExpression()), !dbg !813 + br label %204, !dbg !897 + +204: ; preds = %203, %188 + %.29 = phi i32 [ 0, %203 ], [ %.18, %188 ], !dbg !813 + call void @llvm.dbg.value(metadata i32 %.29, metadata !500, metadata !DIExpression()), !dbg !813 + %205 = load i64, i64* %3, align 8, !dbg !898 + call void @llvm.dbg.value(metadata i32 %20, metadata !486, metadata !DIExpression()), !dbg !899 + call void @llvm.dbg.value(metadata i32 %.29, metadata !488, metadata !DIExpression()), !dbg !899 + call void @llvm.dbg.value(metadata i64 %205, metadata !489, metadata !DIExpression()), !dbg !899 + %206 = sext i32 %20 to i64, !dbg !901 + %207 = shl i64 %206, 63, !dbg !902 + %208 = sext i32 %.29 to i64, !dbg !903 + %209 = shl i64 %208, 52, !dbg !904 + %210 = add i64 %207, %209, !dbg !905 + %211 = add i64 %210, %205, !dbg !906 + br label %212, !dbg !907 + +212: ; preds = %204, %159 + %.010 = phi i64 [ %168, %159 ], [ %211, %204 ], !dbg !813 + br label %213, !dbg !908 + +213: ; preds = %212, %79, %62, %61, %46, %44, %33, %32, %30, %24 + %.02 = phi i64 [ %25, %24 ], [ %31, %30 ], [ 9223372036854775807, %32 ], [ %39, %33 ], [ %45, %44 ], [ %52, %46 ], [ 9223372036854775807, %61 ], [ %68, %62 ], [ %85, %79 ], [ %.010, %212 ], !dbg !626 + ret i64 %.02, !dbg !909 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1) #0 !dbg !910 { + call void @llvm.dbg.value(metadata i64 %0, metadata !913, metadata !DIExpression()), !dbg !914 + call void @llvm.dbg.value(metadata i64 %1, metadata !915, metadata !DIExpression()), !dbg !914 + %3 = call i32 @_Z14float64_is_nany(i64 %0), !dbg !916 + call void @llvm.dbg.value(metadata i32 %3, metadata !917, metadata !DIExpression()), !dbg !914 + %4 = call i32 @_Z24float64_is_signaling_nany(i64 %0), !dbg !918 + call void @llvm.dbg.value(metadata i32 %4, metadata !919, metadata !DIExpression()), !dbg !914 + %5 = call i32 @_Z14float64_is_nany(i64 %1), !dbg !920 + call void @llvm.dbg.value(metadata i32 %5, metadata !921, metadata !DIExpression()), !dbg !914 + %6 = call i32 @_Z24float64_is_signaling_nany(i64 %1), !dbg !922 + call void @llvm.dbg.value(metadata i32 %6, metadata !923, metadata !DIExpression()), !dbg !914 + %7 = or i64 %0, 2251799813685248, !dbg !924 + call void @llvm.dbg.value(metadata i64 %7, metadata !913, metadata !DIExpression()), !dbg !914 + %8 = or i64 %1, 2251799813685248, !dbg !925 + call void @llvm.dbg.value(metadata i64 %8, metadata !915, metadata !DIExpression()), !dbg !914 + %9 = or i32 %4, %6, !dbg !926 + %10 = icmp ne i32 %9, 0, !dbg !928 + br i1 %10, label %11, label %12, !dbg !929 + +11: ; preds = %2 + call void @_Z11float_raisei(i32 16), !dbg !930 + br label %12, !dbg !930 + +12: ; preds = %11, %2 + %13 = icmp ne i32 %6, 0, !dbg !931 + br i1 %13, label %14, label %15, !dbg !931 + +14: ; preds = %12 + br label %26, !dbg !931 + +15: ; preds = %12 + %16 = icmp ne i32 %4, 0, !dbg !932 + br i1 %16, label %17, label %18, !dbg !932 + +17: ; preds = %15 + br label %24, !dbg !932 + +18: ; preds = %15 + %19 = icmp ne i32 %5, 0, !dbg !933 + br i1 %19, label %20, label %21, !dbg !933 + +20: ; preds = %18 + br label %22, !dbg !933 + +21: ; preds = %18 + br label %22, !dbg !933 + +22: ; preds = %21, %20 + %23 = phi i64 [ %8, %20 ], [ %7, %21 ], !dbg !933 + br label %24, !dbg !932 + +24: ; preds = %22, %17 + %25 = phi i64 [ %7, %17 ], [ %23, %22 ], !dbg !932 + br label %26, !dbg !931 + +26: ; preds = %24, %14 + %27 = phi i64 [ %8, %14 ], [ %25, %24 ], !dbg !931 + ret i64 %27, !dbg !934 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i64 @_ZL18estimateDiv128To64yyy(i64 %0, i64 %1, i64 %2) #0 !dbg !935 { + %4 = alloca i64, align 8 + %5 = alloca i64, align 8 + %6 = alloca i64, align 8 + %7 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i64 %0, metadata !938, metadata !DIExpression()), !dbg !939 + call void @llvm.dbg.value(metadata i64 %1, metadata !940, metadata !DIExpression()), !dbg !939 + call void @llvm.dbg.value(metadata i64 %2, metadata !941, metadata !DIExpression()), !dbg !939 + call void @llvm.dbg.declare(metadata i64* %4, metadata !942, metadata !DIExpression()), !dbg !943 + call void @llvm.dbg.declare(metadata i64* %5, metadata !944, metadata !DIExpression()), !dbg !945 + call void @llvm.dbg.declare(metadata i64* %6, metadata !946, metadata !DIExpression()), !dbg !947 + call void @llvm.dbg.declare(metadata i64* %7, metadata !948, metadata !DIExpression()), !dbg !949 + %8 = icmp ule i64 %2, %0, !dbg !950 + br i1 %8, label %9, label %10, !dbg !952 + +9: ; preds = %3 + br label %46, !dbg !953 + +10: ; preds = %3 + %11 = lshr i64 %2, 32, !dbg !954 + call void @llvm.dbg.value(metadata i64 %11, metadata !955, metadata !DIExpression()), !dbg !939 + %12 = shl i64 %11, 32, !dbg !956 + %13 = icmp ule i64 %12, %0, !dbg !957 + br i1 %13, label %14, label %15, !dbg !958 + +14: ; preds = %10 + br label %18, !dbg !958 + +15: ; preds = %10 + %16 = udiv i64 %0, %11, !dbg !959 + %17 = shl i64 %16, 32, !dbg !960 + br label %18, !dbg !958 + +18: ; preds = %15, %14 + %19 = phi i64 [ -4294967296, %14 ], [ %17, %15 ], !dbg !958 + call void @llvm.dbg.value(metadata i64 %19, metadata !961, metadata !DIExpression()), !dbg !939 + call void @_Z10mul64To128yyPyS_(i64 %2, i64 %19, i64* %6, i64* %7), !dbg !962 + %20 = load i64, i64* %6, align 8, !dbg !963 + %21 = load i64, i64* %7, align 8, !dbg !964 + call void @_Z6sub128yyyyPyS_(i64 %0, i64 %1, i64 %20, i64 %21, i64* %4, i64* %5), !dbg !965 + br label %22, !dbg !966 + +22: ; preds = %25, %18 + %.01 = phi i64 [ %19, %18 ], [ %26, %25 ], !dbg !939 + call void @llvm.dbg.value(metadata i64 %.01, metadata !961, metadata !DIExpression()), !dbg !939 + %23 = load i64, i64* %4, align 8, !dbg !967 + %24 = icmp slt i64 %23, 0, !dbg !968 + br i1 %24, label %25, label %30, !dbg !966 + +25: ; preds = %22 + %26 = sub i64 %.01, 4294967296, !dbg !969 + call void @llvm.dbg.value(metadata i64 %26, metadata !961, metadata !DIExpression()), !dbg !939 + %27 = shl i64 %2, 32, !dbg !971 + call void @llvm.dbg.value(metadata i64 %27, metadata !972, metadata !DIExpression()), !dbg !939 + %28 = load i64, i64* %4, align 8, !dbg !973 + %29 = load i64, i64* %5, align 8, !dbg !974 + call void @_Z6add128yyyyPyS_(i64 %28, i64 %29, i64 %11, i64 %27, i64* %4, i64* %5), !dbg !975 + br label %22, !dbg !966, !llvm.loop !976 + +30: ; preds = %22 + %31 = load i64, i64* %4, align 8, !dbg !978 + %32 = shl i64 %31, 32, !dbg !979 + %33 = load i64, i64* %5, align 8, !dbg !980 + %34 = lshr i64 %33, 32, !dbg !981 + %35 = or i64 %32, %34, !dbg !982 + store i64 %35, i64* %4, align 8, !dbg !983 + %36 = shl i64 %11, 32, !dbg !984 + %37 = load i64, i64* %4, align 8, !dbg !985 + %38 = icmp ule i64 %36, %37, !dbg !986 + br i1 %38, label %39, label %40, !dbg !987 + +39: ; preds = %30 + br label %43, !dbg !987 + +40: ; preds = %30 + %41 = load i64, i64* %4, align 8, !dbg !988 + %42 = udiv i64 %41, %11, !dbg !989 + br label %43, !dbg !987 + +43: ; preds = %40, %39 + %44 = phi i64 [ 4294967295, %39 ], [ %42, %40 ], !dbg !987 + %45 = or i64 %.01, %44, !dbg !990 + call void @llvm.dbg.value(metadata i64 %45, metadata !961, metadata !DIExpression()), !dbg !939 + br label %46, !dbg !991 + +46: ; preds = %43, %9 + %.0 = phi i64 [ -1, %9 ], [ %45, %43 ], !dbg !939 + ret i64 %.0, !dbg !992 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i32 @_ZL19countLeadingZeros32j(i32 %0) #0 !dbg !20 { + call void @llvm.dbg.value(metadata i32 %0, metadata !993, metadata !DIExpression()), !dbg !994 + call void @llvm.dbg.value(metadata i32 0, metadata !995, metadata !DIExpression()), !dbg !994 + %2 = icmp ult i32 %0, 65536, !dbg !996 + br i1 %2, label %3, label %6, !dbg !998 + +3: ; preds = %1 + %4 = add nsw i32 0, 16, !dbg !999 + call void @llvm.dbg.value(metadata i32 %4, metadata !995, metadata !DIExpression()), !dbg !994 + %5 = shl i32 %0, 16, !dbg !1001 + call void @llvm.dbg.value(metadata i32 %5, metadata !993, metadata !DIExpression()), !dbg !994 + br label %6, !dbg !1002 + +6: ; preds = %3, %1 + %.01 = phi i32 [ %4, %3 ], [ 0, %1 ], !dbg !994 + %.0 = phi i32 [ %5, %3 ], [ %0, %1 ] + call void @llvm.dbg.value(metadata i32 %.0, metadata !993, metadata !DIExpression()), !dbg !994 + call void @llvm.dbg.value(metadata i32 %.01, metadata !995, metadata !DIExpression()), !dbg !994 + %7 = icmp ult i32 %.0, 16777216, !dbg !1003 + br i1 %7, label %8, label %11, !dbg !1005 + +8: ; preds = %6 + %9 = add nsw i32 %.01, 8, !dbg !1006 + call void @llvm.dbg.value(metadata i32 %9, metadata !995, metadata !DIExpression()), !dbg !994 + %10 = shl i32 %.0, 8, !dbg !1008 + call void @llvm.dbg.value(metadata i32 %10, metadata !993, metadata !DIExpression()), !dbg !994 + br label %11, !dbg !1009 + +11: ; preds = %8, %6 + %.12 = phi i32 [ %9, %8 ], [ %.01, %6 ], !dbg !994 + %.1 = phi i32 [ %10, %8 ], [ %.0, %6 ], !dbg !994 + call void @llvm.dbg.value(metadata i32 %.1, metadata !993, metadata !DIExpression()), !dbg !994 + call void @llvm.dbg.value(metadata i32 %.12, metadata !995, metadata !DIExpression()), !dbg !994 + %12 = lshr i32 %.1, 24, !dbg !1010 + %13 = zext i32 %12 to i64, !dbg !1011 + %14 = getelementptr inbounds [256 x i32], [256 x i32]* bitcast (<{ [128 x i32], [128 x i32] }>* @_ZZL19countLeadingZeros32jE21countLeadingZerosHigh to [256 x i32]*), i64 0, i64 %13, !dbg !1011 + %15 = load i32, i32* %14, align 4, !dbg !1011 + %16 = add nsw i32 %.12, %15, !dbg !1012 + call void @llvm.dbg.value(metadata i32 %16, metadata !995, metadata !DIExpression()), !dbg !994 + ret i32 %16, !dbg !1013 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { mustprogress noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { alwaysinline mustprogress nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { alwaysinline mustprogress uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #4 = { mustprogress noinline uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #5 = { nounwind } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!272, !273, !274, !275, !276} +!llvm.ident = !{!277} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "float_rounding_mode", scope: !2, file: !3, line: 60, type: !16, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "Ubuntu clang version 13.0.1-2ubuntu2.2", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !13, imports: !30, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "dfdiv/float64_div.cpp", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!4 = !{} +!5 = !{!6, !9, !11} +!6 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits64", file: !7, line: 70, baseType: !8) +!7 = !DIFile(filename: "dfdiv/include/SPARC-GCC.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!8 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!9 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits16", file: !7, line: 68, baseType: !10) +!10 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned) +!11 = !DIDerivedType(tag: DW_TAG_typedef, name: "sbits64", file: !7, line: 71, baseType: !12) +!12 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!13 = !{!0, !14, !18} +!14 = !DIGlobalVariableExpression(var: !15, expr: !DIExpression()) +!15 = distinct !DIGlobalVariable(name: "float_exception_flags", scope: !2, file: !3, line: 61, type: !16, isLocal: false, isDefinition: true) +!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "int8", file: !7, line: 59, baseType: !17) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) +!19 = distinct !DIGlobalVariable(name: "countLeadingZerosHigh", scope: !20, file: !21, line: 189, type: !26, isLocal: true, isDefinition: true) +!20 = distinct !DISubprogram(name: "countLeadingZeros32", linkageName: "_ZL19countLeadingZeros32j", scope: !21, file: !21, line: 187, type: !22, scopeLine: 188, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!21 = !DIFile(filename: "dfdiv/include/softfloat-macros", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!22 = !DISubroutineType(types: !23) +!23 = !{!16, !24} +!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits32", file: !7, line: 69, baseType: !25) +!25 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 8192, elements: !28) +!27 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !16) +!28 = !{!29} +!29 = !DISubrange(count: 256) +!30 = !{!31, !38, !42, !49, !53, !58, !60, !68, !72, !76, !90, !94, !98, !102, !106, !111, !115, !119, !123, !127, !135, !139, !143, !145, !149, !153, !157, !163, !167, !171, !173, !181, !185, !192, !194, !198, !202, !206, !210, !214, !219, !224, !225, !226, !227, !229, !230, !231, !232, !233, !234, !235, !237, !238, !239, !240, !241, !242, !243, !248, !249, !250, !251, !252, !253, !254, !255, !256, !257, !258, !259, !260, !261, !262, !263, !264, !265, !266, !267, !268, !269, !270, !271} +!31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !33, file: !37, line: 52) +!32 = !DINamespace(name: "std", scope: null) +!33 = !DISubprogram(name: "abs", scope: !34, file: !34, line: 848, type: !35, flags: DIFlagPrototyped, spFlags: 0) +!34 = !DIFile(filename: "/usr/include/stdlib.h", directory: "") +!35 = !DISubroutineType(types: !36) +!36 = !{!17, !17} +!37 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_abs.h", directory: "") +!38 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !39, file: !41, line: 127) +!39 = !DIDerivedType(tag: DW_TAG_typedef, name: "div_t", file: !34, line: 63, baseType: !40) +!40 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 59, size: 64, flags: DIFlagFwdDecl, identifier: "_ZTS5div_t") +!41 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/cstdlib", directory: "") +!42 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !43, file: !41, line: 128) +!43 = !DIDerivedType(tag: DW_TAG_typedef, name: "ldiv_t", file: !34, line: 71, baseType: !44) +!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 67, size: 128, flags: DIFlagTypePassByValue, elements: !45, identifier: "_ZTS6ldiv_t") +!45 = !{!46, !48} +!46 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !44, file: !34, line: 69, baseType: !47, size: 64) +!47 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!48 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !44, file: !34, line: 70, baseType: !47, size: 64, offset: 64) +!49 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !50, file: !41, line: 130) +!50 = !DISubprogram(name: "abort", scope: !34, file: !34, line: 598, type: !51, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!51 = !DISubroutineType(types: !52) +!52 = !{null} +!53 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !54, file: !41, line: 134) +!54 = !DISubprogram(name: "atexit", scope: !34, file: !34, line: 602, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!55 = !DISubroutineType(types: !56) +!56 = !{!17, !57} +!57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) +!58 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !59, file: !41, line: 137) +!59 = !DISubprogram(name: "at_quick_exit", scope: !34, file: !34, line: 607, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!60 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !61, file: !41, line: 140) +!61 = !DISubprogram(name: "atof", scope: !34, file: !34, line: 102, type: !62, flags: DIFlagPrototyped, spFlags: 0) +!62 = !DISubroutineType(types: !63) +!63 = !{!64, !65} +!64 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) +!66 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !67) +!67 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!68 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !69, file: !41, line: 141) +!69 = !DISubprogram(name: "atoi", scope: !34, file: !34, line: 105, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!70 = !DISubroutineType(types: !71) +!71 = !{!17, !65} +!72 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !73, file: !41, line: 142) +!73 = !DISubprogram(name: "atol", scope: !34, file: !34, line: 108, type: !74, flags: DIFlagPrototyped, spFlags: 0) +!74 = !DISubroutineType(types: !75) +!75 = !{!47, !65} +!76 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !77, file: !41, line: 143) +!77 = !DISubprogram(name: "bsearch", scope: !34, file: !34, line: 828, type: !78, flags: DIFlagPrototyped, spFlags: 0) +!78 = !DISubroutineType(types: !79) +!79 = !{!80, !81, !81, !83, !83, !86} +!80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!81 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !82, size: 64) +!82 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) +!83 = !DIDerivedType(tag: DW_TAG_typedef, name: "size_t", file: !84, line: 46, baseType: !85) +!84 = !DIFile(filename: "/usr/lib/llvm-13/lib/clang/13.0.1/include/stddef.h", directory: "") +!85 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!86 = !DIDerivedType(tag: DW_TAG_typedef, name: "__compar_fn_t", file: !34, line: 816, baseType: !87) +!87 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !88, size: 64) +!88 = !DISubroutineType(types: !89) +!89 = !{!17, !81, !81} +!90 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !91, file: !41, line: 144) +!91 = !DISubprogram(name: "calloc", scope: !34, file: !34, line: 543, type: !92, flags: DIFlagPrototyped, spFlags: 0) +!92 = !DISubroutineType(types: !93) +!93 = !{!80, !83, !83} +!94 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !95, file: !41, line: 145) +!95 = !DISubprogram(name: "div", scope: !34, file: !34, line: 860, type: !96, flags: DIFlagPrototyped, spFlags: 0) +!96 = !DISubroutineType(types: !97) +!97 = !{!39, !17, !17} +!98 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !99, file: !41, line: 146) +!99 = !DISubprogram(name: "exit", scope: !34, file: !34, line: 624, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!100 = !DISubroutineType(types: !101) +!101 = !{null, !17} +!102 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !103, file: !41, line: 147) +!103 = !DISubprogram(name: "free", scope: !34, file: !34, line: 555, type: !104, flags: DIFlagPrototyped, spFlags: 0) +!104 = !DISubroutineType(types: !105) +!105 = !{null, !80} +!106 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !107, file: !41, line: 148) +!107 = !DISubprogram(name: "getenv", scope: !34, file: !34, line: 641, type: !108, flags: DIFlagPrototyped, spFlags: 0) +!108 = !DISubroutineType(types: !109) +!109 = !{!110, !65} +!110 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !67, size: 64) +!111 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !112, file: !41, line: 149) +!112 = !DISubprogram(name: "labs", scope: !34, file: !34, line: 849, type: !113, flags: DIFlagPrototyped, spFlags: 0) +!113 = !DISubroutineType(types: !114) +!114 = !{!47, !47} +!115 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !116, file: !41, line: 150) +!116 = !DISubprogram(name: "ldiv", scope: !34, file: !34, line: 862, type: !117, flags: DIFlagPrototyped, spFlags: 0) +!117 = !DISubroutineType(types: !118) +!118 = !{!43, !47, !47} +!119 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !120, file: !41, line: 151) +!120 = !DISubprogram(name: "malloc", scope: !34, file: !34, line: 540, type: !121, flags: DIFlagPrototyped, spFlags: 0) +!121 = !DISubroutineType(types: !122) +!122 = !{!80, !83} +!123 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !124, file: !41, line: 153) +!124 = !DISubprogram(name: "mblen", scope: !34, file: !34, line: 930, type: !125, flags: DIFlagPrototyped, spFlags: 0) +!125 = !DISubroutineType(types: !126) +!126 = !{!17, !65, !83} +!127 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !128, file: !41, line: 154) +!128 = !DISubprogram(name: "mbstowcs", scope: !34, file: !34, line: 941, type: !129, flags: DIFlagPrototyped, spFlags: 0) +!129 = !DISubroutineType(types: !130) +!130 = !{!83, !131, !134, !83} +!131 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !132) +!132 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !133, size: 64) +!133 = !DIBasicType(name: "wchar_t", size: 32, encoding: DW_ATE_signed) +!134 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !65) +!135 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !136, file: !41, line: 155) +!136 = !DISubprogram(name: "mbtowc", scope: !34, file: !34, line: 933, type: !137, flags: DIFlagPrototyped, spFlags: 0) +!137 = !DISubroutineType(types: !138) +!138 = !{!17, !131, !134, !83} +!139 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !140, file: !41, line: 157) +!140 = !DISubprogram(name: "qsort", scope: !34, file: !34, line: 838, type: !141, flags: DIFlagPrototyped, spFlags: 0) +!141 = !DISubroutineType(types: !142) +!142 = !{null, !80, !83, !83, !86} +!143 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !144, file: !41, line: 160) +!144 = !DISubprogram(name: "quick_exit", scope: !34, file: !34, line: 630, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!145 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !146, file: !41, line: 163) +!146 = !DISubprogram(name: "rand", scope: !34, file: !34, line: 454, type: !147, flags: DIFlagPrototyped, spFlags: 0) +!147 = !DISubroutineType(types: !148) +!148 = !{!17} +!149 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !150, file: !41, line: 164) +!150 = !DISubprogram(name: "realloc", scope: !34, file: !34, line: 551, type: !151, flags: DIFlagPrototyped, spFlags: 0) +!151 = !DISubroutineType(types: !152) +!152 = !{!80, !80, !83} +!153 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !154, file: !41, line: 165) +!154 = !DISubprogram(name: "srand", scope: !34, file: !34, line: 456, type: !155, flags: DIFlagPrototyped, spFlags: 0) +!155 = !DISubroutineType(types: !156) +!156 = !{null, !25} +!157 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !158, file: !41, line: 166) +!158 = !DISubprogram(name: "strtod", scope: !34, file: !34, line: 118, type: !159, flags: DIFlagPrototyped, spFlags: 0) +!159 = !DISubroutineType(types: !160) +!160 = !{!64, !134, !161} +!161 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !162) +!162 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !110, size: 64) +!163 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !164, file: !41, line: 167) +!164 = !DISubprogram(name: "strtol", scope: !34, file: !34, line: 177, type: !165, flags: DIFlagPrototyped, spFlags: 0) +!165 = !DISubroutineType(types: !166) +!166 = !{!47, !134, !161, !17} +!167 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !168, file: !41, line: 168) +!168 = !DISubprogram(name: "strtoul", scope: !34, file: !34, line: 181, type: !169, flags: DIFlagPrototyped, spFlags: 0) +!169 = !DISubroutineType(types: !170) +!170 = !{!85, !134, !161, !17} +!171 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !172, file: !41, line: 169) +!172 = !DISubprogram(name: "system", scope: !34, file: !34, line: 791, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!173 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !174, file: !41, line: 171) +!174 = !DISubprogram(name: "wcstombs", scope: !34, file: !34, line: 945, type: !175, flags: DIFlagPrototyped, spFlags: 0) +!175 = !DISubroutineType(types: !176) +!176 = !{!83, !177, !178, !83} +!177 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !110) +!178 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !179) +!179 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !180, size: 64) +!180 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !133) +!181 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !182, file: !41, line: 172) +!182 = !DISubprogram(name: "wctomb", scope: !34, file: !34, line: 937, type: !183, flags: DIFlagPrototyped, spFlags: 0) +!183 = !DISubroutineType(types: !184) +!184 = !{!17, !110, !133} +!185 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !187, file: !41, line: 200) +!186 = !DINamespace(name: "__gnu_cxx", scope: null) +!187 = !DIDerivedType(tag: DW_TAG_typedef, name: "lldiv_t", file: !34, line: 81, baseType: !188) +!188 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 77, size: 128, flags: DIFlagTypePassByValue, elements: !189, identifier: "_ZTS7lldiv_t") +!189 = !{!190, !191} +!190 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !188, file: !34, line: 79, baseType: !12, size: 64) +!191 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !188, file: !34, line: 80, baseType: !12, size: 64, offset: 64) +!192 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !193, file: !41, line: 206) +!193 = !DISubprogram(name: "_Exit", scope: !34, file: !34, line: 636, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!194 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !195, file: !41, line: 210) +!195 = !DISubprogram(name: "llabs", scope: !34, file: !34, line: 852, type: !196, flags: DIFlagPrototyped, spFlags: 0) +!196 = !DISubroutineType(types: !197) +!197 = !{!12, !12} +!198 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !199, file: !41, line: 216) +!199 = !DISubprogram(name: "lldiv", scope: !34, file: !34, line: 866, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!200 = !DISubroutineType(types: !201) +!201 = !{!187, !12, !12} +!202 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !203, file: !41, line: 227) +!203 = !DISubprogram(name: "atoll", scope: !34, file: !34, line: 113, type: !204, flags: DIFlagPrototyped, spFlags: 0) +!204 = !DISubroutineType(types: !205) +!205 = !{!12, !65} +!206 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !207, file: !41, line: 228) +!207 = !DISubprogram(name: "strtoll", scope: !34, file: !34, line: 201, type: !208, flags: DIFlagPrototyped, spFlags: 0) +!208 = !DISubroutineType(types: !209) +!209 = !{!12, !134, !161, !17} +!210 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !211, file: !41, line: 229) +!211 = !DISubprogram(name: "strtoull", scope: !34, file: !34, line: 206, type: !212, flags: DIFlagPrototyped, spFlags: 0) +!212 = !DISubroutineType(types: !213) +!213 = !{!8, !134, !161, !17} +!214 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !215, file: !41, line: 231) +!215 = !DISubprogram(name: "strtof", scope: !34, file: !34, line: 124, type: !216, flags: DIFlagPrototyped, spFlags: 0) +!216 = !DISubroutineType(types: !217) +!217 = !{!218, !134, !161} +!218 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!219 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !220, file: !41, line: 232) +!220 = !DISubprogram(name: "strtold", scope: !34, file: !34, line: 127, type: !221, flags: DIFlagPrototyped, spFlags: 0) +!221 = !DISubroutineType(types: !222) +!222 = !{!223, !134, !161} +!223 = !DIBasicType(name: "long double", size: 128, encoding: DW_ATE_float) +!224 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !187, file: !41, line: 240) +!225 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !193, file: !41, line: 242) +!226 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !195, file: !41, line: 244) +!227 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !228, file: !41, line: 245) +!228 = !DISubprogram(name: "div", linkageName: "_ZN9__gnu_cxx3divExx", scope: !186, file: !41, line: 213, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!229 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !199, file: !41, line: 246) +!230 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !203, file: !41, line: 248) +!231 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !215, file: !41, line: 249) +!232 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !207, file: !41, line: 250) +!233 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !211, file: !41, line: 251) +!234 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !220, file: !41, line: 252) +!235 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !50, file: !236, line: 38) +!236 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/stdlib.h", directory: "") +!237 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !54, file: !236, line: 39) +!238 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !99, file: !236, line: 40) +!239 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !59, file: !236, line: 43) +!240 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !144, file: !236, line: 46) +!241 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !39, file: !236, line: 51) +!242 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !43, file: !236, line: 52) +!243 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !244, file: !236, line: 54) +!244 = !DISubprogram(name: "abs", linkageName: "_ZSt3absg", scope: !32, file: !37, line: 103, type: !245, flags: DIFlagPrototyped, spFlags: 0) +!245 = !DISubroutineType(types: !246) +!246 = !{!247, !247} +!247 = !DIBasicType(name: "__float128", size: 128, encoding: DW_ATE_float) +!248 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !61, file: !236, line: 55) +!249 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !69, file: !236, line: 56) +!250 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !73, file: !236, line: 57) +!251 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !77, file: !236, line: 58) +!252 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !91, file: !236, line: 59) +!253 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !228, file: !236, line: 60) +!254 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !103, file: !236, line: 61) +!255 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !107, file: !236, line: 62) +!256 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !112, file: !236, line: 63) +!257 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !116, file: !236, line: 64) +!258 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !120, file: !236, line: 65) +!259 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !124, file: !236, line: 67) +!260 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !128, file: !236, line: 68) +!261 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !136, file: !236, line: 69) +!262 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !140, file: !236, line: 71) +!263 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !146, file: !236, line: 72) +!264 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !150, file: !236, line: 73) +!265 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !154, file: !236, line: 74) +!266 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !158, file: !236, line: 75) +!267 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !164, file: !236, line: 76) +!268 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !168, file: !236, line: 77) +!269 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !172, file: !236, line: 78) +!270 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !174, file: !236, line: 80) +!271 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !182, file: !236, line: 81) +!272 = !{i32 7, !"Dwarf Version", i32 4} +!273 = !{i32 2, !"Debug Info Version", i32 3} +!274 = !{i32 1, !"wchar_size", i32 4} +!275 = !{i32 7, !"uwtable", i32 1} +!276 = !{i32 7, !"frame-pointer", i32 2} +!277 = !{!"Ubuntu clang version 13.0.1-2ubuntu2.2"} +!278 = distinct !DISubprogram(name: "shift64RightJamming", linkageName: "_Z19shift64RightJammingyiPy", scope: !21, file: !21, line: 60, type: !279, scopeLine: 61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!279 = !DISubroutineType(types: !280) +!280 = !{null, !6, !281, !282} +!281 = !DIDerivedType(tag: DW_TAG_typedef, name: "int16", file: !7, line: 60, baseType: !17) +!282 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!283 = !DILocalVariable(name: "a", arg: 1, scope: !278, file: !21, line: 60, type: !6) +!284 = !DILocation(line: 0, scope: !278) +!285 = !DILocalVariable(name: "count", arg: 2, scope: !278, file: !21, line: 60, type: !281) +!286 = !DILocalVariable(name: "zPtr", arg: 3, scope: !278, file: !21, line: 60, type: !282) +!287 = !DILocation(line: 64, column: 13, scope: !288) +!288 = distinct !DILexicalBlock(scope: !278, file: !21, line: 64, column: 7) +!289 = !DILocation(line: 64, column: 7, scope: !278) +!290 = !DILocalVariable(name: "z", scope: !278, file: !21, line: 62, type: !6) +!291 = !DILocation(line: 67, column: 5, scope: !292) +!292 = distinct !DILexicalBlock(scope: !288, file: !21, line: 65, column: 5) +!293 = !DILocation(line: 68, column: 18, scope: !294) +!294 = distinct !DILexicalBlock(scope: !288, file: !21, line: 68, column: 12) +!295 = !DILocation(line: 68, column: 12, scope: !288) +!296 = !DILocation(line: 70, column: 14, scope: !297) +!297 = distinct !DILexicalBlock(scope: !294, file: !21, line: 69, column: 5) +!298 = !DILocation(line: 70, column: 35, scope: !297) +!299 = !DILocation(line: 70, column: 43, scope: !297) +!300 = !DILocation(line: 70, column: 30, scope: !297) +!301 = !DILocation(line: 70, column: 50, scope: !297) +!302 = !DILocation(line: 70, column: 26, scope: !297) +!303 = !DILocation(line: 70, column: 24, scope: !297) +!304 = !DILocation(line: 71, column: 5, scope: !297) +!305 = !DILocation(line: 74, column: 14, scope: !306) +!306 = distinct !DILexicalBlock(scope: !294, file: !21, line: 73, column: 5) +!307 = !DILocation(line: 74, column: 11, scope: !306) +!308 = !DILocation(line: 0, scope: !294) +!309 = !DILocation(line: 0, scope: !288) +!310 = !DILocation(line: 76, column: 9, scope: !278) +!311 = !DILocation(line: 78, column: 1, scope: !278) +!312 = distinct !DISubprogram(name: "add128", linkageName: "_Z6add128yyyyPyS_", scope: !21, file: !21, line: 88, type: !313, scopeLine: 90, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!313 = !DISubroutineType(types: !314) +!314 = !{null, !6, !6, !6, !6, !282, !282} +!315 = !DILocalVariable(name: "a0", arg: 1, scope: !312, file: !21, line: 88, type: !6) +!316 = !DILocation(line: 0, scope: !312) +!317 = !DILocalVariable(name: "a1", arg: 2, scope: !312, file: !21, line: 88, type: !6) +!318 = !DILocalVariable(name: "b0", arg: 3, scope: !312, file: !21, line: 88, type: !6) +!319 = !DILocalVariable(name: "b1", arg: 4, scope: !312, file: !21, line: 88, type: !6) +!320 = !DILocalVariable(name: "z0Ptr", arg: 5, scope: !312, file: !21, line: 88, type: !282) +!321 = !DILocalVariable(name: "z1Ptr", arg: 6, scope: !312, file: !21, line: 89, type: !282) +!322 = !DILocation(line: 93, column: 11, scope: !312) +!323 = !DILocalVariable(name: "z1", scope: !312, file: !21, line: 91, type: !6) +!324 = !DILocation(line: 94, column: 10, scope: !312) +!325 = !DILocation(line: 95, column: 15, scope: !312) +!326 = !DILocation(line: 95, column: 26, scope: !312) +!327 = !DILocation(line: 95, column: 22, scope: !312) +!328 = !DILocation(line: 95, column: 20, scope: !312) +!329 = !DILocation(line: 95, column: 10, scope: !312) +!330 = !DILocation(line: 97, column: 1, scope: !312) +!331 = distinct !DISubprogram(name: "sub128", linkageName: "_Z6sub128yyyyPyS_", scope: !21, file: !21, line: 108, type: !313, scopeLine: 110, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!332 = !DILocalVariable(name: "a0", arg: 1, scope: !331, file: !21, line: 108, type: !6) +!333 = !DILocation(line: 0, scope: !331) +!334 = !DILocalVariable(name: "a1", arg: 2, scope: !331, file: !21, line: 108, type: !6) +!335 = !DILocalVariable(name: "b0", arg: 3, scope: !331, file: !21, line: 108, type: !6) +!336 = !DILocalVariable(name: "b1", arg: 4, scope: !331, file: !21, line: 108, type: !6) +!337 = !DILocalVariable(name: "z0Ptr", arg: 5, scope: !331, file: !21, line: 108, type: !282) +!338 = !DILocalVariable(name: "z1Ptr", arg: 6, scope: !331, file: !21, line: 109, type: !282) +!339 = !DILocation(line: 112, column: 15, scope: !331) +!340 = !DILocation(line: 112, column: 10, scope: !331) +!341 = !DILocation(line: 113, column: 15, scope: !331) +!342 = !DILocation(line: 113, column: 26, scope: !331) +!343 = !DILocation(line: 113, column: 22, scope: !331) +!344 = !DILocation(line: 113, column: 20, scope: !331) +!345 = !DILocation(line: 113, column: 10, scope: !331) +!346 = !DILocation(line: 115, column: 1, scope: !331) +!347 = distinct !DISubprogram(name: "mul64To128", linkageName: "_Z10mul64To128yyPyS_", scope: !21, file: !21, line: 124, type: !348, scopeLine: 125, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!348 = !DISubroutineType(types: !349) +!349 = !{null, !6, !6, !282, !282} +!350 = !DILocalVariable(name: "a", arg: 1, scope: !347, file: !21, line: 124, type: !6) +!351 = !DILocation(line: 0, scope: !347) +!352 = !DILocalVariable(name: "b", arg: 2, scope: !347, file: !21, line: 124, type: !6) +!353 = !DILocalVariable(name: "z0Ptr", arg: 3, scope: !347, file: !21, line: 124, type: !282) +!354 = !DILocalVariable(name: "z1Ptr", arg: 4, scope: !347, file: !21, line: 124, type: !282) +!355 = !DILocation(line: 129, column: 10, scope: !347) +!356 = !DILocalVariable(name: "aLow", scope: !347, file: !21, line: 126, type: !24) +!357 = !DILocation(line: 130, column: 13, scope: !347) +!358 = !DILocation(line: 130, column: 11, scope: !347) +!359 = !DILocalVariable(name: "aHigh", scope: !347, file: !21, line: 126, type: !24) +!360 = !DILocation(line: 131, column: 10, scope: !347) +!361 = !DILocalVariable(name: "bLow", scope: !347, file: !21, line: 126, type: !24) +!362 = !DILocation(line: 132, column: 13, scope: !347) +!363 = !DILocation(line: 132, column: 11, scope: !347) +!364 = !DILocalVariable(name: "bHigh", scope: !347, file: !21, line: 126, type: !24) +!365 = !DILocation(line: 133, column: 18, scope: !347) +!366 = !DILocation(line: 133, column: 26, scope: !347) +!367 = !DILocation(line: 133, column: 24, scope: !347) +!368 = !DILocalVariable(name: "z1", scope: !347, file: !21, line: 127, type: !6) +!369 = !DILocation(line: 134, column: 24, scope: !347) +!370 = !DILocation(line: 134, column: 32, scope: !347) +!371 = !DILocation(line: 134, column: 30, scope: !347) +!372 = !DILocalVariable(name: "zMiddleA", scope: !347, file: !21, line: 127, type: !6) +!373 = !DILocation(line: 135, column: 24, scope: !347) +!374 = !DILocation(line: 135, column: 33, scope: !347) +!375 = !DILocation(line: 135, column: 31, scope: !347) +!376 = !DILocalVariable(name: "zMiddleB", scope: !347, file: !21, line: 127, type: !6) +!377 = !DILocation(line: 136, column: 18, scope: !347) +!378 = !DILocation(line: 136, column: 27, scope: !347) +!379 = !DILocation(line: 136, column: 25, scope: !347) +!380 = !DILocalVariable(name: "z0", scope: !347, file: !21, line: 127, type: !6) +!381 = !DILocation(line: 137, column: 12, scope: !347) +!382 = !DILocation(line: 138, column: 30, scope: !347) +!383 = !DILocation(line: 138, column: 20, scope: !347) +!384 = !DILocation(line: 138, column: 43, scope: !347) +!385 = !DILocation(line: 138, column: 62, scope: !347) +!386 = !DILocation(line: 138, column: 50, scope: !347) +!387 = !DILocation(line: 138, column: 6, scope: !347) +!388 = !DILocation(line: 139, column: 12, scope: !347) +!389 = !DILocation(line: 140, column: 6, scope: !347) +!390 = !DILocation(line: 141, column: 13, scope: !347) +!391 = !DILocation(line: 141, column: 9, scope: !347) +!392 = !DILocation(line: 141, column: 6, scope: !347) +!393 = !DILocation(line: 142, column: 10, scope: !347) +!394 = !DILocation(line: 143, column: 10, scope: !347) +!395 = !DILocation(line: 145, column: 1, scope: !347) +!396 = distinct !DISubprogram(name: "float_raise", linkageName: "_Z11float_raisei", scope: !397, file: !397, line: 64, type: !398, scopeLine: 65, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!397 = !DIFile(filename: "dfdiv/include/softfloat-specialize", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!398 = !DISubroutineType(types: !399) +!399 = !{null, !16} +!400 = !DILocalVariable(name: "flags", arg: 1, scope: !396, file: !397, line: 64, type: !16) +!401 = !DILocation(line: 0, scope: !396) +!402 = !DILocation(line: 66, column: 25, scope: !396) +!403 = !DILocation(line: 68, column: 1, scope: !396) +!404 = distinct !DISubprogram(name: "float64_is_nan", linkageName: "_Z14float64_is_nany", scope: !397, file: !397, line: 82, type: !405, scopeLine: 83, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!405 = !DISubroutineType(types: !406) +!406 = !{!407, !408} +!407 = !DIDerivedType(tag: DW_TAG_typedef, name: "flag", file: !7, line: 58, baseType: !17) +!408 = !DIDerivedType(tag: DW_TAG_typedef, name: "float64", file: !409, line: 54, baseType: !8) +!409 = !DIFile(filename: "dfdiv/include/softfloat.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!410 = !DILocalVariable(name: "a", arg: 1, scope: !404, file: !397, line: 82, type: !408) +!411 = !DILocation(line: 0, scope: !404) +!412 = !DILocation(line: 85, column: 52, scope: !404) +!413 = !DILocation(line: 85, column: 38, scope: !404) +!414 = !DILocation(line: 85, column: 10, scope: !404) +!415 = !DILocation(line: 85, column: 3, scope: !404) +!416 = distinct !DISubprogram(name: "float64_is_signaling_nan", linkageName: "_Z24float64_is_signaling_nany", scope: !397, file: !397, line: 95, type: !405, scopeLine: 96, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!417 = !DILocalVariable(name: "a", arg: 1, scope: !416, file: !397, line: 95, type: !408) +!418 = !DILocation(line: 0, scope: !416) +!419 = !DILocation(line: 98, column: 15, scope: !416) +!420 = !DILocation(line: 98, column: 22, scope: !416) +!421 = !DILocation(line: 98, column: 31, scope: !416) +!422 = !DILocation(line: 98, column: 41, scope: !416) +!423 = !DILocation(line: 98, column: 47, scope: !416) +!424 = !DILocation(line: 98, column: 44, scope: !416) +!425 = !DILocation(line: 98, column: 10, scope: !416) +!426 = !DILocation(line: 98, column: 3, scope: !416) +!427 = distinct !DISubprogram(name: "extractFloat64Frac", scope: !3, file: !3, line: 87, type: !428, scopeLine: 88, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!428 = !DISubroutineType(types: !429) +!429 = !{!6, !408} +!430 = !DILocalVariable(name: "a", arg: 1, scope: !427, file: !3, line: 87, type: !408) +!431 = !DILocation(line: 0, scope: !427) +!432 = !DILocation(line: 89, column: 12, scope: !427) +!433 = !DILocation(line: 89, column: 3, scope: !427) +!434 = distinct !DISubprogram(name: "extractFloat64Exp", scope: !3, file: !3, line: 102, type: !435, scopeLine: 103, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!435 = !DISubroutineType(types: !436) +!436 = !{!281, !408} +!437 = !DILocalVariable(name: "a", arg: 1, scope: !434, file: !3, line: 102, type: !408) +!438 = !DILocation(line: 0, scope: !434) +!439 = !DILocation(line: 104, column: 13, scope: !434) +!440 = !DILocation(line: 104, column: 20, scope: !434) +!441 = !DILocation(line: 104, column: 10, scope: !434) +!442 = !DILocation(line: 104, column: 3, scope: !434) +!443 = distinct !DISubprogram(name: "extractFloat64Sign", scope: !3, file: !3, line: 117, type: !405, scopeLine: 118, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!444 = !DILocalVariable(name: "a", arg: 1, scope: !443, file: !3, line: 117, type: !408) +!445 = !DILocation(line: 0, scope: !443) +!446 = !DILocation(line: 120, column: 12, scope: !443) +!447 = !DILocation(line: 120, column: 10, scope: !443) +!448 = !DILocation(line: 120, column: 3, scope: !443) +!449 = distinct !DISubprogram(name: "normalizeFloat64Subnormal", linkageName: "_Z25normalizeFloat64SubnormalyPiPy", scope: !3, file: !3, line: 134, type: !450, scopeLine: 135, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!450 = !DISubroutineType(types: !451) +!451 = !{null, !6, !452, !282} +!452 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !281, size: 64) +!453 = !DILocalVariable(name: "aSig", arg: 1, scope: !449, file: !3, line: 134, type: !6) +!454 = !DILocation(line: 0, scope: !449) +!455 = !DILocalVariable(name: "zExpPtr", arg: 2, scope: !449, file: !3, line: 134, type: !452) +!456 = !DILocalVariable(name: "zSigPtr", arg: 3, scope: !449, file: !3, line: 134, type: !282) +!457 = !DILocation(line: 138, column: 16, scope: !449) +!458 = !DILocation(line: 138, column: 43, scope: !449) +!459 = !DILocalVariable(name: "shiftCount", scope: !449, file: !3, line: 136, type: !16) +!460 = !DILocation(line: 139, column: 19, scope: !449) +!461 = !DILocation(line: 139, column: 12, scope: !449) +!462 = !DILocation(line: 140, column: 16, scope: !449) +!463 = !DILocation(line: 140, column: 12, scope: !449) +!464 = !DILocation(line: 141, column: 1, scope: !449) +!465 = distinct !DISubprogram(name: "countLeadingZeros64", linkageName: "_ZL19countLeadingZeros64y", scope: !21, file: !21, line: 231, type: !466, scopeLine: 232, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!466 = !DISubroutineType(types: !467) +!467 = !{!16, !6} +!468 = !DILocalVariable(name: "a", arg: 1, scope: !465, file: !21, line: 231, type: !6) +!469 = !DILocation(line: 0, scope: !465) +!470 = !DILocalVariable(name: "shiftCount", scope: !465, file: !21, line: 233, type: !16) +!471 = !DILocation(line: 236, column: 9, scope: !472) +!472 = distinct !DILexicalBlock(scope: !465, file: !21, line: 236, column: 7) +!473 = !DILocation(line: 236, column: 7, scope: !465) +!474 = !DILocation(line: 238, column: 18, scope: !475) +!475 = distinct !DILexicalBlock(scope: !472, file: !21, line: 237, column: 5) +!476 = !DILocation(line: 239, column: 5, scope: !475) +!477 = !DILocation(line: 242, column: 9, scope: !478) +!478 = distinct !DILexicalBlock(scope: !472, file: !21, line: 241, column: 5) +!479 = !DILocation(line: 244, column: 38, scope: !465) +!480 = !DILocation(line: 244, column: 17, scope: !465) +!481 = !DILocation(line: 244, column: 14, scope: !465) +!482 = !DILocation(line: 245, column: 3, scope: !465) +!483 = distinct !DISubprogram(name: "packFloat64", scope: !3, file: !3, line: 157, type: !484, scopeLine: 158, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!484 = !DISubroutineType(types: !485) +!485 = !{!408, !407, !281, !6} +!486 = !DILocalVariable(name: "zSign", arg: 1, scope: !483, file: !3, line: 157, type: !407) +!487 = !DILocation(line: 0, scope: !483) +!488 = !DILocalVariable(name: "zExp", arg: 2, scope: !483, file: !3, line: 157, type: !281) +!489 = !DILocalVariable(name: "zSig", arg: 3, scope: !483, file: !3, line: 157, type: !6) +!490 = !DILocation(line: 159, column: 21, scope: !483) +!491 = !DILocation(line: 159, column: 28, scope: !483) +!492 = !DILocation(line: 159, column: 48, scope: !483) +!493 = !DILocation(line: 159, column: 54, scope: !483) +!494 = !DILocation(line: 159, column: 35, scope: !483) +!495 = !DILocation(line: 159, column: 61, scope: !483) +!496 = !DILocation(line: 159, column: 3, scope: !483) +!497 = distinct !DISubprogram(name: "roundAndPackFloat64", scope: !3, file: !3, line: 190, type: !484, scopeLine: 191, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!498 = !DILocalVariable(name: "zSign", arg: 1, scope: !497, file: !3, line: 190, type: !407) +!499 = !DILocation(line: 0, scope: !497) +!500 = !DILocalVariable(name: "zExp", arg: 2, scope: !497, file: !3, line: 190, type: !281) +!501 = !DILocalVariable(name: "zSig", arg: 3, scope: !497, file: !3, line: 190, type: !6) +!502 = !DILocation(line: 190, column: 61, scope: !497) +!503 = !DILocation(line: 196, column: 18, scope: !497) +!504 = !DILocalVariable(name: "roundingMode", scope: !497, file: !3, line: 192, type: !16) +!505 = !DILocation(line: 197, column: 36, scope: !497) +!506 = !DILocation(line: 197, column: 22, scope: !497) +!507 = !DILocalVariable(name: "roundNearestEven", scope: !497, file: !3, line: 193, type: !407) +!508 = !DILocalVariable(name: "roundIncrement", scope: !497, file: !3, line: 194, type: !281) +!509 = !DILocation(line: 199, column: 8, scope: !510) +!510 = distinct !DILexicalBlock(scope: !497, file: !3, line: 199, column: 7) +!511 = !DILocation(line: 199, column: 7, scope: !497) +!512 = !DILocation(line: 201, column: 24, scope: !513) +!513 = distinct !DILexicalBlock(scope: !514, file: !3, line: 201, column: 11) +!514 = distinct !DILexicalBlock(scope: !510, file: !3, line: 200, column: 5) +!515 = !DILocation(line: 201, column: 11, scope: !514) +!516 = !DILocation(line: 204, column: 2, scope: !517) +!517 = distinct !DILexicalBlock(scope: !513, file: !3, line: 202, column: 2) +!518 = !DILocation(line: 208, column: 8, scope: !519) +!519 = distinct !DILexicalBlock(scope: !520, file: !3, line: 208, column: 8) +!520 = distinct !DILexicalBlock(scope: !513, file: !3, line: 206, column: 2) +!521 = !DILocation(line: 208, column: 8, scope: !520) +!522 = !DILocation(line: 210, column: 25, scope: !523) +!523 = distinct !DILexicalBlock(scope: !524, file: !3, line: 210, column: 12) +!524 = distinct !DILexicalBlock(scope: !519, file: !3, line: 209, column: 6) +!525 = !DILocation(line: 210, column: 12, scope: !524) +!526 = !DILocation(line: 211, column: 3, scope: !523) +!527 = !DILocation(line: 0, scope: !520) +!528 = !DILocation(line: 212, column: 6, scope: !524) +!529 = !DILocation(line: 215, column: 25, scope: !530) +!530 = distinct !DILexicalBlock(scope: !531, file: !3, line: 215, column: 12) +!531 = distinct !DILexicalBlock(scope: !519, file: !3, line: 214, column: 6) +!532 = !DILocation(line: 215, column: 12, scope: !531) +!533 = !DILocation(line: 216, column: 3, scope: !530) +!534 = !DILocation(line: 0, scope: !519) +!535 = !DILocation(line: 0, scope: !513) +!536 = !DILocation(line: 219, column: 5, scope: !514) +!537 = !DILocation(line: 220, column: 15, scope: !497) +!538 = !DILocation(line: 220, column: 20, scope: !497) +!539 = !DILocalVariable(name: "roundBits", scope: !497, file: !3, line: 194, type: !281) +!540 = !DILocation(line: 221, column: 25, scope: !541) +!541 = distinct !DILexicalBlock(scope: !497, file: !3, line: 221, column: 7) +!542 = !DILocation(line: 221, column: 16, scope: !541) +!543 = !DILocation(line: 221, column: 13, scope: !541) +!544 = !DILocation(line: 221, column: 7, scope: !497) +!545 = !DILocation(line: 223, column: 18, scope: !546) +!546 = distinct !DILexicalBlock(scope: !547, file: !3, line: 223, column: 11) +!547 = distinct !DILexicalBlock(scope: !541, file: !3, line: 222, column: 5) +!548 = !DILocation(line: 224, column: 4, scope: !546) +!549 = !DILocation(line: 224, column: 14, scope: !546) +!550 = !DILocation(line: 224, column: 24, scope: !546) +!551 = !DILocation(line: 224, column: 39, scope: !546) +!552 = !DILocation(line: 224, column: 46, scope: !546) +!553 = !DILocation(line: 224, column: 44, scope: !546) +!554 = !DILocation(line: 224, column: 62, scope: !546) +!555 = !DILocation(line: 223, column: 11, scope: !547) +!556 = !DILocation(line: 226, column: 4, scope: !557) +!557 = distinct !DILexicalBlock(scope: !546, file: !3, line: 225, column: 2) +!558 = !DILocation(line: 0, scope: !483, inlinedAt: !559) +!559 = distinct !DILocation(line: 227, column: 11, scope: !557) +!560 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !559) +!561 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !559) +!562 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !559) +!563 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !559) +!564 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !559) +!565 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !559) +!566 = !DILocation(line: 227, column: 59, scope: !557) +!567 = !DILocation(line: 227, column: 43, scope: !557) +!568 = !DILocation(line: 227, column: 41, scope: !557) +!569 = !DILocation(line: 227, column: 4, scope: !557) +!570 = !DILocation(line: 229, column: 16, scope: !571) +!571 = distinct !DILexicalBlock(scope: !547, file: !3, line: 229, column: 11) +!572 = !DILocation(line: 229, column: 11, scope: !547) +!573 = !DILocalVariable(name: "isTiny", scope: !497, file: !3, line: 193, type: !407) +!574 = !DILocation(line: 234, column: 25, scope: !575) +!575 = distinct !DILexicalBlock(scope: !571, file: !3, line: 230, column: 2) +!576 = !DILocation(line: 234, column: 31, scope: !575) +!577 = !DILocation(line: 234, column: 4, scope: !575) +!578 = !DILocation(line: 236, column: 16, scope: !575) +!579 = !DILocation(line: 236, column: 21, scope: !575) +!580 = !DILocation(line: 237, column: 8, scope: !581) +!581 = distinct !DILexicalBlock(scope: !575, file: !3, line: 237, column: 8) +!582 = !DILocation(line: 237, column: 15, scope: !581) +!583 = !DILocation(line: 237, column: 18, scope: !581) +!584 = !DILocation(line: 237, column: 8, scope: !575) +!585 = !DILocation(line: 238, column: 6, scope: !581) +!586 = !DILocation(line: 239, column: 2, scope: !575) +!587 = !DILocation(line: 240, column: 5, scope: !547) +!588 = !DILocation(line: 241, column: 7, scope: !589) +!589 = distinct !DILexicalBlock(scope: !497, file: !3, line: 241, column: 7) +!590 = !DILocation(line: 241, column: 7, scope: !497) +!591 = !DILocation(line: 242, column: 27, scope: !589) +!592 = !DILocation(line: 242, column: 5, scope: !589) +!593 = !DILocation(line: 243, column: 11, scope: !497) +!594 = !DILocation(line: 243, column: 18, scope: !497) +!595 = !DILocation(line: 243, column: 16, scope: !497) +!596 = !DILocation(line: 243, column: 34, scope: !497) +!597 = !DILocation(line: 243, column: 8, scope: !497) +!598 = !DILocation(line: 244, column: 25, scope: !497) +!599 = !DILocation(line: 244, column: 34, scope: !497) +!600 = !DILocation(line: 244, column: 13, scope: !497) +!601 = !DILocation(line: 244, column: 40, scope: !497) +!602 = !DILocation(line: 244, column: 11, scope: !497) +!603 = !DILocation(line: 244, column: 8, scope: !497) +!604 = !DILocation(line: 245, column: 7, scope: !605) +!605 = distinct !DILexicalBlock(scope: !497, file: !3, line: 245, column: 7) +!606 = !DILocation(line: 245, column: 12, scope: !605) +!607 = !DILocation(line: 245, column: 7, scope: !497) +!608 = !DILocation(line: 246, column: 5, scope: !605) +!609 = !DILocation(line: 247, column: 36, scope: !497) +!610 = !DILocation(line: 0, scope: !483, inlinedAt: !611) +!611 = distinct !DILocation(line: 247, column: 10, scope: !497) +!612 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !611) +!613 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !611) +!614 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !611) +!615 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !611) +!616 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !611) +!617 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !611) +!618 = !DILocation(line: 247, column: 3, scope: !497) +!619 = !DILocation(line: 249, column: 1, scope: !497) +!620 = distinct !DISubprogram(name: "float64_div", scope: !3, file: !3, line: 273, type: !621, scopeLine: 274, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!621 = !DISubroutineType(types: !622) +!622 = !{!408, !623, !624} +!623 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055xAcceleration", file: !3, line: 263, baseType: !408) +!624 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055yAcceleration", file: !3, line: 264, baseType: !408) +!625 = !DILocalVariable(name: "a", arg: 1, scope: !620, file: !3, line: 273, type: !623) +!626 = !DILocation(line: 0, scope: !620) +!627 = !DILocalVariable(name: "b", arg: 2, scope: !620, file: !3, line: 273, type: !624) +!628 = !DILocalVariable(name: "rem0", scope: !620, file: !3, line: 289, type: !6) +!629 = !DILocation(line: 289, column: 10, scope: !620) +!630 = !DILocalVariable(name: "rem1", scope: !620, file: !3, line: 289, type: !6) +!631 = !DILocation(line: 289, column: 16, scope: !620) +!632 = !DILocalVariable(name: "term0", scope: !620, file: !3, line: 289, type: !6) +!633 = !DILocation(line: 289, column: 22, scope: !620) +!634 = !DILocalVariable(name: "term1", scope: !620, file: !3, line: 289, type: !6) +!635 = !DILocation(line: 289, column: 29, scope: !620) +!636 = !DILocation(line: 0, scope: !427, inlinedAt: !637) +!637 = distinct !DILocation(line: 291, column: 10, scope: !620) +!638 = !DILocation(line: 89, column: 12, scope: !427, inlinedAt: !637) +!639 = !DILocalVariable(name: "aSig", scope: !620, file: !3, line: 288, type: !6) +!640 = !DILocation(line: 0, scope: !434, inlinedAt: !641) +!641 = distinct !DILocation(line: 292, column: 10, scope: !620) +!642 = !DILocation(line: 104, column: 13, scope: !434, inlinedAt: !641) +!643 = !DILocation(line: 104, column: 20, scope: !434, inlinedAt: !641) +!644 = !DILocation(line: 104, column: 10, scope: !434, inlinedAt: !641) +!645 = !DILocalVariable(name: "aExp", scope: !620, file: !3, line: 287, type: !281) +!646 = !DILocation(line: 0, scope: !443, inlinedAt: !647) +!647 = distinct !DILocation(line: 293, column: 11, scope: !620) +!648 = !DILocation(line: 120, column: 12, scope: !443, inlinedAt: !647) +!649 = !DILocation(line: 120, column: 10, scope: !443, inlinedAt: !647) +!650 = !DILocalVariable(name: "aSign", scope: !620, file: !3, line: 286, type: !407) +!651 = !DILocation(line: 0, scope: !427, inlinedAt: !652) +!652 = distinct !DILocation(line: 294, column: 10, scope: !620) +!653 = !DILocation(line: 89, column: 12, scope: !427, inlinedAt: !652) +!654 = !DILocalVariable(name: "bSig", scope: !620, file: !3, line: 288, type: !6) +!655 = !DILocation(line: 0, scope: !434, inlinedAt: !656) +!656 = distinct !DILocation(line: 295, column: 10, scope: !620) +!657 = !DILocation(line: 104, column: 13, scope: !434, inlinedAt: !656) +!658 = !DILocation(line: 104, column: 20, scope: !434, inlinedAt: !656) +!659 = !DILocation(line: 104, column: 10, scope: !434, inlinedAt: !656) +!660 = !DILocalVariable(name: "bExp", scope: !620, file: !3, line: 287, type: !281) +!661 = !DILocation(line: 0, scope: !443, inlinedAt: !662) +!662 = distinct !DILocation(line: 296, column: 11, scope: !620) +!663 = !DILocation(line: 120, column: 12, scope: !443, inlinedAt: !662) +!664 = !DILocation(line: 120, column: 10, scope: !443, inlinedAt: !662) +!665 = !DILocalVariable(name: "bSign", scope: !620, file: !3, line: 286, type: !407) +!666 = !DILocation(line: 297, column: 17, scope: !620) +!667 = !DILocalVariable(name: "zSign", scope: !620, file: !3, line: 286, type: !407) +!668 = !DILocation(line: 298, column: 12, scope: !669) +!669 = distinct !DILexicalBlock(scope: !620, file: !3, line: 298, column: 7) +!670 = !DILocation(line: 298, column: 7, scope: !620) +!671 = !DILocation(line: 300, column: 11, scope: !672) +!672 = distinct !DILexicalBlock(scope: !673, file: !3, line: 300, column: 11) +!673 = distinct !DILexicalBlock(scope: !669, file: !3, line: 299, column: 5) +!674 = !DILocation(line: 300, column: 11, scope: !673) +!675 = !DILocation(line: 301, column: 9, scope: !672) +!676 = !DILocation(line: 301, column: 2, scope: !672) +!677 = !DILocation(line: 302, column: 16, scope: !678) +!678 = distinct !DILexicalBlock(scope: !673, file: !3, line: 302, column: 11) +!679 = !DILocation(line: 302, column: 11, scope: !673) +!680 = !DILocation(line: 304, column: 8, scope: !681) +!681 = distinct !DILexicalBlock(scope: !682, file: !3, line: 304, column: 8) +!682 = distinct !DILexicalBlock(scope: !678, file: !3, line: 303, column: 2) +!683 = !DILocation(line: 304, column: 8, scope: !682) +!684 = !DILocation(line: 305, column: 13, scope: !681) +!685 = !DILocation(line: 305, column: 6, scope: !681) +!686 = !DILocation(line: 306, column: 4, scope: !682) +!687 = !DILocation(line: 307, column: 4, scope: !682) +!688 = !DILocation(line: 0, scope: !483, inlinedAt: !689) +!689 = distinct !DILocation(line: 309, column: 14, scope: !673) +!690 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !689) +!691 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !689) +!692 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !689) +!693 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !689) +!694 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !689) +!695 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !689) +!696 = !DILocation(line: 309, column: 7, scope: !673) +!697 = !DILocation(line: 311, column: 12, scope: !698) +!698 = distinct !DILexicalBlock(scope: !620, file: !3, line: 311, column: 7) +!699 = !DILocation(line: 311, column: 7, scope: !620) +!700 = !DILocation(line: 313, column: 11, scope: !701) +!701 = distinct !DILexicalBlock(scope: !702, file: !3, line: 313, column: 11) +!702 = distinct !DILexicalBlock(scope: !698, file: !3, line: 312, column: 5) +!703 = !DILocation(line: 313, column: 11, scope: !702) +!704 = !DILocation(line: 314, column: 9, scope: !701) +!705 = !DILocation(line: 314, column: 2, scope: !701) +!706 = !DILocation(line: 0, scope: !483, inlinedAt: !707) +!707 = distinct !DILocation(line: 315, column: 14, scope: !702) +!708 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !707) +!709 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !707) +!710 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !707) +!711 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !707) +!712 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !707) +!713 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !707) +!714 = !DILocation(line: 315, column: 7, scope: !702) +!715 = !DILocation(line: 317, column: 12, scope: !716) +!716 = distinct !DILexicalBlock(scope: !620, file: !3, line: 317, column: 7) +!717 = !DILocation(line: 317, column: 7, scope: !620) +!718 = !DILocation(line: 319, column: 16, scope: !719) +!719 = distinct !DILexicalBlock(scope: !720, file: !3, line: 319, column: 11) +!720 = distinct !DILexicalBlock(scope: !716, file: !3, line: 318, column: 5) +!721 = !DILocation(line: 319, column: 11, scope: !720) +!722 = !DILocation(line: 321, column: 9, scope: !723) +!723 = distinct !DILexicalBlock(scope: !724, file: !3, line: 321, column: 8) +!724 = distinct !DILexicalBlock(scope: !719, file: !3, line: 320, column: 2) +!725 = !DILocation(line: 321, column: 14, scope: !723) +!726 = !DILocation(line: 321, column: 22, scope: !723) +!727 = !DILocation(line: 321, column: 8, scope: !724) +!728 = !DILocation(line: 323, column: 8, scope: !729) +!729 = distinct !DILexicalBlock(scope: !723, file: !3, line: 322, column: 6) +!730 = !DILocation(line: 324, column: 8, scope: !729) +!731 = !DILocation(line: 326, column: 4, scope: !724) +!732 = !DILocation(line: 0, scope: !483, inlinedAt: !733) +!733 = distinct !DILocation(line: 327, column: 11, scope: !724) +!734 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !733) +!735 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !733) +!736 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !733) +!737 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !733) +!738 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !733) +!739 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !733) +!740 = !DILocation(line: 327, column: 4, scope: !724) +!741 = !DILocation(line: 0, scope: !449, inlinedAt: !742) +!742 = distinct !DILocation(line: 329, column: 7, scope: !720) +!743 = !DILocation(line: 138, column: 16, scope: !449, inlinedAt: !742) +!744 = !DILocation(line: 138, column: 43, scope: !449, inlinedAt: !742) +!745 = !DILocation(line: 139, column: 19, scope: !449, inlinedAt: !742) +!746 = !DILocation(line: 140, column: 16, scope: !449, inlinedAt: !742) +!747 = !DILocation(line: 330, column: 5, scope: !720) +!748 = !DILocation(line: 331, column: 12, scope: !749) +!749 = distinct !DILexicalBlock(scope: !620, file: !3, line: 331, column: 7) +!750 = !DILocation(line: 331, column: 7, scope: !620) +!751 = !DILocation(line: 333, column: 16, scope: !752) +!752 = distinct !DILexicalBlock(scope: !753, file: !3, line: 333, column: 11) +!753 = distinct !DILexicalBlock(scope: !749, file: !3, line: 332, column: 5) +!754 = !DILocation(line: 333, column: 11, scope: !753) +!755 = !DILocation(line: 0, scope: !483, inlinedAt: !756) +!756 = distinct !DILocation(line: 334, column: 9, scope: !752) +!757 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !756) +!758 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !756) +!759 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !756) +!760 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !756) +!761 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !756) +!762 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !756) +!763 = !DILocation(line: 334, column: 2, scope: !752) +!764 = !DILocation(line: 0, scope: !449, inlinedAt: !765) +!765 = distinct !DILocation(line: 335, column: 7, scope: !753) +!766 = !DILocation(line: 138, column: 16, scope: !449, inlinedAt: !765) +!767 = !DILocation(line: 138, column: 43, scope: !449, inlinedAt: !765) +!768 = !DILocation(line: 139, column: 19, scope: !449, inlinedAt: !765) +!769 = !DILocation(line: 140, column: 16, scope: !449, inlinedAt: !765) +!770 = !DILocation(line: 336, column: 5, scope: !753) +!771 = !DILocation(line: 337, column: 15, scope: !620) +!772 = !DILocation(line: 337, column: 22, scope: !620) +!773 = !DILocalVariable(name: "zExp", scope: !620, file: !3, line: 287, type: !281) +!774 = !DILocation(line: 338, column: 16, scope: !620) +!775 = !DILocation(line: 338, column: 46, scope: !620) +!776 = !DILocation(line: 339, column: 16, scope: !620) +!777 = !DILocation(line: 339, column: 46, scope: !620) +!778 = !DILocation(line: 340, column: 21, scope: !779) +!779 = distinct !DILexicalBlock(scope: !620, file: !3, line: 340, column: 7) +!780 = !DILocation(line: 340, column: 12, scope: !779) +!781 = !DILocation(line: 340, column: 7, scope: !620) +!782 = !DILocation(line: 342, column: 12, scope: !783) +!783 = distinct !DILexicalBlock(scope: !779, file: !3, line: 341, column: 5) +!784 = !DILocation(line: 343, column: 7, scope: !783) +!785 = !DILocation(line: 344, column: 5, scope: !783) +!786 = !DILocation(line: 345, column: 10, scope: !620) +!787 = !DILocalVariable(name: "zSig", scope: !620, file: !3, line: 288, type: !6) +!788 = !DILocation(line: 346, column: 13, scope: !789) +!789 = distinct !DILexicalBlock(scope: !620, file: !3, line: 346, column: 7) +!790 = !DILocation(line: 346, column: 22, scope: !789) +!791 = !DILocation(line: 346, column: 7, scope: !620) +!792 = !DILocation(line: 348, column: 7, scope: !793) +!793 = distinct !DILexicalBlock(scope: !789, file: !3, line: 347, column: 5) +!794 = !DILocation(line: 349, column: 24, scope: !793) +!795 = !DILocation(line: 349, column: 31, scope: !793) +!796 = !DILocation(line: 349, column: 7, scope: !793) +!797 = !DILocation(line: 350, column: 7, scope: !793) +!798 = !DILocation(line: 350, column: 24, scope: !793) +!799 = !DILocation(line: 350, column: 29, scope: !793) +!800 = !DILocation(line: 352, column: 4, scope: !801) +!801 = distinct !DILexicalBlock(scope: !793, file: !3, line: 351, column: 2) +!802 = !DILocation(line: 353, column: 12, scope: !801) +!803 = !DILocation(line: 353, column: 18, scope: !801) +!804 = !DILocation(line: 353, column: 4, scope: !801) +!805 = distinct !{!805, !797, !806, !807} +!806 = !DILocation(line: 354, column: 2, scope: !793) +!807 = !{!"llvm.loop.mustprogress"} +!808 = !DILocation(line: 355, column: 16, scope: !793) +!809 = !DILocation(line: 355, column: 21, scope: !793) +!810 = !DILocation(line: 355, column: 15, scope: !793) +!811 = !DILocation(line: 355, column: 12, scope: !793) +!812 = !DILocation(line: 356, column: 5, scope: !793) +!813 = !DILocation(line: 0, scope: !497, inlinedAt: !814) +!814 = distinct !DILocation(line: 357, column: 10, scope: !620) +!815 = !DILocation(line: 190, column: 61, scope: !497, inlinedAt: !814) +!816 = !DILocation(line: 196, column: 18, scope: !497, inlinedAt: !814) +!817 = !DILocation(line: 197, column: 36, scope: !497, inlinedAt: !814) +!818 = !DILocation(line: 197, column: 22, scope: !497, inlinedAt: !814) +!819 = !DILocation(line: 199, column: 8, scope: !510, inlinedAt: !814) +!820 = !DILocation(line: 199, column: 7, scope: !497, inlinedAt: !814) +!821 = !DILocation(line: 201, column: 24, scope: !513, inlinedAt: !814) +!822 = !DILocation(line: 201, column: 11, scope: !514, inlinedAt: !814) +!823 = !DILocation(line: 204, column: 2, scope: !517, inlinedAt: !814) +!824 = !DILocation(line: 208, column: 8, scope: !519, inlinedAt: !814) +!825 = !DILocation(line: 208, column: 8, scope: !520, inlinedAt: !814) +!826 = !DILocation(line: 210, column: 25, scope: !523, inlinedAt: !814) +!827 = !DILocation(line: 210, column: 12, scope: !524, inlinedAt: !814) +!828 = !DILocation(line: 211, column: 3, scope: !523, inlinedAt: !814) +!829 = !DILocation(line: 0, scope: !520, inlinedAt: !814) +!830 = !DILocation(line: 212, column: 6, scope: !524, inlinedAt: !814) +!831 = !DILocation(line: 215, column: 25, scope: !530, inlinedAt: !814) +!832 = !DILocation(line: 215, column: 12, scope: !531, inlinedAt: !814) +!833 = !DILocation(line: 216, column: 3, scope: !530, inlinedAt: !814) +!834 = !DILocation(line: 0, scope: !519, inlinedAt: !814) +!835 = !DILocation(line: 0, scope: !513, inlinedAt: !814) +!836 = !DILocation(line: 219, column: 5, scope: !514, inlinedAt: !814) +!837 = !DILocation(line: 220, column: 15, scope: !497, inlinedAt: !814) +!838 = !DILocation(line: 220, column: 20, scope: !497, inlinedAt: !814) +!839 = !DILocation(line: 221, column: 25, scope: !541, inlinedAt: !814) +!840 = !DILocation(line: 221, column: 16, scope: !541, inlinedAt: !814) +!841 = !DILocation(line: 221, column: 13, scope: !541, inlinedAt: !814) +!842 = !DILocation(line: 221, column: 7, scope: !497, inlinedAt: !814) +!843 = !DILocation(line: 223, column: 18, scope: !546, inlinedAt: !814) +!844 = !DILocation(line: 224, column: 4, scope: !546, inlinedAt: !814) +!845 = !DILocation(line: 224, column: 14, scope: !546, inlinedAt: !814) +!846 = !DILocation(line: 224, column: 24, scope: !546, inlinedAt: !814) +!847 = !DILocation(line: 224, column: 39, scope: !546, inlinedAt: !814) +!848 = !DILocation(line: 224, column: 46, scope: !546, inlinedAt: !814) +!849 = !DILocation(line: 224, column: 44, scope: !546, inlinedAt: !814) +!850 = !DILocation(line: 224, column: 62, scope: !546, inlinedAt: !814) +!851 = !DILocation(line: 223, column: 11, scope: !547, inlinedAt: !814) +!852 = !DILocation(line: 226, column: 4, scope: !557, inlinedAt: !814) +!853 = !DILocation(line: 0, scope: !483, inlinedAt: !854) +!854 = distinct !DILocation(line: 227, column: 11, scope: !557, inlinedAt: !814) +!855 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !854) +!856 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !854) +!857 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !854) +!858 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !854) +!859 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !854) +!860 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !854) +!861 = !DILocation(line: 227, column: 59, scope: !557, inlinedAt: !814) +!862 = !DILocation(line: 227, column: 43, scope: !557, inlinedAt: !814) +!863 = !DILocation(line: 227, column: 41, scope: !557, inlinedAt: !814) +!864 = !DILocation(line: 227, column: 4, scope: !557, inlinedAt: !814) +!865 = !DILocation(line: 229, column: 16, scope: !571, inlinedAt: !814) +!866 = !DILocation(line: 229, column: 11, scope: !547, inlinedAt: !814) +!867 = !DILocation(line: 234, column: 25, scope: !575, inlinedAt: !814) +!868 = !DILocation(line: 234, column: 31, scope: !575, inlinedAt: !814) +!869 = !DILocation(line: 234, column: 4, scope: !575, inlinedAt: !814) +!870 = !DILocation(line: 236, column: 16, scope: !575, inlinedAt: !814) +!871 = !DILocation(line: 236, column: 21, scope: !575, inlinedAt: !814) +!872 = !DILocation(line: 237, column: 8, scope: !581, inlinedAt: !814) +!873 = !DILocation(line: 237, column: 15, scope: !581, inlinedAt: !814) +!874 = !DILocation(line: 237, column: 18, scope: !581, inlinedAt: !814) +!875 = !DILocation(line: 237, column: 8, scope: !575, inlinedAt: !814) +!876 = !DILocation(line: 238, column: 6, scope: !581, inlinedAt: !814) +!877 = !DILocation(line: 239, column: 2, scope: !575, inlinedAt: !814) +!878 = !DILocation(line: 240, column: 5, scope: !547, inlinedAt: !814) +!879 = !DILocation(line: 241, column: 7, scope: !589, inlinedAt: !814) +!880 = !DILocation(line: 241, column: 7, scope: !497, inlinedAt: !814) +!881 = !DILocation(line: 242, column: 27, scope: !589, inlinedAt: !814) +!882 = !DILocation(line: 242, column: 5, scope: !589, inlinedAt: !814) +!883 = !DILocation(line: 243, column: 11, scope: !497, inlinedAt: !814) +!884 = !DILocation(line: 243, column: 18, scope: !497, inlinedAt: !814) +!885 = !DILocation(line: 243, column: 16, scope: !497, inlinedAt: !814) +!886 = !DILocation(line: 243, column: 34, scope: !497, inlinedAt: !814) +!887 = !DILocation(line: 243, column: 8, scope: !497, inlinedAt: !814) +!888 = !DILocation(line: 244, column: 25, scope: !497, inlinedAt: !814) +!889 = !DILocation(line: 244, column: 34, scope: !497, inlinedAt: !814) +!890 = !DILocation(line: 244, column: 13, scope: !497, inlinedAt: !814) +!891 = !DILocation(line: 244, column: 40, scope: !497, inlinedAt: !814) +!892 = !DILocation(line: 244, column: 11, scope: !497, inlinedAt: !814) +!893 = !DILocation(line: 244, column: 8, scope: !497, inlinedAt: !814) +!894 = !DILocation(line: 245, column: 7, scope: !605, inlinedAt: !814) +!895 = !DILocation(line: 245, column: 12, scope: !605, inlinedAt: !814) +!896 = !DILocation(line: 245, column: 7, scope: !497, inlinedAt: !814) +!897 = !DILocation(line: 246, column: 5, scope: !605, inlinedAt: !814) +!898 = !DILocation(line: 247, column: 36, scope: !497, inlinedAt: !814) +!899 = !DILocation(line: 0, scope: !483, inlinedAt: !900) +!900 = distinct !DILocation(line: 247, column: 10, scope: !497, inlinedAt: !814) +!901 = !DILocation(line: 159, column: 21, scope: !483, inlinedAt: !900) +!902 = !DILocation(line: 159, column: 28, scope: !483, inlinedAt: !900) +!903 = !DILocation(line: 159, column: 48, scope: !483, inlinedAt: !900) +!904 = !DILocation(line: 159, column: 54, scope: !483, inlinedAt: !900) +!905 = !DILocation(line: 159, column: 35, scope: !483, inlinedAt: !900) +!906 = !DILocation(line: 159, column: 61, scope: !483, inlinedAt: !900) +!907 = !DILocation(line: 247, column: 3, scope: !497, inlinedAt: !814) +!908 = !DILocation(line: 357, column: 3, scope: !620) +!909 = !DILocation(line: 359, column: 1, scope: !620) +!910 = distinct !DISubprogram(name: "propagateFloat64NaN", linkageName: "_ZL19propagateFloat64NaNyy", scope: !397, file: !397, line: 109, type: !911, scopeLine: 110, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!911 = !DISubroutineType(types: !912) +!912 = !{!408, !408, !408} +!913 = !DILocalVariable(name: "a", arg: 1, scope: !910, file: !397, line: 109, type: !408) +!914 = !DILocation(line: 0, scope: !910) +!915 = !DILocalVariable(name: "b", arg: 2, scope: !910, file: !397, line: 109, type: !408) +!916 = !DILocation(line: 113, column: 12, scope: !910) +!917 = !DILocalVariable(name: "aIsNaN", scope: !910, file: !397, line: 111, type: !407) +!918 = !DILocation(line: 114, column: 21, scope: !910) +!919 = !DILocalVariable(name: "aIsSignalingNaN", scope: !910, file: !397, line: 111, type: !407) +!920 = !DILocation(line: 115, column: 12, scope: !910) +!921 = !DILocalVariable(name: "bIsNaN", scope: !910, file: !397, line: 111, type: !407) +!922 = !DILocation(line: 116, column: 21, scope: !910) +!923 = !DILocalVariable(name: "bIsSignalingNaN", scope: !910, file: !397, line: 111, type: !407) +!924 = !DILocation(line: 117, column: 5, scope: !910) +!925 = !DILocation(line: 118, column: 5, scope: !910) +!926 = !DILocation(line: 119, column: 23, scope: !927) +!927 = distinct !DILexicalBlock(scope: !910, file: !397, line: 119, column: 7) +!928 = !DILocation(line: 119, column: 7, scope: !927) +!929 = !DILocation(line: 119, column: 7, scope: !910) +!930 = !DILocation(line: 120, column: 5, scope: !927) +!931 = !DILocation(line: 121, column: 10, scope: !910) +!932 = !DILocation(line: 121, column: 32, scope: !910) +!933 = !DILocation(line: 121, column: 54, scope: !910) +!934 = !DILocation(line: 121, column: 3, scope: !910) +!935 = distinct !DISubprogram(name: "estimateDiv128To64", linkageName: "_ZL18estimateDiv128To64yyy", scope: !21, file: !21, line: 157, type: !936, scopeLine: 158, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!936 = !DISubroutineType(types: !937) +!937 = !{!6, !6, !6, !6} +!938 = !DILocalVariable(name: "a0", arg: 1, scope: !935, file: !21, line: 157, type: !6) +!939 = !DILocation(line: 0, scope: !935) +!940 = !DILocalVariable(name: "a1", arg: 2, scope: !935, file: !21, line: 157, type: !6) +!941 = !DILocalVariable(name: "b", arg: 3, scope: !935, file: !21, line: 157, type: !6) +!942 = !DILocalVariable(name: "rem0", scope: !935, file: !21, line: 160, type: !6) +!943 = !DILocation(line: 160, column: 10, scope: !935) +!944 = !DILocalVariable(name: "rem1", scope: !935, file: !21, line: 160, type: !6) +!945 = !DILocation(line: 160, column: 16, scope: !935) +!946 = !DILocalVariable(name: "term0", scope: !935, file: !21, line: 160, type: !6) +!947 = !DILocation(line: 160, column: 22, scope: !935) +!948 = !DILocalVariable(name: "term1", scope: !935, file: !21, line: 160, type: !6) +!949 = !DILocation(line: 160, column: 29, scope: !935) +!950 = !DILocation(line: 163, column: 9, scope: !951) +!951 = distinct !DILexicalBlock(scope: !935, file: !21, line: 163, column: 7) +!952 = !DILocation(line: 163, column: 7, scope: !935) +!953 = !DILocation(line: 164, column: 5, scope: !951) +!954 = !DILocation(line: 165, column: 10, scope: !935) +!955 = !DILocalVariable(name: "b0", scope: !935, file: !21, line: 159, type: !6) +!956 = !DILocation(line: 166, column: 11, scope: !935) +!957 = !DILocation(line: 166, column: 17, scope: !935) +!958 = !DILocation(line: 166, column: 7, scope: !935) +!959 = !DILocation(line: 166, column: 59, scope: !935) +!960 = !DILocation(line: 166, column: 65, scope: !935) +!961 = !DILocalVariable(name: "z", scope: !935, file: !21, line: 161, type: !6) +!962 = !DILocation(line: 167, column: 3, scope: !935) +!963 = !DILocation(line: 168, column: 19, scope: !935) +!964 = !DILocation(line: 168, column: 26, scope: !935) +!965 = !DILocation(line: 168, column: 3, scope: !935) +!966 = !DILocation(line: 169, column: 3, scope: !935) +!967 = !DILocation(line: 169, column: 21, scope: !935) +!968 = !DILocation(line: 169, column: 27, scope: !935) +!969 = !DILocation(line: 171, column: 9, scope: !970) +!970 = distinct !DILexicalBlock(scope: !935, file: !21, line: 170, column: 5) +!971 = !DILocation(line: 172, column: 14, scope: !970) +!972 = !DILocalVariable(name: "b1", scope: !935, file: !21, line: 159, type: !6) +!973 = !DILocation(line: 173, column: 15, scope: !970) +!974 = !DILocation(line: 173, column: 21, scope: !970) +!975 = !DILocation(line: 173, column: 7, scope: !970) +!976 = distinct !{!976, !966, !977, !807} +!977 = !DILocation(line: 174, column: 5, scope: !935) +!978 = !DILocation(line: 175, column: 11, scope: !935) +!979 = !DILocation(line: 175, column: 16, scope: !935) +!980 = !DILocation(line: 175, column: 26, scope: !935) +!981 = !DILocation(line: 175, column: 31, scope: !935) +!982 = !DILocation(line: 175, column: 23, scope: !935) +!983 = !DILocation(line: 175, column: 8, scope: !935) +!984 = !DILocation(line: 176, column: 12, scope: !935) +!985 = !DILocation(line: 176, column: 21, scope: !935) +!986 = !DILocation(line: 176, column: 18, scope: !935) +!987 = !DILocation(line: 176, column: 8, scope: !935) +!988 = !DILocation(line: 176, column: 42, scope: !935) +!989 = !DILocation(line: 176, column: 47, scope: !935) +!990 = !DILocation(line: 176, column: 5, scope: !935) +!991 = !DILocation(line: 177, column: 3, scope: !935) +!992 = !DILocation(line: 179, column: 1, scope: !935) +!993 = !DILocalVariable(name: "a", arg: 1, scope: !20, file: !21, line: 187, type: !24) +!994 = !DILocation(line: 0, scope: !20) +!995 = !DILocalVariable(name: "shiftCount", scope: !20, file: !21, line: 207, type: !16) +!996 = !DILocation(line: 210, column: 9, scope: !997) +!997 = distinct !DILexicalBlock(scope: !20, file: !21, line: 210, column: 7) +!998 = !DILocation(line: 210, column: 7, scope: !20) +!999 = !DILocation(line: 212, column: 18, scope: !1000) +!1000 = distinct !DILexicalBlock(scope: !997, file: !21, line: 211, column: 5) +!1001 = !DILocation(line: 213, column: 9, scope: !1000) +!1002 = !DILocation(line: 214, column: 5, scope: !1000) +!1003 = !DILocation(line: 215, column: 9, scope: !1004) +!1004 = distinct !DILexicalBlock(scope: !20, file: !21, line: 215, column: 7) +!1005 = !DILocation(line: 215, column: 7, scope: !20) +!1006 = !DILocation(line: 217, column: 18, scope: !1007) +!1007 = distinct !DILexicalBlock(scope: !1004, file: !21, line: 216, column: 5) +!1008 = !DILocation(line: 218, column: 9, scope: !1007) +!1009 = !DILocation(line: 219, column: 5, scope: !1007) +!1010 = !DILocation(line: 220, column: 41, scope: !20) +!1011 = !DILocation(line: 220, column: 17, scope: !20) +!1012 = !DILocation(line: 220, column: 14, scope: !20) +!1013 = !DILocation(line: 221, column: 3, scope: !20) diff --git a/applications/newton/llvm-ir/CHStone_test/float64_mul.ll b/applications/newton/llvm-ir/CHStone_test/float64_mul.ll new file mode 100644 index 000000000..bb87be5f1 --- /dev/null +++ b/applications/newton/llvm-ir/CHStone_test/float64_mul.ll @@ -0,0 +1,1597 @@ +; ModuleID = 'float64_mul.ll' +source_filename = "dfmul/float64_mul.cpp" +target datalayout = "e-m:e-p270:32:32-p271:32:32-p272:64:64-i64:64-f80:128-n8:16:32:64-S128" +target triple = "x86_64-pc-linux-gnu" + +@float_rounding_mode = dso_local global i32 0, align 4, !dbg !0 +@float_exception_flags = dso_local global i32 0, align 4, !dbg !14 +@_ZZL19countLeadingZeros32jE21countLeadingZerosHigh = internal constant <{ [128 x i32], [128 x i32] }> <{ [128 x i32] [i32 8, i32 7, i32 6, i32 6, i32 5, i32 5, i32 5, i32 5, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 4, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 3, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 2, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1, i32 1], [128 x i32] zeroinitializer }>, align 16, !dbg !18 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z19shift64RightJammingyiPy(i64 %0, i32 %1, i64* %2) #0 !dbg !278 { + call void @llvm.dbg.value(metadata i64 %0, metadata !283, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i32 %1, metadata !285, metadata !DIExpression()), !dbg !284 + call void @llvm.dbg.value(metadata i64* %2, metadata !286, metadata !DIExpression()), !dbg !284 + %4 = icmp eq i32 %1, 0, !dbg !287 + br i1 %4, label %5, label %6, !dbg !289 + +5: ; preds = %3 + call void @llvm.dbg.value(metadata i64 %0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22, !dbg !291 + +6: ; preds = %3 + %7 = icmp slt i32 %1, 64, !dbg !293 + br i1 %7, label %8, label %18, !dbg !295 + +8: ; preds = %6 + %9 = zext i32 %1 to i64, !dbg !296 + %10 = lshr i64 %0, %9, !dbg !296 + %11 = sub nsw i32 0, %1, !dbg !298 + %12 = and i32 %11, 63, !dbg !299 + %13 = zext i32 %12 to i64, !dbg !300 + %14 = shl i64 %0, %13, !dbg !300 + %15 = icmp ne i64 %14, 0, !dbg !301 + %16 = zext i1 %15 to i64, !dbg !302 + %17 = or i64 %10, %16, !dbg !303 + call void @llvm.dbg.value(metadata i64 %17, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21, !dbg !304 + +18: ; preds = %6 + %19 = icmp ne i64 %0, 0, !dbg !305 + %20 = zext i1 %19 to i64, !dbg !307 + call void @llvm.dbg.value(metadata i64 %20, metadata !290, metadata !DIExpression()), !dbg !284 + br label %21 + +21: ; preds = %18, %8 + %.0 = phi i64 [ %17, %8 ], [ %20, %18 ], !dbg !308 + call void @llvm.dbg.value(metadata i64 %.0, metadata !290, metadata !DIExpression()), !dbg !284 + br label %22 + +22: ; preds = %21, %5 + %.1 = phi i64 [ %0, %5 ], [ %.0, %21 ], !dbg !309 + call void @llvm.dbg.value(metadata i64 %.1, metadata !290, metadata !DIExpression()), !dbg !284 + store i64 %.1, i64* %2, align 8, !dbg !310 + ret void, !dbg !311 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.declare(metadata, metadata, metadata) #1 + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z10mul64To128yyPyS_(i64 %0, i64 %1, i64* %2, i64* %3) #0 !dbg !312 { + call void @llvm.dbg.value(metadata i64 %0, metadata !315, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64 %1, metadata !317, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %2, metadata !318, metadata !DIExpression()), !dbg !316 + call void @llvm.dbg.value(metadata i64* %3, metadata !319, metadata !DIExpression()), !dbg !316 + %5 = trunc i64 %0 to i32, !dbg !320 + call void @llvm.dbg.value(metadata i32 %5, metadata !321, metadata !DIExpression()), !dbg !316 + %6 = lshr i64 %0, 32, !dbg !322 + %7 = trunc i64 %6 to i32, !dbg !323 + call void @llvm.dbg.value(metadata i32 %7, metadata !324, metadata !DIExpression()), !dbg !316 + %8 = trunc i64 %1 to i32, !dbg !325 + call void @llvm.dbg.value(metadata i32 %8, metadata !326, metadata !DIExpression()), !dbg !316 + %9 = lshr i64 %1, 32, !dbg !327 + %10 = trunc i64 %9 to i32, !dbg !328 + call void @llvm.dbg.value(metadata i32 %10, metadata !329, metadata !DIExpression()), !dbg !316 + %11 = zext i32 %5 to i64, !dbg !330 + %12 = zext i32 %8 to i64, !dbg !331 + %13 = mul i64 %11, %12, !dbg !332 + call void @llvm.dbg.value(metadata i64 %13, metadata !333, metadata !DIExpression()), !dbg !316 + %14 = zext i32 %5 to i64, !dbg !334 + %15 = zext i32 %10 to i64, !dbg !335 + %16 = mul i64 %14, %15, !dbg !336 + call void @llvm.dbg.value(metadata i64 %16, metadata !337, metadata !DIExpression()), !dbg !316 + %17 = zext i32 %7 to i64, !dbg !338 + %18 = zext i32 %8 to i64, !dbg !339 + %19 = mul i64 %17, %18, !dbg !340 + call void @llvm.dbg.value(metadata i64 %19, metadata !341, metadata !DIExpression()), !dbg !316 + %20 = zext i32 %7 to i64, !dbg !342 + %21 = zext i32 %10 to i64, !dbg !343 + %22 = mul i64 %20, %21, !dbg !344 + call void @llvm.dbg.value(metadata i64 %22, metadata !345, metadata !DIExpression()), !dbg !316 + %23 = add i64 %16, %19, !dbg !346 + call void @llvm.dbg.value(metadata i64 %23, metadata !337, metadata !DIExpression()), !dbg !316 + %24 = icmp ult i64 %23, %19, !dbg !347 + %25 = zext i1 %24 to i64, !dbg !348 + %26 = shl i64 %25, 32, !dbg !349 + %27 = lshr i64 %23, 32, !dbg !350 + %28 = add i64 %26, %27, !dbg !351 + %29 = add i64 %22, %28, !dbg !352 + call void @llvm.dbg.value(metadata i64 %29, metadata !345, metadata !DIExpression()), !dbg !316 + %30 = shl i64 %23, 32, !dbg !353 + call void @llvm.dbg.value(metadata i64 %30, metadata !337, metadata !DIExpression()), !dbg !316 + %31 = add i64 %13, %30, !dbg !354 + call void @llvm.dbg.value(metadata i64 %31, metadata !333, metadata !DIExpression()), !dbg !316 + %32 = icmp ult i64 %31, %30, !dbg !355 + %33 = zext i1 %32 to i64, !dbg !356 + %34 = add i64 %29, %33, !dbg !357 + call void @llvm.dbg.value(metadata i64 %34, metadata !345, metadata !DIExpression()), !dbg !316 + store i64 %31, i64* %3, align 8, !dbg !358 + store i64 %34, i64* %2, align 8, !dbg !359 + ret void, !dbg !360 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local void @_Z11float_raisei(i32 %0) #0 !dbg !361 { + call void @llvm.dbg.value(metadata i32 %0, metadata !365, metadata !DIExpression()), !dbg !366 + %2 = load i32, i32* @float_exception_flags, align 4, !dbg !367 + %3 = or i32 %2, %0, !dbg !367 + store i32 %3, i32* @float_exception_flags, align 4, !dbg !367 + ret void, !dbg !368 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z14float64_is_nany(i64 %0) #0 !dbg !369 { + call void @llvm.dbg.value(metadata i64 %0, metadata !375, metadata !DIExpression()), !dbg !376 + %2 = shl i64 %0, 1, !dbg !377 + %3 = icmp ult i64 -9007199254740992, %2, !dbg !378 + %4 = zext i1 %3 to i32, !dbg !379 + ret i32 %4, !dbg !380 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i32 @_Z24float64_is_signaling_nany(i64 %0) #0 !dbg !381 { + call void @llvm.dbg.value(metadata i64 %0, metadata !382, metadata !DIExpression()), !dbg !383 + %2 = lshr i64 %0, 51, !dbg !384 + %3 = and i64 %2, 4095, !dbg !385 + %4 = icmp eq i64 %3, 4094, !dbg !386 + br i1 %4, label %5, label %8, !dbg !387 + +5: ; preds = %1 + %6 = and i64 %0, 2251799813685247, !dbg !388 + %7 = icmp ne i64 %6, 0, !dbg !389 + br label %8 + +8: ; preds = %5, %1 + %9 = phi i1 [ false, %1 ], [ %7, %5 ], !dbg !383 + %10 = zext i1 %9 to i32, !dbg !390 + ret i32 %10, !dbg !391 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @extractFloat64Frac(i64 %0) #2 !dbg !392 { + call void @llvm.dbg.value(metadata i64 %0, metadata !395, metadata !DIExpression()), !dbg !396 + %2 = and i64 %0, 4503599627370495, !dbg !397 + ret i64 %2, !dbg !398 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Exp(i64 %0) #2 !dbg !399 { + call void @llvm.dbg.value(metadata i64 %0, metadata !402, metadata !DIExpression()), !dbg !403 + %2 = lshr i64 %0, 52, !dbg !404 + %3 = and i64 %2, 2047, !dbg !405 + %4 = trunc i64 %3 to i32, !dbg !406 + ret i32 %4, !dbg !407 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i32 @extractFloat64Sign(i64 %0) #2 !dbg !408 { + call void @llvm.dbg.value(metadata i64 %0, metadata !409, metadata !DIExpression()), !dbg !410 + %2 = lshr i64 %0, 63, !dbg !411 + %3 = trunc i64 %2 to i32, !dbg !412 + ret i32 %3, !dbg !413 +} + +; Function Attrs: mustprogress noinline uwtable +define dso_local void @normalizeFloat64Subnormal(i64 %0, i32* %1, i64* %2) #3 !dbg !414 { + call void @llvm.dbg.value(metadata i64 %0, metadata !418, metadata !DIExpression()), !dbg !419 + call void @llvm.dbg.value(metadata i32* %1, metadata !420, metadata !DIExpression()), !dbg !419 + call void @llvm.dbg.value(metadata i64* %2, metadata !421, metadata !DIExpression()), !dbg !419 + %4 = call i32 @_ZL19countLeadingZeros64y(i64 %0), !dbg !422 + %5 = sub nsw i32 %4, 11, !dbg !423 + call void @llvm.dbg.value(metadata i32 %5, metadata !424, metadata !DIExpression()), !dbg !419 + %6 = zext i32 %5 to i64, !dbg !425 + %7 = shl i64 %0, %6, !dbg !425 + store i64 %7, i64* %2, align 8, !dbg !426 + %8 = sub nsw i32 1, %5, !dbg !427 + store i32 %8, i32* %1, align 4, !dbg !428 + ret void, !dbg !429 +} + +; Function Attrs: mustprogress noinline uwtable +define internal i32 @_ZL19countLeadingZeros64y(i64 %0) #3 !dbg !430 { + call void @llvm.dbg.value(metadata i64 %0, metadata !433, metadata !DIExpression()), !dbg !434 + call void @llvm.dbg.value(metadata i32 0, metadata !435, metadata !DIExpression()), !dbg !434 + %2 = icmp ult i64 %0, 4294967296, !dbg !436 + br i1 %2, label %3, label %5, !dbg !438 + +3: ; preds = %1 + %4 = add nsw i32 0, 32, !dbg !439 + call void @llvm.dbg.value(metadata i32 %4, metadata !435, metadata !DIExpression()), !dbg !434 + br label %7, !dbg !441 + +5: ; preds = %1 + %6 = lshr i64 %0, 32, !dbg !442 + call void @llvm.dbg.value(metadata i64 %6, metadata !433, metadata !DIExpression()), !dbg !434 + br label %7 + +7: ; preds = %5, %3 + %.01 = phi i32 [ %4, %3 ], [ 0, %5 ], !dbg !434 + %.0 = phi i64 [ %0, %3 ], [ %6, %5 ] + call void @llvm.dbg.value(metadata i64 %.0, metadata !433, metadata !DIExpression()), !dbg !434 + call void @llvm.dbg.value(metadata i32 %.01, metadata !435, metadata !DIExpression()), !dbg !434 + %8 = trunc i64 %.0 to i32, !dbg !444 + %9 = call i32 @_ZL19countLeadingZeros32j(i32 %8), !dbg !445 + %10 = add nsw i32 %.01, %9, !dbg !446 + call void @llvm.dbg.value(metadata i32 %10, metadata !435, metadata !DIExpression()), !dbg !434 + ret i32 %10, !dbg !447 +} + +; Function Attrs: alwaysinline mustprogress nounwind uwtable +define dso_local i64 @packFloat64(i32 %0, i32 %1, i64 %2) #2 !dbg !448 { + call void @llvm.dbg.value(metadata i32 %0, metadata !451, metadata !DIExpression()), !dbg !452 + call void @llvm.dbg.value(metadata i32 %1, metadata !453, metadata !DIExpression()), !dbg !452 + call void @llvm.dbg.value(metadata i64 %2, metadata !454, metadata !DIExpression()), !dbg !452 + %4 = sext i32 %0 to i64, !dbg !455 + %5 = shl i64 %4, 63, !dbg !456 + %6 = sext i32 %1 to i64, !dbg !457 + %7 = shl i64 %6, 52, !dbg !458 + %8 = add i64 %5, %7, !dbg !459 + %9 = add i64 %8, %2, !dbg !460 + ret i64 %9, !dbg !461 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define dso_local i64 @roundAndPackFloat64(i32 %0, i32 %1, i64 %2) #0 !dbg !462 { + %4 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i32 %0, metadata !463, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 %1, metadata !465, metadata !DIExpression()), !dbg !464 + store i64 %2, i64* %4, align 8 + call void @llvm.dbg.declare(metadata i64* %4, metadata !466, metadata !DIExpression()), !dbg !467 + %5 = load i32, i32* @float_rounding_mode, align 4, !dbg !468 + call void @llvm.dbg.value(metadata i32 %5, metadata !469, metadata !DIExpression()), !dbg !464 + %6 = icmp eq i32 %5, 0, !dbg !470 + %7 = zext i1 %6 to i32, !dbg !471 + call void @llvm.dbg.value(metadata i32 %7, metadata !472, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 512, metadata !473, metadata !DIExpression()), !dbg !464 + %8 = icmp ne i32 %7, 0, !dbg !474 + br i1 %8, label %24, label %9, !dbg !476 + +9: ; preds = %3 + %10 = icmp eq i32 %5, 1, !dbg !477 + br i1 %10, label %11, label %12, !dbg !480 + +11: ; preds = %9 + call void @llvm.dbg.value(metadata i32 0, metadata !473, metadata !DIExpression()), !dbg !464 + br label %23, !dbg !481 + +12: ; preds = %9 + call void @llvm.dbg.value(metadata i32 1023, metadata !473, metadata !DIExpression()), !dbg !464 + %13 = icmp ne i32 %0, 0, !dbg !483 + br i1 %13, label %14, label %18, !dbg !486 + +14: ; preds = %12 + %15 = icmp eq i32 %5, 2, !dbg !487 + br i1 %15, label %16, label %17, !dbg !490 + +16: ; preds = %14 + call void @llvm.dbg.value(metadata i32 0, metadata !473, metadata !DIExpression()), !dbg !464 + br label %17, !dbg !491 + +17: ; preds = %16, %14 + %.01 = phi i32 [ 0, %16 ], [ 1023, %14 ], !dbg !492 + call void @llvm.dbg.value(metadata i32 %.01, metadata !473, metadata !DIExpression()), !dbg !464 + br label %22, !dbg !493 + +18: ; preds = %12 + %19 = icmp eq i32 %5, 3, !dbg !494 + br i1 %19, label %20, label %21, !dbg !497 + +20: ; preds = %18 + call void @llvm.dbg.value(metadata i32 0, metadata !473, metadata !DIExpression()), !dbg !464 + br label %21, !dbg !498 + +21: ; preds = %20, %18 + %.12 = phi i32 [ 0, %20 ], [ 1023, %18 ], !dbg !492 + call void @llvm.dbg.value(metadata i32 %.12, metadata !473, metadata !DIExpression()), !dbg !464 + br label %22 + +22: ; preds = %21, %17 + %.2 = phi i32 [ %.01, %17 ], [ %.12, %21 ], !dbg !499 + call void @llvm.dbg.value(metadata i32 %.2, metadata !473, metadata !DIExpression()), !dbg !464 + br label %23 + +23: ; preds = %22, %11 + %.3 = phi i32 [ 0, %11 ], [ %.2, %22 ], !dbg !500 + call void @llvm.dbg.value(metadata i32 %.3, metadata !473, metadata !DIExpression()), !dbg !464 + br label %24, !dbg !501 + +24: ; preds = %23, %3 + %.4 = phi i32 [ 512, %3 ], [ %.3, %23 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.4, metadata !473, metadata !DIExpression()), !dbg !464 + %25 = load i64, i64* %4, align 8, !dbg !502 + %26 = and i64 %25, 1023, !dbg !503 + %27 = trunc i64 %26 to i32, !dbg !502 + call void @llvm.dbg.value(metadata i32 %27, metadata !504, metadata !DIExpression()), !dbg !464 + %28 = trunc i32 %1 to i16, !dbg !505 + %29 = zext i16 %28 to i32, !dbg !507 + %30 = icmp sle i32 2045, %29, !dbg !508 + br i1 %30, label %31, label %64, !dbg !509 + +31: ; preds = %24 + %32 = icmp slt i32 2045, %1, !dbg !510 + br i1 %32, label %40, label %33, !dbg !513 + +33: ; preds = %31 + %34 = icmp eq i32 %1, 2045, !dbg !514 + br i1 %34, label %35, label %50, !dbg !515 + +35: ; preds = %33 + %36 = load i64, i64* %4, align 8, !dbg !516 + %37 = sext i32 %.4 to i64, !dbg !517 + %38 = add i64 %36, %37, !dbg !518 + %39 = icmp slt i64 %38, 0, !dbg !519 + br i1 %39, label %40, label %50, !dbg !520 + +40: ; preds = %35, %31 + call void @_Z11float_raisei(i32 9), !dbg !521 + call void @llvm.dbg.value(metadata i32 %0, metadata !451, metadata !DIExpression()), !dbg !523 + call void @llvm.dbg.value(metadata i32 2047, metadata !453, metadata !DIExpression()), !dbg !523 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !523 + %41 = sext i32 %0 to i64, !dbg !525 + %42 = shl i64 %41, 63, !dbg !526 + %43 = sext i32 2047 to i64, !dbg !527 + %44 = shl i64 %43, 52, !dbg !528 + %45 = add i64 %42, %44, !dbg !529 + %46 = add i64 %45, 0, !dbg !530 + %47 = icmp eq i32 %.4, 0, !dbg !531 + %48 = zext i1 %47 to i64, !dbg !532 + %49 = sub i64 %46, %48, !dbg !533 + br label %93, !dbg !534 + +50: ; preds = %35, %33 + %51 = icmp slt i32 %1, 0, !dbg !535 + br i1 %51, label %52, label %63, !dbg !537 + +52: ; preds = %50 + call void @llvm.dbg.value(metadata i32 1, metadata !538, metadata !DIExpression()), !dbg !464 + %53 = load i64, i64* %4, align 8, !dbg !539 + %54 = sub nsw i32 0, %1, !dbg !541 + call void @_Z19shift64RightJammingyiPy(i64 %53, i32 %54, i64* %4), !dbg !542 + call void @llvm.dbg.value(metadata i32 0, metadata !465, metadata !DIExpression()), !dbg !464 + %55 = load i64, i64* %4, align 8, !dbg !543 + %56 = and i64 %55, 1023, !dbg !544 + %57 = trunc i64 %56 to i32, !dbg !543 + call void @llvm.dbg.value(metadata i32 %57, metadata !504, metadata !DIExpression()), !dbg !464 + %58 = icmp ne i32 1, 0, !dbg !545 + br i1 %58, label %59, label %62, !dbg !547 + +59: ; preds = %52 + %60 = icmp ne i32 %57, 0, !dbg !548 + br i1 %60, label %61, label %62, !dbg !549 + +61: ; preds = %59 + call void @_Z11float_raisei(i32 4), !dbg !550 + br label %62, !dbg !550 + +62: ; preds = %61, %59, %52 + br label %63, !dbg !551 + +63: ; preds = %62, %50 + %.03 = phi i32 [ 0, %62 ], [ %1, %50 ] + %.0 = phi i32 [ %57, %62 ], [ %27, %50 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.0, metadata !504, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 %.03, metadata !465, metadata !DIExpression()), !dbg !464 + br label %64, !dbg !552 + +64: ; preds = %63, %24 + %.14 = phi i32 [ %.03, %63 ], [ %1, %24 ] + %.1 = phi i32 [ %.0, %63 ], [ %27, %24 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.1, metadata !504, metadata !DIExpression()), !dbg !464 + call void @llvm.dbg.value(metadata i32 %.14, metadata !465, metadata !DIExpression()), !dbg !464 + %65 = icmp ne i32 %.1, 0, !dbg !553 + br i1 %65, label %66, label %69, !dbg !555 + +66: ; preds = %64 + %67 = load i32, i32* @float_exception_flags, align 4, !dbg !556 + %68 = or i32 %67, 1, !dbg !556 + store i32 %68, i32* @float_exception_flags, align 4, !dbg !556 + br label %69, !dbg !557 + +69: ; preds = %66, %64 + %70 = load i64, i64* %4, align 8, !dbg !558 + %71 = sext i32 %.4 to i64, !dbg !559 + %72 = add i64 %70, %71, !dbg !560 + %73 = lshr i64 %72, 10, !dbg !561 + store i64 %73, i64* %4, align 8, !dbg !562 + %74 = xor i32 %.1, 512, !dbg !563 + %75 = icmp eq i32 %74, 0, !dbg !564 + %76 = zext i1 %75 to i32, !dbg !565 + %77 = and i32 %76, %7, !dbg !566 + %78 = xor i32 %77, -1, !dbg !567 + %79 = sext i32 %78 to i64, !dbg !567 + %80 = load i64, i64* %4, align 8, !dbg !568 + %81 = and i64 %80, %79, !dbg !568 + store i64 %81, i64* %4, align 8, !dbg !568 + %82 = load i64, i64* %4, align 8, !dbg !569 + %83 = icmp eq i64 %82, 0, !dbg !571 + br i1 %83, label %84, label %85, !dbg !572 + +84: ; preds = %69 + call void @llvm.dbg.value(metadata i32 0, metadata !465, metadata !DIExpression()), !dbg !464 + br label %85, !dbg !573 + +85: ; preds = %84, %69 + %.25 = phi i32 [ 0, %84 ], [ %.14, %69 ], !dbg !464 + call void @llvm.dbg.value(metadata i32 %.25, metadata !465, metadata !DIExpression()), !dbg !464 + %86 = load i64, i64* %4, align 8, !dbg !574 + call void @llvm.dbg.value(metadata i32 %0, metadata !451, metadata !DIExpression()), !dbg !575 + call void @llvm.dbg.value(metadata i32 %.25, metadata !453, metadata !DIExpression()), !dbg !575 + call void @llvm.dbg.value(metadata i64 %86, metadata !454, metadata !DIExpression()), !dbg !575 + %87 = sext i32 %0 to i64, !dbg !577 + %88 = shl i64 %87, 63, !dbg !578 + %89 = sext i32 %.25 to i64, !dbg !579 + %90 = shl i64 %89, 52, !dbg !580 + %91 = add i64 %88, %90, !dbg !581 + %92 = add i64 %91, %86, !dbg !582 + br label %93, !dbg !583 + +93: ; preds = %85, %40 + %.06 = phi i64 [ %49, %40 ], [ %92, %85 ], !dbg !464 + ret i64 %.06, !dbg !584 +} + +; Function Attrs: mustprogress noinline uwtable +define dso_local i64 @float64_mul(i64 %0, i64 %1) #3 !dbg !585 { + %3 = alloca i32, align 4 + %4 = alloca i32, align 4 + %5 = alloca i64, align 8 + %6 = alloca i64, align 8 + %7 = alloca i64, align 8 + %8 = alloca i64, align 8 + call void @llvm.dbg.value(metadata i64 %0, metadata !590, metadata !DIExpression()), !dbg !591 + call void @llvm.dbg.value(metadata i64 %1, metadata !592, metadata !DIExpression()), !dbg !591 + call void @llvm.dbg.declare(metadata i32* %3, metadata !593, metadata !DIExpression()), !dbg !594 + call void @llvm.dbg.declare(metadata i32* %4, metadata !595, metadata !DIExpression()), !dbg !596 + call void @llvm.dbg.declare(metadata i64* %5, metadata !597, metadata !DIExpression()), !dbg !598 + call void @llvm.dbg.declare(metadata i64* %6, metadata !599, metadata !DIExpression()), !dbg !600 + call void @llvm.dbg.declare(metadata i64* %7, metadata !601, metadata !DIExpression()), !dbg !602 + call void @llvm.dbg.declare(metadata i64* %8, metadata !603, metadata !DIExpression()), !dbg !604 + call void @llvm.dbg.value(metadata i64 %0, metadata !395, metadata !DIExpression()), !dbg !605 + %9 = and i64 %0, 4503599627370495, !dbg !607 + store i64 %9, i64* %5, align 8, !dbg !608 + call void @llvm.dbg.value(metadata i64 %0, metadata !402, metadata !DIExpression()), !dbg !609 + %10 = lshr i64 %0, 52, !dbg !611 + %11 = and i64 %10, 2047, !dbg !612 + %12 = trunc i64 %11 to i32, !dbg !613 + store i32 %12, i32* %3, align 4, !dbg !614 + call void @llvm.dbg.value(metadata i64 %0, metadata !409, metadata !DIExpression()), !dbg !615 + %13 = lshr i64 %0, 63, !dbg !617 + %14 = trunc i64 %13 to i32, !dbg !618 + call void @llvm.dbg.value(metadata i32 %14, metadata !619, metadata !DIExpression()), !dbg !591 + call void @llvm.dbg.value(metadata i64 %1, metadata !395, metadata !DIExpression()), !dbg !620 + %15 = and i64 %1, 4503599627370495, !dbg !622 + store i64 %15, i64* %6, align 8, !dbg !623 + call void @llvm.dbg.value(metadata i64 %1, metadata !402, metadata !DIExpression()), !dbg !624 + %16 = lshr i64 %1, 52, !dbg !626 + %17 = and i64 %16, 2047, !dbg !627 + %18 = trunc i64 %17 to i32, !dbg !628 + store i32 %18, i32* %4, align 4, !dbg !629 + call void @llvm.dbg.value(metadata i64 %1, metadata !409, metadata !DIExpression()), !dbg !630 + %19 = lshr i64 %1, 63, !dbg !632 + %20 = trunc i64 %19 to i32, !dbg !633 + call void @llvm.dbg.value(metadata i32 %20, metadata !634, metadata !DIExpression()), !dbg !591 + %21 = xor i32 %14, %20, !dbg !635 + call void @llvm.dbg.value(metadata i32 %21, metadata !636, metadata !DIExpression()), !dbg !591 + %22 = load i32, i32* %3, align 4, !dbg !637 + %23 = icmp eq i32 %22, 2047, !dbg !639 + br i1 %23, label %24, label %49, !dbg !640 + +24: ; preds = %2 + %25 = load i64, i64* %5, align 8, !dbg !641 + %26 = icmp ne i64 %25, 0, !dbg !641 + br i1 %26, label %33, label %27, !dbg !644 + +27: ; preds = %24 + %28 = load i32, i32* %4, align 4, !dbg !645 + %29 = icmp eq i32 %28, 2047, !dbg !646 + br i1 %29, label %30, label %35, !dbg !647 + +30: ; preds = %27 + %31 = load i64, i64* %6, align 8, !dbg !648 + %32 = icmp ne i64 %31, 0, !dbg !648 + br i1 %32, label %33, label %35, !dbg !649 + +33: ; preds = %30, %24 + %34 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !650 + br label %129, !dbg !651 + +35: ; preds = %30, %27 + %36 = load i32, i32* %4, align 4, !dbg !652 + %37 = sext i32 %36 to i64, !dbg !652 + %38 = load i64, i64* %6, align 8, !dbg !654 + %39 = or i64 %37, %38, !dbg !655 + %40 = icmp eq i64 %39, 0, !dbg !656 + br i1 %40, label %41, label %42, !dbg !657 + +41: ; preds = %35 + call void @_Z11float_raisei(i32 16), !dbg !658 + br label %129, !dbg !660 + +42: ; preds = %35 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !661 + call void @llvm.dbg.value(metadata i32 2047, metadata !453, metadata !DIExpression()), !dbg !661 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !661 + %43 = sext i32 %21 to i64, !dbg !663 + %44 = shl i64 %43, 63, !dbg !664 + %45 = sext i32 2047 to i64, !dbg !665 + %46 = shl i64 %45, 52, !dbg !666 + %47 = add i64 %44, %46, !dbg !667 + %48 = add i64 %47, 0, !dbg !668 + br label %129, !dbg !669 + +49: ; preds = %2 + %50 = load i32, i32* %4, align 4, !dbg !670 + %51 = icmp eq i32 %50, 2047, !dbg !672 + br i1 %51, label %52, label %71, !dbg !673 + +52: ; preds = %49 + %53 = load i64, i64* %6, align 8, !dbg !674 + %54 = icmp ne i64 %53, 0, !dbg !674 + br i1 %54, label %55, label %57, !dbg !677 + +55: ; preds = %52 + %56 = call i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1), !dbg !678 + br label %129, !dbg !679 + +57: ; preds = %52 + %58 = load i32, i32* %3, align 4, !dbg !680 + %59 = sext i32 %58 to i64, !dbg !680 + %60 = load i64, i64* %5, align 8, !dbg !682 + %61 = or i64 %59, %60, !dbg !683 + %62 = icmp eq i64 %61, 0, !dbg !684 + br i1 %62, label %63, label %64, !dbg !685 + +63: ; preds = %57 + call void @_Z11float_raisei(i32 16), !dbg !686 + br label %129, !dbg !688 + +64: ; preds = %57 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !689 + call void @llvm.dbg.value(metadata i32 2047, metadata !453, metadata !DIExpression()), !dbg !689 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !689 + %65 = sext i32 %21 to i64, !dbg !691 + %66 = shl i64 %65, 63, !dbg !692 + %67 = sext i32 2047 to i64, !dbg !693 + %68 = shl i64 %67, 52, !dbg !694 + %69 = add i64 %66, %68, !dbg !695 + %70 = add i64 %69, 0, !dbg !696 + br label %129, !dbg !697 + +71: ; preds = %49 + %72 = load i32, i32* %3, align 4, !dbg !698 + %73 = icmp eq i32 %72, 0, !dbg !700 + br i1 %73, label %74, label %86, !dbg !701 + +74: ; preds = %71 + %75 = load i64, i64* %5, align 8, !dbg !702 + %76 = icmp eq i64 %75, 0, !dbg !705 + br i1 %76, label %77, label %84, !dbg !706 + +77: ; preds = %74 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !707 + call void @llvm.dbg.value(metadata i32 0, metadata !453, metadata !DIExpression()), !dbg !707 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !707 + %78 = sext i32 %21 to i64, !dbg !709 + %79 = shl i64 %78, 63, !dbg !710 + %80 = sext i32 0 to i64, !dbg !711 + %81 = shl i64 %80, 52, !dbg !712 + %82 = add i64 %79, %81, !dbg !713 + %83 = add i64 %82, 0, !dbg !714 + br label %129, !dbg !715 + +84: ; preds = %74 + %85 = load i64, i64* %5, align 8, !dbg !716 + call void @normalizeFloat64Subnormal(i64 %85, i32* %3, i64* %5), !dbg !717 + br label %86, !dbg !718 + +86: ; preds = %84, %71 + %87 = load i32, i32* %4, align 4, !dbg !719 + %88 = icmp eq i32 %87, 0, !dbg !721 + br i1 %88, label %89, label %101, !dbg !722 + +89: ; preds = %86 + %90 = load i64, i64* %6, align 8, !dbg !723 + %91 = icmp eq i64 %90, 0, !dbg !726 + br i1 %91, label %92, label %99, !dbg !727 + +92: ; preds = %89 + call void @llvm.dbg.value(metadata i32 %21, metadata !451, metadata !DIExpression()), !dbg !728 + call void @llvm.dbg.value(metadata i32 0, metadata !453, metadata !DIExpression()), !dbg !728 + call void @llvm.dbg.value(metadata i64 0, metadata !454, metadata !DIExpression()), !dbg !728 + %93 = sext i32 %21 to i64, !dbg !730 + %94 = shl i64 %93, 63, !dbg !731 + %95 = sext i32 0 to i64, !dbg !732 + %96 = shl i64 %95, 52, !dbg !733 + %97 = add i64 %94, %96, !dbg !734 + %98 = add i64 %97, 0, !dbg !735 + br label %129, !dbg !736 + +99: ; preds = %89 + %100 = load i64, i64* %6, align 8, !dbg !737 + call void @normalizeFloat64Subnormal(i64 %100, i32* %4, i64* %6), !dbg !738 + br label %101, !dbg !739 + +101: ; preds = %99, %86 + %102 = load i32, i32* %3, align 4, !dbg !740 + %103 = load i32, i32* %4, align 4, !dbg !741 + %104 = add nsw i32 %102, %103, !dbg !742 + %105 = sub nsw i32 %104, 1023, !dbg !743 + call void @llvm.dbg.value(metadata i32 %105, metadata !744, metadata !DIExpression()), !dbg !591 + %106 = load i64, i64* %5, align 8, !dbg !745 + %107 = or i64 %106, 4503599627370496, !dbg !746 + %108 = shl i64 %107, 10, !dbg !747 + store i64 %108, i64* %5, align 8, !dbg !748 + %109 = load i64, i64* %6, align 8, !dbg !749 + %110 = or i64 %109, 4503599627370496, !dbg !750 + %111 = shl i64 %110, 11, !dbg !751 + store i64 %111, i64* %6, align 8, !dbg !752 + %112 = load i64, i64* %5, align 8, !dbg !753 + %113 = load i64, i64* %6, align 8, !dbg !754 + call void @_Z10mul64To128yyPyS_(i64 %112, i64 %113, i64* %7, i64* %8), !dbg !755 + %114 = load i64, i64* %8, align 8, !dbg !756 + %115 = icmp ne i64 %114, 0, !dbg !757 + %116 = zext i1 %115 to i64, !dbg !758 + %117 = load i64, i64* %7, align 8, !dbg !759 + %118 = or i64 %117, %116, !dbg !759 + store i64 %118, i64* %7, align 8, !dbg !759 + %119 = load i64, i64* %7, align 8, !dbg !760 + %120 = shl i64 %119, 1, !dbg !762 + %121 = icmp sle i64 0, %120, !dbg !763 + br i1 %121, label %122, label %126, !dbg !764 + +122: ; preds = %101 + %123 = load i64, i64* %7, align 8, !dbg !765 + %124 = shl i64 %123, 1, !dbg !765 + store i64 %124, i64* %7, align 8, !dbg !765 + %125 = add nsw i32 %105, -1, !dbg !767 + call void @llvm.dbg.value(metadata i32 %125, metadata !744, metadata !DIExpression()), !dbg !591 + br label %126, !dbg !768 + +126: ; preds = %122, %101 + %.0 = phi i32 [ %125, %122 ], [ %105, %101 ], !dbg !591 + call void @llvm.dbg.value(metadata i32 %.0, metadata !744, metadata !DIExpression()), !dbg !591 + %127 = load i64, i64* %7, align 8, !dbg !769 + %128 = call i64 @roundAndPackFloat64(i32 %21, i32 %.0, i64 %127), !dbg !770 + br label %129, !dbg !771 + +129: ; preds = %126, %92, %77, %64, %63, %55, %42, %41, %33 + %.01 = phi i64 [ %34, %33 ], [ 9223372036854775807, %41 ], [ %48, %42 ], [ %56, %55 ], [ 9223372036854775807, %63 ], [ %70, %64 ], [ %83, %77 ], [ %98, %92 ], [ %128, %126 ], !dbg !591 + ret i64 %.01, !dbg !772 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i64 @_ZL19propagateFloat64NaNyy(i64 %0, i64 %1) #0 !dbg !773 { + call void @llvm.dbg.value(metadata i64 %0, metadata !776, metadata !DIExpression()), !dbg !777 + call void @llvm.dbg.value(metadata i64 %1, metadata !778, metadata !DIExpression()), !dbg !777 + %3 = call i32 @_Z14float64_is_nany(i64 %0), !dbg !779 + call void @llvm.dbg.value(metadata i32 %3, metadata !780, metadata !DIExpression()), !dbg !777 + %4 = call i32 @_Z24float64_is_signaling_nany(i64 %0), !dbg !781 + call void @llvm.dbg.value(metadata i32 %4, metadata !782, metadata !DIExpression()), !dbg !777 + %5 = call i32 @_Z14float64_is_nany(i64 %1), !dbg !783 + call void @llvm.dbg.value(metadata i32 %5, metadata !784, metadata !DIExpression()), !dbg !777 + %6 = call i32 @_Z24float64_is_signaling_nany(i64 %1), !dbg !785 + call void @llvm.dbg.value(metadata i32 %6, metadata !786, metadata !DIExpression()), !dbg !777 + %7 = or i64 %0, 2251799813685248, !dbg !787 + call void @llvm.dbg.value(metadata i64 %7, metadata !776, metadata !DIExpression()), !dbg !777 + %8 = or i64 %1, 2251799813685248, !dbg !788 + call void @llvm.dbg.value(metadata i64 %8, metadata !778, metadata !DIExpression()), !dbg !777 + %9 = or i32 %4, %6, !dbg !789 + %10 = icmp ne i32 %9, 0, !dbg !791 + br i1 %10, label %11, label %12, !dbg !792 + +11: ; preds = %2 + call void @_Z11float_raisei(i32 16), !dbg !793 + br label %12, !dbg !793 + +12: ; preds = %11, %2 + %13 = icmp ne i32 %6, 0, !dbg !794 + br i1 %13, label %14, label %15, !dbg !794 + +14: ; preds = %12 + br label %26, !dbg !794 + +15: ; preds = %12 + %16 = icmp ne i32 %4, 0, !dbg !795 + br i1 %16, label %17, label %18, !dbg !795 + +17: ; preds = %15 + br label %24, !dbg !795 + +18: ; preds = %15 + %19 = icmp ne i32 %5, 0, !dbg !796 + br i1 %19, label %20, label %21, !dbg !796 + +20: ; preds = %18 + br label %22, !dbg !796 + +21: ; preds = %18 + br label %22, !dbg !796 + +22: ; preds = %21, %20 + %23 = phi i64 [ %8, %20 ], [ %7, %21 ], !dbg !796 + br label %24, !dbg !795 + +24: ; preds = %22, %17 + %25 = phi i64 [ %7, %17 ], [ %23, %22 ], !dbg !795 + br label %26, !dbg !794 + +26: ; preds = %24, %14 + %27 = phi i64 [ %8, %14 ], [ %25, %24 ], !dbg !794 + ret i64 %27, !dbg !797 +} + +; Function Attrs: mustprogress noinline nounwind uwtable +define internal i32 @_ZL19countLeadingZeros32j(i32 %0) #0 !dbg !20 { + call void @llvm.dbg.value(metadata i32 %0, metadata !798, metadata !DIExpression()), !dbg !799 + call void @llvm.dbg.value(metadata i32 0, metadata !800, metadata !DIExpression()), !dbg !799 + %2 = icmp ult i32 %0, 65536, !dbg !801 + br i1 %2, label %3, label %6, !dbg !803 + +3: ; preds = %1 + %4 = add nsw i32 0, 16, !dbg !804 + call void @llvm.dbg.value(metadata i32 %4, metadata !800, metadata !DIExpression()), !dbg !799 + %5 = shl i32 %0, 16, !dbg !806 + call void @llvm.dbg.value(metadata i32 %5, metadata !798, metadata !DIExpression()), !dbg !799 + br label %6, !dbg !807 + +6: ; preds = %3, %1 + %.01 = phi i32 [ %4, %3 ], [ 0, %1 ], !dbg !799 + %.0 = phi i32 [ %5, %3 ], [ %0, %1 ] + call void @llvm.dbg.value(metadata i32 %.0, metadata !798, metadata !DIExpression()), !dbg !799 + call void @llvm.dbg.value(metadata i32 %.01, metadata !800, metadata !DIExpression()), !dbg !799 + %7 = icmp ult i32 %.0, 16777216, !dbg !808 + br i1 %7, label %8, label %11, !dbg !810 + +8: ; preds = %6 + %9 = add nsw i32 %.01, 8, !dbg !811 + call void @llvm.dbg.value(metadata i32 %9, metadata !800, metadata !DIExpression()), !dbg !799 + %10 = shl i32 %.0, 8, !dbg !813 + call void @llvm.dbg.value(metadata i32 %10, metadata !798, metadata !DIExpression()), !dbg !799 + br label %11, !dbg !814 + +11: ; preds = %8, %6 + %.12 = phi i32 [ %9, %8 ], [ %.01, %6 ], !dbg !799 + %.1 = phi i32 [ %10, %8 ], [ %.0, %6 ], !dbg !799 + call void @llvm.dbg.value(metadata i32 %.1, metadata !798, metadata !DIExpression()), !dbg !799 + call void @llvm.dbg.value(metadata i32 %.12, metadata !800, metadata !DIExpression()), !dbg !799 + %12 = lshr i32 %.1, 24, !dbg !815 + %13 = zext i32 %12 to i64, !dbg !816 + %14 = getelementptr inbounds [256 x i32], [256 x i32]* bitcast (<{ [128 x i32], [128 x i32] }>* @_ZZL19countLeadingZeros32jE21countLeadingZerosHigh to [256 x i32]*), i64 0, i64 %13, !dbg !816 + %15 = load i32, i32* %14, align 4, !dbg !816 + %16 = add nsw i32 %.12, %15, !dbg !817 + call void @llvm.dbg.value(metadata i32 %16, metadata !800, metadata !DIExpression()), !dbg !799 + ret i32 %16, !dbg !818 +} + +; Function Attrs: nofree nosync nounwind readnone speculatable willreturn +declare void @llvm.dbg.value(metadata, metadata, metadata) #1 + +attributes #0 = { mustprogress noinline nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #1 = { nofree nosync nounwind readnone speculatable willreturn } +attributes #2 = { alwaysinline mustprogress nounwind uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } +attributes #3 = { mustprogress noinline uwtable "frame-pointer"="all" "min-legal-vector-width"="0" "no-trapping-math"="true" "stack-protector-buffer-size"="8" "target-cpu"="x86-64" "target-features"="+cx8,+fxsr,+mmx,+sse,+sse2,+x87" "tune-cpu"="generic" } + +!llvm.dbg.cu = !{!2} +!llvm.module.flags = !{!272, !273, !274, !275, !276} +!llvm.ident = !{!277} + +!0 = !DIGlobalVariableExpression(var: !1, expr: !DIExpression()) +!1 = distinct !DIGlobalVariable(name: "float_rounding_mode", scope: !2, file: !3, line: 61, type: !16, isLocal: false, isDefinition: true) +!2 = distinct !DICompileUnit(language: DW_LANG_C_plus_plus_14, file: !3, producer: "Ubuntu clang version 13.0.1-2ubuntu2.2", isOptimized: false, runtimeVersion: 0, emissionKind: FullDebug, enums: !4, retainedTypes: !5, globals: !13, imports: !30, splitDebugInlining: false, nameTableKind: None) +!3 = !DIFile(filename: "dfmul/float64_mul.cpp", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!4 = !{} +!5 = !{!6, !9, !11} +!6 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits64", file: !7, line: 70, baseType: !8) +!7 = !DIFile(filename: "dfmul/include/SPARC-GCC.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!8 = !DIBasicType(name: "long long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!9 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits16", file: !7, line: 68, baseType: !10) +!10 = !DIBasicType(name: "unsigned short", size: 16, encoding: DW_ATE_unsigned) +!11 = !DIDerivedType(tag: DW_TAG_typedef, name: "sbits64", file: !7, line: 71, baseType: !12) +!12 = !DIBasicType(name: "long long int", size: 64, encoding: DW_ATE_signed) +!13 = !{!0, !14, !18} +!14 = !DIGlobalVariableExpression(var: !15, expr: !DIExpression()) +!15 = distinct !DIGlobalVariable(name: "float_exception_flags", scope: !2, file: !3, line: 62, type: !16, isLocal: false, isDefinition: true) +!16 = !DIDerivedType(tag: DW_TAG_typedef, name: "int8", file: !7, line: 59, baseType: !17) +!17 = !DIBasicType(name: "int", size: 32, encoding: DW_ATE_signed) +!18 = !DIGlobalVariableExpression(var: !19, expr: !DIExpression()) +!19 = distinct !DIGlobalVariable(name: "countLeadingZerosHigh", scope: !20, file: !21, line: 118, type: !26, isLocal: true, isDefinition: true) +!20 = distinct !DISubprogram(name: "countLeadingZeros32", linkageName: "_ZL19countLeadingZeros32j", scope: !21, file: !21, line: 116, type: !22, scopeLine: 117, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!21 = !DIFile(filename: "dfmul/include/softfloat-macros", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!22 = !DISubroutineType(types: !23) +!23 = !{!16, !24} +!24 = !DIDerivedType(tag: DW_TAG_typedef, name: "bits32", file: !7, line: 69, baseType: !25) +!25 = !DIBasicType(name: "unsigned int", size: 32, encoding: DW_ATE_unsigned) +!26 = !DICompositeType(tag: DW_TAG_array_type, baseType: !27, size: 8192, elements: !28) +!27 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !16) +!28 = !{!29} +!29 = !DISubrange(count: 256) +!30 = !{!31, !38, !42, !49, !53, !58, !60, !68, !72, !76, !90, !94, !98, !102, !106, !111, !115, !119, !123, !127, !135, !139, !143, !145, !149, !153, !157, !163, !167, !171, !173, !181, !185, !192, !194, !198, !202, !206, !210, !214, !219, !224, !225, !226, !227, !229, !230, !231, !232, !233, !234, !235, !237, !238, !239, !240, !241, !242, !243, !248, !249, !250, !251, !252, !253, !254, !255, !256, !257, !258, !259, !260, !261, !262, !263, !264, !265, !266, !267, !268, !269, !270, !271} +!31 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !33, file: !37, line: 52) +!32 = !DINamespace(name: "std", scope: null) +!33 = !DISubprogram(name: "abs", scope: !34, file: !34, line: 848, type: !35, flags: DIFlagPrototyped, spFlags: 0) +!34 = !DIFile(filename: "/usr/include/stdlib.h", directory: "") +!35 = !DISubroutineType(types: !36) +!36 = !{!17, !17} +!37 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/bits/std_abs.h", directory: "") +!38 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !39, file: !41, line: 127) +!39 = !DIDerivedType(tag: DW_TAG_typedef, name: "div_t", file: !34, line: 63, baseType: !40) +!40 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 59, size: 64, flags: DIFlagFwdDecl, identifier: "_ZTS5div_t") +!41 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/cstdlib", directory: "") +!42 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !43, file: !41, line: 128) +!43 = !DIDerivedType(tag: DW_TAG_typedef, name: "ldiv_t", file: !34, line: 71, baseType: !44) +!44 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 67, size: 128, flags: DIFlagTypePassByValue, elements: !45, identifier: "_ZTS6ldiv_t") +!45 = !{!46, !48} +!46 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !44, file: !34, line: 69, baseType: !47, size: 64) +!47 = !DIBasicType(name: "long int", size: 64, encoding: DW_ATE_signed) +!48 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !44, file: !34, line: 70, baseType: !47, size: 64, offset: 64) +!49 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !50, file: !41, line: 130) +!50 = !DISubprogram(name: "abort", scope: !34, file: !34, line: 598, type: !51, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!51 = !DISubroutineType(types: !52) +!52 = !{null} +!53 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !54, file: !41, line: 134) +!54 = !DISubprogram(name: "atexit", scope: !34, file: !34, line: 602, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!55 = !DISubroutineType(types: !56) +!56 = !{!17, !57} +!57 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !51, size: 64) +!58 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !59, file: !41, line: 137) +!59 = !DISubprogram(name: "at_quick_exit", scope: !34, file: !34, line: 607, type: !55, flags: DIFlagPrototyped, spFlags: 0) +!60 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !61, file: !41, line: 140) +!61 = !DISubprogram(name: "atof", scope: !34, file: !34, line: 102, type: !62, flags: DIFlagPrototyped, spFlags: 0) +!62 = !DISubroutineType(types: !63) +!63 = !{!64, !65} +!64 = !DIBasicType(name: "double", size: 64, encoding: DW_ATE_float) +!65 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !66, size: 64) +!66 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !67) +!67 = !DIBasicType(name: "char", size: 8, encoding: DW_ATE_signed_char) +!68 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !69, file: !41, line: 141) +!69 = !DISubprogram(name: "atoi", scope: !34, file: !34, line: 105, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!70 = !DISubroutineType(types: !71) +!71 = !{!17, !65} +!72 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !73, file: !41, line: 142) +!73 = !DISubprogram(name: "atol", scope: !34, file: !34, line: 108, type: !74, flags: DIFlagPrototyped, spFlags: 0) +!74 = !DISubroutineType(types: !75) +!75 = !{!47, !65} +!76 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !77, file: !41, line: 143) +!77 = !DISubprogram(name: "bsearch", scope: !34, file: !34, line: 828, type: !78, flags: DIFlagPrototyped, spFlags: 0) +!78 = !DISubroutineType(types: !79) +!79 = !{!80, !81, !81, !83, !83, !86} +!80 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: null, size: 64) +!81 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !82, size: 64) +!82 = !DIDerivedType(tag: DW_TAG_const_type, baseType: null) +!83 = !DIDerivedType(tag: DW_TAG_typedef, name: "size_t", file: !84, line: 46, baseType: !85) +!84 = !DIFile(filename: "/usr/lib/llvm-13/lib/clang/13.0.1/include/stddef.h", directory: "") +!85 = !DIBasicType(name: "long unsigned int", size: 64, encoding: DW_ATE_unsigned) +!86 = !DIDerivedType(tag: DW_TAG_typedef, name: "__compar_fn_t", file: !34, line: 816, baseType: !87) +!87 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !88, size: 64) +!88 = !DISubroutineType(types: !89) +!89 = !{!17, !81, !81} +!90 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !91, file: !41, line: 144) +!91 = !DISubprogram(name: "calloc", scope: !34, file: !34, line: 543, type: !92, flags: DIFlagPrototyped, spFlags: 0) +!92 = !DISubroutineType(types: !93) +!93 = !{!80, !83, !83} +!94 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !95, file: !41, line: 145) +!95 = !DISubprogram(name: "div", scope: !34, file: !34, line: 860, type: !96, flags: DIFlagPrototyped, spFlags: 0) +!96 = !DISubroutineType(types: !97) +!97 = !{!39, !17, !17} +!98 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !99, file: !41, line: 146) +!99 = !DISubprogram(name: "exit", scope: !34, file: !34, line: 624, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!100 = !DISubroutineType(types: !101) +!101 = !{null, !17} +!102 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !103, file: !41, line: 147) +!103 = !DISubprogram(name: "free", scope: !34, file: !34, line: 555, type: !104, flags: DIFlagPrototyped, spFlags: 0) +!104 = !DISubroutineType(types: !105) +!105 = !{null, !80} +!106 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !107, file: !41, line: 148) +!107 = !DISubprogram(name: "getenv", scope: !34, file: !34, line: 641, type: !108, flags: DIFlagPrototyped, spFlags: 0) +!108 = !DISubroutineType(types: !109) +!109 = !{!110, !65} +!110 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !67, size: 64) +!111 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !112, file: !41, line: 149) +!112 = !DISubprogram(name: "labs", scope: !34, file: !34, line: 849, type: !113, flags: DIFlagPrototyped, spFlags: 0) +!113 = !DISubroutineType(types: !114) +!114 = !{!47, !47} +!115 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !116, file: !41, line: 150) +!116 = !DISubprogram(name: "ldiv", scope: !34, file: !34, line: 862, type: !117, flags: DIFlagPrototyped, spFlags: 0) +!117 = !DISubroutineType(types: !118) +!118 = !{!43, !47, !47} +!119 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !120, file: !41, line: 151) +!120 = !DISubprogram(name: "malloc", scope: !34, file: !34, line: 540, type: !121, flags: DIFlagPrototyped, spFlags: 0) +!121 = !DISubroutineType(types: !122) +!122 = !{!80, !83} +!123 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !124, file: !41, line: 153) +!124 = !DISubprogram(name: "mblen", scope: !34, file: !34, line: 930, type: !125, flags: DIFlagPrototyped, spFlags: 0) +!125 = !DISubroutineType(types: !126) +!126 = !{!17, !65, !83} +!127 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !128, file: !41, line: 154) +!128 = !DISubprogram(name: "mbstowcs", scope: !34, file: !34, line: 941, type: !129, flags: DIFlagPrototyped, spFlags: 0) +!129 = !DISubroutineType(types: !130) +!130 = !{!83, !131, !134, !83} +!131 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !132) +!132 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !133, size: 64) +!133 = !DIBasicType(name: "wchar_t", size: 32, encoding: DW_ATE_signed) +!134 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !65) +!135 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !136, file: !41, line: 155) +!136 = !DISubprogram(name: "mbtowc", scope: !34, file: !34, line: 933, type: !137, flags: DIFlagPrototyped, spFlags: 0) +!137 = !DISubroutineType(types: !138) +!138 = !{!17, !131, !134, !83} +!139 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !140, file: !41, line: 157) +!140 = !DISubprogram(name: "qsort", scope: !34, file: !34, line: 838, type: !141, flags: DIFlagPrototyped, spFlags: 0) +!141 = !DISubroutineType(types: !142) +!142 = !{null, !80, !83, !83, !86} +!143 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !144, file: !41, line: 160) +!144 = !DISubprogram(name: "quick_exit", scope: !34, file: !34, line: 630, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!145 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !146, file: !41, line: 163) +!146 = !DISubprogram(name: "rand", scope: !34, file: !34, line: 454, type: !147, flags: DIFlagPrototyped, spFlags: 0) +!147 = !DISubroutineType(types: !148) +!148 = !{!17} +!149 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !150, file: !41, line: 164) +!150 = !DISubprogram(name: "realloc", scope: !34, file: !34, line: 551, type: !151, flags: DIFlagPrototyped, spFlags: 0) +!151 = !DISubroutineType(types: !152) +!152 = !{!80, !80, !83} +!153 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !154, file: !41, line: 165) +!154 = !DISubprogram(name: "srand", scope: !34, file: !34, line: 456, type: !155, flags: DIFlagPrototyped, spFlags: 0) +!155 = !DISubroutineType(types: !156) +!156 = !{null, !25} +!157 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !158, file: !41, line: 166) +!158 = !DISubprogram(name: "strtod", scope: !34, file: !34, line: 118, type: !159, flags: DIFlagPrototyped, spFlags: 0) +!159 = !DISubroutineType(types: !160) +!160 = !{!64, !134, !161} +!161 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !162) +!162 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !110, size: 64) +!163 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !164, file: !41, line: 167) +!164 = !DISubprogram(name: "strtol", scope: !34, file: !34, line: 177, type: !165, flags: DIFlagPrototyped, spFlags: 0) +!165 = !DISubroutineType(types: !166) +!166 = !{!47, !134, !161, !17} +!167 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !168, file: !41, line: 168) +!168 = !DISubprogram(name: "strtoul", scope: !34, file: !34, line: 181, type: !169, flags: DIFlagPrototyped, spFlags: 0) +!169 = !DISubroutineType(types: !170) +!170 = !{!85, !134, !161, !17} +!171 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !172, file: !41, line: 169) +!172 = !DISubprogram(name: "system", scope: !34, file: !34, line: 791, type: !70, flags: DIFlagPrototyped, spFlags: 0) +!173 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !174, file: !41, line: 171) +!174 = !DISubprogram(name: "wcstombs", scope: !34, file: !34, line: 945, type: !175, flags: DIFlagPrototyped, spFlags: 0) +!175 = !DISubroutineType(types: !176) +!176 = !{!83, !177, !178, !83} +!177 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !110) +!178 = !DIDerivedType(tag: DW_TAG_restrict_type, baseType: !179) +!179 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !180, size: 64) +!180 = !DIDerivedType(tag: DW_TAG_const_type, baseType: !133) +!181 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !182, file: !41, line: 172) +!182 = !DISubprogram(name: "wctomb", scope: !34, file: !34, line: 937, type: !183, flags: DIFlagPrototyped, spFlags: 0) +!183 = !DISubroutineType(types: !184) +!184 = !{!17, !110, !133} +!185 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !187, file: !41, line: 200) +!186 = !DINamespace(name: "__gnu_cxx", scope: null) +!187 = !DIDerivedType(tag: DW_TAG_typedef, name: "lldiv_t", file: !34, line: 81, baseType: !188) +!188 = distinct !DICompositeType(tag: DW_TAG_structure_type, file: !34, line: 77, size: 128, flags: DIFlagTypePassByValue, elements: !189, identifier: "_ZTS7lldiv_t") +!189 = !{!190, !191} +!190 = !DIDerivedType(tag: DW_TAG_member, name: "quot", scope: !188, file: !34, line: 79, baseType: !12, size: 64) +!191 = !DIDerivedType(tag: DW_TAG_member, name: "rem", scope: !188, file: !34, line: 80, baseType: !12, size: 64, offset: 64) +!192 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !193, file: !41, line: 206) +!193 = !DISubprogram(name: "_Exit", scope: !34, file: !34, line: 636, type: !100, flags: DIFlagPrototyped | DIFlagNoReturn, spFlags: 0) +!194 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !195, file: !41, line: 210) +!195 = !DISubprogram(name: "llabs", scope: !34, file: !34, line: 852, type: !196, flags: DIFlagPrototyped, spFlags: 0) +!196 = !DISubroutineType(types: !197) +!197 = !{!12, !12} +!198 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !199, file: !41, line: 216) +!199 = !DISubprogram(name: "lldiv", scope: !34, file: !34, line: 866, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!200 = !DISubroutineType(types: !201) +!201 = !{!187, !12, !12} +!202 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !203, file: !41, line: 227) +!203 = !DISubprogram(name: "atoll", scope: !34, file: !34, line: 113, type: !204, flags: DIFlagPrototyped, spFlags: 0) +!204 = !DISubroutineType(types: !205) +!205 = !{!12, !65} +!206 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !207, file: !41, line: 228) +!207 = !DISubprogram(name: "strtoll", scope: !34, file: !34, line: 201, type: !208, flags: DIFlagPrototyped, spFlags: 0) +!208 = !DISubroutineType(types: !209) +!209 = !{!12, !134, !161, !17} +!210 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !211, file: !41, line: 229) +!211 = !DISubprogram(name: "strtoull", scope: !34, file: !34, line: 206, type: !212, flags: DIFlagPrototyped, spFlags: 0) +!212 = !DISubroutineType(types: !213) +!213 = !{!8, !134, !161, !17} +!214 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !215, file: !41, line: 231) +!215 = !DISubprogram(name: "strtof", scope: !34, file: !34, line: 124, type: !216, flags: DIFlagPrototyped, spFlags: 0) +!216 = !DISubroutineType(types: !217) +!217 = !{!218, !134, !161} +!218 = !DIBasicType(name: "float", size: 32, encoding: DW_ATE_float) +!219 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !186, entity: !220, file: !41, line: 232) +!220 = !DISubprogram(name: "strtold", scope: !34, file: !34, line: 127, type: !221, flags: DIFlagPrototyped, spFlags: 0) +!221 = !DISubroutineType(types: !222) +!222 = !{!223, !134, !161} +!223 = !DIBasicType(name: "long double", size: 128, encoding: DW_ATE_float) +!224 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !187, file: !41, line: 240) +!225 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !193, file: !41, line: 242) +!226 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !195, file: !41, line: 244) +!227 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !228, file: !41, line: 245) +!228 = !DISubprogram(name: "div", linkageName: "_ZN9__gnu_cxx3divExx", scope: !186, file: !41, line: 213, type: !200, flags: DIFlagPrototyped, spFlags: 0) +!229 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !199, file: !41, line: 246) +!230 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !203, file: !41, line: 248) +!231 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !215, file: !41, line: 249) +!232 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !207, file: !41, line: 250) +!233 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !211, file: !41, line: 251) +!234 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !32, entity: !220, file: !41, line: 252) +!235 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !50, file: !236, line: 38) +!236 = !DIFile(filename: "/usr/bin/../lib/gcc/x86_64-linux-gnu/11/../../../../include/c++/11/stdlib.h", directory: "") +!237 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !54, file: !236, line: 39) +!238 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !99, file: !236, line: 40) +!239 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !59, file: !236, line: 43) +!240 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !144, file: !236, line: 46) +!241 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !39, file: !236, line: 51) +!242 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !43, file: !236, line: 52) +!243 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !244, file: !236, line: 54) +!244 = !DISubprogram(name: "abs", linkageName: "_ZSt3absg", scope: !32, file: !37, line: 103, type: !245, flags: DIFlagPrototyped, spFlags: 0) +!245 = !DISubroutineType(types: !246) +!246 = !{!247, !247} +!247 = !DIBasicType(name: "__float128", size: 128, encoding: DW_ATE_float) +!248 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !61, file: !236, line: 55) +!249 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !69, file: !236, line: 56) +!250 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !73, file: !236, line: 57) +!251 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !77, file: !236, line: 58) +!252 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !91, file: !236, line: 59) +!253 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !228, file: !236, line: 60) +!254 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !103, file: !236, line: 61) +!255 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !107, file: !236, line: 62) +!256 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !112, file: !236, line: 63) +!257 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !116, file: !236, line: 64) +!258 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !120, file: !236, line: 65) +!259 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !124, file: !236, line: 67) +!260 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !128, file: !236, line: 68) +!261 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !136, file: !236, line: 69) +!262 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !140, file: !236, line: 71) +!263 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !146, file: !236, line: 72) +!264 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !150, file: !236, line: 73) +!265 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !154, file: !236, line: 74) +!266 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !158, file: !236, line: 75) +!267 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !164, file: !236, line: 76) +!268 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !168, file: !236, line: 77) +!269 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !172, file: !236, line: 78) +!270 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !174, file: !236, line: 80) +!271 = !DIImportedEntity(tag: DW_TAG_imported_declaration, scope: !2, entity: !182, file: !236, line: 81) +!272 = !{i32 7, !"Dwarf Version", i32 4} +!273 = !{i32 2, !"Debug Info Version", i32 3} +!274 = !{i32 1, !"wchar_size", i32 4} +!275 = !{i32 7, !"uwtable", i32 1} +!276 = !{i32 7, !"frame-pointer", i32 2} +!277 = !{!"Ubuntu clang version 13.0.1-2ubuntu2.2"} +!278 = distinct !DISubprogram(name: "shift64RightJamming", linkageName: "_Z19shift64RightJammingyiPy", scope: !21, file: !21, line: 60, type: !279, scopeLine: 61, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!279 = !DISubroutineType(types: !280) +!280 = !{null, !6, !281, !282} +!281 = !DIDerivedType(tag: DW_TAG_typedef, name: "int16", file: !7, line: 60, baseType: !17) +!282 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !6, size: 64) +!283 = !DILocalVariable(name: "a", arg: 1, scope: !278, file: !21, line: 60, type: !6) +!284 = !DILocation(line: 0, scope: !278) +!285 = !DILocalVariable(name: "count", arg: 2, scope: !278, file: !21, line: 60, type: !281) +!286 = !DILocalVariable(name: "zPtr", arg: 3, scope: !278, file: !21, line: 60, type: !282) +!287 = !DILocation(line: 64, column: 13, scope: !288) +!288 = distinct !DILexicalBlock(scope: !278, file: !21, line: 64, column: 7) +!289 = !DILocation(line: 64, column: 7, scope: !278) +!290 = !DILocalVariable(name: "z", scope: !278, file: !21, line: 62, type: !6) +!291 = !DILocation(line: 67, column: 5, scope: !292) +!292 = distinct !DILexicalBlock(scope: !288, file: !21, line: 65, column: 5) +!293 = !DILocation(line: 68, column: 18, scope: !294) +!294 = distinct !DILexicalBlock(scope: !288, file: !21, line: 68, column: 12) +!295 = !DILocation(line: 68, column: 12, scope: !288) +!296 = !DILocation(line: 70, column: 14, scope: !297) +!297 = distinct !DILexicalBlock(scope: !294, file: !21, line: 69, column: 5) +!298 = !DILocation(line: 70, column: 35, scope: !297) +!299 = !DILocation(line: 70, column: 43, scope: !297) +!300 = !DILocation(line: 70, column: 30, scope: !297) +!301 = !DILocation(line: 70, column: 50, scope: !297) +!302 = !DILocation(line: 70, column: 26, scope: !297) +!303 = !DILocation(line: 70, column: 24, scope: !297) +!304 = !DILocation(line: 71, column: 5, scope: !297) +!305 = !DILocation(line: 74, column: 14, scope: !306) +!306 = distinct !DILexicalBlock(scope: !294, file: !21, line: 73, column: 5) +!307 = !DILocation(line: 74, column: 11, scope: !306) +!308 = !DILocation(line: 0, scope: !294) +!309 = !DILocation(line: 0, scope: !288) +!310 = !DILocation(line: 76, column: 9, scope: !278) +!311 = !DILocation(line: 78, column: 1, scope: !278) +!312 = distinct !DISubprogram(name: "mul64To128", linkageName: "_Z10mul64To128yyPyS_", scope: !21, file: !21, line: 87, type: !313, scopeLine: 88, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!313 = !DISubroutineType(types: !314) +!314 = !{null, !6, !6, !282, !282} +!315 = !DILocalVariable(name: "a", arg: 1, scope: !312, file: !21, line: 87, type: !6) +!316 = !DILocation(line: 0, scope: !312) +!317 = !DILocalVariable(name: "b", arg: 2, scope: !312, file: !21, line: 87, type: !6) +!318 = !DILocalVariable(name: "z0Ptr", arg: 3, scope: !312, file: !21, line: 87, type: !282) +!319 = !DILocalVariable(name: "z1Ptr", arg: 4, scope: !312, file: !21, line: 87, type: !282) +!320 = !DILocation(line: 92, column: 10, scope: !312) +!321 = !DILocalVariable(name: "aLow", scope: !312, file: !21, line: 89, type: !24) +!322 = !DILocation(line: 93, column: 13, scope: !312) +!323 = !DILocation(line: 93, column: 11, scope: !312) +!324 = !DILocalVariable(name: "aHigh", scope: !312, file: !21, line: 89, type: !24) +!325 = !DILocation(line: 94, column: 10, scope: !312) +!326 = !DILocalVariable(name: "bLow", scope: !312, file: !21, line: 89, type: !24) +!327 = !DILocation(line: 95, column: 13, scope: !312) +!328 = !DILocation(line: 95, column: 11, scope: !312) +!329 = !DILocalVariable(name: "bHigh", scope: !312, file: !21, line: 89, type: !24) +!330 = !DILocation(line: 96, column: 18, scope: !312) +!331 = !DILocation(line: 96, column: 26, scope: !312) +!332 = !DILocation(line: 96, column: 24, scope: !312) +!333 = !DILocalVariable(name: "z1", scope: !312, file: !21, line: 90, type: !6) +!334 = !DILocation(line: 97, column: 24, scope: !312) +!335 = !DILocation(line: 97, column: 32, scope: !312) +!336 = !DILocation(line: 97, column: 30, scope: !312) +!337 = !DILocalVariable(name: "zMiddleA", scope: !312, file: !21, line: 90, type: !6) +!338 = !DILocation(line: 98, column: 24, scope: !312) +!339 = !DILocation(line: 98, column: 33, scope: !312) +!340 = !DILocation(line: 98, column: 31, scope: !312) +!341 = !DILocalVariable(name: "zMiddleB", scope: !312, file: !21, line: 90, type: !6) +!342 = !DILocation(line: 99, column: 18, scope: !312) +!343 = !DILocation(line: 99, column: 27, scope: !312) +!344 = !DILocation(line: 99, column: 25, scope: !312) +!345 = !DILocalVariable(name: "z0", scope: !312, file: !21, line: 90, type: !6) +!346 = !DILocation(line: 100, column: 12, scope: !312) +!347 = !DILocation(line: 101, column: 30, scope: !312) +!348 = !DILocation(line: 101, column: 20, scope: !312) +!349 = !DILocation(line: 101, column: 43, scope: !312) +!350 = !DILocation(line: 101, column: 62, scope: !312) +!351 = !DILocation(line: 101, column: 50, scope: !312) +!352 = !DILocation(line: 101, column: 6, scope: !312) +!353 = !DILocation(line: 102, column: 12, scope: !312) +!354 = !DILocation(line: 103, column: 6, scope: !312) +!355 = !DILocation(line: 104, column: 13, scope: !312) +!356 = !DILocation(line: 104, column: 9, scope: !312) +!357 = !DILocation(line: 104, column: 6, scope: !312) +!358 = !DILocation(line: 105, column: 10, scope: !312) +!359 = !DILocation(line: 106, column: 10, scope: !312) +!360 = !DILocation(line: 108, column: 1, scope: !312) +!361 = distinct !DISubprogram(name: "float_raise", linkageName: "_Z11float_raisei", scope: !362, file: !362, line: 64, type: !363, scopeLine: 65, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!362 = !DIFile(filename: "dfmul/include/softfloat-specialize", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!363 = !DISubroutineType(types: !364) +!364 = !{null, !16} +!365 = !DILocalVariable(name: "flags", arg: 1, scope: !361, file: !362, line: 64, type: !16) +!366 = !DILocation(line: 0, scope: !361) +!367 = !DILocation(line: 66, column: 25, scope: !361) +!368 = !DILocation(line: 68, column: 1, scope: !361) +!369 = distinct !DISubprogram(name: "float64_is_nan", linkageName: "_Z14float64_is_nany", scope: !362, file: !362, line: 81, type: !370, scopeLine: 82, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!370 = !DISubroutineType(types: !371) +!371 = !{!372, !373} +!372 = !DIDerivedType(tag: DW_TAG_typedef, name: "flag", file: !7, line: 58, baseType: !17) +!373 = !DIDerivedType(tag: DW_TAG_typedef, name: "float64", file: !374, line: 54, baseType: !8) +!374 = !DIFile(filename: "dfmul/include/softfloat.h", directory: "/home/xyf/CoSense/applications/newton/llvm-ir/CHStone_test") +!375 = !DILocalVariable(name: "a", arg: 1, scope: !369, file: !362, line: 81, type: !373) +!376 = !DILocation(line: 0, scope: !369) +!377 = !DILocation(line: 84, column: 52, scope: !369) +!378 = !DILocation(line: 84, column: 38, scope: !369) +!379 = !DILocation(line: 84, column: 10, scope: !369) +!380 = !DILocation(line: 84, column: 3, scope: !369) +!381 = distinct !DISubprogram(name: "float64_is_signaling_nan", linkageName: "_Z24float64_is_signaling_nany", scope: !362, file: !362, line: 94, type: !370, scopeLine: 95, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!382 = !DILocalVariable(name: "a", arg: 1, scope: !381, file: !362, line: 94, type: !373) +!383 = !DILocation(line: 0, scope: !381) +!384 = !DILocation(line: 97, column: 15, scope: !381) +!385 = !DILocation(line: 97, column: 22, scope: !381) +!386 = !DILocation(line: 97, column: 31, scope: !381) +!387 = !DILocation(line: 97, column: 41, scope: !381) +!388 = !DILocation(line: 97, column: 47, scope: !381) +!389 = !DILocation(line: 97, column: 44, scope: !381) +!390 = !DILocation(line: 97, column: 10, scope: !381) +!391 = !DILocation(line: 97, column: 3, scope: !381) +!392 = distinct !DISubprogram(name: "extractFloat64Frac", scope: !3, file: !3, line: 89, type: !393, scopeLine: 89, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!393 = !DISubroutineType(types: !394) +!394 = !{!6, !373} +!395 = !DILocalVariable(name: "a", arg: 1, scope: !392, file: !3, line: 89, type: !373) +!396 = !DILocation(line: 0, scope: !392) +!397 = !DILocation(line: 90, column: 12, scope: !392) +!398 = !DILocation(line: 90, column: 3, scope: !392) +!399 = distinct !DISubprogram(name: "extractFloat64Exp", scope: !3, file: !3, line: 103, type: !400, scopeLine: 103, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!400 = !DISubroutineType(types: !401) +!401 = !{!281, !373} +!402 = !DILocalVariable(name: "a", arg: 1, scope: !399, file: !3, line: 103, type: !373) +!403 = !DILocation(line: 0, scope: !399) +!404 = !DILocation(line: 104, column: 13, scope: !399) +!405 = !DILocation(line: 104, column: 20, scope: !399) +!406 = !DILocation(line: 104, column: 10, scope: !399) +!407 = !DILocation(line: 104, column: 3, scope: !399) +!408 = distinct !DISubprogram(name: "extractFloat64Sign", scope: !3, file: !3, line: 117, type: !370, scopeLine: 117, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!409 = !DILocalVariable(name: "a", arg: 1, scope: !408, file: !3, line: 117, type: !373) +!410 = !DILocation(line: 0, scope: !408) +!411 = !DILocation(line: 118, column: 12, scope: !408) +!412 = !DILocation(line: 118, column: 10, scope: !408) +!413 = !DILocation(line: 118, column: 3, scope: !408) +!414 = distinct !DISubprogram(name: "normalizeFloat64Subnormal", scope: !3, file: !3, line: 133, type: !415, scopeLine: 133, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!415 = !DISubroutineType(types: !416) +!416 = !{null, !6, !417, !282} +!417 = !DIDerivedType(tag: DW_TAG_pointer_type, baseType: !281, size: 64) +!418 = !DILocalVariable(name: "aSig", arg: 1, scope: !414, file: !3, line: 133, type: !6) +!419 = !DILocation(line: 0, scope: !414) +!420 = !DILocalVariable(name: "zExpPtr", arg: 2, scope: !414, file: !3, line: 133, type: !417) +!421 = !DILocalVariable(name: "zSigPtr", arg: 3, scope: !414, file: !3, line: 133, type: !282) +!422 = !DILocation(line: 136, column: 16, scope: !414) +!423 = !DILocation(line: 136, column: 43, scope: !414) +!424 = !DILocalVariable(name: "shiftCount", scope: !414, file: !3, line: 134, type: !16) +!425 = !DILocation(line: 137, column: 19, scope: !414) +!426 = !DILocation(line: 137, column: 12, scope: !414) +!427 = !DILocation(line: 138, column: 16, scope: !414) +!428 = !DILocation(line: 138, column: 12, scope: !414) +!429 = !DILocation(line: 140, column: 1, scope: !414) +!430 = distinct !DISubprogram(name: "countLeadingZeros64", linkageName: "_ZL19countLeadingZeros64y", scope: !21, file: !21, line: 160, type: !431, scopeLine: 161, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!431 = !DISubroutineType(types: !432) +!432 = !{!16, !6} +!433 = !DILocalVariable(name: "a", arg: 1, scope: !430, file: !21, line: 160, type: !6) +!434 = !DILocation(line: 0, scope: !430) +!435 = !DILocalVariable(name: "shiftCount", scope: !430, file: !21, line: 162, type: !16) +!436 = !DILocation(line: 165, column: 9, scope: !437) +!437 = distinct !DILexicalBlock(scope: !430, file: !21, line: 165, column: 7) +!438 = !DILocation(line: 165, column: 7, scope: !430) +!439 = !DILocation(line: 167, column: 18, scope: !440) +!440 = distinct !DILexicalBlock(scope: !437, file: !21, line: 166, column: 5) +!441 = !DILocation(line: 168, column: 5, scope: !440) +!442 = !DILocation(line: 171, column: 9, scope: !443) +!443 = distinct !DILexicalBlock(scope: !437, file: !21, line: 170, column: 5) +!444 = !DILocation(line: 173, column: 38, scope: !430) +!445 = !DILocation(line: 173, column: 17, scope: !430) +!446 = !DILocation(line: 173, column: 14, scope: !430) +!447 = !DILocation(line: 174, column: 3, scope: !430) +!448 = distinct !DISubprogram(name: "packFloat64", scope: !3, file: !3, line: 159, type: !449, scopeLine: 159, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!449 = !DISubroutineType(types: !450) +!450 = !{!373, !372, !281, !6} +!451 = !DILocalVariable(name: "zSign", arg: 1, scope: !448, file: !3, line: 159, type: !372) +!452 = !DILocation(line: 0, scope: !448) +!453 = !DILocalVariable(name: "zExp", arg: 2, scope: !448, file: !3, line: 159, type: !281) +!454 = !DILocalVariable(name: "zSig", arg: 3, scope: !448, file: !3, line: 159, type: !6) +!455 = !DILocation(line: 160, column: 21, scope: !448) +!456 = !DILocation(line: 160, column: 28, scope: !448) +!457 = !DILocation(line: 160, column: 48, scope: !448) +!458 = !DILocation(line: 160, column: 54, scope: !448) +!459 = !DILocation(line: 160, column: 35, scope: !448) +!460 = !DILocation(line: 160, column: 61, scope: !448) +!461 = !DILocation(line: 160, column: 3, scope: !448) +!462 = distinct !DISubprogram(name: "roundAndPackFloat64", scope: !3, file: !3, line: 190, type: !449, scopeLine: 190, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!463 = !DILocalVariable(name: "zSign", arg: 1, scope: !462, file: !3, line: 190, type: !372) +!464 = !DILocation(line: 0, scope: !462) +!465 = !DILocalVariable(name: "zExp", arg: 2, scope: !462, file: !3, line: 190, type: !281) +!466 = !DILocalVariable(name: "zSig", arg: 3, scope: !462, file: !3, line: 190, type: !6) +!467 = !DILocation(line: 190, column: 61, scope: !462) +!468 = !DILocation(line: 195, column: 18, scope: !462) +!469 = !DILocalVariable(name: "roundingMode", scope: !462, file: !3, line: 191, type: !16) +!470 = !DILocation(line: 196, column: 36, scope: !462) +!471 = !DILocation(line: 196, column: 22, scope: !462) +!472 = !DILocalVariable(name: "roundNearestEven", scope: !462, file: !3, line: 192, type: !372) +!473 = !DILocalVariable(name: "roundIncrement", scope: !462, file: !3, line: 193, type: !281) +!474 = !DILocation(line: 198, column: 8, scope: !475) +!475 = distinct !DILexicalBlock(scope: !462, file: !3, line: 198, column: 7) +!476 = !DILocation(line: 198, column: 7, scope: !462) +!477 = !DILocation(line: 200, column: 24, scope: !478) +!478 = distinct !DILexicalBlock(scope: !479, file: !3, line: 200, column: 11) +!479 = distinct !DILexicalBlock(scope: !475, file: !3, line: 199, column: 5) +!480 = !DILocation(line: 200, column: 11, scope: !479) +!481 = !DILocation(line: 203, column: 2, scope: !482) +!482 = distinct !DILexicalBlock(scope: !478, file: !3, line: 201, column: 2) +!483 = !DILocation(line: 207, column: 8, scope: !484) +!484 = distinct !DILexicalBlock(scope: !485, file: !3, line: 207, column: 8) +!485 = distinct !DILexicalBlock(scope: !478, file: !3, line: 205, column: 2) +!486 = !DILocation(line: 207, column: 8, scope: !485) +!487 = !DILocation(line: 209, column: 25, scope: !488) +!488 = distinct !DILexicalBlock(scope: !489, file: !3, line: 209, column: 12) +!489 = distinct !DILexicalBlock(scope: !484, file: !3, line: 208, column: 6) +!490 = !DILocation(line: 209, column: 12, scope: !489) +!491 = !DILocation(line: 210, column: 3, scope: !488) +!492 = !DILocation(line: 0, scope: !485) +!493 = !DILocation(line: 211, column: 6, scope: !489) +!494 = !DILocation(line: 214, column: 25, scope: !495) +!495 = distinct !DILexicalBlock(scope: !496, file: !3, line: 214, column: 12) +!496 = distinct !DILexicalBlock(scope: !484, file: !3, line: 213, column: 6) +!497 = !DILocation(line: 214, column: 12, scope: !496) +!498 = !DILocation(line: 215, column: 3, scope: !495) +!499 = !DILocation(line: 0, scope: !484) +!500 = !DILocation(line: 0, scope: !478) +!501 = !DILocation(line: 218, column: 5, scope: !479) +!502 = !DILocation(line: 219, column: 15, scope: !462) +!503 = !DILocation(line: 219, column: 20, scope: !462) +!504 = !DILocalVariable(name: "roundBits", scope: !462, file: !3, line: 193, type: !281) +!505 = !DILocation(line: 220, column: 25, scope: !506) +!506 = distinct !DILexicalBlock(scope: !462, file: !3, line: 220, column: 7) +!507 = !DILocation(line: 220, column: 16, scope: !506) +!508 = !DILocation(line: 220, column: 13, scope: !506) +!509 = !DILocation(line: 220, column: 7, scope: !462) +!510 = !DILocation(line: 222, column: 18, scope: !511) +!511 = distinct !DILexicalBlock(scope: !512, file: !3, line: 222, column: 11) +!512 = distinct !DILexicalBlock(scope: !506, file: !3, line: 221, column: 5) +!513 = !DILocation(line: 223, column: 4, scope: !511) +!514 = !DILocation(line: 223, column: 14, scope: !511) +!515 = !DILocation(line: 223, column: 24, scope: !511) +!516 = !DILocation(line: 223, column: 39, scope: !511) +!517 = !DILocation(line: 223, column: 46, scope: !511) +!518 = !DILocation(line: 223, column: 44, scope: !511) +!519 = !DILocation(line: 223, column: 62, scope: !511) +!520 = !DILocation(line: 222, column: 11, scope: !512) +!521 = !DILocation(line: 225, column: 4, scope: !522) +!522 = distinct !DILexicalBlock(scope: !511, file: !3, line: 224, column: 2) +!523 = !DILocation(line: 0, scope: !448, inlinedAt: !524) +!524 = distinct !DILocation(line: 226, column: 11, scope: !522) +!525 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !524) +!526 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !524) +!527 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !524) +!528 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !524) +!529 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !524) +!530 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !524) +!531 = !DILocation(line: 226, column: 59, scope: !522) +!532 = !DILocation(line: 226, column: 43, scope: !522) +!533 = !DILocation(line: 226, column: 41, scope: !522) +!534 = !DILocation(line: 226, column: 4, scope: !522) +!535 = !DILocation(line: 228, column: 16, scope: !536) +!536 = distinct !DILexicalBlock(scope: !512, file: !3, line: 228, column: 11) +!537 = !DILocation(line: 228, column: 11, scope: !512) +!538 = !DILocalVariable(name: "isTiny", scope: !462, file: !3, line: 192, type: !372) +!539 = !DILocation(line: 233, column: 25, scope: !540) +!540 = distinct !DILexicalBlock(scope: !536, file: !3, line: 229, column: 2) +!541 = !DILocation(line: 233, column: 31, scope: !540) +!542 = !DILocation(line: 233, column: 4, scope: !540) +!543 = !DILocation(line: 235, column: 16, scope: !540) +!544 = !DILocation(line: 235, column: 21, scope: !540) +!545 = !DILocation(line: 236, column: 8, scope: !546) +!546 = distinct !DILexicalBlock(scope: !540, file: !3, line: 236, column: 8) +!547 = !DILocation(line: 236, column: 15, scope: !546) +!548 = !DILocation(line: 236, column: 18, scope: !546) +!549 = !DILocation(line: 236, column: 8, scope: !540) +!550 = !DILocation(line: 237, column: 6, scope: !546) +!551 = !DILocation(line: 238, column: 2, scope: !540) +!552 = !DILocation(line: 239, column: 5, scope: !512) +!553 = !DILocation(line: 240, column: 7, scope: !554) +!554 = distinct !DILexicalBlock(scope: !462, file: !3, line: 240, column: 7) +!555 = !DILocation(line: 240, column: 7, scope: !462) +!556 = !DILocation(line: 241, column: 27, scope: !554) +!557 = !DILocation(line: 241, column: 5, scope: !554) +!558 = !DILocation(line: 242, column: 11, scope: !462) +!559 = !DILocation(line: 242, column: 18, scope: !462) +!560 = !DILocation(line: 242, column: 16, scope: !462) +!561 = !DILocation(line: 242, column: 34, scope: !462) +!562 = !DILocation(line: 242, column: 8, scope: !462) +!563 = !DILocation(line: 243, column: 25, scope: !462) +!564 = !DILocation(line: 243, column: 34, scope: !462) +!565 = !DILocation(line: 243, column: 13, scope: !462) +!566 = !DILocation(line: 243, column: 40, scope: !462) +!567 = !DILocation(line: 243, column: 11, scope: !462) +!568 = !DILocation(line: 243, column: 8, scope: !462) +!569 = !DILocation(line: 244, column: 7, scope: !570) +!570 = distinct !DILexicalBlock(scope: !462, file: !3, line: 244, column: 7) +!571 = !DILocation(line: 244, column: 12, scope: !570) +!572 = !DILocation(line: 244, column: 7, scope: !462) +!573 = !DILocation(line: 245, column: 5, scope: !570) +!574 = !DILocation(line: 246, column: 36, scope: !462) +!575 = !DILocation(line: 0, scope: !448, inlinedAt: !576) +!576 = distinct !DILocation(line: 246, column: 10, scope: !462) +!577 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !576) +!578 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !576) +!579 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !576) +!580 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !576) +!581 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !576) +!582 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !576) +!583 = !DILocation(line: 246, column: 3, scope: !462) +!584 = !DILocation(line: 248, column: 1, scope: !462) +!585 = distinct !DISubprogram(name: "float64_mul", scope: !3, file: !3, line: 272, type: !586, scopeLine: 272, flags: DIFlagPrototyped, spFlags: DISPFlagDefinition, unit: !2, retainedNodes: !4) +!586 = !DISubroutineType(types: !587) +!587 = !{!373, !588, !589} +!588 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055xAcceleration", file: !3, line: 262, baseType: !373) +!589 = !DIDerivedType(tag: DW_TAG_typedef, name: "bmx055yAcceleration", file: !3, line: 263, baseType: !373) +!590 = !DILocalVariable(name: "a", arg: 1, scope: !585, file: !3, line: 272, type: !588) +!591 = !DILocation(line: 0, scope: !585) +!592 = !DILocalVariable(name: "b", arg: 2, scope: !585, file: !3, line: 272, type: !589) +!593 = !DILocalVariable(name: "aExp", scope: !585, file: !3, line: 284, type: !281) +!594 = !DILocation(line: 284, column: 9, scope: !585) +!595 = !DILocalVariable(name: "bExp", scope: !585, file: !3, line: 284, type: !281) +!596 = !DILocation(line: 284, column: 15, scope: !585) +!597 = !DILocalVariable(name: "aSig", scope: !585, file: !3, line: 285, type: !6) +!598 = !DILocation(line: 285, column: 10, scope: !585) +!599 = !DILocalVariable(name: "bSig", scope: !585, file: !3, line: 285, type: !6) +!600 = !DILocation(line: 285, column: 16, scope: !585) +!601 = !DILocalVariable(name: "zSig0", scope: !585, file: !3, line: 285, type: !6) +!602 = !DILocation(line: 285, column: 22, scope: !585) +!603 = !DILocalVariable(name: "zSig1", scope: !585, file: !3, line: 285, type: !6) +!604 = !DILocation(line: 285, column: 29, scope: !585) +!605 = !DILocation(line: 0, scope: !392, inlinedAt: !606) +!606 = distinct !DILocation(line: 287, column: 10, scope: !585) +!607 = !DILocation(line: 90, column: 12, scope: !392, inlinedAt: !606) +!608 = !DILocation(line: 287, column: 8, scope: !585) +!609 = !DILocation(line: 0, scope: !399, inlinedAt: !610) +!610 = distinct !DILocation(line: 288, column: 10, scope: !585) +!611 = !DILocation(line: 104, column: 13, scope: !399, inlinedAt: !610) +!612 = !DILocation(line: 104, column: 20, scope: !399, inlinedAt: !610) +!613 = !DILocation(line: 104, column: 10, scope: !399, inlinedAt: !610) +!614 = !DILocation(line: 288, column: 8, scope: !585) +!615 = !DILocation(line: 0, scope: !408, inlinedAt: !616) +!616 = distinct !DILocation(line: 289, column: 11, scope: !585) +!617 = !DILocation(line: 118, column: 12, scope: !408, inlinedAt: !616) +!618 = !DILocation(line: 118, column: 10, scope: !408, inlinedAt: !616) +!619 = !DILocalVariable(name: "aSign", scope: !585, file: !3, line: 283, type: !372) +!620 = !DILocation(line: 0, scope: !392, inlinedAt: !621) +!621 = distinct !DILocation(line: 290, column: 10, scope: !585) +!622 = !DILocation(line: 90, column: 12, scope: !392, inlinedAt: !621) +!623 = !DILocation(line: 290, column: 8, scope: !585) +!624 = !DILocation(line: 0, scope: !399, inlinedAt: !625) +!625 = distinct !DILocation(line: 291, column: 10, scope: !585) +!626 = !DILocation(line: 104, column: 13, scope: !399, inlinedAt: !625) +!627 = !DILocation(line: 104, column: 20, scope: !399, inlinedAt: !625) +!628 = !DILocation(line: 104, column: 10, scope: !399, inlinedAt: !625) +!629 = !DILocation(line: 291, column: 8, scope: !585) +!630 = !DILocation(line: 0, scope: !408, inlinedAt: !631) +!631 = distinct !DILocation(line: 292, column: 11, scope: !585) +!632 = !DILocation(line: 118, column: 12, scope: !408, inlinedAt: !631) +!633 = !DILocation(line: 118, column: 10, scope: !408, inlinedAt: !631) +!634 = !DILocalVariable(name: "bSign", scope: !585, file: !3, line: 283, type: !372) +!635 = !DILocation(line: 293, column: 17, scope: !585) +!636 = !DILocalVariable(name: "zSign", scope: !585, file: !3, line: 283, type: !372) +!637 = !DILocation(line: 294, column: 7, scope: !638) +!638 = distinct !DILexicalBlock(scope: !585, file: !3, line: 294, column: 7) +!639 = !DILocation(line: 294, column: 12, scope: !638) +!640 = !DILocation(line: 294, column: 7, scope: !585) +!641 = !DILocation(line: 296, column: 11, scope: !642) +!642 = distinct !DILexicalBlock(scope: !643, file: !3, line: 296, column: 11) +!643 = distinct !DILexicalBlock(scope: !638, file: !3, line: 295, column: 5) +!644 = !DILocation(line: 296, column: 16, scope: !642) +!645 = !DILocation(line: 296, column: 21, scope: !642) +!646 = !DILocation(line: 296, column: 26, scope: !642) +!647 = !DILocation(line: 296, column: 36, scope: !642) +!648 = !DILocation(line: 296, column: 39, scope: !642) +!649 = !DILocation(line: 296, column: 11, scope: !643) +!650 = !DILocation(line: 297, column: 9, scope: !642) +!651 = !DILocation(line: 297, column: 2, scope: !642) +!652 = !DILocation(line: 298, column: 12, scope: !653) +!653 = distinct !DILexicalBlock(scope: !643, file: !3, line: 298, column: 11) +!654 = !DILocation(line: 298, column: 19, scope: !653) +!655 = !DILocation(line: 298, column: 17, scope: !653) +!656 = !DILocation(line: 298, column: 25, scope: !653) +!657 = !DILocation(line: 298, column: 11, scope: !643) +!658 = !DILocation(line: 300, column: 4, scope: !659) +!659 = distinct !DILexicalBlock(scope: !653, file: !3, line: 299, column: 2) +!660 = !DILocation(line: 301, column: 4, scope: !659) +!661 = !DILocation(line: 0, scope: !448, inlinedAt: !662) +!662 = distinct !DILocation(line: 303, column: 14, scope: !643) +!663 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !662) +!664 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !662) +!665 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !662) +!666 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !662) +!667 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !662) +!668 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !662) +!669 = !DILocation(line: 303, column: 7, scope: !643) +!670 = !DILocation(line: 305, column: 7, scope: !671) +!671 = distinct !DILexicalBlock(scope: !585, file: !3, line: 305, column: 7) +!672 = !DILocation(line: 305, column: 12, scope: !671) +!673 = !DILocation(line: 305, column: 7, scope: !585) +!674 = !DILocation(line: 307, column: 11, scope: !675) +!675 = distinct !DILexicalBlock(scope: !676, file: !3, line: 307, column: 11) +!676 = distinct !DILexicalBlock(scope: !671, file: !3, line: 306, column: 5) +!677 = !DILocation(line: 307, column: 11, scope: !676) +!678 = !DILocation(line: 308, column: 9, scope: !675) +!679 = !DILocation(line: 308, column: 2, scope: !675) +!680 = !DILocation(line: 309, column: 12, scope: !681) +!681 = distinct !DILexicalBlock(scope: !676, file: !3, line: 309, column: 11) +!682 = !DILocation(line: 309, column: 19, scope: !681) +!683 = !DILocation(line: 309, column: 17, scope: !681) +!684 = !DILocation(line: 309, column: 25, scope: !681) +!685 = !DILocation(line: 309, column: 11, scope: !676) +!686 = !DILocation(line: 311, column: 4, scope: !687) +!687 = distinct !DILexicalBlock(scope: !681, file: !3, line: 310, column: 2) +!688 = !DILocation(line: 312, column: 4, scope: !687) +!689 = !DILocation(line: 0, scope: !448, inlinedAt: !690) +!690 = distinct !DILocation(line: 314, column: 14, scope: !676) +!691 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !690) +!692 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !690) +!693 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !690) +!694 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !690) +!695 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !690) +!696 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !690) +!697 = !DILocation(line: 314, column: 7, scope: !676) +!698 = !DILocation(line: 316, column: 7, scope: !699) +!699 = distinct !DILexicalBlock(scope: !585, file: !3, line: 316, column: 7) +!700 = !DILocation(line: 316, column: 12, scope: !699) +!701 = !DILocation(line: 316, column: 7, scope: !585) +!702 = !DILocation(line: 318, column: 11, scope: !703) +!703 = distinct !DILexicalBlock(scope: !704, file: !3, line: 318, column: 11) +!704 = distinct !DILexicalBlock(scope: !699, file: !3, line: 317, column: 5) +!705 = !DILocation(line: 318, column: 16, scope: !703) +!706 = !DILocation(line: 318, column: 11, scope: !704) +!707 = !DILocation(line: 0, scope: !448, inlinedAt: !708) +!708 = distinct !DILocation(line: 319, column: 9, scope: !703) +!709 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !708) +!710 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !708) +!711 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !708) +!712 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !708) +!713 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !708) +!714 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !708) +!715 = !DILocation(line: 319, column: 2, scope: !703) +!716 = !DILocation(line: 320, column: 34, scope: !704) +!717 = !DILocation(line: 320, column: 7, scope: !704) +!718 = !DILocation(line: 321, column: 5, scope: !704) +!719 = !DILocation(line: 322, column: 7, scope: !720) +!720 = distinct !DILexicalBlock(scope: !585, file: !3, line: 322, column: 7) +!721 = !DILocation(line: 322, column: 12, scope: !720) +!722 = !DILocation(line: 322, column: 7, scope: !585) +!723 = !DILocation(line: 324, column: 11, scope: !724) +!724 = distinct !DILexicalBlock(scope: !725, file: !3, line: 324, column: 11) +!725 = distinct !DILexicalBlock(scope: !720, file: !3, line: 323, column: 5) +!726 = !DILocation(line: 324, column: 16, scope: !724) +!727 = !DILocation(line: 324, column: 11, scope: !725) +!728 = !DILocation(line: 0, scope: !448, inlinedAt: !729) +!729 = distinct !DILocation(line: 325, column: 9, scope: !724) +!730 = !DILocation(line: 160, column: 21, scope: !448, inlinedAt: !729) +!731 = !DILocation(line: 160, column: 28, scope: !448, inlinedAt: !729) +!732 = !DILocation(line: 160, column: 48, scope: !448, inlinedAt: !729) +!733 = !DILocation(line: 160, column: 54, scope: !448, inlinedAt: !729) +!734 = !DILocation(line: 160, column: 35, scope: !448, inlinedAt: !729) +!735 = !DILocation(line: 160, column: 61, scope: !448, inlinedAt: !729) +!736 = !DILocation(line: 325, column: 2, scope: !724) +!737 = !DILocation(line: 326, column: 34, scope: !725) +!738 = !DILocation(line: 326, column: 7, scope: !725) +!739 = !DILocation(line: 327, column: 5, scope: !725) +!740 = !DILocation(line: 328, column: 10, scope: !585) +!741 = !DILocation(line: 328, column: 17, scope: !585) +!742 = !DILocation(line: 328, column: 15, scope: !585) +!743 = !DILocation(line: 328, column: 22, scope: !585) +!744 = !DILocalVariable(name: "zExp", scope: !585, file: !3, line: 284, type: !281) +!745 = !DILocation(line: 329, column: 11, scope: !585) +!746 = !DILocation(line: 329, column: 16, scope: !585) +!747 = !DILocation(line: 329, column: 46, scope: !585) +!748 = !DILocation(line: 329, column: 8, scope: !585) +!749 = !DILocation(line: 330, column: 11, scope: !585) +!750 = !DILocation(line: 330, column: 16, scope: !585) +!751 = !DILocation(line: 330, column: 46, scope: !585) +!752 = !DILocation(line: 330, column: 8, scope: !585) +!753 = !DILocation(line: 331, column: 15, scope: !585) +!754 = !DILocation(line: 331, column: 21, scope: !585) +!755 = !DILocation(line: 331, column: 3, scope: !585) +!756 = !DILocation(line: 332, column: 13, scope: !585) +!757 = !DILocation(line: 332, column: 19, scope: !585) +!758 = !DILocation(line: 332, column: 12, scope: !585) +!759 = !DILocation(line: 332, column: 9, scope: !585) +!760 = !DILocation(line: 333, column: 23, scope: !761) +!761 = distinct !DILexicalBlock(scope: !585, file: !3, line: 333, column: 7) +!762 = !DILocation(line: 333, column: 29, scope: !761) +!763 = !DILocation(line: 333, column: 9, scope: !761) +!764 = !DILocation(line: 333, column: 7, scope: !585) +!765 = !DILocation(line: 335, column: 13, scope: !766) +!766 = distinct !DILexicalBlock(scope: !761, file: !3, line: 334, column: 5) +!767 = !DILocation(line: 336, column: 7, scope: !766) +!768 = !DILocation(line: 337, column: 5, scope: !766) +!769 = !DILocation(line: 338, column: 44, scope: !585) +!770 = !DILocation(line: 338, column: 10, scope: !585) +!771 = !DILocation(line: 338, column: 3, scope: !585) +!772 = !DILocation(line: 340, column: 1, scope: !585) +!773 = distinct !DISubprogram(name: "propagateFloat64NaN", linkageName: "_ZL19propagateFloat64NaNyy", scope: !362, file: !362, line: 108, type: !774, scopeLine: 109, flags: DIFlagPrototyped, spFlags: DISPFlagLocalToUnit | DISPFlagDefinition, unit: !2, retainedNodes: !4) +!774 = !DISubroutineType(types: !775) +!775 = !{!373, !373, !373} +!776 = !DILocalVariable(name: "a", arg: 1, scope: !773, file: !362, line: 108, type: !373) +!777 = !DILocation(line: 0, scope: !773) +!778 = !DILocalVariable(name: "b", arg: 2, scope: !773, file: !362, line: 108, type: !373) +!779 = !DILocation(line: 112, column: 12, scope: !773) +!780 = !DILocalVariable(name: "aIsNaN", scope: !773, file: !362, line: 110, type: !372) +!781 = !DILocation(line: 113, column: 21, scope: !773) +!782 = !DILocalVariable(name: "aIsSignalingNaN", scope: !773, file: !362, line: 110, type: !372) +!783 = !DILocation(line: 114, column: 12, scope: !773) +!784 = !DILocalVariable(name: "bIsNaN", scope: !773, file: !362, line: 110, type: !372) +!785 = !DILocation(line: 115, column: 21, scope: !773) +!786 = !DILocalVariable(name: "bIsSignalingNaN", scope: !773, file: !362, line: 110, type: !372) +!787 = !DILocation(line: 116, column: 5, scope: !773) +!788 = !DILocation(line: 117, column: 5, scope: !773) +!789 = !DILocation(line: 118, column: 23, scope: !790) +!790 = distinct !DILexicalBlock(scope: !773, file: !362, line: 118, column: 7) +!791 = !DILocation(line: 118, column: 7, scope: !790) +!792 = !DILocation(line: 118, column: 7, scope: !773) +!793 = !DILocation(line: 119, column: 5, scope: !790) +!794 = !DILocation(line: 120, column: 10, scope: !773) +!795 = !DILocation(line: 120, column: 32, scope: !773) +!796 = !DILocation(line: 120, column: 54, scope: !773) +!797 = !DILocation(line: 120, column: 3, scope: !773) +!798 = !DILocalVariable(name: "a", arg: 1, scope: !20, file: !21, line: 116, type: !24) +!799 = !DILocation(line: 0, scope: !20) +!800 = !DILocalVariable(name: "shiftCount", scope: !20, file: !21, line: 136, type: !16) +!801 = !DILocation(line: 139, column: 9, scope: !802) +!802 = distinct !DILexicalBlock(scope: !20, file: !21, line: 139, column: 7) +!803 = !DILocation(line: 139, column: 7, scope: !20) +!804 = !DILocation(line: 141, column: 18, scope: !805) +!805 = distinct !DILexicalBlock(scope: !802, file: !21, line: 140, column: 5) +!806 = !DILocation(line: 142, column: 9, scope: !805) +!807 = !DILocation(line: 143, column: 5, scope: !805) +!808 = !DILocation(line: 144, column: 9, scope: !809) +!809 = distinct !DILexicalBlock(scope: !20, file: !21, line: 144, column: 7) +!810 = !DILocation(line: 144, column: 7, scope: !20) +!811 = !DILocation(line: 146, column: 18, scope: !812) +!812 = distinct !DILexicalBlock(scope: !809, file: !21, line: 145, column: 5) +!813 = !DILocation(line: 147, column: 9, scope: !812) +!814 = !DILocation(line: 148, column: 5, scope: !812) +!815 = !DILocation(line: 149, column: 41, scope: !20) +!816 = !DILocation(line: 149, column: 17, scope: !20) +!817 = !DILocation(line: 149, column: 14, scope: !20) +!818 = !DILocation(line: 150, column: 3, scope: !20) diff --git a/applications/newton/llvm-ir/Include/stm32f303xc.h b/applications/newton/llvm-ir/Include/stm32f303xc.h new file mode 100644 index 000000000..557420e9a --- /dev/null +++ b/applications/newton/llvm-ir/Include/stm32f303xc.h @@ -0,0 +1,13485 @@ +/** + ****************************************************************************** + * @file stm32f303xc.h + * @author MCD Application Team + * @brief CMSIS STM32F303xC Devices Peripheral Access Layer Header File. + * + * This file contains: + * - Data structures and the address mapping for all peripherals + * - Peripheral's registers declarations and bits definition + * - Macros to access peripheral's registers hardware + * + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS_Device + * @{ + */ + +/** @addtogroup stm32f303xc + * @{ + */ + +#ifndef __STM32F303xC_H +#define __STM32F303xC_H + +#ifdef __cplusplus + extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS + * @{ + */ + +/** + * @brief Configuration of the Cortex-M4 Processor and Core Peripherals + */ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F303xC devices provide an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F303xC devices use 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< STM32F303xC devices provide an FPU */ + +/** + * @} + */ + +/** @addtogroup Peripheral_interrupt_number_definition + * @{ + */ + +/** + * @brief STM32F303xC devices Interrupt Number Definition, according to the selected device + * in @ref Library_configuration_section + */ +typedef enum +{ +/****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 3 Cortex-M4 Hard Fault Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ +/****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line 19 */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line 20 */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_TSC_IRQn = 8, /*!< EXTI Line2 Interrupt and Touch Sense Controller Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 Interrupt */ + DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 Interrupt */ + DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 Interrupt */ + DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 Interrupt */ + DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 Interrupt */ + DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 Interrupt */ + DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 Interrupt */ + ADC1_2_IRQn = 18, /*!< ADC1 & ADC2 Interrupts */ + USB_HP_CAN_TX_IRQn = 19, /*!< USB Device High Priority or CAN TX Interrupts */ + USB_LP_CAN_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN RX0 Interrupts */ + CAN_RX1_IRQn = 21, /*!< CAN RX1 Interrupt */ + CAN_SCE_IRQn = 22, /*!< CAN SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt & EXTI Line23 Interrupt (I2C1 wakeup) */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt & EXTI Line24 Interrupt (I2C2 wakeup) */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt & EXTI Line25 Interrupt (USART1 wakeup) */ + USART2_IRQn = 38, /*!< USART2 global Interrupt & EXTI Line26 Interrupt (USART2 wakeup) */ + USART3_IRQn = 39, /*!< USART3 global Interrupt & EXTI Line28 Interrupt (USART3 wakeup) */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line 17 Interrupt */ + USBWakeUp_IRQn = 42, /*!< USB Wakeup Interrupt */ + TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ + TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ + TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt & EXTI Line34 Interrupt (UART4 wakeup) */ + UART5_IRQn = 53, /*!< UART5 global Interrupt & EXTI Line35 Interrupt (UART5 wakeup) */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC underrun error Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_IRQn = 59, /*!< DMA2 Channel 4 global Interrupt */ + DMA2_Channel5_IRQn = 60, /*!< DMA2 Channel 5 global Interrupt */ + ADC4_IRQn = 61, /*!< ADC4 global Interrupt */ + COMP1_2_3_IRQn = 64, /*!< COMP1, COMP2 and COMP3 global Interrupt via EXTI Line21, 22 and 29*/ + COMP4_5_6_IRQn = 65, /*!< COMP4, COMP5 and COMP6 global Interrupt via EXTI Line30, 31 and 32*/ + COMP7_IRQn = 66, /*!< COMP7 global Interrupt via EXTI Line33 */ + USB_HP_IRQn = 74, /*!< USB High Priority global Interrupt */ + USB_LP_IRQn = 75, /*!< USB Low Priority global Interrupt */ + USBWakeUp_RMP_IRQn = 76, /*!< USB Wakeup Interrupt remap */ + FPU_IRQn = 81, /*!< Floating point Interrupt */ +} IRQn_Type; + +/** + * @} + */ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f3xx.h" /* STM32F3xx System Header */ +#include + +/** @addtogroup Peripheral_registers_structures + * @{ + */ + +/** + * @brief Analog to Digital Converter + */ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */ + uint32_t RESERVED0; /*!< Reserved, 0x010 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, 0x01C */ + __IO uint32_t TR1; /*!< ADC watchdog threshold register 1, Address offset: 0x20 */ + __IO uint32_t TR2; /*!< ADC watchdog threshold register 2, Address offset: 0x24 */ + __IO uint32_t TR3; /*!< ADC watchdog threshold register 3, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x02C */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x044 */ + uint32_t RESERVED4; /*!< Reserved, 0x048 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t DIFSEL; /*!< ADC Differential Mode Selection Register, Address offset: 0xB0 */ + __IO uint32_t CALFACT; /*!< ADC Calibration Factors, Address offset: 0xB4 */ + +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */ + uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1/3 base address + 0x30C */ +} ADC_Common_TypeDef; + +/** + * @brief Controller Area Network TxMailBox + */ +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** + * @brief Controller Area Network FIFOMailBox + */ +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** + * @brief Controller Area Network FilterRegister + */ +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** + * @brief Controller Area Network + */ +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** + * @brief Analog Comparators + */ +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, Address offset: 0x00 */ +} COMP_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, used for bits common to several COMP instances, Address offset: 0x00 */ +} COMP_Common_TypeDef; + +/** + * @brief CRC calculation unit + */ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** + * @brief Digital to Analog Converter + */ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** + * @brief Debug MCU + */ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + +/** + * @brief DMA Controller + */ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CMAR; /*!< DMA channel x memory address register */ +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} DMA_TypeDef; + +/** + * @brief External Interrupt/Event Controller + */ + +typedef struct +{ + __IO uint32_t IMR; /*! $ir_file" + clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o "$ir_file" "$file" + if [ $? -ne 0 ]; then + echo "Error generating LLVM IR for $file" + exit 1 + fi + + echo "Optimizing $ir_file with mem2reg" + opt --mem2reg -S -o "$ir_file" "$ir_file" + if [ $? -ne 0 ]; then + echo "Error optimizing $ir_file with mem2reg" + exit 1 + fi + + echo "Converting $ir_file to $bc_file" + llvm-as "$ir_file" -o "$bc_file" + + + + if [ $? -ne 0 ]; then + echo "Error converting $ir_file to bitcode" + exit 1 + fi +done + + llvm-nm $OUTPUT_PATH/*.bc + + + +echo "Step 2: Merge LLVM bitcode files into merged.bc" +llvm-link --only-needed "$OUTPUT_PATH"/*.bc -o "$OUTPUT_PATH/merged.bc" +if [ $? -ne 0 ]; then + echo "Error merging bitcode files" + exit 1 +fi +echo "Merged bitcode files into $OUTPUT_PATH/merged.bc" + +echo "Step 3: Convert merged.bc back to LLVM IR (merged.ll)" +llvm-dis "$OUTPUT_PATH/merged.bc" -o "$OUTPUT_PATH/merged.ll" +if [ $? -ne 0 ]; then + echo "Error converting merged.bc to LLVM IR" + exit 1 +fi +echo "Converted $OUTPUT_PATH/merged.bc to $OUTPUT_PATH/merged.ll" + + +echo "Step 3: Use newton for optimization and quantization" +cd $HOME/CoSense/src/newton && ./newton-linux-EN \ + --llvm-ir=$OUTPUT_PATH/merged.ll \ + --llvm-ir-liveness-check \ + --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/BMX055.nt + +#echo "Step 4: Optimize the quantized LLVM IR file" +#opt -inline -S -o $OUTPUT_PATH/out.ll $OUTPUT_PATH/merged_output.ll + + +#echo "Step 5: Compile the optimized LLVM IR file to bitcode" +#llvm-as $OUTPUT_PATH/out.ll -o $OUTPUT_PATH/out.bc +# +# +#echo "Step 6: Compile the bitcode file to assembly" +#llc $OUTPUT_PATH/out.bc -o $OUTPUT_PATH/out.s +# +# +#echo "Step 7: Compile the assembly file to object file" +#clang -c $OUTPUT_PATH/out.s -o $OUTPUT_PATH/out.o +# +# +#echo "Step 8: Package the object file into a static library" +#ar -rc $OUTPUT_PATH/libout.a $OUTPUT_PATH/out.o +# +# +#echo "Step 9: Compile the test file and link with the static library" +#clang $OUTPUT_PATH/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$OUTPUT_PATH -lout -O3 -Os -g -o $OUTPUT_PATH/main_out -lm +# +# +#echo "Step 10: Run the test executable" +#$OUTPUT_PATH/main_out diff --git a/applications/newton/llvm-ir/LTO/MadgwickAHRS.h b/applications/newton/llvm-ir/LTO/MadgwickAHRS.h new file mode 100644 index 000000000..85d794523 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickAHRS.h @@ -0,0 +1,29 @@ +#ifndef MADGWICK_AHRS_H +#define MADGWICK_AHRS_H + +#include + + + +// Type Definitions +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + +// Function Declarations +static void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif // MADGWICK_AHRS_H diff --git a/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c new file mode 100644 index 000000000..b2316f2b8 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.c @@ -0,0 +1,122 @@ +#include "MadgwickAHRSupdate.h" +#include "MadgwickMath.h" +#include "MadgwickIMU.h" +#include "SharedVariables.h" +//--------------------------------------------------------------------------------------------------- +// Variable definitions + + + +void +MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float hx, hy; + float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + + + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2.0f * q0 * mx; + _2q0my = 2.0f * q0 * my; + _2q0mz = 2.0f * q0 * mz; + _2q1mx = 2.0f * q1 * mx; + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _2q0q2 = 2.0f * q0 * q2; + _2q2q3 = 2.0f * q2 * q3; + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; + hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; + _2bx = sqrtf(hx * hx + hy * hy); + _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h new file mode 100644 index 000000000..f7d06af61 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickAHRSupdate.h @@ -0,0 +1,29 @@ +#ifndef MADGWICK_AHRS_H +#define MADGWICK_AHRS_H + +#include + + + +// Type Definitions +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + +// Function Declarations +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif // MADGWICK_AHRS_H diff --git a/applications/newton/llvm-ir/LTO/MadgwickIMU.c b/applications/newton/llvm-ir/LTO/MadgwickIMU.c new file mode 100644 index 000000000..24742ff24 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickIMU.c @@ -0,0 +1,84 @@ +#include "MadgwickIMU.h" +#include "MadgwickMath.h" +#include "SharedVariables.h" + + + +void +MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _4q0 = 4.0f * q0; + _4q1 = 4.0f * q1; + _4q2 = 4.0f * q2; + _8q1 = 8.0f * q1; + _8q2 = 8.0f * q2; + q0q0 = q0 * q0; + q1q1 = q1 * q1; + q2q2 = q2 * q2; + q3q3 = q3 * q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; + s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; + s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; + s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} diff --git a/applications/newton/llvm-ir/LTO/MadgwickIMU.h b/applications/newton/llvm-ir/LTO/MadgwickIMU.h new file mode 100644 index 000000000..251d4022d --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickIMU.h @@ -0,0 +1,20 @@ +#ifndef MADGWICK_IMU_H +#define MADGWICK_IMU_H + +// Type Definitions +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + +// Function Declarations +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif // MADGWICK_IMU_H diff --git a/applications/newton/llvm-ir/LTO/MadgwickMath.c b/applications/newton/llvm-ir/LTO/MadgwickMath.c new file mode 100644 index 000000000..7f63732d2 --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickMath.c @@ -0,0 +1,12 @@ +#include "MadgwickMath.h" + +// Fast inverse square root +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i >> 1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} diff --git a/applications/newton/llvm-ir/LTO/MadgwickMath.h b/applications/newton/llvm-ir/LTO/MadgwickMath.h new file mode 100644 index 000000000..a5899673d --- /dev/null +++ b/applications/newton/llvm-ir/LTO/MadgwickMath.h @@ -0,0 +1,7 @@ +#ifndef MADGWICK_MATH_H +#define MADGWICK_MATH_H + +// Function Declarations +float invSqrt(float x); + +#endif // MADGWICK_MATH_H diff --git a/applications/newton/llvm-ir/MahonyAHRS.sh b/applications/newton/llvm-ir/MahonyAHRS.sh new file mode 100755 index 000000000..461e52b79 --- /dev/null +++ b/applications/newton/llvm-ir/MahonyAHRS.sh @@ -0,0 +1,59 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +FILE_PATH="$HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS_opt.ll" + + #Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MahonyAHRS.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +#cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/test.nt + +cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/BMX055.nt +# +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +#llvm-dis $HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS_opt.bc -o $HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS_opt.ll +cd $HOME/CoSense/applications/newton/llvm-ir/&& +./replace.sh $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll + +#python3 replace.py + + +# Step 4: Optimize the generated LLVM IR file +echo "Step 4: Optimize the generated LLVM IR file" +#opt $HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt -mem2reg $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/MahonyAHRS_opt.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +echo "Step 7: Compile the assembly file to object file" +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" + +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +#echo "Step 10: Run the test executable" +#$HOME/CoSense/applications/newton/llvm-ir/main_out + diff --git a/applications/newton/llvm-ir/MahonyAHRS_original.sh b/applications/newton/llvm-ir/MahonyAHRS_original.sh new file mode 100644 index 000000000..e69de29bb diff --git a/applications/newton/llvm-ir/Makefile b/applications/newton/llvm-ir/Makefile index 8d68e6a1c..96b114d1c 100644 --- a/applications/newton/llvm-ir/Makefile +++ b/applications/newton/llvm-ir/Makefile @@ -36,7 +36,7 @@ endif all: default -default: application.ll simple_control_flow.ll inferBound.ll inferBoundControlFlow.ll e_exp.ll sincosf.ll e_log.ll e_acosh.ll e_j0.ll e_y0.ll e_rem_pio2.ll benchmark_suite.ll phi_two_global_arrays.ll func_call.ll test_shift.ll vec_add.ll vec_add_8.ll MadgwickAHRSfix.ll MadgwickAHRS_softfloat.ll MadgwickAHRS.ll arm_sqrt_q15.ll +default: application.ll simple_control_flow.ll inferBound.ll inferBoundControlFlow.ll e_exp.ll sincosf.ll e_log.ll e_acosh.ll e_j0.ll e_y0.ll e_rem_pio2.ll benchmark_suite.ll phi_two_global_arrays.ll func_call.ll test_shift.ll vec_add.ll vec_add_8.ll MadgwickAHRSfix.ll MadgwickAHRS_softfloat.ll MadgwickAHRS.ll arm_sqrt_q15.ll MadgwickAHRS_softfloat.ll : MadgwickAHRS_softfloat.c @echo Compiling $*.c @@ -47,7 +47,7 @@ MadgwickAHRS_softfloat.ll : MadgwickAHRS_softfloat.c %.ll : %.c @echo Compiling $*.c $(CC) $(TARGET_FLAG) $(CC_FP_FLAG) $(MACRO_FLAG) -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm $(COMMON_FLAGS) -o $@ $< - opt $@ $(OPT_FP_FLAG) --mem2reg --instsimplify -S -o $@ + opt $@ $(OPT_FP_FLAG) --mem2reg -S -o $@ clean:: $(QUIET)rm -f *.ll *.bc diff --git a/applications/newton/llvm-ir/SQNR.sh b/applications/newton/llvm-ir/SQNR.sh new file mode 100755 index 000000000..ce2d11133 --- /dev/null +++ b/applications/newton/llvm-ir/SQNR.sh @@ -0,0 +1,67 @@ +#!/bin/bash + +# Parse input argument for algorithm +while [[ $# -gt 0 ]]; do + case $1 in + --algo) + if [[ "$2" == "MadgwickAHRS" || "$2" == "MahonyAHRS" || "$2" == "sensfusion6" ]]; then + ALGO="$2" + shift 2 + else + echo "Error: Invalid algorithm '$2'. Use 'MadgwickAHRS', 'MahonyAHRS', or 'sensfusion6'." + exit 1 + fi + ;; + *) + echo "Usage: $0 [--algo MadgwickAHRS|MahonyAHRS|sensfusion6]" + exit 1 + ;; + esac +done + +OUTPUT_FILE="sqnr_results_${ALGO,,}.txt" + +# Clear old file contents +> "$OUTPUT_FILE" + +# Directory where make is executed (source directory) +MAKE_DIR="/home/xyf/CoSense/src/newton" + +# Current directory (where the test scripts are located) +TEST_DIR=$(pwd) + +#Run fp test +./testOriginal.sh "$ALGO" + +# Print header into file +echo "| MAX_PRECISION_BITS | SQNR (dB) |" >> "$OUTPUT_FILE" +echo "|--------------------|-----------|" >> "$OUTPUT_FILE" + + + +# Loop over MAX_PRECISION_BITS values from 8 to 18 +for i in {12..13}; do + echo "Compiling and testing with MAX_PRECISION_BITS = $i ..." + + # Switch to the make directory for compilation + pushd "$MAKE_DIR" > /dev/null + rm -f newton-irPass-LLVMIR-quantization.o newton-irPass-LLVMIR-optimizeByRange.o + make MAX_PRECISION_BITS=$i + popd > /dev/null + + # Switch back to the test scripts directory (current directory) + cd "$TEST_DIR" || exit + + # Run the test + ./testMadgwick.sh "$ALGO" + + # Extract sqnr line + sqnr_VALUE=$(python3 rms_error.py --filter "$ALGO" | grep "SQNR (dB)" | awk -F': ' '{print $2}') + echo "MAX_PRECISION_BITS=$i, SQNR (dB)=$sqnr_VALUE" + + # Append as a clean table row + printf "| %-18s | %-9s |\n" "$i" "$sqnr_VALUE" >> "$OUTPUT_FILE" +done + +echo "All tests completed. Results saved to $OUTPUT_FILE." +python3 plot_sqnr.py "$OUTPUT_FILE" diff --git a/applications/newton/llvm-ir/build_and_run.sh b/applications/newton/llvm-ir/build_and_run.sh new file mode 100755 index 000000000..c77718667 --- /dev/null +++ b/applications/newton/llvm-ir/build_and_run.sh @@ -0,0 +1,44 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/floating_point_operations.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd $USER_HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $USER_HOME/CoSense/applications/newton/sensors/test.nt + +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +llvm-dis $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll + +# Step 4: Optimize the generated LLVM IR file +echo "Step 4: Optimize the generated LLVM IR file" +opt $USER_HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll --simplifycfg --instsimplify -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.llperformace -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +echo "Step 7: Compile the assembly file to object file" +clang -c $USER_HOME/CoSense/applications/newton/llvm-ir/out.s -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_floating_point_operations.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +$USER_HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/c-files/Kalman.cpp b/applications/newton/llvm-ir/c-files/Kalman.cpp new file mode 100644 index 000000000..e61f34fd0 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Kalman.cpp @@ -0,0 +1,104 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + +This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#include "Kalman.h" +#include + Kalman::Kalman() { + /* We will set the variables like so, these can also be tuned by the user */ + Q_angle = 0.001f; + Q_bias = 0.003f; + R_measure = 0.03f; + + angle = 0.0f; // Reset the angle + bias = 0.0f; // Reset bias + + P[0][0] = 0.0f; // Since we assume that the bias is 0 and we know the starting angle (use setAngle), the error covariance matrix is set like so - see: http://en.wikipedia.org/wiki/Kalman_filter#Example_application.2C_technical + P[0][1] = 0.0f; + P[1][0] = 0.0f; + P[1][1] = 0.0f; +}; + +// The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds +float Kalman::getAngle(float newAngle, float newRate, float dt) { + // KasBot V2 - Kalman filter module - http://www.x-firm.com/?page_id=145 + // Modified by Kristian Lauszus + // See my blog post for more information: http://blog.tkjelectronics.dk/2012/09/a-practical-approach-to-kalman-filter-and-how-to-implement-it + + // Discrete Kalman filter time update equations - Time Update ("Predict") + // Update xhat - Project the state ahead + /* Step 1 */ + rate = newRate - bias; + angle += dt * rate; + + // Update estimation error covariance - Project the error covariance ahead + /* Step 2 */ + P[0][0] += dt * (dt*P[1][1] - P[0][1] - P[1][0] + Q_angle); + P[0][1] -= dt * P[1][1]; + P[1][0] -= dt * P[1][1]; + P[1][1] += Q_bias * dt; + + // Discrete Kalman filter measurement update equations - Measurement Update ("Correct") + // Calculate Kalman gain - Compute the Kalman gain + /* Step 4 */ + float S = P[0][0] + R_measure; // Estimate error + /* Step 5 */ + float K[2]; // Kalman gain - This is a 2x1 vector + K[0] = P[0][0] / S; + K[1] = P[1][0] / S; + + // Calculate angle and bias - Update estimate with measurement zk (newAngle) + /* Step 3 */ + float y = newAngle - angle; // Angle difference + /* Step 6 */ + angle += K[0] * y; + bias += K[1] * y; + + // Calculate estimation error covariance - Update the error covariance + /* Step 7 */ + float P00_temp = P[0][0]; + float P01_temp = P[0][1]; + + P[0][0] -= K[0] * P00_temp; + P[0][1] -= K[0] * P01_temp; + P[1][0] -= K[1] * P00_temp; + P[1][1] -= K[1] * P01_temp; + + return angle; +}; + +void Kalman::setAngle(float angle) { this->angle = angle; }; // Used to set angle, this should be set as the starting angle +float Kalman::getRate() { return this->rate; }; // Return the unbiased rate + +/* These are used to tune the Kalman filter */ +void Kalman::setQangle(float Q_angle) { this->Q_angle = Q_angle; }; +void Kalman::setQbias(float Q_bias) { this->Q_bias = Q_bias; }; +void Kalman::setRmeasure(float R_measure) { this->R_measure = R_measure; }; + +float Kalman::getQangle() { return this->Q_angle; }; +float Kalman::getQbias() { return this->Q_bias; }; +float Kalman::getRmeasure() { return this->R_measure; }; + +int main() { + Kalman kalman; + kalman.setAngle(0.0f); // 设置初始角度为0度 + + // 进行一些简单的测试操作 + float angle = kalman.getAngle(1.0f, 0.1f, 0.1f); + std::cout << "Estimated angle: " << angle << std::endl; + + return 0; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/Kalman.h b/applications/newton/llvm-ir/c-files/Kalman.h new file mode 100644 index 000000000..a7387c0a1 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Kalman.h @@ -0,0 +1,59 @@ +/* Copyright (C) 2012 Kristian Lauszus, TKJ Electronics. All rights reserved. + +This software may be distributed and modified under the terms of the GNU + General Public License version 2 (GPL2) as published by the Free Software + Foundation and appearing in the file GPL2.TXT included in the packaging of + this file. Please note that GPL2 Section 2[b] requires that all works based + on this software must also be made publicly available under the terms of + the GPL2 ("Copyleft"). + + Contact information + ------------------- + + Kristian Lauszus, TKJ Electronics + Web : http://www.tkjelectronics.com + e-mail : kristianl@tkjelectronics.com + */ + +#ifndef _Kalman_h_ +#define _Kalman_h_ + + class Kalman { + public: + Kalman(); + + // The angle should be in degrees and the rate should be in degrees per second and the delta time in seconds + float getAngle(float newAngle, float newRate, float dt); + + void setAngle(float angle); // Used to set angle, this should be set as the starting angle + float getRate(); // Return the unbiased rate + + /* These are used to tune the Kalman filter */ + void setQangle(float Q_angle); + /** + * setQbias(float Q_bias) + * Default value (0.003f) is in Kalman.cpp. + * Raise this to follow input more closely, + * lower this to smooth result of kalman filter. + */ + void setQbias(float Q_bias); + void setRmeasure(float R_measure); + + float getQangle(); + float getQbias(); + float getRmeasure(); + + private: + /* Kalman filter variables */ + float Q_angle; // Process noise variance for the accelerometer + float Q_bias; // Process noise variance for the gyro bias + float R_measure; // Measurement noise variance - this is actually the variance of the measurement noise + + float angle; // The angle calculated by the Kalman filter - part of the 2x1 state vector + float bias; // The gyro bias calculated by the Kalman filter - part of the 2x1 state vector + float rate; // Unbiased rate calculated from the rate and the calculated bias - you have to call getAngle to update the rate + + float P[2][2]; // Error covariance matrix - This is a 2x2 matrix +}; + +#endif \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c index 944df8c62..78c77c190 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.c @@ -22,9 +22,20 @@ // #define sampleFreq 512.0f // sample frequency in Hz // #define sampleFreq 100.0f // sample frequency in Hz -#define sampleFreq 28.0f // sample frequency in Hz +//#define sampleFreq 28.0f // sample frequency in Hz +#define sampleFreq 128.0f // sample frequency in Hz #define betaDef 0.1f // 2 * proportional gain +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + #ifndef lowerBound #define lowerBound -16 #endif @@ -47,15 +58,15 @@ volatile float beta = betaDef; // 2 * proportional gain (Kp) // 1/sqrtf(); float invSqrt(float x) { - float halfx = 0.5f * x; - float y = x; -//#pragma unsupported - long i = *(long*)&y; - i = 0x5f3759df - (i>>1); - y = *(float*)&i; -//#end - y = y * (1.5f - (halfx * y * y)); - return y; + float halfx = 0.5f * x; + float y = x; + //#pragma unsupported + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + //#end + y = y * (1.5f - (halfx * y * y)); + return y; } //==================================================================================================== @@ -64,16 +75,22 @@ float invSqrt(float x) { //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { - float q0 = *q0_ptr; - float q1 = *q1_ptr; - float q2 = *q2_ptr; - float q3 = *q3_ptr; +//void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + +void +MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; #ifdef ASSUME - __builtin_assume(ax > lowerBound && ax < upperBound); - __builtin_assume(ay > lowerBound && ay < upperBound); - __builtin_assume(az > lowerBound && az < upperBound); + __builtin_assume(ax > lowerBound && ax < upperBound); + __builtin_assume(ay > lowerBound && ay < upperBound); + __builtin_assume(az > lowerBound && az < upperBound);b #endif float recipNorm; float s0, s1, s2, s3; @@ -93,12 +110,15 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { // Normalise accelerometer measurement recipNorm = invSqrt(ax * ax + ay * ay + az * az); -// printf("1: %f\n", recipNorm); + // printf("1: %f\n", recipNorm); ax *= recipNorm; ay *= recipNorm; az *= recipNorm; @@ -134,7 +154,7 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float // Reference direction of Earth's magnetic field hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; - _2bx = sqrt(hx * hx + hy * hy); + _2bx = sqrtf(hx * hx + hy * hy); _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; _4bx = 2.0f * _2bx; _4bz = 2.0f * _2bz; @@ -165,29 +185,38 @@ void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float // Normalise quaternion recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); -// printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", -// q0, q1, q2, q3, recipNorm); + // printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", + // q0, q1, q2, q3, recipNorm); q0 *= recipNorm; q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; - -// printf("Original: q0 = %f\n", q0); + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + + //q0,q1,q2,q3 + // printf("Final quaternion value: q0 = %d\n", q0); + // printf("Final quaternion value: q1 = %d\n", q1); + // printf("Final quaternion value: q2 = %d\n", q2); + // printf("Final quaternion value: q3 = %d\n", q3); + + // printf("Original: q0 = %f\n", q0); } //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { - float q0 = *q0_ptr; - float q1 = *q1_ptr; - float q2 = *q2_ptr; - float q3 = *q3_ptr; + +void +MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; float recipNorm; float s0, s1, s2, s3; float qDot1, qDot2, qDot3, qDot4; @@ -206,7 +235,7 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo recipNorm = invSqrt(ax * ax + ay * ay + az * az); ax *= recipNorm; ay *= recipNorm; - az *= recipNorm; + az *= recipNorm; // Auxiliary variables to avoid repeated arithmetic _2q0 = 2.0f * q0; @@ -253,12 +282,12 @@ void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, flo q1 *= recipNorm; q2 *= recipNorm; q3 *= recipNorm; - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; } //==================================================================================================== // END OF CODE -//==================================================================================================== +//==================================================================================================== \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRS.h b/applications/newton/llvm-ir/c-files/MadgwickAHRS.h index 5e6d5df9e..4178f35fb 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRS.h +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRS.h @@ -26,11 +26,11 @@ // Function declarations void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, - float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); #endif //===================================================================================================== // End of file -//===================================================================================================== +//===================================================================================================== \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c new file mode 100644 index 000000000..87e5e1bba --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.c @@ -0,0 +1,295 @@ +#include +#include +#include "MadgwickAHRSDiff.h" + +#define sampleFreq 28 // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain + +typedef float bmx055xAcceleration; +typedef float bmx055yAcceleration; +typedef float bmx055zAcceleration; +typedef float bmx055xAngularRate; +typedef float bmx055yAngularRate; +typedef float bmx055zAngularRate; +typedef float bmx055xMagneto; +typedef float bmx055yMagneto; +typedef float bmx055zMagneto; + + +// Quantize a floating-point value to fixed-point format +int32_t quantize(float value, int32_t frac_base) { + return (int32_t)round(value * frac_base); // Quantize to fixed-point +} + + +// Align a smaller frac_q to the largest frac_q +int32_t align_to_frac_q(int32_t value, int32_t frac_q_value, int32_t target_frac_q) { + if (frac_q_value < target_frac_q) { + return value << (target_frac_q - frac_q_value); // Left shift to align + } + return value; // No shift needed if already at the target_frac_q +} + + + + + + +// Define function to multiply two fixed-point numbers with different precision +int32_t +mulfix(int32_t x, int32_t y, int32_t input_frac_q1, int32_t input_frac_q2, int32_t output_frac_q) +{ + int64_t result = (int64_t)x * y; + int32_t shift = input_frac_q1 + input_frac_q2 - output_frac_q; + if (shift > 0) + { + return result >> shift; // Right shift to reduce value + } + else + { + return result << (-shift); // Left shift to enlarge value + } +} + +// Convert one fixed-point number to another precision +int32_t +convert_frac_q(int32_t value, int32_t input_frac_q, int32_t output_frac_q) +{ + if (input_frac_q > output_frac_q) + { + return value >> (input_frac_q - output_frac_q); // Right shift to reduce value + } + else + { + return value << (output_frac_q - input_frac_q); // Left shift to enlarge value + } +} + +/* + * Compute square root of x and reciprocal with Goldschmidt's method + */ + +int32_t sqrt_rsqrt(int32_t x, int recip, int32_t frac_q) { + int32_t frac_base = (1 << frac_q); // Calculate the base value dynamically from frac_q + + if (recip) { + int32_t int_halfx = mulfix(0.5 * frac_base, x, frac_q, frac_q, frac_q); // half * x + float fp_y = (float)x / frac_base; // Convert to floating-point for approximation + long i = *(long*)&fp_y; + i = 0x5f3759df - (i >> 1); // Initial magic number approximation + fp_y = *(float*)&i; + int32_t int_y = fp_y * frac_base; // Convert back to fixed-point + int_y = mulfix(int_y, + ((int32_t)(1.5f * frac_base) - mulfix(mulfix(int_halfx, int_y, frac_q, frac_q, frac_q), int_y, frac_q, frac_q, frac_q)), + frac_q, frac_q, frac_q); + return int_y; + } else { + int32_t res = (int32_t)sqrt((double)x) << (frac_q / 2); // sqrt(x) in integer, shifted to frac_q/2 + + if (frac_q % 2) { + // If frac_q is odd, scale by sqrt(2) to adjust precision. + return mulfix(res, 1.414213562 * frac_base, frac_q / 2, frac_q, frac_q); + } else { + return res; + } + } +} + + +void +MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr) +{ + // Read the current quaternion values + int32_t gx_fixed = quantize(gx, FRAC_BASE_GYRO_X); + int32_t gy_fixed = quantize(gy, FRAC_BASE_GYRO_Y); + int32_t gz_fixed = quantize(gz, FRAC_BASE_GYRO_Z); + + int32_t ax_fixed = quantize(ax, FRAC_BASE_ACCEL_X); + int32_t ay_fixed = quantize(ay, FRAC_BASE_ACCEL_Y); + int32_t az_fixed = quantize(az, FRAC_BASE_ACCEL_Z); + + int32_t mx_fixed = quantize(mx, FRAC_BASE_MAG_X); + int32_t my_fixed = quantize(my, FRAC_BASE_MAG_Y); + int32_t mz_fixed = quantize(mz, FRAC_BASE_MAG_Z); + + + + // + + + + + + + + + + + + + + + + + + + int32_t q0 = *q0_ptr; + int32_t q1 = *q1_ptr; + int32_t q2 = *q2_ptr; + int32_t q3 = *q3_ptr; + + int32_t recipNorm; + int32_t s0, s1, s2, s3; + int32_t qDot1, qDot2, qDot3, qDot4; + int32_t hx, hy; + int32_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz; + int32_t _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3; + int32_t q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // Convert gyroscope data to a common precision + gx = convert_frac_q(gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); // Keeping it as FRAC_Q_GYRO_X + gy = convert_frac_q(gy, FRAC_Q_GYRO_Y, FRAC_Q_GYRO_X); // Converting to common precision + gz = convert_frac_q(gz, FRAC_Q_GYRO_Z, FRAC_Q_GYRO_X); // Converting to common precision + + // Use IMU algorithm if magnetometer measurement is invalid (avoids NaN in magnetometer normalization) + if ((mx == 0x0) && (my == 0x0) && (mz == 0x0)) + { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = (-mulfix(q1, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q2, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q3, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + qDot2 = (mulfix(q0, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(q2, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q3, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + qDot3 = (mulfix(q0, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q1, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(q3, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + qDot4 = (mulfix(q0, gz, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(q1, gy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(q2, gx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)) / 2; + + // Compute feedback only if accelerometer measurement is valid (avoids NaN in accelerometer normalization) + if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) + { + // Normalize accelerometer measurement to common precision + recipNorm = sqrt_rsqrt(mulfix(ax, ax, FRAC_Q_ACCEL_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + + mulfix(ay, ay, FRAC_Q_ACCEL_Y, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) + + mulfix(az, az, FRAC_Q_ACCEL_Z, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X), + true, FRAC_Q_GYRO_X); + + ax = mulfix(ax, recipNorm, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X); + ay = mulfix(ay, recipNorm, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y); + az = mulfix(az, recipNorm, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Z); + + // Normalize magnetometer measurement to common precision + recipNorm = sqrt_rsqrt(mulfix(mx, mx, FRAC_Q_MAG_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + + mulfix(my, my, FRAC_Q_MAG_Y, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + + mulfix(mz, mz, FRAC_Q_MAG_Z, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X), + true, FRAC_Q_GYRO_X); + + mx = mulfix(mx, recipNorm, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_MAG_X); + my = mulfix(my, recipNorm, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y); + mz = mulfix(mz, recipNorm, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_MAG_Z); + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2 * mulfix(q0, mx, FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X); + _2q0my = 2 * mulfix(q0, my, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X); + _2q1mx = 2 * mulfix(q1, mx, FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X); + _2q0 = 2 * q0; + _2q1 = 2 * q1; + _2q2 = 2 * q2; + _2q3 = 2 * q3; + _2q0q2 = 2 * mulfix(q0, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + _2q2q3 = 2 * mulfix(q2, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q0 = mulfix(q0, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q1 = mulfix(q0, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q2 = mulfix(q0, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q0q3 = mulfix(q0, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1q1 = mulfix(q1, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1q2 = mulfix(q1, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1q3 = mulfix(q1, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q2q2 = mulfix(q2, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q2q3 = mulfix(q2, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q3q3 = mulfix(q3, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + // Reference direction of Earth's magnetic field + hx = mulfix(mx, q0q0, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_2q0my, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q0mz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mx, q1q1, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q1, my, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X), q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q1, mz, FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X), q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mx, q2q2, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mx, q3q3, FRAC_Q_MAG_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + hy = mulfix(_2q0mx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(my, q0q0, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_2q0mz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q1mx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(my, q1q1, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(my, q2q2, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q2, mz, FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X), q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(my, q3q3, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + _2bx = sqrt_rsqrt(mulfix(hx, hx, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(hy, hy, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), + false, FRAC_Q_GYRO_X); + + _2bz = -mulfix(_2q0mx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q0my, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mz, q0q0, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2q1mx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mz, q1q1, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2q2, my, FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X), q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(mz, q2q2, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(mz, q3q3, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + _4bx = 2 * _2bx; + _4bz = 2 * _2bz; + + // Gradient descent algorithm corrective step + s0 = -mulfix(_2q2, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + mulfix(_2q1, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) - mulfix(mulfix(_2bz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + mulfix((-mulfix(_2bx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, +mulfix(mulfix(_2bx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); FRAC_Q_GYRO_X); + + s1 = mulfix(_2q3, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + mulfix(_2q0, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) - 4 * mulfix(q1, (FRAC_BASE_GYRO_X - 2 * q1q1 - 2 * q2q2 - az), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bz, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_4bz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); + + s2 = -mulfix(_2q0, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) + mulfix(_2q3, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) - 4 * mulfix(q2, (FRAC_BASE_GYRO_X - 2 * q1q1 - 2 * q2q2 - az), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Z, FRAC_Q_GYRO_X) + mulfix((-mulfix(_4bx, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_2bz, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) + mulfix((mulfix(_2bx, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + mulfix((mulfix(_2bx, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mulfix(_4bz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); + + s3 = mulfix(_2q1, (2 * q1q3 - _2q0q2 - ax), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_X, FRAC_Q_GYRO_X) ++ mulfix(_2q2, (2 * q0q1 + _2q2q3 - ay), FRAC_Q_GYRO_X, FRAC_Q_ACCEL_Y, FRAC_Q_GYRO_X) ++ mulfix((-mulfix(_4bx, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)), + (mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, (q1q3 - q0q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + - mx), FRAC_Q_GYRO_X, FRAC_Q_MAG_X, FRAC_Q_GYRO_X) ++ mulfix((-mulfix(_2bx, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X)),(mulfix(_2bx, (q1q2 - q0q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) ++ mulfix(_2bz, (q0q1 + q2q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) +- my), FRAC_Q_GYRO_X, FRAC_Q_MAG_Y, FRAC_Q_GYRO_X) + mulfix(mulfix(_2bx, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), + (mulfix(_2bx, (q0q2 + q1q3), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2), FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + - mz), FRAC_Q_GYRO_X, FRAC_Q_MAG_Z, FRAC_Q_GYRO_X); + + + // Normalize the step magnitude + recipNorm = sqrt_rsqrt(mulfix(s0, s0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(s1, s1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(s2, s2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(s3, s3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), true, FRAC_Q_GYRO_X); + + // Apply normalization + s0 = mulfix(s0, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + s1 = mulfix(s1, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + s2 = mulfix(s2, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + s3 = mulfix(s3, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + // Apply feedback step + qDot1 -= mulfix(beta, s0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + qDot2 -= mulfix(beta, s1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + qDot3 -= mulfix(beta, s2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + qDot4 -= mulfix(beta, s3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + } + + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 / sampleFreq; + q1 += qDot2 / sampleFreq; + q2 += qDot3 / sampleFreq; + q3 += qDot4 / sampleFreq; + + // Normalize quaternion + recipNorm = sqrt_rsqrt(mulfix(q0, q0, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(q1, q1, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(q2, q2, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X) + + mulfix(q3, q3, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X), true, FRAC_Q_GYRO_X); + + q0 = mulfix(q0, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q1 = mulfix(q1, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q2 = mulfix(q2, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + q3 = mulfix(q3, recipNorm, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X, FRAC_Q_GYRO_X); + + // Update the output quaternion pointers + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h new file mode 100644 index 000000000..ef2bfd508 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSDiff.h @@ -0,0 +1,40 @@ + + +// Define the precision of the gyroscope sensors (Gyroscope) data types +#define FRAC_Q_GYRO_X 16 // Precision bits for x-axis gyroscope data +#define FRAC_Q_GYRO_Y 16 // Precision bits for y-axis gyroscope data +#define FRAC_Q_GYRO_Z 16 // Precision bits for z-axis gyroscope data + +#define FRAC_BASE_GYRO_X (1 << FRAC_Q_GYRO_X) // 2^16, base value for x-axis gyroscope +#define FRAC_BASE_GYRO_Y (1 << FRAC_Q_GYRO_Y) // 2^16, base value for y-axis gyroscope +#define FRAC_BASE_GYRO_Z (1 << FRAC_Q_GYRO_Z) // 2^16, base value for z-axis gyroscope + +// Define the precision of the accelerometer sensors (Acceleration) data types +#define FRAC_Q_ACCEL_X 12 // Precision bits for x-axis acceleration data +#define FRAC_Q_ACCEL_Y 12 // Precision bits for y-axis acceleration data +#define FRAC_Q_ACCEL_Z 12 // Precision bits for z-axis acceleration data + +#define FRAC_BASE_ACCEL_X (1 << FRAC_Q_ACCEL_X) // 2^12, base value for x-axis acceleration +#define FRAC_BASE_ACCEL_Y (1 << FRAC_Q_ACCEL_Y) // 2^12, base value for y-axis acceleration +#define FRAC_BASE_ACCEL_Z (1 << FRAC_Q_ACCEL_Z) // 2^12, base value for z-axis acceleration + +// Define the precision of the magnetometer sensors (Magnetometer) data types +#define FRAC_Q_MAG_X 13 // Precision bits for x-axis magnetometer data +#define FRAC_Q_MAG_Y 13 // Precision bits for y-axis magnetometer data +#define FRAC_Q_MAG_Z 15 // Precision bits for z-axis magnetometer data + +#define FRAC_BASE_MAG_X (1 << FRAC_Q_MAG_X) // 2^13, base value for x-axis magnetometer +#define FRAC_BASE_MAG_Y (1 << FRAC_Q_MAG_Y) // 2^13, base value for y-axis magnetometer +#define FRAC_BASE_MAG_Z (1 << FRAC_Q_MAG_Z) // 2^15, base value for z-axis magnetometer + + +//void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, +// int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); + + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + + diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c index 060d5a38a..3272535b3 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c @@ -48,17 +48,19 @@ volatile bmx055xAcceleration beta = (uint8_t)(betaDef*FRAC_BASE); //0.1f // 2 // m=21 1/Yest=1448.0000 Yest=0.0000 Yest_hex=0 // m=22 1/Yest=2048.0000 Yest=0.0000 Yest_hex=0 -int32_t + + +inline __attribute__((always_inline))int32_t mulfix(int32_t x, int32_t y) { -// int32_t result; -// int64_t temp; -// temp = (int64_t)x * (int64_t)y; -// temp += K; -// result = round(temp/FRAC_BASE); -// return result; -// return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; - return ((int64_t)x*y)>>FRAC_Q; + // int32_t result; + // int64_t temp; + // temp = (int64_t)x * (int64_t)y; + // temp += K; + // result = round(temp/FRAC_BASE); + // return result; + // return ((int64_t)(x*y)) > 0 ? ((int64_t)(x*y))>>FRAC_Q : (((int64_t)(x*y))>>FRAC_Q)+1; + return ((int64_t)x*y)>>FRAC_Q; } /* @@ -66,24 +68,24 @@ mulfix(int32_t x, int32_t y) */ int32_t sqrt_rsqrt(int32_t x, int recip) { - if (recip) { - int32_t int_halfx = mulfix(0.5*FRAC_BASE, x); - float fp_y = (float)x/FRAC_BASE; - long i = *(long*)&fp_y; - i = 0x5f3759df - (i>>1); - fp_y = *(float*)&i; - int32_t int_y = fp_y*FRAC_BASE; - int_y = mulfix(int_y, ((int32_t)(1.5f*FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); - return int_y; -// fp_y = fp_y * (1.5f - (halfx * fp_y * fp_y)); -// return fp_y*FRAC_BASE; - } else { - int32_t res = (int32_t)sqrt((double)x)<<(FRAC_Q/2); - if (FRAC_Q%2) - return res*1.414213562; - else - return res; - } + if (recip) { + int32_t int_halfx = mulfix(0.5*FRAC_BASE, x); + float fp_y = (float)x/FRAC_BASE; + long i = *(long*)&fp_y; + i = 0x5f3759df - (i>>1); + fp_y = *(float*)&i; + int32_t int_y = fp_y*FRAC_BASE; + int_y = mulfix(int_y, ((int32_t)(1.5f*FRAC_BASE) - (mulfix(mulfix(int_halfx, int_y), int_y)))); + return int_y; + // fp_y = fp_y * (1.5f - (halfx * fp_y * fp_y)); + // return fp_y*FRAC_BASE; + } else { + int32_t res = (int32_t)sqrt((double)x)<<(FRAC_Q/2); + if (FRAC_Q%2) + return res*1.414213562; + else + return res; + } } //==================================================================================================== @@ -92,16 +94,16 @@ sqrt_rsqrt(int32_t x, int recip) { //--------------------------------------------------------------------------------------------------- // AHRS algorithm update -void +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, - bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, - bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { - int32_t q0 = *q0_ptr; - int32_t q1 = *q1_ptr; - int32_t q2 = *q2_ptr; - int32_t q3 = *q3_ptr; + int32_t q0 = *q0_ptr; + int32_t q1 = *q1_ptr; + int32_t q2 = *q2_ptr; + int32_t q3 = *q3_ptr; int32_t recipNorm; int32_t s0, s1, s2, s3; @@ -126,7 +128,7 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR // Normalise accelerometer measurement recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); -// printf("1: %f\n", (double)recipNorm/FRAC_BASE); + // printf("1: %f\n", (double)recipNorm/FRAC_BASE); ax = mulfix(ax, recipNorm); ay = mulfix(ay, recipNorm); az = mulfix(az, recipNorm); @@ -161,98 +163,98 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR // Reference direction of Earth's magnetic field hx = mulfix(mx, q0q0) - - mulfix(_2q0my, q3) - + mulfix(_2q0mz, q2) - + mulfix(mx, q1q1) - + mulfix(mulfix(_2q1, my), q2) - + mulfix(mulfix(_2q1, mz), q3) - - mulfix(mx, q2q2) - - mulfix(mx, q3q3); + - mulfix(_2q0my, q3) + + mulfix(_2q0mz, q2) + + mulfix(mx, q1q1) + + mulfix(mulfix(_2q1, my), q2) + + mulfix(mulfix(_2q1, mz), q3) + - mulfix(mx, q2q2) + - mulfix(mx, q3q3); hy = mulfix(_2q0mx, q3) - + mulfix(my, q0q0) - - mulfix(_2q0mz, q1) - + mulfix(_2q1mx, q2) - - mulfix(my, q1q1) - + mulfix(my, q2q2) - + mulfix(mulfix(_2q2, mz), q3) - - mulfix(my, q3q3); + + mulfix(my, q0q0) + - mulfix(_2q0mz, q1) + + mulfix(_2q1mx, q2) + - mulfix(my, q1q1) + + mulfix(my, q2q2) + + mulfix(mulfix(_2q2, mz), q3) + - mulfix(my, q3q3); _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); _2bz = -mulfix(_2q0mx, q2) - + mulfix(_2q0my, q1) - + mulfix(mz, q0q0) - + mulfix(_2q1mx, q3) - - mulfix(mz, q1q1) - + mulfix(mulfix(_2q2, my), q3) - - mulfix(mz, q2q2) - + mulfix(mz, q3q3); + + mulfix(_2q0my, q1) + + mulfix(mz, q0q0) + + mulfix(_2q1mx, q3) + - mulfix(mz, q1q1) + + mulfix(mulfix(_2q2, my), q3) + - mulfix(mz, q2q2) + + mulfix(mz, q3q3); _4bx = 2*_2bx; _4bz = 2*_2bz; // Gradient decent algorithm corrective step s0 = - mulfix(_2q2, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q1, (2*q0q1 + _2q2q3 - ay)) - - mulfix(mulfix(_2bz, q2), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix(mulfix(_2bx, q2), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); + + mulfix(_2q1, (2*q0q1 + _2q2q3 - ay)) + - mulfix(mulfix(_2bz, q2), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix(mulfix(_2bx, q2), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); s1 = mulfix(_2q3, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q0, (2*q0q1 + _2q2q3 - ay)) - - 4 * mulfix(q1, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) - + mulfix(mulfix(_2bz, q3), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); + + mulfix(_2q0, (2*q0q1 + _2q2q3 - ay)) + - 4 * mulfix(q1, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) + + mulfix(mulfix(_2bz, q3), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); s2 = - mulfix(_2q0, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q3, (2*q0q1 + _2q2q3 - ay)) - - 4 * mulfix(q2, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) - + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); + + mulfix(_2q3, (2*q0q1 + _2q2q3 - ay)) + - 4 * mulfix(q2, (FRAC_BASE - 2*q1q1 - 2*q2q2 - az)) + + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); s3 = mulfix(_2q1, (2*q1q3 - _2q0q2 - ax)) - + mulfix(_2q2, (2*q0q1 + _2q2q3 - ay)) - + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), ( - mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) - + mulfix(_2bz, (q1q3 - q0q2)) - - mx)) - + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), ( - mulfix(_2bx, (q1q2 - q0q3)) - + mulfix(_2bz, (q0q1 + q2q3)) - - my)) - + mulfix(mulfix(_2bx, q1), ( - mulfix(_2bx, (q0q2 + q1q3)) - + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) - - mz)); + + mulfix(_2q2, (2*q0q1 + _2q2q3 - ay)) + + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), ( + mulfix(_2bx, (DEC2FRAC(0.5) - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) + - mx)) + + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), ( + mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) + - my)) + + mulfix(mulfix(_2bx, q1), ( + mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (DEC2FRAC(0.5) - q1q1 - q2q2)) + - mz)); recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude s0 = mulfix(s0, recipNorm); s1 = mulfix(s1, recipNorm); s2 = mulfix(s2, recipNorm); s3 = mulfix(s3, recipNorm); - + /* 2nd iter normalizaton */ // recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude // s0 = mulfix(s0, recipNorm); @@ -275,46 +277,28 @@ MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularR // Normalise quaternion recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); -// printf("q0=%f, q1=%f, q2=%f, q3=%f, recipNorm=%f\n", -// (double)q0/FRAC_BASE, -// (double)q1/FRAC_BASE, -// (double)q2/FRAC_BASE, -// (double)q3/FRAC_BASE, -// (double)recipNorm/FRAC_BASE); q0 = mulfix(q0, recipNorm); q1 = mulfix(q1, recipNorm); q2 = mulfix(q2, recipNorm); q3 = mulfix(q3, recipNorm); - *q0_ptr = q0; - *q1_ptr = q1; - *q2_ptr = q2; - *q3_ptr = q3; - -// printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", -// DISPLAY_INT(q0), DISPLAY_FRAC(q0), -// DISPLAY_INT(q1), DISPLAY_FRAC(q1), -// DISPLAY_INT(q2), DISPLAY_FRAC(q2), -// DISPLAY_INT(q3), DISPLAY_FRAC(q3)); - - // /* 2nd iter normalization */ - // recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); - // q0 = mulfix(q0, recipNorm); - // q1 = mulfix(q1, recipNorm); - // q2 = mulfix(q2, recipNorm); - // q3 = mulfix(q3, recipNorm); + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; + } //--------------------------------------------------------------------------------------------------- // IMU algorithm update -void +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, - bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { - int32_t q0 = *q0_ptr; - int32_t q1 = *q1_ptr; - int32_t q2 = *q2_ptr; - int32_t q3 = *q3_ptr; + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr) { + int32_t q0 = *q0_ptr; + int32_t q1 = *q1_ptr; + int32_t q2 = *q2_ptr; + int32_t q3 = *q3_ptr; int32_t recipNorm; int32_t s0, s1, s2, s3; int32_t qDot1, qDot2, qDot3, qDot4; @@ -351,30 +335,30 @@ MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngul q3q3 = mulfix(q3, q3); // Gradient decent algorithm corrective step - s0 = mulfix(_4q0, q2q2) - + mulfix(_2q2, ax) - + mulfix(_4q0, q1q1) - - mulfix(_2q1, ay); - s1 = mulfix(_4q1, q3q3) - - mulfix(_2q3, ax) - + 4 * mulfix(q0q0, q1) - - mulfix(_2q0, ay) - - _4q1 - + mulfix(_8q1, q1q1) - + mulfix(_8q1, q2q2) - + mulfix(_4q1, az); + s0 = mulfix(_4q0, q2q2) + + mulfix(_2q2, ax) + + mulfix(_4q0, q1q1) + - mulfix(_2q1, ay); + s1 = mulfix(_4q1, q3q3) + - mulfix(_2q3, ax) + + 4 * mulfix(q0q0, q1) + - mulfix(_2q0, ay) + - _4q1 + + mulfix(_8q1, q1q1) + + mulfix(_8q1, q2q2) + + mulfix(_4q1, az); s2 = 4 * mulfix(q0q0, q2) - + mulfix(_2q0, ax) - + mulfix(_4q2, q3q3) - - mulfix(_2q3, ay) - - _4q2 - + mulfix(_8q2, q1q1) - + mulfix(_8q2, q2q2) - + mulfix(_4q2, az); - s3 = 4 * mulfix(q1q1, q3) - - mulfix(_2q1, ax) - + 4 * mulfix(q2q2, q3) - - mulfix(_2q2, ay); + + mulfix(_2q0, ax) + + mulfix(_4q2, q3q3) + - mulfix(_2q3, ay) + - _4q2 + + mulfix(_8q2, q1q1) + + mulfix(_8q2, q2q2) + + mulfix(_4q2, az); + s3 = 4 * mulfix(q1q1, q3) + - mulfix(_2q1, ax) + + 4 * mulfix(q2q2, q3) + - mulfix(_2q2, ay); recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); // normalise step magnitude s0 = mulfix(s0, recipNorm); s1 = mulfix(s1, recipNorm); @@ -400,8 +384,8 @@ MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngul q1 = mulfix(q1, recipNorm); q2 = mulfix(q2, recipNorm); q3 = mulfix(q3, recipNorm); - q0_ptr = &q0; - q1_ptr = &q1; - q2_ptr = &q2; - q3_ptr = &q3; -} + q0_ptr = &q0; + q1_ptr = &q1; + q2_ptr = &q2; + q3_ptr = &q3; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h index cddd75f92..717f21ab7 100644 --- a/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.h @@ -1,29 +1,29 @@ /* - Authored 2021, Orestis Kaparounakis. +Authored 2021, Orestis Kaparounakis. - All rights reserved. +All rights reserved. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ #include #include #include #include -#define FRAC_Q 10 -#define K (1 << (FRAC_Q - 1)) +#define FRAC_Q 16 #define FRAC_BASE (1<>FRAC_Q) + (((uint32_t)_x)>>(sizeof(int32_t)*FRAC_Q-1))) @@ -31,8 +31,8 @@ #define FORMAT_FIXP "%s%d.%04d" void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, - int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); + int32_t* q0_ptr, int32_t* q1_ptr, int32_t* q2_ptr, int32_t* q3_ptr); int32_t sqrt_rsqrt(int32_t x, int recip); diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c new file mode 100644 index 000000000..07b340691 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.c @@ -0,0 +1,183 @@ +#include "MadgwickAHRSfixi8.h" + +#define sampleFreq 28 // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain +#define FRAC_BASE 16 // 定点数基准 +#define FRAC_Q 4 // 定点数小数部分位数 + +typedef int8_t bmx055xAcceleration; +typedef int8_t bmx055yAcceleration; +typedef int8_t bmx055zAcceleration; +typedef int8_t bmx055xAngularRate; +typedef int8_t bmx055yAngularRate; +typedef int8_t bmx055zAngularRate; +typedef int8_t bmx055xMagneto; +typedef int8_t bmx055yMagneto; +typedef int8_t bmx055zMagneto; + +volatile int8_t beta = (int8_t)(betaDef * FRAC_BASE); // 缩放后的beta + +// 定点乘法运算 +int8_t mulfix(int8_t x, int8_t y) { + return (int16_t)x * (int16_t)y / FRAC_BASE; +} + +// 计算平方根和倒数 +int8_t sqrt_rsqrt(int8_t x, int recip) { + if (recip) { + int8_t int_halfx = mulfix((int8_t)(0.5 * FRAC_BASE), x); + float fp_y = (float)x / FRAC_BASE; + int i = *(int*)&fp_y; + i = 0x5f3759df - (i >> 1); // Fast inverse square root approximation + fp_y = *(float*)&i; + int8_t int_y = (int8_t)(fp_y * FRAC_BASE); + int_y = mulfix(int_y, (int8_t)((1.5f * FRAC_BASE) - mulfix(mulfix(int_halfx, int_y), int_y))); + return int_y; + } else { + int8_t res = (int8_t)sqrt((double)x) << (FRAC_Q / 2); + if (FRAC_Q % 2) { + return mulfix(res, (int8_t)(1.414213562 * FRAC_BASE)); + } else { + return res; + } + } +} + +//==================================================================================================== +// Madgwick算法更新函数 + +void MadgwickAHRSupdate(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + bmx055xMagneto mx, bmx055yMagneto my, bmx055zMagneto mz, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr) { + + int8_t q0 = *q0_ptr; + int8_t q1 = *q1_ptr; + int8_t q2 = *q2_ptr; + int8_t q3 = *q3_ptr; + + int8_t recipNorm; + int8_t s0, s1, s2, s3; + int8_t qDot1, qDot2, qDot3, qDot4; + int8_t hx, hy; + int8_t _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz; + int8_t _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // 当磁力计无效时,使用IMU算法 + if ((mx == 0x0) && (my == 0x0) && (mz == 0x0)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // 根据陀螺仪的变化更新四元数 + qDot1 = (-mulfix(q1, gx) - mulfix(q2, gy) - mulfix(q3, gz)) / 2; + qDot2 = ( mulfix(q0, gx) + mulfix(q2, gz) - mulfix(q3, gy)) / 2; + qDot3 = ( mulfix(q0, gy) - mulfix(q1, gz) + mulfix(q3, gx)) / 2; + qDot4 = ( mulfix(q0, gz) + mulfix(q1, gy) - mulfix(q2, gx)) / 2; + + // 如果加速度计有效,则进行归一化 + if (!((ax == 0x0) && (ay == 0x0) && (az == 0x0))) { + recipNorm = sqrt_rsqrt(mulfix(ax, ax) + mulfix(ay, ay) + mulfix(az, az), true); + ax = mulfix(ax, recipNorm); + ay = mulfix(ay, recipNorm); + az = mulfix(az, recipNorm); + + recipNorm = sqrt_rsqrt(mulfix(mx, mx) + mulfix(my, my) + mulfix(mz, mz), true); + mx = mulfix(mx, recipNorm); + my = mulfix(my, recipNorm); + mz = mulfix(mz, recipNorm); + + // 算法修正 + _2q0mx = 2 * mulfix(q0, mx); + _2q0my = 2 * mulfix(q0, my); + _2q0mz = 2 * mulfix(q0, mz); + _2q1mx = 2 * mulfix(q1, mx); + _2q0 = 2 * q0; + _2q1 = 2 * q1; + _2q2 = 2 * q2; + _2q3 = 2 * q3; + _2q0q2 = 2 * mulfix(q0, q2); + _2q2q3 = 2 * mulfix(q2, q3); + q0q0 = mulfix(q0, q0); + q0q1 = mulfix(q0, q1); + q0q2 = mulfix(q0, q2); + q0q3 = mulfix(q0, q3); + q1q1 = mulfix(q1, q1); + q1q2 = mulfix(q1, q2); + q1q3 = mulfix(q1, q3); + q2q2 = mulfix(q2, q2); + q2q3 = mulfix(q2, q3); + q3q3 = mulfix(q3, q3); + + // 计算地磁方向 + hx = mulfix(mx, q0q0) - mulfix(_2q0my, q3) + mulfix(_2q0mz, q2) + mulfix(mx, q1q1) + + mulfix(mulfix(_2q1, my), q2) + mulfix(mulfix(_2q1, mz), q3) - mulfix(mx, q2q2) - mulfix(mx, q3q3); + hy = mulfix(_2q0mx, q3) + mulfix(my, q0q0) - mulfix(_2q0mz, q1) + mulfix(_2q1mx, q2) - + mulfix(my, q1q1) + mulfix(my, q2q2) + mulfix(mulfix(_2q2, mz), q3) - mulfix(my, q3q3); + _2bx = sqrt_rsqrt(mulfix(hx, hx) + mulfix(hy, hy), false); + _2bz = -mulfix(_2q0mx, q2) + mulfix(_2q0my, q1) + mulfix(mz, q0q0) + mulfix(_2q1mx, q3) - + mulfix(mz, q1q1) + mulfix(mulfix(_2q2, my), q3) - mulfix(mz, q2q2) + mulfix(mz, q3q3); + _4bx = 2 * _2bx; + _4bz = 2 * _2bz; + + // 梯度下降法修正 + s0 = -mulfix(_2q2, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q1, (2 * q0q1 + _2q2q3 - ay)) - + mulfix(mulfix(_2bz, q2), (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + + mulfix((-mulfix(_2bx, q3) + mulfix(_2bz, q1)), (mulfix(_2bx, (q1q2 - q0q3)) + mulfix(_2bz, (q0q1 + q2q3)) - my)) + + mulfix(mulfix(_2bx, q2), (mulfix(_2bx, (q0q2 + q1q3)) + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + s1 = mulfix(_2q3, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q0, (2 * q0q1 + _2q2q3 - ay)) - + 4 * mulfix(q1, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + mulfix(mulfix(_2bz, q3), + (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + + mulfix(mulfix(_2bx, q2) + mulfix(_2bz, q0), (mulfix(_2bx, (q1q2 - q0q3)) + mulfix(_2bz, (q0q1 + q2q3)) - my)) + + mulfix(mulfix(_2bx, q3) - mulfix(_4bz, q1), (mulfix(_2bx, (q0q2 + q1q3)) + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + s2 = -mulfix(_2q0, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q3, (2 * q0q1 + _2q2q3 - ay)) - + 4 * mulfix(q2, (FRAC_BASE - 2 * q1q1 - 2 * q2q2 - az)) + mulfix((-mulfix(_4bx, q2) - mulfix(_2bz, q0)), + (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + + mulfix((mulfix(_2bx, q1) + mulfix(_2bz, q3)), (mulfix(_2bx, (q1q2 - q0q3)) + mulfix(_2bz, (q0q1 + q2q3)) - my)) + + mulfix((mulfix(_2bx, q0) - mulfix(_4bz, q2)), (mulfix(_2bx, (q0q2 + q1q3)) + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + s3 = mulfix(_2q1, (2 * q1q3 - _2q0q2 - ax)) + mulfix(_2q2, (2 * q0q1 + _2q2q3 - ay)) + + mulfix((-mulfix(_4bx, q3) + mulfix(_2bz, q1)), (mulfix(_2bx, (FRAC_BASE / 2 - q2q2 - q3q3)) + + mulfix(_2bz, (q1q3 - q0q2)) - mx)) + mulfix((-mulfix(_2bx, q0) + mulfix(_2bz, q2)), (mulfix(_2bx, (q1q2 - q0q3)) + + mulfix(_2bz, (q0q1 + q2q3)) - my)) + mulfix(mulfix(_2bx, q1), (mulfix(_2bx, (q0q2 + q1q3)) + + mulfix(_2bz, (FRAC_BASE / 2 - q1q1 - q2q2)) - mz)); + recipNorm = sqrt_rsqrt(mulfix(s0, s0) + mulfix(s1, s1) + mulfix(s2, s2) + mulfix(s3, s3), true); + s0 = mulfix(s0, recipNorm); + s1 = mulfix(s1, recipNorm); + s2 = mulfix(s2, recipNorm); + s3 = mulfix(s3, recipNorm); + + // 应用反馈修正 + qDot1 -= mulfix(beta, s0); + qDot2 -= mulfix(beta, s1); + qDot3 -= mulfix(beta, s2); + qDot4 -= mulfix(beta, s3); + } + + // 更新四元数 + q0 += qDot1 / sampleFreq; + q1 += qDot2 / sampleFreq; + q2 += qDot3 / sampleFreq; + q3 += qDot4 / sampleFreq; + + // 归一化四元数 + recipNorm = sqrt_rsqrt(mulfix(q0, q0) + mulfix(q1, q1) + mulfix(q2, q2) + mulfix(q3, q3), true); + q0 = mulfix(q0, recipNorm); + q1 = mulfix(q1, recipNorm); + q2 = mulfix(q2, recipNorm); + q3 = mulfix(q3, recipNorm); + + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} + +//==================================================================================================== +// IMU-only update function + +void MadgwickAHRSupdateIMU(bmx055xAngularRate gx, bmx055yAngularRate gy, bmx055zAngularRate gz, + bmx055xAcceleration ax, bmx055yAcceleration ay, bmx055zAcceleration az, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr) { + // IMU-only update逻辑可以参照上面的MadgwickAHRSupdate编写。 + // 基本上可以移除磁力计相关的计算步骤。 +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h new file mode 100644 index 000000000..b1b3963b9 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MadgwickAHRSfixi8.h @@ -0,0 +1,54 @@ +/* +Authored 2021, Orestis Kaparounakis. + +All rights reserved. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ + +#ifndef MADGWICK_AHRS_FIX_H +#define MADGWICK_AHRS_FIX_H + +#include +#include +#include +#include + +// 定义定点数精度为2位 +#define FRAC_Q 2 +#define K (1 << (FRAC_Q - 1)) +#define FRAC_BASE (1 << FRAC_Q) // FRAC_BASE = 4 +#define DEC2FRAC(_d) ((int8_t)(_d * FRAC_BASE)) // 将小数转换为定点数格式 +#define DISPLAY_INT(_x) ((_x >> FRAC_Q)) // 显示定点数的整数部分 +#define DISPLAY_FRAC(_y) (((FRAC_BASE - 1) & _y) * (10000 / FRAC_BASE)) // 显示定点数的小数部分 +#define FORMAT_FIXP "%s%d.%04d" // 格式化输出定点数 + +// 四元数更新函数 +void MadgwickAHRSupdate(int8_t gx, int8_t gy, int8_t gz, int8_t ax, int8_t ay, int8_t az, int8_t mx, int8_t my, int8_t mz, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr); + +// IMU-only更新函数 +void MadgwickAHRSupdateIMU(int8_t gx, int8_t gy, int8_t gz, int8_t ax, int8_t ay, int8_t az, + int8_t* q0_ptr, int8_t* q1_ptr, int8_t* q2_ptr, int8_t* q3_ptr); + +// 计算平方根或倒数平方根 +int8_t sqrt_rsqrt(int8_t x, int recip); + +// 定点乘法函数 (内联优化) +inline int8_t fixmul(int8_t x, int8_t y) { +// 定点乘法后将结果右移以保持精度 +return ((int16_t)x * y) >> FRAC_Q; +} + +#endif /* MADGWICK_AHRS_FIX_H */ diff --git a/applications/newton/llvm-ir/c-files/Madgwick_global.c b/applications/newton/llvm-ir/c-files/Madgwick_global.c new file mode 100644 index 000000000..9c7f7dfbf --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Madgwick_global.c @@ -0,0 +1,234 @@ +//===================================================================================================== +// MadgwickAHRS.c +//===================================================================================================== +// +// Implementation of Madgwick's IMU and AHRS algorithms. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// 19/02/2012 SOH Madgwick Magnetometer measurement is normalised +// +//===================================================================================================== + +//--------------------------------------------------------------------------------------------------- +// Header files + +#include "MadgwickAHRS_global.h" +#include + +//--------------------------------------------------------------------------------------------------- +// Definitions + +// #define sampleFreq 512.0f // sample frequency in Hz +// #define sampleFreq 100.0f // sample frequency in Hz +//#define sampleFreq 128.0f // sample frequency in Hz +#define sampleFreq 28.0f // sample frequency in Hz +#define betaDef 0.1f // 2 * proportional gain + +//--------------------------------------------------------------------------------------------------- +// Variable definitions + +volatile float beta = betaDef; // 2 * proportional gain (Kp) +volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +float invSqrt(float x); + +//==================================================================================================== +// Functions + +//--------------------------------------------------------------------------------------------------- +// AHRS algorithm update + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float hx, hy; + float _2q0mx, _2q0my, _2q0mz, _2q1mx, _2bx, _2bz, _4bx, _4bz, _2q0, _2q1, _2q2, _2q3, _2q0q2, _2q2q3, q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + + // warpPrint("In MadgwickAHRSupdate!!\n"); + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MadgwickAHRSupdateIMU(gx, gy, gz, ax, ay, az); + return; + } + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0mx = 2.0f * q0 * mx; + _2q0my = 2.0f * q0 * my; + _2q0mz = 2.0f * q0 * mz; + _2q1mx = 2.0f * q1 * mx; + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _2q0q2 = 2.0f * q0 * q2; + _2q2q3 = 2.0f * q2 * q3; + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = mx * q0q0 - _2q0my * q3 + _2q0mz * q2 + mx * q1q1 + _2q1 * my * q2 + _2q1 * mz * q3 - mx * q2q2 - mx * q3q3; + hy = _2q0mx * q3 + my * q0q0 - _2q0mz * q1 + _2q1mx * q2 - my * q1q1 + my * q2q2 + _2q2 * mz * q3 - my * q3q3; + _2bx = sqrt(hx * hx + hy * hy); + _2bz = -_2q0mx * q2 + _2q0my * q1 + mz * q0q0 + _2q1mx * q3 - mz * q1q1 + _2q2 * my * q3 - mz * q2q2 + mz * q3q3; + _4bx = 2.0f * _2bx; + _4bz = 2.0f * _2bz; + + // Gradient decent algorithm corrective step + s0 = -_2q2 * (2.0f * q1q3 - _2q0q2 - ax) + _2q1 * (2.0f * q0q1 + _2q2q3 - ay) - _2bz * q2 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q3 + _2bz * q1) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q2 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s1 = _2q3 * (2.0f * q1q3 - _2q0q2 - ax) + _2q0 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q1 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + _2bz * q3 * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q2 + _2bz * q0) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q3 - _4bz * q1) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s2 = -_2q0 * (2.0f * q1q3 - _2q0q2 - ax) + _2q3 * (2.0f * q0q1 + _2q2q3 - ay) - 4.0f * q2 * (1 - 2.0f * q1q1 - 2.0f * q2q2 - az) + (-_4bx * q2 - _2bz * q0) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (_2bx * q1 + _2bz * q3) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + (_2bx * q0 - _4bz * q2) * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + s3 = _2q1 * (2.0f * q1q3 - _2q0q2 - ax) + _2q2 * (2.0f * q0q1 + _2q2q3 - ay) + (-_4bx * q3 + _2bz * q1) * (_2bx * (0.5f - q2q2 - q3q3) + _2bz * (q1q3 - q0q2) - mx) + (-_2bx * q0 + _2bz * q2) * (_2bx * (q1q2 - q0q3) + _2bz * (q0q1 + q2q3) - my) + _2bx * q1 * (_2bx * (q0q2 + q1q3) + _2bz * (0.5f - q1q1 - q2q2) - mz); + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + + + + +//--------------------------------------------------------------------------------------------------- +// IMU algorithm update + +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az) { + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2q0, _2q1, _2q2, _2q3, _4q0, _4q1, _4q2 ,_8q1, _8q2, q0q0, q1q1, q2q2, q3q3; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz); + qDot2 = 0.5f * (q0 * gx + q2 * gz - q3 * gy); + qDot3 = 0.5f * (q0 * gy - q1 * gz + q3 * gx); + qDot4 = 0.5f * (q0 * gz + q1 * gy - q2 * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2q0 = 2.0f * q0; + _2q1 = 2.0f * q1; + _2q2 = 2.0f * q2; + _2q3 = 2.0f * q3; + _4q0 = 4.0f * q0; + _4q1 = 4.0f * q1; + _4q2 = 4.0f * q2; + _8q1 = 8.0f * q1; + _8q2 = 8.0f * q2; + q0q0 = q0 * q0; + q1q1 = q1 * q1; + q2q2 = q2 * q2; + q3q3 = q3 * q3; + + // Gradient decent algorithm corrective step + s0 = _4q0 * q2q2 + _2q2 * ax + _4q0 * q1q1 - _2q1 * ay; + s1 = _4q1 * q3q3 - _2q3 * ax + 4.0f * q0q0 * q1 - _2q0 * ay - _4q1 + _8q1 * q1q1 + _8q1 * q2q2 + _4q1 * az; + s2 = 4.0f * q0q0 * q2 + _2q0 * ax + _4q2 * q3q3 - _2q3 * ay - _4q2 + _8q2 * q1q1 + _8q2 * q2q2 + _4q2 * az; + s3 = 4.0f * q1q1 * q3 - _2q1 * ax + 4.0f * q2q2 * q3 - _2q2 * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + + // Integrate rate of change of quaternion to yield quaternion + q0 += qDot1 * (1.0f / sampleFreq); + q1 += qDot2 * (1.0f / sampleFreq); + q2 += qDot3 * (1.0f / sampleFreq); + q3 += qDot4 * (1.0f / sampleFreq); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} + +//==================================================================================================== +// END OF CODE +//==================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/Madgwick_global.h b/applications/newton/llvm-ir/c-files/Madgwick_global.h new file mode 100644 index 000000000..fc26b4bec --- /dev/null +++ b/applications/newton/llvm-ir/c-files/Madgwick_global.h @@ -0,0 +1,31 @@ +//===================================================================================================== +// MadgwickAHRS.h +//===================================================================================================== +// +// Implementation of Madgwick's IMU and AHRS algorithms. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== +#ifndef MadgwickAHRS_h +#define MadgwickAHRS_h + +//---------------------------------------------------------------------------------------------------- +// Variable declaration + +// extern volatile float beta; // algorithm gain +// extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz); +void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az); + +#endif +//===================================================================================================== +// End of file +//===================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/MahonyAHRS.c b/applications/newton/llvm-ir/c-files/MahonyAHRS.c new file mode 100644 index 000000000..5fb080a89 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MahonyAHRS.c @@ -0,0 +1,248 @@ +//===================================================================================================== +// MahonyAHRS.c +//===================================================================================================== +// +// Madgwick's implementation of Mayhony's AHRS algorithm. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== + +//--------------------------------------------------------------------------------------------------- +// Header files + +#include "MahonyAHRS.h" +#include + +//--------------------------------------------------------------------------------------------------- +// Definitions + +//#define sampleFreq 512.0f // sample frequency in Hz +#define sampleFreq 128.0f // sample frequency in Hz +#define twoKpDef (2.0f * 0.5f) // 2 * proportional gain +#define twoKiDef (2.0f * 0.0f) // 2 * integral gain + +//--------------------------------------------------------------------------------------------------- +// Variable definitions + +volatile float twoKp = twoKpDef; // 2 * proportional gain (Kp) +volatile float twoKi = twoKiDef; // 2 * integral gain (Ki) +//volatile float q0 = 1.0f, q1 = 0.0f, q2 = 0.0f, q3 = 0.0f; // quaternion of sensor frame relative to auxiliary frame +volatile float integralFBx = 0.0f, integralFBy = 0.0f, integralFBz = 0.0f; // integral error terms scaled by Ki + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +float invSqrt(float x); + +//==================================================================================================== +// Functions + +//--------------------------------------------------------------------------------------------------- +// AHRS algorithm update + +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + + + float recipNorm; + float q0q0, q0q1, q0q2, q0q3, q1q1, q1q2, q1q3, q2q2, q2q3, q3q3; + float hx, hy, bx, bz; + float halfvx, halfvy, halfvz, halfwx, halfwy, halfwz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Use IMU algorithm if magnetometer measurement invalid (avoids NaN in magnetometer normalisation) + if((mx == 0.0f) && (my == 0.0f) && (mz == 0.0f)) { + MahonyAHRSupdateIMU(gx, gy, gz, ax, ay, az, q0_ptr, q1_ptr, q2_ptr, q3_ptr); + return; + } + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Normalise magnetometer measurement + recipNorm = invSqrt(mx * mx + my * my + mz * mz); + mx *= recipNorm; + my *= recipNorm; + mz *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + q0q0 = q0 * q0; + q0q1 = q0 * q1; + q0q2 = q0 * q2; + q0q3 = q0 * q3; + q1q1 = q1 * q1; + q1q2 = q1 * q2; + q1q3 = q1 * q3; + q2q2 = q2 * q2; + q2q3 = q2 * q3; + q3q3 = q3 * q3; + + // Reference direction of Earth's magnetic field + hx = 2.0f * (mx * (0.5f - q2q2 - q3q3) + my * (q1q2 - q0q3) + mz * (q1q3 + q0q2)); + hy = 2.0f * (mx * (q1q2 + q0q3) + my * (0.5f - q1q1 - q3q3) + mz * (q2q3 - q0q1)); + bx = sqrtf(hx * hx + hy * hy); + bz = 2.0f * (mx * (q1q3 - q0q2) + my * (q2q3 + q0q1) + mz * (0.5f - q1q1 - q2q2)); + + // Estimated direction of gravity and magnetic field + halfvx = q1q3 - q0q2; + halfvy = q0q1 + q2q3; + halfvz = q0q0 - 0.5f + q3q3; + halfwx = bx * (0.5f - q2q2 - q3q3) + bz * (q1q3 - q0q2); + halfwy = bx * (q1q2 - q0q3) + bz * (q0q1 + q2q3); + halfwz = bx * (q0q2 + q1q3) + bz * (0.5f - q1q1 - q2q2); + + // Error is sum of cross product between estimated direction and measured direction of field vectors + halfex = (ay * halfvz - az * halfvy) + (my * halfwz - mz * halfwy); + halfey = (az * halfvx - ax * halfvz) + (mz * halfwx - mx * halfwz); + halfez = (ax * halfvy - ay * halfvx) + (mx * halfwy - my * halfwx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; + *q0_ptr = q0; + *q1_ptr = q1; + *q2_ptr = q2; + *q3_ptr = q3; +} + +//--------------------------------------------------------------------------------------------------- +// IMU algorithm update + +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + float q0 = *q0_ptr; + float q1 = *q1_ptr; + float q2 = *q2_ptr; + float q3 = *q3_ptr; + float recipNorm; + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) { + + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = q1 * q3 - q0 * q2; + halfvy = q0 * q1 + q2 * q3; + halfvz = q0 * q0 - 0.5f + q3 * q3; + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) { + integralFBx += twoKi * halfex * (1.0f / sampleFreq); // integral error scaled by Ki + integralFBy += twoKi * halfey * (1.0f / sampleFreq); + integralFBz += twoKi * halfez * (1.0f / sampleFreq); + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * (1.0f / sampleFreq)); // pre-multiply common factors + gy *= (0.5f * (1.0f / sampleFreq)); + gz *= (0.5f * (1.0f / sampleFreq)); + qa = q0; + qb = q1; + qc = q2; + q0 += (-qb * gx - qc * gy - q3 * gz); + q1 += (qa * gx + qc * gz - q3 * gy); + q2 += (qa * gy - qb * gz + q3 * gx); + q3 += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); + q0 *= recipNorm; + q1 *= recipNorm; + q2 *= recipNorm; + q3 *= recipNorm; +} + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root + +float invSqrt(float x) { + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} + +//==================================================================================================== +// END OF CODE +//==================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/MahonyAHRS.h b/applications/newton/llvm-ir/c-files/MahonyAHRS.h new file mode 100644 index 000000000..158e366e5 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/MahonyAHRS.h @@ -0,0 +1,34 @@ +//===================================================================================================== +// MahonyAHRS.h +//===================================================================================================== +// +// Madgwick's implementation of Mayhony's AHRS algorithm. +// See: http://www.x-io.co.uk/node/8#open_source_ahrs_and_imu_algorithms +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +// +//===================================================================================================== +#ifndef MahonyAHRS_h +#define MahonyAHRS_h + +//---------------------------------------------------------------------------------------------------- +// Variable declaration + +// extern volatile float twoKp; // 2 * proportional gain (Kp) +// extern volatile float twoKi; // 2 * integral gain (Ki) +// extern volatile float q0, q1, q2, q3; // quaternion of sensor frame relative to auxiliary frame + +//--------------------------------------------------------------------------------------------------- +// Function declarations + +void MahonyAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); +void MahonyAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr); + +#endif +//===================================================================================================== +// End of file +//===================================================================================================== diff --git a/applications/newton/llvm-ir/c-files/fft b/applications/newton/llvm-ir/c-files/fft new file mode 100755 index 0000000000000000000000000000000000000000..69604a9204e38f463a61a3d41a00d77bff390d7a GIT binary patch literal 16400 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 +#include +#include + +// 复数结构体 +typedef struct { + double real; + double imag; +} Complex; + +// 复数相加 +Complex complex_add(Complex a, Complex b) { + Complex result; + result.real = a.real + b.real; + result.imag = a.imag + b.imag; + return result; +} + +// 复数相减 +Complex complex_sub(Complex a, Complex b) { + Complex result; + result.real = a.real - b.real; + result.imag = a.imag - b.imag; + return result; +} + +// 复数相乘 +Complex complex_mul(Complex a, Complex b) { + Complex result; + result.real = a.real * b.real - a.imag * b.imag; + result.imag = a.real * b.imag + a.imag * b.real; + return result; +} + +// 快速傅里叶变换 +void fft(Complex *x, int n) { + if (n <= 1) return; + + // 将输入数组分成偶数和奇数部分 + Complex *even = (Complex *)malloc(n / 2 * sizeof(Complex)); + Complex *odd = (Complex *)malloc(n / 2 * sizeof(Complex)); + for (int i = 0; i < n / 2; i++) { + even[i] = x[i * 2]; + odd[i] = x[i * 2 + 1]; + } + + // 递归计算子部分 + fft(even, n / 2); + fft(odd, n / 2); + + // 合并子部分结果 + for (int k = 0; k < n / 2; k++) { + double t = -2 * M_PI * k / n; + Complex w = { cos(t), sin(t) }; + Complex t_mul = complex_mul(w, odd[k]); + x[k] = complex_add(even[k], t_mul); + x[k + n / 2] = complex_sub(even[k], t_mul); + } + + free(even); + free(odd); +} + +// 打印复数数组 +void print_complex_array(Complex *x, int n) { + for (int i = 0; i < n; i++) { + printf("(%f, %f)\n", x[i].real, x[i].imag); + } +} + +int main() { + // 输入序列的长度(必须是2的幂次) + int n = 8; + + // 创建复数数组并初始化为输入信号 + Complex x[n]; + for (int i = 0; i < n; i++) { + x[i].real = i; + x[i].imag = 0.0; + } + + // 打印输入信号 + printf("Input:\n"); + print_complex_array(x, n); + + // 计算FFT + fft(x, n); + + // 打印FFT结果 + printf("Output:\n"); + print_complex_array(x, n); + + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/fixmul_test.c b/applications/newton/llvm-ir/c-files/fixmul_test.c new file mode 100644 index 000000000..adb2a5f7c --- /dev/null +++ b/applications/newton/llvm-ir/c-files/fixmul_test.c @@ -0,0 +1,18 @@ +#include +#include +#include + +#define FRAC_Q 10 // 假设定点小数使用16位 + +int32_t fixmul(int32_t x, int32_t y) { + return ((int64_t)x * y) >> FRAC_Q; +} + +int main() { + int32_t a = 32767; + int32_t b = 32767; + for (long i = 0; i < 100000000; i++) { + fixmul(a, b); + } + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/float_mul_test.c b/applications/newton/llvm-ir/c-files/float_mul_test.c new file mode 100644 index 000000000..08928ad93 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/float_mul_test.c @@ -0,0 +1,16 @@ +#include +#include +#include + +float float_mul(float x, float y) { + return x * y; +} + +int main() { + float a = 32767.0f; // 浮点测试也使用相同的数值范围 + float b = 32767.0f; + for (long i = 0; i < 100000000; i++) { + float_mul(a, b); + } + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.c b/applications/newton/llvm-ir/c-files/floating_point_operations.c new file mode 100644 index 000000000..33739338c --- /dev/null +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.c @@ -0,0 +1,451 @@ + +#include +#include "floating_point_operations.h" +#include +#include +#include + +// Function to perform basic floating point operations +// Function to perform addition +// float perform_addition(float a, float b) { +// +// return a + b; +// } +// +// +// +////// Function to perform subtraction +// float perform_subtraction(float a, float b) { +// return a - b; +// } +// +// float perform_constant_subtraction(float a) { +// return 1.5f - a; +// } +// +// float perform_constant_subtraction2(float a) { +// return 0.5 - a; +// } + +//////// +////////// Function to perform multiplication +// float perform_multiplication(float a, float b) { +// return a * b; +// } +//// +// float perform_division(float a, float b) { +// return a / b; +// }c + +// float +// perform_constant_multiplication(float a) +//{ +// return 0.5*a; +// } +//// +// float +// perform_constant_multiplication2(float a) +// { +// return 0.51*a; +// } +// +// float +// perform_constant_multiplication3(float a) +// { +// return 2.0f*a; +// } + +// float +// perform_constant_multiplication4(float a) +// { +// return a*a; +// } +// +// +// +// float +// perform_constant_multiplication3(float a) +// { +// return 2*a; +// } + +// float +// perform_constant_multiplication(float a) +// { +// return 0.5 *a; +// } + +// 2x2矩阵乘法 +// void multiply_matrices(float A[2][2], float B[2][2], float C[2][2]) { +// // 简化的矩阵乘法实现,不使用循环 +// C[0][0] = A[0][0] * B[0][0] + A[0][1] * B[1][0]; +// C[0][1] = A[0][0] * B[0][1] + A[0][1] * B[1][1]; +// C[1][0] = A[1][0] * B[0][0] + A[1][1] * B[1][0]; +// C[1][1] = A[1][0] * B[0][1] + A[1][1] * B[1][1]; +// } + +// 2x2矩阵加法 +// void add_matrices() { +// // 直接对应元素相加 +// float A[2][2] = {{1, 2}, {3, 4}}; +// float B[2][2] = {{5, 6}, {7, 8}}; +// float C[2][2]; // 结果矩阵 +// +// //矩阵加法 +// C[0][0] = A[0][0] + B[0][0]; +// C[0][1] = A[0][1] + B[0][1]; +// C[1][0] = A[1][0] + B[1][0]; +// C[1][1] = A[1][1] + B[1][1]; +// } + +// float +// perform_constant_division(float a) +//{ +// return a / 0.21; +// } + +// float compute_sin(float angle) { +// return sinf(angle); +// } + +// float compute_cos(float angle) { +// return cosf(angle); +// } + +// float compute_sqrt(float number) { +// return sqrtf(number); +// } + +// double compute_pow_double( double base, double exponent) { +// return powf(base, exponent); +// } + +// float compute_pow(float base, float exponent) { +// return powf(base, exponent); +// } + +// double compute_sqrt_double(double number) { +// double number2 = number +2.14; +// return sqrt(number2); +// +// } + +// 交换两个指针指向的浮点数值 +// void swap_float_values(float *a, float *b) { +// float temp = *a; +// *a = *b; +// *b = temp; +//} + +// +// float +// perform_operations(float a, float b, float c, float d) +//{ +// float result = a * b; // First multiplication +// result = result + c; // Addition +// result = result - d; // Subtraction +// result = result * 1.5f; // Second multiplication with a constant +// return result; +// } + +// float computePolynomial(float x) { +// float result = 0.0; +// for (int i = 0; i < 1000; ++i) { +// result += 0.3 * x * x * x + 0.2 * x * x + 0.5 * x + 0.7; +// } +// return result; +// } +// #define PI 3.14159265358979323846 + +// void fft(float* real, float* imag, int n) { +// int i, j, k, m, m_max; +// float t_real, t_imag, u_real, u_imag, w_real, w_imag, theta; +// +// // Bit-reverse +// for (i = 0, j = 0; i < n; ++i) { +// if (j > i) { +// t_real = real[j]; +// real[j] = real[i]; +// real[i] = t_real; +// t_imag = imag[j]; +// imag[j] = imag[i]; +// imag[i] = t_imag; +// } +// for (k = n >> 1; (j ^= k) < k; k >>= 1); +// } +// +// // FFT +// for (m = 1; (m_max = m << 1) <= n; m = m_max) { +// theta = -2.0 * PI / m_max; +// w_real = 1.0; +// w_imag = 0.0; +// u_real = cos(theta); +// u_imag = sin(theta); +// +// for (k = 0; k < m; ++k) { +// for (i = k; i < n; i += m_max) { +// j = i + m; +// t_real = w_real * real[j] - w_imag * imag[j]; +// t_imag = w_real * imag[j] + w_imag * real[j]; +// real[j] = real[i] - t_real; +// imag[j] = imag[i] - t_imag; +// real[i] += t_real; +// imag[i] += t_imag; +// } +// t_real = w_real; +// w_real = t_real * u_real - w_imag * u_imag; +// w_imag = t_real * u_imag + w_imag * u_real; +// } +// } +// } + +// double polynomial(double x) { +// // Evaluate a polynomial: P(x) = 3x^4 - 5x^3 + 2x^2 - x + 7 +// double result = 5.51*x*x*x*x + 0.21*x*x*x + 0.3*x*x + x; +//// return result; +// //double result = x*x*x*x; +// return result; +//} + +// double +// polynomial(double x) +//{ +// // Evaluate a more complex polynomial +// double result = 0.0; +// +// // High-order terms of the polynomial +// for (int i = 1; i <= 10; i++) +// { +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 7.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += pow(x, 16) * i; +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 7.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// } +// +// // Nested operations to increase complexity +// for (int i = 1; i <= 10; i++) +// { +// for (int j = 1; j <= 100; j++) +// { +// result += 8.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += 7.51 * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x * x; +// result += pow(x, 4) * i * j; +// result += 8.51 * x * x * x * x * x * x ; +// } +// } +// return result; +// } +// + +// +// return result; +//} + +// 计算线性回归参数 a (斜率) 和 b (截距) +// void linear_regression(int n, double x[], double y[], double *a, double *b) { +// double sum_x = 0, sum_y = 0, sum_xy = 0, sum_xx = 0; +// int i; +// +// for (i = 0; i < n; i++) { +// sum_x += x[i]; +// sum_y += y[i]; +// sum_xy += x[i] * y[i]; +// sum_xx += x[i] * x[i]; +// } +// +// // 计算斜率 (a) 和截距 (b) +// *a = (n * sum_xy - sum_x * sum_y) / (n * sum_xx - sum_x * sum_x); +// *b = (sum_y - (*a) * sum_x) / n; +//} + +//// 对两个 double 进行加法 +// double add(double a, double b) { +// return a + b; +// } +// +//// 对两个 double 进行乘法 +// double multiply(double a, double b) { +// return a * b; +// } +// +//// 对 double 进行幂运算 +// double power(double base, double exponent) { +// return pow(base, exponent); +// } +// +//// double_add_test 函数的重构版本 +// void double_add_test(double* leftOp, double* rightOp, double* result, size_t iteration_num) { +// for (size_t idx = 0; idx < iteration_num; idx++) { +// double sum = add(leftOp[idx], rightOp[idx]); +// result[idx] = sum; +// +// double x = add(leftOp[idx], 12.789); +// double y = add(rightOp[idx], 15.653); +// double z = add(x, y); +// double pow_result = power(z, 2.5); +// result[idx] = multiply(result[idx], pow_result); +// } +// return; +// } + +// float updateQuaternion_w(float q0, float q1, float q2, float q3, float ax, float ay, float az, float gx, float gy, float gz, float beta, float deltaT) { +// float norm = sqrt(ax * ax + ay * ay + az * az); +// if (norm == 0.0f) return q0; +// norm = 1.0f / norm; +// ax *= norm; +// ay *= norm; +// az *= norm; +// +// float f1 = 2.0f * (q1 * q3 - q0 * q2) - ax; +// float f2 = 2.0f * (q0 * q1 + q2 * q3) - ay; +// float f3 = 1.0f - 2.0f * (q1 * q1 + q2 * q2) - az; +// float J_14or21 = 2.0f * q1; +// +// float step0 = J_14or21 * f2 - 2.0f * q2 * f1; +// norm = sqrt(step0 * step0); +// norm = 1.0f / norm; +// step0 *= norm; +// +// float qDot0 = 0.5f * (-q1 * gx - q2 * gy - q3 * gz) - beta * step0; +// q0 += qDot0 * deltaT; +// +// norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); +// norm = 1.0f / norm; +// return q0 * norm; +// } +// +// float updateQuaternion_x(float q0, float q1, float q2, float q3, float ax, float ay, float az, float gx, float gy, float gz, float beta, float deltaT) { +// float norm = sqrt(ax * ax + ay * ay + az * az); +// if (norm == 0.0f) return q1; +// norm = 1.0f / norm; +// ax *= norm; +// ay *= norm; +// az *= norm; +// +// float f1 = 2.0f * (q1 * q3 - q0 * q2) - ax; +// float f2 = 2.0f * (q0 * q1 + q2 * q3) - ay; +// float f3 = 1.0f - 2.0f * (q1 * q1 + q2 * q2) - az; +// float J_14or21 = 2.0f * q1; +// +// float step1 = 2.0f * q3 * f1 + J_14or21 * f2 - 2.0f * (q0 * f2 + q2 * f1); +// norm = sqrt(step1 * step1); +// norm = 1.0f / norm; +// step1 *= norm; +// +// float qDot1 = 0.5f * (q0 * gx + q2 * gz - q3 * gy) - beta * step1; +// q1 += qDot1 * deltaT; +// +// norm = sqrt(q0 * q0 + q1 * q1 + q2 * q2 + q3 * q3); +// norm = 1.0f / norm; +// return q1 * norm; +// } +// +// float normalizeAccelerometer(float ax, float ay, float az) { +// float norm = sqrt(ax * ax + ay * ay + az * az); +// return norm == 0.0f ? 0.0f : 1.0f / norm; +// } +// +// float computeGradientStep0(float q0, float q1, float q2, float q3, float ax, float ay, float az) { +// float f1 = 2.0f * (q1 * q3 - q0 * q2) - ax; +// float f2 = 2.0f * (q0 * q1 + q2 * q3) - ay; +// float f3 = 1.0f - 2.0f * (q1 * q1 + q2 * q2) - az; +// float J_14or21 = 2.0f * q1; +// +// float step0 = J_14or21 * f2 - 2.0f * q2 * f1; +// float norm = sqrt(step0 * step0); +// norm = 1.0f / norm; +// return step0 * norm; +// } + +// typedef uint64_t float64; + +// void float64_add(float64 a, float64 b, float64 *result) { +// // Extract components of first number +// int sign1 = (a >> 63) & 1; +// int exponent1 = (a >> 52) & 0x7FF; +// uint64_t mantissa1 = a & 0xFFFFFFFFFFFFF; +// +// // Normalize mantissa to have an implicit leading one if it's not zero +// if (exponent1 != 0) mantissa1 |= 0x10000000000000; +// +// // Extract components of second number +// int sign2 = (b >> 63) & 1; +// int exponent2 = (b >> 52) & 0x7FF; +// uint64_t mantissa2 = b & 0xFFFFFFFFFFFFF; +// +// if (exponent2 != 0) mantissa2 |= 0x10000000000000; +// +// // Align mantissas +// if (exponent1 > exponent2) { +// mantissa2 >>= (exponent1 - exponent2); +// exponent2 = exponent1; +// } else if (exponent1 < exponent2) { +// mantissa1 >>= (exponent2 - exponent1); +// exponent1 = exponent2; +// } +// +// // Add mantissas (ignore signs for simplicity in this example) +// uint64_t mantissa = mantissa1 + mantissa2; +// +// // Normalize result +// while (mantissa >= (1ULL << 53)) { +// mantissa >>= 1; +// exponent1++; +// } +// +// // Avoid double normalization +// if (exponent1 >= 0x7FF) { +// exponent1 = 0x7FF; +// mantissa = 0; // Produce infinity if overflow +// } else if (mantissa >= (1ULL << 52)) { +// mantissa &= ~(1ULL << 52); +// } +// +// // Combine components back into a double +// *result = ((uint64_t)sign1 << 63) | ((uint64_t)exponent1 << 52) | (mantissa & 0xFFFFFFFFFFFFF); +// } + +// float invSqrt(float x) { +// float halfx = 0.5f * x; +// float y = x; +// //#pragma unsupported +// long i = *(long*)&y; +// i = 0x5f3759df - (i>>1); +// y = *(float*)&i; +// //#end +// y = y * (1.5f - (halfx * y * y)); +// return y; +// } + +// void +// MadgwickAHRSupdateIMU(float ax, +// float * q0_ptr) +//{ +// float q0 = *q0_ptr; +// } +// +// void +// MadgwickAHRSupdate(float gx, float ax, +// float * q0_ptr) +//{ +// float q0 = *q0_ptr; +// if((ax == 0.0f)) { +// MadgwickAHRSupdateIMU(ax, q0_ptr); +// return; +// } +// } + +// void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, +// float* q0_ptr, float* q1_ptr, float* q2_ptr, float* q3_ptr) { + +void +MadgwickAHRSupdate(float mx, + float * q0_ptr) +{ + float q0 = *q0_ptr; + float _2q0mx; + + _2q0mx = 2.0f * q0 * mx; +} diff --git a/applications/newton/llvm-ir/c-files/floating_point_operations.h b/applications/newton/llvm-ir/c-files/floating_point_operations.h new file mode 100644 index 000000000..5ad4901c5 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/floating_point_operations.h @@ -0,0 +1,23 @@ +// +// Created by 13862 on 2024/7/20. +// + +#ifndef COSENSE_FLOATING_POINT_OPERATIONS_H +#define COSENSE_FLOATING_POINT_OPERATIONS_H + + +//declare global variables +//extern float param1_global; +//extern float param2_global; + +float perform_addition(float a, float b); + +float perform_subtraction(float a, float b); +float perform_multiplication(float a, float b); +float perform_division(float a, float b); + + + + + +#endif // COSENSE_FLOATING_POINT_OPERATIONS_H diff --git a/applications/newton/llvm-ir/c-files/kalman_core.c b/applications/newton/llvm-ir/c-files/kalman_core.c new file mode 100644 index 000000000..cd048607e --- /dev/null +++ b/applications/newton/llvm-ir/c-files/kalman_core.c @@ -0,0 +1,812 @@ +/** +* Authored by Michael Hamer (http://www.mikehamer.info), June 2016 +* Thank you to Mark Mueller (www.mwm.im) for advice during implementation, +* and for derivation of the original filter in the below-cited paper. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================================ +* +* The Kalman filter implemented in this file is based on the papers: +* +* "Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation" +* http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=7139421 +* +* and +* +* "Covariance Correction Step for Kalman Filtering with an Attitude" +* http://arc.aiaa.org/doi/abs/10.2514/1.G000848 +* +* Academic citation would be appreciated. +* +* BIBTEX ENTRIES: + @INPROCEEDINGS{MuellerHamerUWB2015, + author = {Mueller, Mark W and Hamer, Michael and D’Andrea, Raffaello}, + title = {Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation}, + booktitle = {2015 IEEE International Conference on Robotics and Automation (ICRA)}, + year = {2015}, + month = {May}, + pages = {1730-1736}, + doi = {10.1109/ICRA.2015.7139421}, + ISSN = {1050-4729}} + + @ARTICLE{MuellerCovariance2016, + author={Mueller, Mark W and Hehn, Markus and D’Andrea, Raffaello}, + title={Covariance Correction Step for Kalman Filtering with an Attitude}, + journal={Journal of Guidance, Control, and Dynamics}, + pages={1--7}, + year={2016}, + publisher={American Institute of Aeronautics and Astronautics}} +* +* ============================================================================ +* +* MAJOR CHANGELOG: +* 2016.06.28, Mike Hamer: Initial version +* 2019.04.12, Kristoffer Richardsson: Refactored, separated kalman implementation from OS related functionality +*/ + +#include "kalman_core.h" +#include "cfassert.h" +#include "autoconf.h" + +#include "physicalConstants.h" + +#include "math3d.h" +#include "static_mem.h" + + // #define DEBUG_STATE_CHECK + + // the reversion of pitch and roll to zero +#ifdef CONFIG_DECK_LOCO_2D_POSITION +#define ROLLPITCH_ZERO_REVERSION (0.0f) +#else +#define ROLLPITCH_ZERO_REVERSION (0.001f) +#endif + + + /** +* Supporting and utility functions + */ + +#ifdef DEBUG_STATE_CHECK +static void assertStateNotNaN(const kalmanCoreData_t* this) { + if ((isnan(this->S[KC_STATE_X])) || + (isnan(this->S[KC_STATE_Y])) || + (isnan(this->S[KC_STATE_Z])) || + (isnan(this->S[KC_STATE_PX])) || + (isnan(this->S[KC_STATE_PY])) || + (isnan(this->S[KC_STATE_PZ])) || + (isnan(this->S[KC_STATE_D0])) || + (isnan(this->S[KC_STATE_D1])) || + (isnan(this->S[KC_STATE_D2])) || + (isnan(this->q[0])) || + (isnan(this->q[1])) || + (isnan(this->q[2])) || + (isnan(this->q[3]))) + { + ASSERT(false); + } + + for(int i=0; iP[i][j])) + { + ASSERT(false); + } + } + } + } +#else +static void assertStateNotNaN(const kalmanCoreData_t* this) + { + return; + } +#endif + + + // The bounds on the covariance, these shouldn't be hit, but sometimes are... why? +#define MAX_COVARIANCE (100) +#define MIN_COVARIANCE (1e-6f) + + // Small number epsilon, to prevent dividing by zero +#define EPS (1e-6f) + + void kalmanCoreDefaultParams(kalmanCoreParams_t* params) + { + // Initial variances, uncertain of position, but know we're stationary and roughly flat + params->stdDevInitialPosition_xy = 100; + params->stdDevInitialPosition_z = 1; + params->stdDevInitialVelocity = 0.01; + params->stdDevInitialAttitude_rollpitch = 0.01; + params->stdDevInitialAttitude_yaw = 0.01; + + params->procNoiseAcc_xy = 0.5f; + params->procNoiseAcc_z = 1.0f; + params->procNoiseVel = 0; + params->procNoisePos = 0; + params->procNoiseAtt = 0; + params->measNoiseBaro = 2.0f; // meters + params->measNoiseGyro_rollpitch = 0.1f; // radians per second + params->measNoiseGyro_yaw = 0.1f; // radians per second + + params->initialX = 0.0; + params->initialY = 0.0; + params->initialZ = 0.0; + + // Initial yaw of the Crazyflie in radians. + // 0 --- facing positive X + // PI / 2 --- facing positive Y + // PI --- facing negative X + // 3 * PI / 2 --- facing negative Y + params->initialYaw = 0.0; + } + + void kalmanCoreInit(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs) + { + // Reset all data to 0 (like upon system reset) + memset(this, 0, sizeof(kalmanCoreData_t)); + + this->S[KC_STATE_X] = params->initialX; + this->S[KC_STATE_Y] = params->initialY; + this->S[KC_STATE_Z] = params->initialZ; + // this->S[KC_STATE_PX] = 0; + // this->S[KC_STATE_PY] = 0; + // this->S[KC_STATE_PZ] = 0; + // this->S[KC_STATE_D0] = 0; + // this->S[KC_STATE_D1] = 0; + // this->S[KC_STATE_D2] = 0; + + // reset the attitude quaternion + this->initialQuaternion[0] = arm_cos_f32(params->initialYaw / 2); + this->initialQuaternion[1] = 0.0; + this->initialQuaternion[2] = 0.0; + this->initialQuaternion[3] = arm_sin_f32(params->initialYaw / 2); + for (int i = 0; i < 4; i++) { this->q[i] = this->initialQuaternion[i]; } + + // then set the initial rotation matrix to the identity. This only affects + // the first prediction step, since in the finalization, after shifting + // attitude errors into the attitude state, the rotation matrix is updated. + for(int i=0; i<3; i++) { for(int j=0; j<3; j++) { this->R[i][j] = i==j ? 1 : 0; }} + + for (int i=0; i< KC_STATE_DIM; i++) { + for (int j=0; j < KC_STATE_DIM; j++) { + this->P[i][j] = 0; // set covariances to zero (diagonals will be changed from zero in the next section) + } + } + + // initialize state variances + this->P[KC_STATE_X][KC_STATE_X] = powf(params->stdDevInitialPosition_xy, 2); + this->P[KC_STATE_Y][KC_STATE_Y] = powf(params->stdDevInitialPosition_xy, 2); + this->P[KC_STATE_Z][KC_STATE_Z] = powf(params->stdDevInitialPosition_z, 2); + + this->P[KC_STATE_PX][KC_STATE_PX] = powf(params->stdDevInitialVelocity, 2); + this->P[KC_STATE_PY][KC_STATE_PY] = powf(params->stdDevInitialVelocity, 2); + this->P[KC_STATE_PZ][KC_STATE_PZ] = powf(params->stdDevInitialVelocity, 2); + + this->P[KC_STATE_D0][KC_STATE_D0] = powf(params->stdDevInitialAttitude_rollpitch, 2); + this->P[KC_STATE_D1][KC_STATE_D1] = powf(params->stdDevInitialAttitude_rollpitch, 2); + this->P[KC_STATE_D2][KC_STATE_D2] = powf(params->stdDevInitialAttitude_yaw, 2); + + this->Pm.numRows = KC_STATE_DIM; + this->Pm.numCols = KC_STATE_DIM; + this->Pm.pData = (float*)this->P; + + this->baroReferenceHeight = 0.0; + + this->isUpdated = false; + this->lastPredictionMs = nowMs; + this->lastProcessNoiseUpdateMs = nowMs; + } + + void kalmanCoreScalarUpdate(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, float error, float stdMeasNoise) + { + // The Kalman gain as a column vector + NO_DMA_CCM_SAFE_ZERO_INIT static float K[KC_STATE_DIM]; + static arm_matrix_instance_f32 Km = {KC_STATE_DIM, 1, (float *)K}; + + // Temporary matrices for the covariance updates + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float tmpNN1d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN1m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN1d}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float tmpNN2d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN2m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN2d}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float tmpNN3d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN3m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN3d}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float HTd[KC_STATE_DIM * 1]; + static arm_matrix_instance_f32 HTm = {KC_STATE_DIM, 1, HTd}; + + NO_DMA_CCM_SAFE_ZERO_INIT __attribute__((aligned(4))) static float PHTd[KC_STATE_DIM * 1]; + static arm_matrix_instance_f32 PHTm = {KC_STATE_DIM, 1, PHTd}; + + ASSERT(Hm->numRows == 1); + ASSERT(Hm->numCols == KC_STATE_DIM); + + // ====== INNOVATION COVARIANCE ====== + + mat_trans(Hm, &HTm); + mat_mult(&this->Pm, &HTm, &PHTm); // PH' + float R = stdMeasNoise*stdMeasNoise; + float HPHR = R; // HPH' + R + for (int i=0; ipData[i]*PHTd[i]; // this obviously only works if the update is scalar (as in this function) + } + ASSERT(!isnan(HPHR)); + + // ====== MEASUREMENT UPDATE ====== + // Calculate the Kalman gain and perform the state update + for (int i=0; iS[i] = this->S[i] + K[i] * error; // state update + } + assertStateNotNaN(this); + + // ====== COVARIANCE UPDATE ====== + mat_mult(&Km, Hm, &tmpNN1m); // KH + for (int i=0; iPm, &tmpNN3m); // (KH - I)*P + mat_mult(&tmpNN3m, &tmpNN2m, &this->Pm); // (KH - I)*P*(KH - I)' + assertStateNotNaN(this); + // add the measurement variance and ensure boundedness and symmetry + // TODO: Why would it hit these bounds? Needs to be investigated. + for (int i=0; iP[i][j] + 0.5f*this->P[j][i] + v; // add measurement noise + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + + assertStateNotNaN(this); + + this->isUpdated = true; + } + + void kalmanCoreUpdateWithPKE(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, arm_matrix_instance_f32 *Km, arm_matrix_instance_f32 *P_w_m, float error) + { + // kalman filter update with weighted covariance matrix P_w_m, kalman gain Km, and innovation error + // Temporary matrices for the covariance updates + static float tmpNN1d[KC_STATE_DIM][KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN1m = {KC_STATE_DIM, KC_STATE_DIM, (float *)tmpNN1d}; + for (int i=0; iS[i] = this->S[i] + Km->pData[i] * error; + } + // ====== COVARIANCE UPDATE ====== // + mat_mult(Km, Hm, &tmpNN1m); // KH, the Kalman Gain and H are the updated Kalman Gain and H + mat_scale(&tmpNN1m, -1.0f, &tmpNN1m); // I-KH + for (int i=0; iP, Ppo, sizeof(this->P)); + + assertStateNotNaN(this); + + for (int i=0; iP[i][j] + 0.5f*this->P[j][i]; + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + assertStateNotNaN(this); + + this->isUpdated = true; + } + + void kalmanCoreUpdateWithBaro(kalmanCoreData_t *this, const kalmanCoreParams_t *params, float baroAsl, bool quadIsFlying) + { + float h[KC_STATE_DIM] = {0}; + arm_matrix_instance_f32 H = {1, KC_STATE_DIM, h}; + + h[KC_STATE_Z] = 1; + + if (!quadIsFlying || this->baroReferenceHeight < 1) { + //TODO: maybe we could track the zero height as a state. Would be especially useful if UWB anchors had barometers. + this->baroReferenceHeight = baroAsl; + } + + float meas = (baroAsl - this->baroReferenceHeight); + kalmanCoreScalarUpdate(this, &H, meas - this->S[KC_STATE_Z], params->measNoiseBaro); + } + + static void predictDt(kalmanCoreData_t* this, Axis3f *acc, Axis3f *gyro, float dt, bool quadIsFlying) + { + /* Here we discretize (euler forward) and linearise the quadrocopter dynamics in order + * to push the covariance forward. + * + * QUADROCOPTER DYNAMICS (see paper): + * + * \dot{x} = R(I + [[d]])p + * \dot{p} = f/m * e3 - [[\omega]]p - g(I - [[d]])R^-1 e3 //drag negligible + * \dot{d} = \omega + * + * where [[.]] is the cross-product matrix of . + * \omega are the gyro measurements + * e3 is the column vector [0 0 1]' + * I is the identity + * R is the current attitude as a rotation matrix + * f/m is the mass-normalized motor force (acceleration in the body's z direction) + * g is gravity + * x, p, d are the quad's states + * note that d (attitude error) is zero at the beginning of each iteration, + * since error information is incorporated into R after each Kalman update. + */ + + // The linearized update matrix + NO_DMA_CCM_SAFE_ZERO_INIT static float A[KC_STATE_DIM][KC_STATE_DIM]; + static __attribute__((aligned(4))) arm_matrix_instance_f32 Am = { KC_STATE_DIM, KC_STATE_DIM, (float *)A}; // linearized dynamics for covariance update; + + // Temporary matrices for the covariance updates + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN1d[KC_STATE_DIM * KC_STATE_DIM]; + static __attribute__((aligned(4))) arm_matrix_instance_f32 tmpNN1m = { KC_STATE_DIM, KC_STATE_DIM, tmpNN1d}; + + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN2d[KC_STATE_DIM * KC_STATE_DIM]; + static __attribute__((aligned(4))) arm_matrix_instance_f32 tmpNN2m = { KC_STATE_DIM, KC_STATE_DIM, tmpNN2d}; + + float dt2 = dt*dt; + + // ====== DYNAMICS LINEARIZATION ====== + // Initialize as the identity + A[KC_STATE_X][KC_STATE_X] = 1; + A[KC_STATE_Y][KC_STATE_Y] = 1; + A[KC_STATE_Z][KC_STATE_Z] = 1; + + A[KC_STATE_PX][KC_STATE_PX] = 1; + A[KC_STATE_PY][KC_STATE_PY] = 1; + A[KC_STATE_PZ][KC_STATE_PZ] = 1; + + A[KC_STATE_D0][KC_STATE_D0] = 1; + A[KC_STATE_D1][KC_STATE_D1] = 1; + A[KC_STATE_D2][KC_STATE_D2] = 1; + + // position from body-frame velocity + A[KC_STATE_X][KC_STATE_PX] = this->R[0][0]*dt; + A[KC_STATE_Y][KC_STATE_PX] = this->R[1][0]*dt; + A[KC_STATE_Z][KC_STATE_PX] = this->R[2][0]*dt; + + A[KC_STATE_X][KC_STATE_PY] = this->R[0][1]*dt; + A[KC_STATE_Y][KC_STATE_PY] = this->R[1][1]*dt; + A[KC_STATE_Z][KC_STATE_PY] = this->R[2][1]*dt; + + A[KC_STATE_X][KC_STATE_PZ] = this->R[0][2]*dt; + A[KC_STATE_Y][KC_STATE_PZ] = this->R[1][2]*dt; + A[KC_STATE_Z][KC_STATE_PZ] = this->R[2][2]*dt; + + // position from attitude error + A[KC_STATE_X][KC_STATE_D0] = (this->S[KC_STATE_PY]*this->R[0][2] - this->S[KC_STATE_PZ]*this->R[0][1])*dt; + A[KC_STATE_Y][KC_STATE_D0] = (this->S[KC_STATE_PY]*this->R[1][2] - this->S[KC_STATE_PZ]*this->R[1][1])*dt; + A[KC_STATE_Z][KC_STATE_D0] = (this->S[KC_STATE_PY]*this->R[2][2] - this->S[KC_STATE_PZ]*this->R[2][1])*dt; + + A[KC_STATE_X][KC_STATE_D1] = (- this->S[KC_STATE_PX]*this->R[0][2] + this->S[KC_STATE_PZ]*this->R[0][0])*dt; + A[KC_STATE_Y][KC_STATE_D1] = (- this->S[KC_STATE_PX]*this->R[1][2] + this->S[KC_STATE_PZ]*this->R[1][0])*dt; + A[KC_STATE_Z][KC_STATE_D1] = (- this->S[KC_STATE_PX]*this->R[2][2] + this->S[KC_STATE_PZ]*this->R[2][0])*dt; + + A[KC_STATE_X][KC_STATE_D2] = (this->S[KC_STATE_PX]*this->R[0][1] - this->S[KC_STATE_PY]*this->R[0][0])*dt; + A[KC_STATE_Y][KC_STATE_D2] = (this->S[KC_STATE_PX]*this->R[1][1] - this->S[KC_STATE_PY]*this->R[1][0])*dt; + A[KC_STATE_Z][KC_STATE_D2] = (this->S[KC_STATE_PX]*this->R[2][1] - this->S[KC_STATE_PY]*this->R[2][0])*dt; + + // body-frame velocity from body-frame velocity + A[KC_STATE_PX][KC_STATE_PX] = 1; //drag negligible + A[KC_STATE_PY][KC_STATE_PX] =-gyro->z*dt; + A[KC_STATE_PZ][KC_STATE_PX] = gyro->y*dt; + + A[KC_STATE_PX][KC_STATE_PY] = gyro->z*dt; + A[KC_STATE_PY][KC_STATE_PY] = 1; //drag negligible + A[KC_STATE_PZ][KC_STATE_PY] =-gyro->x*dt; + + A[KC_STATE_PX][KC_STATE_PZ] =-gyro->y*dt; + A[KC_STATE_PY][KC_STATE_PZ] = gyro->x*dt; + A[KC_STATE_PZ][KC_STATE_PZ] = 1; //drag negligible + + // body-frame velocity from attitude error + A[KC_STATE_PX][KC_STATE_D0] = 0; + A[KC_STATE_PY][KC_STATE_D0] = -GRAVITY_MAGNITUDE*this->R[2][2]*dt; + A[KC_STATE_PZ][KC_STATE_D0] = GRAVITY_MAGNITUDE*this->R[2][1]*dt; + + A[KC_STATE_PX][KC_STATE_D1] = GRAVITY_MAGNITUDE*this->R[2][2]*dt; + A[KC_STATE_PY][KC_STATE_D1] = 0; + A[KC_STATE_PZ][KC_STATE_D1] = -GRAVITY_MAGNITUDE*this->R[2][0]*dt; + + A[KC_STATE_PX][KC_STATE_D2] = -GRAVITY_MAGNITUDE*this->R[2][1]*dt; + A[KC_STATE_PY][KC_STATE_D2] = GRAVITY_MAGNITUDE*this->R[2][0]*dt; + A[KC_STATE_PZ][KC_STATE_D2] = 0; + + // attitude error from attitude error + /** + * At first glance, it may not be clear where the next values come from, since they do not appear directly in the + * dynamics. In this prediction step, we skip the step of first updating attitude-error, and then incorporating the + * new error into the current attitude (which requires a rotation of the attitude-error covariance). Instead, we + * directly update the body attitude, however still need to rotate the covariance, which is what you see below. + * + * This comes from a second order approximation to: + * Sigma_post = exps(-d) Sigma_pre exps(-d)' + * ~ (I + [[-d]] + [[-d]]^2 / 2) Sigma_pre (I + [[-d]] + [[-d]]^2 / 2)' + * where d is the attitude error expressed as Rodriges parameters, ie. d0 = 1/2*gyro.x*dt under the assumption that + * d = [0,0,0] at the beginning of each prediction step and that gyro.x is constant over the sampling period + * + * As derived in "Covariance Correction Step for Kalman Filtering with an Attitude" + * http://arc.aiaa.org/doi/abs/10.2514/1.G000848 + */ + float d0 = gyro->x*dt/2; + float d1 = gyro->y*dt/2; + float d2 = gyro->z*dt/2; + + A[KC_STATE_D0][KC_STATE_D0] = 1 - d1*d1/2 - d2*d2/2; + A[KC_STATE_D0][KC_STATE_D1] = d2 + d0*d1/2; + A[KC_STATE_D0][KC_STATE_D2] = -d1 + d0*d2/2; + + A[KC_STATE_D1][KC_STATE_D0] = -d2 + d0*d1/2; + A[KC_STATE_D1][KC_STATE_D1] = 1 - d0*d0/2 - d2*d2/2; + A[KC_STATE_D1][KC_STATE_D2] = d0 + d1*d2/2; + + A[KC_STATE_D2][KC_STATE_D0] = d1 + d0*d2/2; + A[KC_STATE_D2][KC_STATE_D1] = -d0 + d1*d2/2; + A[KC_STATE_D2][KC_STATE_D2] = 1 - d0*d0/2 - d1*d1/2; + + + // ====== COVARIANCE UPDATE ====== + mat_mult(&Am, &this->Pm, &tmpNN1m); // A P + mat_trans(&Am, &tmpNN2m); // A' + mat_mult(&tmpNN1m, &tmpNN2m, &this->Pm); // A P A' + // Process noise is added after the return from the prediction step + + // ====== PREDICTION STEP ====== + // The prediction depends on whether we're on the ground, or in flight. + // When flying, the accelerometer directly measures thrust (hence is useless to estimate body angle while flying) + + float dx, dy, dz; + float tmpSPX, tmpSPY, tmpSPZ; + float zacc; + + if (quadIsFlying) // only acceleration in z direction + { + // Use accelerometer and not commanded thrust, as this has proper physical units + zacc = acc->z; + + // position updates in the body frame (will be rotated to inertial frame) + dx = this->S[KC_STATE_PX] * dt; + dy = this->S[KC_STATE_PY] * dt; + dz = this->S[KC_STATE_PZ] * dt + zacc * dt2 / 2.0f; // thrust can only be produced in the body's Z direction + + // position update + this->S[KC_STATE_X] += this->R[0][0] * dx + this->R[0][1] * dy + this->R[0][2] * dz; + this->S[KC_STATE_Y] += this->R[1][0] * dx + this->R[1][1] * dy + this->R[1][2] * dz; + this->S[KC_STATE_Z] += this->R[2][0] * dx + this->R[2][1] * dy + this->R[2][2] * dz - GRAVITY_MAGNITUDE * dt2 / 2.0f; + + // keep previous time step's state for the update + tmpSPX = this->S[KC_STATE_PX]; + tmpSPY = this->S[KC_STATE_PY]; + tmpSPZ = this->S[KC_STATE_PZ]; + + // body-velocity update: accelerometers - gyros cross velocity - gravity in body frame + this->S[KC_STATE_PX] += dt * (gyro->z * tmpSPY - gyro->y * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][0]); + this->S[KC_STATE_PY] += dt * (-gyro->z * tmpSPX + gyro->x * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][1]); + this->S[KC_STATE_PZ] += dt * (zacc + gyro->y * tmpSPX - gyro->x * tmpSPY - GRAVITY_MAGNITUDE * this->R[2][2]); + } + else // Acceleration can be in any direction, as measured by the accelerometer. This occurs, eg. in freefall or while being carried. + { + // position updates in the body frame (will be rotated to inertial frame) + dx = this->S[KC_STATE_PX] * dt + acc->x * dt2 / 2.0f; + dy = this->S[KC_STATE_PY] * dt + acc->y * dt2 / 2.0f; + dz = this->S[KC_STATE_PZ] * dt + acc->z * dt2 / 2.0f; // thrust can only be produced in the body's Z direction + + // position update + this->S[KC_STATE_X] += this->R[0][0] * dx + this->R[0][1] * dy + this->R[0][2] * dz; + this->S[KC_STATE_Y] += this->R[1][0] * dx + this->R[1][1] * dy + this->R[1][2] * dz; + this->S[KC_STATE_Z] += this->R[2][0] * dx + this->R[2][1] * dy + this->R[2][2] * dz - GRAVITY_MAGNITUDE * dt2 / 2.0f; + + // keep previous time step's state for the update + tmpSPX = this->S[KC_STATE_PX]; + tmpSPY = this->S[KC_STATE_PY]; + tmpSPZ = this->S[KC_STATE_PZ]; + + // body-velocity update: accelerometers - gyros cross velocity - gravity in body frame + this->S[KC_STATE_PX] += dt * (acc->x + gyro->z * tmpSPY - gyro->y * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][0]); + this->S[KC_STATE_PY] += dt * (acc->y - gyro->z * tmpSPX + gyro->x * tmpSPZ - GRAVITY_MAGNITUDE * this->R[2][1]); + this->S[KC_STATE_PZ] += dt * (acc->z + gyro->y * tmpSPX - gyro->x * tmpSPY - GRAVITY_MAGNITUDE * this->R[2][2]); + } + + // attitude update (rotate by gyroscope), we do this in quaternions + // this is the gyroscope angular velocity integrated over the sample period + float dtwx = dt*gyro->x; + float dtwy = dt*gyro->y; + float dtwz = dt*gyro->z; + + // compute the quaternion values in [w,x,y,z] order + float angle = arm_sqrt(dtwx*dtwx + dtwy*dtwy + dtwz*dtwz) + EPS; + float ca = arm_cos_f32(angle/2.0f); + float sa = arm_sin_f32(angle/2.0f); + float dq[4] = {ca , sa*dtwx/angle , sa*dtwy/angle , sa*dtwz/angle}; + + float tmpq0; + float tmpq1; + float tmpq2; + float tmpq3; + + // rotate the quad's attitude by the delta quaternion vector computed above + tmpq0 = dq[0]*this->q[0] - dq[1]*this->q[1] - dq[2]*this->q[2] - dq[3]*this->q[3]; + tmpq1 = dq[1]*this->q[0] + dq[0]*this->q[1] + dq[3]*this->q[2] - dq[2]*this->q[3]; + tmpq2 = dq[2]*this->q[0] - dq[3]*this->q[1] + dq[0]*this->q[2] + dq[1]*this->q[3]; + tmpq3 = dq[3]*this->q[0] + dq[2]*this->q[1] - dq[1]*this->q[2] + dq[0]*this->q[3]; + + if (! quadIsFlying) { + float keep = 1.0f - ROLLPITCH_ZERO_REVERSION; + + tmpq0 = keep * tmpq0 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[0]; + tmpq1 = keep * tmpq1 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[1]; + tmpq2 = keep * tmpq2 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[2]; + tmpq3 = keep * tmpq3 + ROLLPITCH_ZERO_REVERSION * this->initialQuaternion[3]; + } + + // normalize and store the result + float norm = arm_sqrt(tmpq0*tmpq0 + tmpq1*tmpq1 + tmpq2*tmpq2 + tmpq3*tmpq3) + EPS; + this->q[0] = tmpq0/norm; this->q[1] = tmpq1/norm; this->q[2] = tmpq2/norm; this->q[3] = tmpq3/norm; + assertStateNotNaN(this); + + this->isUpdated = true; + } + + void kalmanCorePredict(kalmanCoreData_t* this, Axis3f *acc, Axis3f *gyro, const uint32_t nowMs, bool quadIsFlying) { + float dt = (nowMs - this->lastPredictionMs) / 1000.0f; + predictDt(this, acc, gyro, dt, quadIsFlying); + this->lastPredictionMs = nowMs; + } + + + static void addProcessNoiseDt(kalmanCoreData_t *this, const kalmanCoreParams_t *params, float dt) + { + this->P[KC_STATE_X][KC_STATE_X] += powf(params->procNoiseAcc_xy*dt*dt + params->procNoiseVel*dt + params->procNoisePos, 2); // add process noise on position + this->P[KC_STATE_Y][KC_STATE_Y] += powf(params->procNoiseAcc_xy*dt*dt + params->procNoiseVel*dt + params->procNoisePos, 2); // add process noise on position + this->P[KC_STATE_Z][KC_STATE_Z] += powf(params->procNoiseAcc_z*dt*dt + params->procNoiseVel*dt + params->procNoisePos, 2); // add process noise on position + + this->P[KC_STATE_PX][KC_STATE_PX] += powf(params->procNoiseAcc_xy*dt + params->procNoiseVel, 2); // add process noise on velocity + this->P[KC_STATE_PY][KC_STATE_PY] += powf(params->procNoiseAcc_xy*dt + params->procNoiseVel, 2); // add process noise on velocity + this->P[KC_STATE_PZ][KC_STATE_PZ] += powf(params->procNoiseAcc_z*dt + params->procNoiseVel, 2); // add process noise on velocity + + this->P[KC_STATE_D0][KC_STATE_D0] += powf(params->measNoiseGyro_rollpitch * dt + params->procNoiseAtt, 2); + this->P[KC_STATE_D1][KC_STATE_D1] += powf(params->measNoiseGyro_rollpitch * dt + params->procNoiseAtt, 2); + this->P[KC_STATE_D2][KC_STATE_D2] += powf(params->measNoiseGyro_yaw * dt + params->procNoiseAtt, 2); + + for (int i=0; iP[i][j] + 0.5f*this->P[j][i]; + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + + assertStateNotNaN(this); + } + + void kalmanCoreAddProcessNoise(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs) { + float dt = (nowMs - this->lastProcessNoiseUpdateMs) / 1000.0f; + if (dt > 0.0f) { + addProcessNoiseDt(this, params, dt); + this->lastProcessNoiseUpdateMs = nowMs; + } + } + + bool kalmanCoreFinalize(kalmanCoreData_t* this) + { + // Only finalize if data is updated + if (! this->isUpdated) { + return false; + } + + + // Matrix to rotate the attitude covariances once updated + NO_DMA_CCM_SAFE_ZERO_INIT static float A[KC_STATE_DIM][KC_STATE_DIM]; + static arm_matrix_instance_f32 Am = {KC_STATE_DIM, KC_STATE_DIM, (float *)A}; + + // Temporary matrices for the covariance updates + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN1d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN1m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN1d}; + + NO_DMA_CCM_SAFE_ZERO_INIT static float tmpNN2d[KC_STATE_DIM * KC_STATE_DIM]; + static arm_matrix_instance_f32 tmpNN2m = {KC_STATE_DIM, KC_STATE_DIM, tmpNN2d}; + + // Incorporate the attitude error (Kalman filter state) with the attitude + float v0 = this->S[KC_STATE_D0]; + float v1 = this->S[KC_STATE_D1]; + float v2 = this->S[KC_STATE_D2]; + + // Move attitude error into attitude if any of the angle errors are large enough + if ((fabsf(v0) > 0.1e-3f || fabsf(v1) > 0.1e-3f || fabsf(v2) > 0.1e-3f) && (fabsf(v0) < 10 && fabsf(v1) < 10 && fabsf(v2) < 10)) + { + float angle = arm_sqrt(v0*v0 + v1*v1 + v2*v2) + EPS; + float ca = arm_cos_f32(angle / 2.0f); + float sa = arm_sin_f32(angle / 2.0f); + float dq[4] = {ca, sa * v0 / angle, sa * v1 / angle, sa * v2 / angle}; + + // rotate the quad's attitude by the delta quaternion vector computed above + float tmpq0 = dq[0] * this->q[0] - dq[1] * this->q[1] - dq[2] * this->q[2] - dq[3] * this->q[3]; + float tmpq1 = dq[1] * this->q[0] + dq[0] * this->q[1] + dq[3] * this->q[2] - dq[2] * this->q[3]; + float tmpq2 = dq[2] * this->q[0] - dq[3] * this->q[1] + dq[0] * this->q[2] + dq[1] * this->q[3]; + float tmpq3 = dq[3] * this->q[0] + dq[2] * this->q[1] - dq[1] * this->q[2] + dq[0] * this->q[3]; + + // normalize and store the result + float norm = arm_sqrt(tmpq0 * tmpq0 + tmpq1 * tmpq1 + tmpq2 * tmpq2 + tmpq3 * tmpq3) + EPS; + this->q[0] = tmpq0 / norm; + this->q[1] = tmpq1 / norm; + this->q[2] = tmpq2 / norm; + this->q[3] = tmpq3 / norm; + + /** Rotate the covariance, since we've rotated the body + * + * This comes from a second order approximation to: + * Sigma_post = exps(-d) Sigma_pre exps(-d)' + * ~ (I + [[-d]] + [[-d]]^2 / 2) Sigma_pre (I + [[-d]] + [[-d]]^2 / 2)' + * where d is the attitude error expressed as Rodriges parameters, ie. d = tan(|v|/2)*v/|v| + * + * As derived in "Covariance Correction Step for Kalman Filtering with an Attitude" + * http://arc.aiaa.org/doi/abs/10.2514/1.G000848 + */ + + float d0 = v0/2; // the attitude error vector (v0,v1,v2) is small, + float d1 = v1/2; // so we use a first order approximation to d0 = tan(|v0|/2)*v0/|v0| + float d2 = v2/2; + + A[KC_STATE_X][KC_STATE_X] = 1; + A[KC_STATE_Y][KC_STATE_Y] = 1; + A[KC_STATE_Z][KC_STATE_Z] = 1; + + A[KC_STATE_PX][KC_STATE_PX] = 1; + A[KC_STATE_PY][KC_STATE_PY] = 1; + A[KC_STATE_PZ][KC_STATE_PZ] = 1; + + A[KC_STATE_D0][KC_STATE_D0] = 1 - d1*d1/2 - d2*d2/2; + A[KC_STATE_D0][KC_STATE_D1] = d2 + d0*d1/2; + A[KC_STATE_D0][KC_STATE_D2] = -d1 + d0*d2/2; + + A[KC_STATE_D1][KC_STATE_D0] = -d2 + d0*d1/2; + A[KC_STATE_D1][KC_STATE_D1] = 1 - d0*d0/2 - d2*d2/2; + A[KC_STATE_D1][KC_STATE_D2] = d0 + d1*d2/2; + + A[KC_STATE_D2][KC_STATE_D0] = d1 + d0*d2/2; + A[KC_STATE_D2][KC_STATE_D1] = -d0 + d1*d2/2; + A[KC_STATE_D2][KC_STATE_D2] = 1 - d0*d0/2 - d1*d1/2; + + mat_trans(&Am, &tmpNN1m); // A' + mat_mult(&Am, &this->Pm, &tmpNN2m); // AP + mat_mult(&tmpNN2m, &tmpNN1m, &this->Pm); //APA' + } + + // convert the new attitude to a rotation matrix, such that we can rotate body-frame velocity and acc + this->R[0][0] = this->q[0] * this->q[0] + this->q[1] * this->q[1] - this->q[2] * this->q[2] - this->q[3] * this->q[3]; + this->R[0][1] = 2 * this->q[1] * this->q[2] - 2 * this->q[0] * this->q[3]; + this->R[0][2] = 2 * this->q[1] * this->q[3] + 2 * this->q[0] * this->q[2]; + + this->R[1][0] = 2 * this->q[1] * this->q[2] + 2 * this->q[0] * this->q[3]; + this->R[1][1] = this->q[0] * this->q[0] - this->q[1] * this->q[1] + this->q[2] * this->q[2] - this->q[3] * this->q[3]; + this->R[1][2] = 2 * this->q[2] * this->q[3] - 2 * this->q[0] * this->q[1]; + + this->R[2][0] = 2 * this->q[1] * this->q[3] - 2 * this->q[0] * this->q[2]; + this->R[2][1] = 2 * this->q[2] * this->q[3] + 2 * this->q[0] * this->q[1]; + this->R[2][2] = this->q[0] * this->q[0] - this->q[1] * this->q[1] - this->q[2] * this->q[2] + this->q[3] * this->q[3]; + + // reset the attitude error + this->S[KC_STATE_D0] = 0; + this->S[KC_STATE_D1] = 0; + this->S[KC_STATE_D2] = 0; + + // enforce symmetry of the covariance matrix, and ensure the values stay bounded + for (int i=0; iP[i][j] + 0.5f*this->P[j][i]; + if (isnan(p) || p > MAX_COVARIANCE) { + this->P[i][j] = this->P[j][i] = MAX_COVARIANCE; + } else if ( i==j && p < MIN_COVARIANCE ) { + this->P[i][j] = this->P[j][i] = MIN_COVARIANCE; + } else { + this->P[i][j] = this->P[j][i] = p; + } + } + } + + assertStateNotNaN(this); + + this->isUpdated = false; + return true; + } + + void kalmanCoreExternalizeState(const kalmanCoreData_t* this, state_t *state, const Axis3f *acc) + { + // position state is already in world frame + state->position = (point_t){ + .x = this->S[KC_STATE_X], + .y = this->S[KC_STATE_Y], + .z = this->S[KC_STATE_Z] + }; + + // velocity is in body frame and needs to be rotated to world frame + state->velocity = (velocity_t){ + .x = this->R[0][0]*this->S[KC_STATE_PX] + this->R[0][1]*this->S[KC_STATE_PY] + this->R[0][2]*this->S[KC_STATE_PZ], + .y = this->R[1][0]*this->S[KC_STATE_PX] + this->R[1][1]*this->S[KC_STATE_PY] + this->R[1][2]*this->S[KC_STATE_PZ], + .z = this->R[2][0]*this->S[KC_STATE_PX] + this->R[2][1]*this->S[KC_STATE_PY] + this->R[2][2]*this->S[KC_STATE_PZ] + }; + + // Accelerometer measurements are in the body frame and need to be rotated to world frame. + // Furthermore, the legacy code requires acc.z to be acceleration without gravity. + // Finally, note that these accelerations are in Gs, and not in m/s^2, hence - 1 for removing gravity + state->acc = (acc_t){ + .x = this->R[0][0]*acc->x + this->R[0][1]*acc->y + this->R[0][2]*acc->z, + .y = this->R[1][0]*acc->x + this->R[1][1]*acc->y + this->R[1][2]*acc->z, + .z = this->R[2][0]*acc->x + this->R[2][1]*acc->y + this->R[2][2]*acc->z - 1 + }; + + // convert the new attitude into Euler YPR + float yaw = atan2f(2*(this->q[1]*this->q[2]+this->q[0]*this->q[3]) , this->q[0]*this->q[0] + this->q[1]*this->q[1] - this->q[2]*this->q[2] - this->q[3]*this->q[3]); + float pitch = asinf(-2*(this->q[1]*this->q[3] - this->q[0]*this->q[2])); + float roll = atan2f(2*(this->q[2]*this->q[3]+this->q[0]*this->q[1]) , this->q[0]*this->q[0] - this->q[1]*this->q[1] - this->q[2]*this->q[2] + this->q[3]*this->q[3]); + + // Save attitude, adjusted for the legacy CF2 body coordinate system + state->attitude = (attitude_t){ + .roll = roll*RAD_TO_DEG, + .pitch = -pitch*RAD_TO_DEG, + .yaw = yaw*RAD_TO_DEG + }; + + // Save quaternion, hopefully one day this could be used in a better controller. + // Note that this is not adjusted for the legacy coordinate system + state->attitudeQuaternion = (quaternion_t){ + .w = this->q[0], + .x = this->q[1], + .y = this->q[2], + .z = this->q[3] + }; + + assertStateNotNaN(this); + } + + // Reset a state to 0 with max covariance + // If called often, this decouples the state to the rest of the filter + static void decoupleState(kalmanCoreData_t* this, kalmanCoreStateIdx_t state) + { + // Set all covariance to 0 + for(int i=0; iP[state][i] = 0; + this->P[i][state] = 0; + } + // Set state variance to maximum + this->P[state][state] = MAX_COVARIANCE; + // set state to zero + this->S[state] = 0; + } + + void kalmanCoreDecoupleXY(kalmanCoreData_t* this) + { + decoupleState(this, KC_STATE_X); + decoupleState(this, KC_STATE_PX); + decoupleState(this, KC_STATE_Y); + decoupleState(this, KC_STATE_PY); + } diff --git a/applications/newton/llvm-ir/c-files/kalman_core.h b/applications/newton/llvm-ir/c-files/kalman_core.h new file mode 100644 index 000000000..d78ea2478 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/kalman_core.h @@ -0,0 +1,170 @@ +/** +* Authored by Michael Hamer (http://www.mikehamer.info), June 2016 +* Thank you to Mark Mueller (www.mwm.im) for advice during implementation, +* and for derivation of the original filter in the below-cited paper. +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* ============================================================================ +* +* The Kalman filter implemented in this file is based on the papers: +* +* "Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation" +* http://ieeexplore.ieee.org/xpl/articleDetails.jsp?arnumber=7139421 +* +* and +* +* "Covariance Correction Step for Kalman Filtering with an Attitude" +* http://arc.aiaa.org/doi/abs/10.2514/1.G000848 +* +* Academic citation would be appreciated. +* +* BIBTEX ENTRIES: + @INPROCEEDINGS{MuellerHamerUWB2015, + author = {Mueller, Mark W and Hamer, Michael and D'Andrea, Raffaello}, + title = {Fusing ultra-wideband range measurements with accelerometers and rate gyroscopes for quadrocopter state estimation}, + booktitle = {2015 IEEE International Conference on Robotics and Automation (ICRA)}, + year = {2015}, + month = {May}, + pages = {1730-1736}, + doi = {10.1109/ICRA.2015.7139421}, + ISSN = {1050-4729}} + +@ARTICLE{MuellerCovariance2016, + author={Mueller, Mark W and Hehn, Markus and D'Andrea, Raffaello}, + title={Covariance Correction Step for Kalman Filtering with an Attitude}, + journal={Journal of Guidance, Control, and Dynamics}, + pages={1--7}, + year={2016}, + publisher={American Institute of Aeronautics and Astronautics}} +* +* ============================================================================ +*/ + +#pragma once + +#include "cf_math.h" +#include "stabilizer_types.h" + +// Indexes to access the quad's state, stored as a column vector +typedef enum +{ + KC_STATE_X, KC_STATE_Y, KC_STATE_Z, KC_STATE_PX, KC_STATE_PY, KC_STATE_PZ, KC_STATE_D0, KC_STATE_D1, KC_STATE_D2, KC_STATE_DIM +} kalmanCoreStateIdx_t; + + + // The data used by the kalman core implementation. + typedef struct { + /** + * Quadrocopter State + * + * The internally-estimated state is: + * - X, Y, Z: the quad's position in the global frame + * - PX, PY, PZ: the quad's velocity in its body frame + * - D0, D1, D2: attitude error + * + * For more information, refer to the paper + */ + float S[KC_STATE_DIM]; + + // The quad's attitude as a quaternion (w,x,y,z) + // We store as a quaternion to allow easy normalization (in comparison to a rotation matrix), + // while also being robust against singularities (in comparison to euler angles) + float q[4]; + + // The quad's attitude as a rotation matrix (used by the prediction, updated by the finalization) + float R[3][3]; + + // The covariance matrix + __attribute__((aligned(4))) float P[KC_STATE_DIM][KC_STATE_DIM]; + arm_matrix_instance_f32 Pm; + + float baroReferenceHeight; + + // Quaternion used for initial orientation [w,x,y,z] + float initialQuaternion[4]; + + // Tracks whether an update to the state has been made, and the state therefore requires finalization + bool isUpdated; + + uint32_t lastPredictionMs; + uint32_t lastProcessNoiseUpdateMs; + } kalmanCoreData_t; + + // The parameters used by the filter + typedef struct { + // Initial variances, uncertain of position, but know we're stationary and roughly flat + float stdDevInitialPosition_xy; + float stdDevInitialPosition_z; + float stdDevInitialVelocity; + float stdDevInitialAttitude_rollpitch; + float stdDevInitialAttitude_yaw; + + float procNoiseAcc_xy; + float procNoiseAcc_z; + float procNoiseVel; + float procNoisePos; + float procNoiseAtt; + float measNoiseBaro; // meters + float measNoiseGyro_rollpitch; // radians per second + float measNoiseGyro_yaw; // radians per second + + float initialX; + float initialY; + float initialZ; + + // Initial yaw of the Crazyflie in radians. + // 0 --- facing positive X + // PI / 2 --- facing positive Y + // PI --- facing negative X + // 3 * PI / 2 --- facing negative Y + float initialYaw; + } kalmanCoreParams_t; + + /* - Load default parameters */ + void kalmanCoreDefaultParams(kalmanCoreParams_t *params); + + /* - Initialize Kalman State */ + void kalmanCoreInit(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs); + + /* - Measurement updates based on sensors */ + + // Barometer + void kalmanCoreUpdateWithBaro(kalmanCoreData_t *this, const kalmanCoreParams_t *params, float baroAsl, bool quadIsFlying); + + /** +* Primary Kalman filter functions +* +* The filter progresses as: +* - Predicting the current state forward */ + void kalmanCorePredict(kalmanCoreData_t *this, Axis3f *acc, Axis3f *gyro, const uint32_t nowMs, bool quadIsFlying); + + void kalmanCoreAddProcessNoise(kalmanCoreData_t *this, const kalmanCoreParams_t *params, const uint32_t nowMs); + + /** +* @brief Finalization to incorporate attitude error into body attitude +* +* @param this Core data +* @return true The state was finalized +* @return false The state was not changed and did not require finalization + */ + bool kalmanCoreFinalize(kalmanCoreData_t* this); + + /* - Externalization to move the filter's internal state into the external state expected by other modules */ + void kalmanCoreExternalizeState(const kalmanCoreData_t* this, state_t *state, const Axis3f *acc); + + void kalmanCoreDecoupleXY(kalmanCoreData_t* this); + + void kalmanCoreScalarUpdate(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, float error, float stdMeasNoise); + + void kalmanCoreUpdateWithPKE(kalmanCoreData_t* this, arm_matrix_instance_f32 *Hm, arm_matrix_instance_f32 *Km, arm_matrix_instance_f32 *P_w_m, float error); diff --git a/applications/newton/llvm-ir/c-files/sensfusion6.c b/applications/newton/llvm-ir/c-files/sensfusion6.c new file mode 100644 index 000000000..24fd45364 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/sensfusion6.c @@ -0,0 +1,248 @@ +/** +* || ____ _ __ +* +------+ / __ )(_) /_______________ _____ ___ +* | 0xBC | / __ / / __/ ___/ ___/ __ `/_ / / _ \ +* +------+ / /_/ / / /_/ /__/ / / /_/ / / /_/ __/ +* || || /_____/_/\__/\___/_/ \__,_/ /___/\___/ +* +* Crazyflie Firmware +* +* Copyright (C) 2011-2012 Bitcraze AB +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +* +*/ + + + + + +#include +#define CONFIG_IMU_MADGWICK_QUATERNION +//#define CONFIG_IMU_MAHONY_QUATERNION_IMU +#define dt (1.0f/100.0f) // sample frequency in Hz + +#ifdef CONFIG_IMU_MADGWICK_QUATERNION +#define BETA_DEF 0.01f // 2 * proportional gain +#else // MAHONY_QUATERNION_IMU +#define TWO_KP_DEF (2.0f * 0.4f) // 2 * proportional gain +#define TWO_KI_DEF (2.0f * 0.001f) // 2 * integral gain +#endif + +#ifdef CONFIG_IMU_MADGWICK_QUATERNION + + +volatile float beta = BETA_DEF; +#else // MAHONY_QUATERNION_IMU + +float twoKp = TWO_KP_DEF; // 2 * proportional gain (Kp) +float twoKi = TWO_KI_DEF; // 2 * integral gain (Ki) +float integralFBx = 0.0f; +float integralFBy = 0.0f; +float integralFBz = 0.0f; // integral error terms scaled by Ki +#endif + +volatile float M_PI_F = 3.14159265358979323846f; + + +static float invSqrt(float x); + +#ifdef CONFIG_IMU_MADGWICK_QUATERNION +//void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az, float dt,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +{ + float qw = *qw_ptr; + float qx = *qx_ptr; + float qy = *qy_ptr; + float qz = *qz_ptr; + + float recipNorm; + float s0, s1, s2, s3; + float qDot1, qDot2, qDot3, qDot4; + float _2qw, _2qx, _2qy, _2qz, _4qw, _4qx, _4qy ,_8qx, _8qy, qwqw, qxqx, qyqy, qzqz; + + // Rate of change of quaternion from gyroscope + qDot1 = 0.5f * (-qx * gx - qy * gy - qz * gz); + qDot2 = 0.5f * (qw * gx + qy * gz - qz * gy); + qDot3 = 0.5f * (qw * gy - qx * gz + qz * gx); + qDot4 = 0.5f * (qw * gz + qx * gy - qy * gx); + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) + { + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Auxiliary variables to avoid repeated arithmetic + _2qw = 2.0f * qw; + _2qx = 2.0f * qx; + _2qy = 2.0f * qy; + _2qz = 2.0f * qz; + _4qw = 4.0f * qw; + _4qx = 4.0f * qx; + _4qy = 4.0f * qy; + _8qx = 8.0f * qx; + _8qy = 8.0f * qy; + qwqw = qw * qw; + qwqw = qw * qw; + qxqx = qx * qx; + qyqy = qy * qy; + qzqz = qz * qz; + + // Gradient decent algorithm corrective step + s0 = _4qw * qyqy + _2qy * ax + _4qw * qxqx - _2qx * ay; + s1 = _4qx * qzqz - _2qz * ax + 4.0f * qwqw * qx - _2qw * ay - _4qx + _8qx * qxqx + _8qx * qyqy + _4qx * az; + s2 = 4.0f * qwqw * qy + _2qw * ax + _4qy * qzqz - _2qz * ay - _4qy + _8qy * qxqx + _8qy * qyqy + _4qy * az; + s3 = 4.0f * qxqx * qz - _2qx * ax + 4.0f * qyqy * qz - _2qy * ay; + recipNorm = invSqrt(s0 * s0 + s1 * s1 + s2 * s2 + s3 * s3); // normalise step magnitude + s0 *= recipNorm; + s1 *= recipNorm; + s2 *= recipNorm; + s3 *= recipNorm; + + // Apply feedback step + qDot1 -= beta * s0; + qDot2 -= beta * s1; + qDot3 -= beta * s2; + qDot4 -= beta * s3; + } + // Integrate rate of change of quaternion to yield quaternion + qw += qDot1 * dt; + qx += qDot2 * dt; + qy += qDot3 * dt; + qz += qDot4 * dt; + + // Normalise quaternion + recipNorm = invSqrt(qw*qw + qx*qx + qy*qy + qz*qz); + qw *= recipNorm; + qx *= recipNorm; + qy *= recipNorm; + qz *= recipNorm; + + // Store results in the output pointers + *qw_ptr = qw; + *qx_ptr = qx; + *qy_ptr = qy; + *qz_ptr = qz; +} +#else +// Madgwick's implementation of Mahony's AHRS algorithm. +// See: http://www.x-io.co.uk/open-source-ahrs-with-x-imu +// +// Date Author Notes +// 29/09/2011 SOH Madgwick Initial release +// 02/10/2011 SOH Madgwick Optimised for reduced CPU load +void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr) +{ + + float qw = *qw_ptr; + float qx = *qx_ptr; + float qy = *qy_ptr; + float qz = *qz_ptr; + + float recipNorm; + float halfvx, halfvy, halfvz; + float halfex, halfey, halfez; + float qa, qb, qc; + + gx = gx * M_PI_F / 180; + gy = gy * M_PI_F / 180; + gz = gz * M_PI_F / 180; + + // Compute feedback only if accelerometer measurement valid (avoids NaN in accelerometer normalisation) + if(!((ax == 0.0f) && (ay == 0.0f) && (az == 0.0f))) + { + // Normalise accelerometer measurement + recipNorm = invSqrt(ax * ax + ay * ay + az * az); + ax *= recipNorm; + ay *= recipNorm; + az *= recipNorm; + + // Estimated direction of gravity and vector perpendicular to magnetic flux + halfvx = qx * qz - qw * qy; + halfvy = qw * qx + qy * qz; + halfvz = qw * qw - 0.5f + qz * qz; + + // Error is sum of cross product between estimated and measured direction of gravity + halfex = (ay * halfvz - az * halfvy); + halfey = (az * halfvx - ax * halfvz); + halfez = (ax * halfvy - ay * halfvx); + + // Compute and apply integral feedback if enabled + if(twoKi > 0.0f) + { + integralFBx += twoKi * halfex * dt; // integral error scaled by Ki + integralFBy += twoKi * halfey * dt; + integralFBz += twoKi * halfez * dt; + gx += integralFBx; // apply integral feedback + gy += integralFBy; + gz += integralFBz; + } + else + { + integralFBx = 0.0f; // prevent integral windup + integralFBy = 0.0f; + integralFBz = 0.0f; + } + + // Apply proportional feedback + gx += twoKp * halfex; + gy += twoKp * halfey; + gz += twoKp * halfez; + } + + // Integrate rate of change of quaternion + gx *= (0.5f * dt); // pre-multiply common factors + gy *= (0.5f * dt); + gz *= (0.5f * dt); + qa = qw; + qb = qx; + qc = qy; + qw += (-qb * gx - qc * gy - qz * gz); + qx += (qa * gx + qc * gz - qz * gy); + qy += (qa * gy - qb * gz + qz * gx); + qz += (qa * gz + qb * gy - qc * gx); + + // Normalise quaternion + recipNorm = invSqrt(qw * qw + qx * qx + qy * qy + qz * qz); + qw *= recipNorm; + qx *= recipNorm; + qy *= recipNorm; + qz *= recipNorm; + + // Store results in the output pointers + *qw_ptr = qw; + *qx_ptr = qx; + *qy_ptr = qy; + *qz_ptr = qz; + +} +#endif + +//--------------------------------------------------------------------------------------------------- +// Fast inverse square-root +// See: http://en.wikipedia.org/wiki/Fast_inverse_square_root +float invSqrt(float x) +{ + float halfx = 0.5f * x; + float y = x; + long i = *(long*)&y; + i = 0x5f3759df - (i>>1); + y = *(float*)&i; + y = y * (1.5f - (halfx * y * y)); + return y; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/sensfusion6.h b/applications/newton/llvm-ir/c-files/sensfusion6.h new file mode 100644 index 000000000..183a6e48c --- /dev/null +++ b/applications/newton/llvm-ir/c-files/sensfusion6.h @@ -0,0 +1,30 @@ +/** +* || ____ _ __ ______ +* +------+ / __ )(_) /_/ ____/_________ _____ ___ +* | 0xBC | / __ / / __/ / / ___/ __ `/_ / / _ \ +* +------+ / /_/ / / /_/ /___ / / / /_/ / / /_/ __/ +* || || /_____/_/\__/\____//_/ \__,_/ /___/\___/ +* +* Crazyflie control firmware +* +* Copyright (C) 2011-2012 Bitcraze AB +* +* This program is free software: you can redistribute it and/or modify +* it under the terms of the GNU General Public License as published by +* the Free Software Foundation, in version 3. +* +* This program is distributed in the hope that it will be useful, +* but WITHOUT ANY WARRANTY; without even the implied warranty of +* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +* GNU General Public License for more details. +* +* You should have received a copy of the GNU General Public License +* along with this program. If not, see . +* +*/ + +#ifndef SENSORFUSION6_H_ +#define SENSORFUSION6_H_ + +void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, float az,float* qw_ptr, float* qx_ptr, float* qy_ptr, float* qz_ptr); +#endif /* SENSORFUSION6_H_ */ \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_Kalman.c b/applications/newton/llvm-ir/c-files/test_Kalman.c new file mode 100644 index 000000000..d8b02e5d1 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_Kalman.c @@ -0,0 +1,27 @@ +#include +#include "Kalman.h" + +int main() { + // 初始化Kalman滤波器实例 + Kalman kalman; + kalman.setAngle(0.0f); // 设置初始角度为0度 + + // 硬编码的数据集,模拟传感器输入 + // newAngle: 角度测量值, newRate: 角速度测量值, dt: 时间间隔 + float newAngle[] = {0.5, 1.0, 1.5, 2.0, 2.5, 3.0, 2.5, 2.0, 1.5, 1.0}; + float newRate[] = {0.05, 0.1, 0.15, 0.2, 0.25, 0.3, 0.25, 0.2, 0.15, 0.1}; + float dt = 0.1f; // 时间间隔 100毫秒 + + int dataSize = sizeof(newAngle) / sizeof(newAngle[0]); + + // 打印表头 + printf("Time(s)\tMeasured Angle\tMeasured Rate\tFiltered Angle\n"); + + // 使用硬编码数据测试Kalman滤波器 + for (int i = 0; i < dataSize; i++) { + float filteredAngle = kalman.getAngle(newAngle[i], newRate[i], dt); + printf("%0.1f\t%f\t%f\t%f\n", i * dt, newAngle[i], newRate[i], filteredAngle); + } + + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c new file mode 100644 index 000000000..15ae55118 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c @@ -0,0 +1,341 @@ +/* +* Madgwick test case (run locally) +* Compilation command in applications/newton/llvm-ir/performance_test/Makefile +* +* How to compile and run? +* 1. `make perf_madgwick` FP hardware (by default) +* 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) +* 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) +* 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format +* */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(INT_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#include "MadgwickAHRS.h" +#elif defined(FP_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#if defined(SOFT_FLOAT_LIB) +#include "MadgwickAHRS_softfloat.h" +#else +#include "MadgwickAHRS.h" +#endif + +#else +#error "Must set data type: FP or INT" +#endif + +#define DATA_SIZE 1000 +#define ITERATION 10 + +/*************************************** +* Timer functions of the test framework +***************************************/ +// readticks(unsigned int *result, int enabled) +//{ +// struct timeval t; +// unsigned int cc; +// unsigned int val; +// if (!enabled) { +// // program the performance-counter control-register: +// asm volatile("msr pmcr_el0, %0" : : "r" (17)); +// //enable all counters +// asm volatile("msr PMCNTENSET_EL0, %0" : : "r" (0x8000000f)); +// //clear the overflow +// asm volatile("msr PMOVSCLR_EL0, %0" : : "r" (0x8000000f)); +// enabled = 1; +// } +// //read the coutner value +// asm volatile("mrs %0, PMCCNTR_EL0" : "=r" (cc)); +// gettimeofday(&t,(struct timezone *) 0); +// result[0] = cc; +// result[1] = t.tv_usec; +// result[2] = t.tv_sec; +// } + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} + +int +main() +{ + FILE * fp = fopen("input.csv", "r"); + + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + + double time[DATA_SIZE]; +#if defined(INT_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + + // q0[i] = 1.0f; + // q1[i] = 0.0f; + // q2[i] = 0.0f; + // q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + +#elif defined(FP_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + + // q0[i] = 1.0f; + // q1[i] = 0.0f; + // q2[i] = 0.0f; + // q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; +#else +#error "Must set data type: FP or INT" +#endif + + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: +#if defined(INT_DATA_TYPE) + + mag_x[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_x[row - 2] = atof(value); +#endif + break; + case 2: +#if defined(INT_DATA_TYPE) + + mag_y[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_y[row - 2] = atof(value); +#endif + break; + case 3: +#if defined(INT_DATA_TYPE) + + mag_z[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_z[row - 2] = atof(value); +#endif + break; + case 4: +#if defined(INT_DATA_TYPE) + // gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_x[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_x[row - 2] = atof(value) * 61; +#endif + break; + case 5: +#if defined(INT_DATA_TYPE) + // gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_y[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_y[row - 2] = atof(value) * 61; +#endif + break; + case 6: +#if defined(INT_DATA_TYPE) + // gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_z[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_z[row - 2] = atof(value) * 61; +#endif + break; + case 7: +#if defined(INT_DATA_TYPE) + // acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_x[row - 2] = atof(value) * 2; +#endif + break; + case 8: +#if defined(INT_DATA_TYPE) + // acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_y[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_y[row - 2] = atof(value) * 2; +#endif + break; + case 9: +#if defined(INT_DATA_TYPE) + // acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_z[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_z[row - 2] = atof(value) * 2; +#endif + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } + + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + // unsigned int init[3] = {0}; + // unsigned int start[3] = {0}; + // unsigned int end[3] = {0}; + // unsigned int overhead = 0; + // + // readticks(init, 0); + // readticks(start, 1); + // readticks(end, 1); + // + // overhead = end[0] - start[0]; + // readticks(init, 0); + // readticks(start, 1); + + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + + // end[0] = end[0] - start[0] - overhead; + // printf("clock cycles= %d\n", end[0]); + + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); + +#if defined(FP_DATA_TYPE) + FILE * fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); +#elif defined(INT_DATA_TYPE) + FILE * fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + + + +#endif + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c new file mode 100644 index 000000000..7a5f61d1f --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c @@ -0,0 +1,421 @@ +#include +#include +#include +#include +#include +#include +#include +#include +#define FRAC_Q 16 +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 +#define ITERATION 10 +#define DATA_SIZE 1000 +// #include "MadgwickAHRSfix.h" +extern volatile int32_t q0, q1, q2, q3; +extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); + +//extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, int32_t * q0_ptr,int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +extern int32_t sqrt_rsqrt(int32_t x, int recip); +// #include "MadgwickAHRS.h" + +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct { + int quantized1; + int quantized2; + int quantized3; + int quantized4; +} QuantizedValues; + +typedef struct { + float dequantized1; + float dequantized2; + float dequantized3; + float dequantized4; +} DequantizedValues; + +typedef struct { + uint64_t start; + uint64_t current; + uint64_t min; + uint64_t max; +} ELAPSED_TIME; + +ELAPSED_TIME elapsed_time_tbl[10]; + +// static inline uint64_t rdtsc() { +// uint32_t lo, hi; +// __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +// return ((uint64_t)hi << 32) | lo; +// } +// +// void elapsed_time_start(uint32_t i) { +// elapsed_time_tbl[i].start = rdtsc(); +// } +// +// void elapsed_time_stop(uint32_t i) { +// uint64_t stop = rdtsc(); +// ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; +// p_tbl->current = stop - p_tbl->start; +// if (p_tbl->max < p_tbl->current) { +// p_tbl->max = p_tbl->current; +// } +// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { +// p_tbl->min = p_tbl->current; +// } +// } + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +// Quantization function +int +quantize(float value, int frac_base) +{ + return round(value * frac_base); +} + +QuantizedValues +quantize4(float value1, float value2, float value3, float value4, int frac_base) +{ + QuantizedValues result; + result.quantized1 = roundf(value1 * frac_base); + result.quantized2 = roundf(value2 * frac_base); + result.quantized3 = roundf(value3 * frac_base); + result.quantized4 = roundf(value4 * frac_base); + return result; +} + +DequantizedValues +dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) +{ + DequantizedValues result; + result.dequantized1 = (float)(quantized1/FRAC_BASE); + result.dequantized2 = (float)(quantized2/FRAC_BASE); + result.dequantized3 = (float)(quantized3/FRAC_BASE); + result.dequantized4 = (float)(quantized4/FRAC_BASE); + return result; +} + +// Dequantization function +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); +} + +extern int perform_addition(float a, float b); + +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} +int +main() +{ + FILE * fp = fopen("input.csv", "r"); + + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + double time[DATA_SIZE]; + + int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = (0.64306622f * FRAC_BASE); + q1[i] = (0.02828862f * FRAC_BASE); + q2[i] = (-0.00567953f * FRAC_BASE); + q3[i] = (-0.76526684f * FRAC_BASE); + } + + int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: + mag_x[row - 2] = round(atof(value) * FRAC_BASE); + + //mag_x[row - 2] = atof(value); + break; + case 2: + mag_y[row - 2] = round(atof(value) * FRAC_BASE); + + //mag_y[row - 2] = atof(value) ; + break; + case 3: + mag_z[row - 2] = round(atof(value) * FRAC_BASE); + //mag_z[row - 2] = atof(value); + break; + case 4: + gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_x[row - 2] = atof(value) * 61; + + break; + case 5: + gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_y[row - 2] = atof(value) * 61; + break; + case 6: + gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + //gyr_z[row - 2] = atof(value) * 61; + break; + case 7: + //acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; + break; + case 8: + acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_y[row - 2] = atof(value) * 2; + break; + case 9: + acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + //acc_z[row - 2] = atof(value) * 2; + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } + + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + // elapsed_time_start(idx); // 开始计时 + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); +// quantize4(0.64306622f, 0.02828862f, -0.00567953f, -0.76526684f, FRAC_BASE); + + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + + + dequantize4(q0[ts], q1[ts], q2[ts], q3[ts]); + } + + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + // elapsed_time_stop(idx); // 结束计时 + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu ns\n", average_time); + + // 打印出每次迭代的最大、最小和当前时间 + // for (size_t idx = 0; idx < ITERATION; idx++) { + // + // printf("Cycle count for iteration %zu: %lu cycles\n", idx, elapsed_time_tbl[idx].current); + // } + + FILE * fptr = fopen("int_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + // printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, (double)q0[ts]/FRAC_BASE, + // ts, (double)q1[ts]/FRAC_BASE, + // ts, (double)q2[ts]/FRAC_BASE, + // ts, (double)q3[ts]/FRAC_BASE); +// fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", +// ts, (double)q0[ts]/FRAC_BASE, +// ts, (double)q1[ts]/FRAC_BASE, +// ts, (double)q2[ts]/FRAC_BASE, +// ts, (double)q3[ts]/FRAC_BASE); + + + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, (float)q0[ts]/FRAC_BASE, + ts, (float)q1[ts]/FRAC_BASE, + ts, (float)q2[ts]/FRAC_BASE, + ts, (float)q3[ts]/FRAC_BASE); + +// fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", +// ts, (double)(q0[ts] >> FRAC_Q), +// ts, (double)(q1[ts] >> FRAC_Q), +// ts, (double)(q2[ts] >> FRAC_Q), +// ts, (double)(q3[ts] >> FRAC_Q)); + } + fclose(fptr); + return 0; +} + +// int main () +//{ +// /* +// * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +// */ +// +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; +// +// // result +// int num14 = 657; +// int num15 = 28; +// int num16 = -6; +// int num17 = -785; +// +// int32_t mag_x = quantize(num1, FRAC_BASE); +// int32_t mag_y = quantize(num2, FRAC_BASE); +// int32_t mag_z = quantize(num3, FRAC_BASE); +// int32_t gyr_x = quantize(num4, FRAC_BASE); +// int32_t gyr_y = quantize(num5, FRAC_BASE); +// int32_t gyr_z = quantize(num6, FRAC_BASE); +// int32_t acc_x = quantize(num7, FRAC_BASE); +// int32_t acc_y = quantize(num8, FRAC_BASE); +// int32_t acc_z = quantize(num9, FRAC_BASE); +// int32_t q0 = quantize(num10, FRAC_BASE); +// int32_t q1 = quantize(num11, FRAC_BASE); +// int32_t q2 = quantize(num12, FRAC_BASE); +// int32_t q3 = quantize(num13, FRAC_BASE); +// +// u_int64_t time_slots[ITERATION]; +// +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// +// +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// acc_x, acc_y, acc_z, +// mag_x, mag_y, mag_z, +// &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// } +// +// // tic toc quantize4耗时 +// timespec timer = tic(); +// QuantizedValues quantizedValues = quantize4(num10, num11, num12, num13, FRAC_BASE); +// timespec durationQuantize = toc(&timer, "Quantize"); +// printf("quantization time = %lu nm\n", durationQuantize.tv_nsec); +// +// // tic toc dequantize4耗时 +// timer = tic(); +// DequantizedValues dequantizedValues = dequantize4(num14, num15, num16, num17); +// timespec durationDequantize = toc(&timer, "Dequantize"); +// printf("dequantization time = %lu nm\n", durationDequantize.tv_nsec); +// +//// u_int64_t average_time = 0; +//// for (size_t idx = 0; idx < ITERATION; idx++) +//// { +//// average_time += time_slots[idx]; +//// } +//// average_time /= ITERATION; +//// printf("average time = %lu nm\n", average_time); +// +// u_int64_t totalTime = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// totalTime += time_slots[idx]; +// } +// printf("total time = %lu nm\n", totalTime); +// +// // 计算total time, quantize+dequantize+average +// //u_int64_t totalTime = durationQuantize.tv_nsec + durationDequantize.tv_nsec + average_time; +// //printf("total time = %lu nm\n", totalTime); +//} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_Original.c b/applications/newton/llvm-ir/c-files/test_Original.c new file mode 100644 index 000000000..132408a85 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_Original.c @@ -0,0 +1,393 @@ +#include +#include +#include +#include +#include +#include +#include +#include + +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 +#define ITERATION 10 +#define DATA_SIZE 1000 + +extern volatile float q0, q1, q2, q3; +extern void MadgwickAHRSupdate(float gx, float gy, float gz, float ax, float ay, float az, float mx, float my, float mz, + float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); +extern void MadgwickAHRSupdateIMU(float gx, float gy, float gz, float ax, float ay, float az, + float * q0_ptr, float * q1_ptr, float * q2_ptr, float * q3_ptr); + +/*************************************** + * Timer functions of the test framework + ***************************************/ +typedef struct { + int quantized1; + int quantized2; + int quantized3; + int quantized4; +} QuantizedValues; + +typedef struct { + float dequantized1; + float dequantized2; + float dequantized3; + float dequantized4; +} DequantizedValues; + +typedef struct timespec timespec; + +//typedef struct { +// uint64_t start; +// uint64_t current; +// uint64_t min; +// uint64_t max; +//} ELAPSED_TIME; +// +//ELAPSED_TIME elapsed_time_tbl[10]; // 示例,根据需要调整大小 +// +//static inline uint64_t rdtsc() { +// uint32_t lo, hi; +// __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); +// return ((uint64_t)hi << 32) | lo; +//} +// +// +// +//void elapsed_time_start(uint32_t i) { +// elapsed_time_tbl[i].start = rdtsc(); +//} +// +//void elapsed_time_stop(uint32_t i) { +// uint64_t stop = rdtsc(); +// ELAPSED_TIME *p_tbl = &elapsed_time_tbl[i]; +// p_tbl->current = stop - p_tbl->start; +// if (p_tbl->max < p_tbl->current) { +// p_tbl->max = p_tbl->current; +// } +// if (p_tbl->min == 0 || p_tbl->min > p_tbl->current) { +// p_tbl->min = p_tbl->current; +// } +//} + + + + + + + + +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +// Quantization function +int +quantize(float value, int frac_base) +{ + return round(value * frac_base); +} + +// Dequantization function +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); +} + +QuantizedValues +quantize4(float value1, float value2, float value3, float value4, int frac_base) +{ + QuantizedValues result; + result.quantized1 = round(value1 * frac_base); + result.quantized2 = round(value2 * frac_base); + result.quantized3 = round(value3 * frac_base); + result.quantized4 = round(value4 * frac_base); + return result; +} + +DequantizedValues +dequantize4(int quantized1, int quantized2, int quantized3, int quantized4) +{ + DequantizedValues result; + result.dequantized1 = (float)(quantized1 >> FRAC_Q); + result.dequantized2 = (float)(quantized2 >> FRAC_Q); + result.dequantized3 = (float)(quantized3 >> FRAC_Q); + result.dequantized4 = (float)(quantized4 >> FRAC_Q); + return result; +} + +extern int perform_addition(float a, float b); + +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} +int +main() +{ + FILE * fp = fopen("input.csv", "r"); + + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + double time[DATA_SIZE]; + + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + + char buffer[1024]; + int row = 0, column = 0; + + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + + case 1: + mag_x[row - 2] = atof(value); + break; + case 2: + mag_y[row - 2] = atof(value); + break; + case 3: + mag_z[row - 2] = atof(value); + break; + case 4: + gyr_x[row - 2] = atof(value) * 61; + break; + case 5: + gyr_y[row - 2] = atof(value) * 61; + break; + case 6: + gyr_z[row - 2] = atof(value) * 61; + break; + case 7: + acc_x[row - 2] = atof(value) * 2; + break; + case 8: + acc_y[row - 2] = atof(value) * 2; + break; + case 9: + acc_z[row - 2] = atof(value) * 2; + break; + default: + break; + } + value = strtok(NULL, ", "); + column++; + } + } + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + //elapsed_time_start(idx); // 开始计时 + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + mag_x[ts], mag_y[ts], mag_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + + + } + + //elapsed_time_stop(idx); // 结束计时 + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); + +// // 打印出每次迭代的最大、最小和当前时间 +// for (size_t idx = 0; idx < ITERATION; idx++) { +// printf("Iteration %zu: Current Time = %lu, Max Time = %lu, Min Time = %lu\n", +// idx, elapsed_time_tbl[idx].current, elapsed_time_tbl[idx].max, elapsed_time_tbl[idx].min); +// } + + FILE * fptr = fopen("fp_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + // printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + // ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + return 0; +} + + +//int main () +//{ +// /* +// * Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +// 0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +// */ +// +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; +// +// // result +// int num14 = 657; +// int num15 = 28; +// int num16 = -6; +// int num17 = -785; +// +//// int32_t mag_x = quantize(num1, FRAC_BASE); +//// int32_t mag_y = quantize(num2, FRAC_BASE); +//// int32_t mag_z = quantize(num3, FRAC_BASE); +//// int32_t gyr_x = quantize(num4, FRAC_BASE); +//// int32_t gyr_y = quantize(num5, FRAC_BASE); +//// int32_t gyr_z = quantize(num6, FRAC_BASE); +//// int32_t acc_x = quantize(num7, FRAC_BASE); +//// int32_t acc_y = quantize(num8, FRAC_BASE); +//// int32_t acc_z = quantize(num9, FRAC_BASE); +//// int32_t q0 = quantize(num10, FRAC_BASE); +//// int32_t q1 = quantize(num11, FRAC_BASE); +//// int32_t q2 = quantize(num12, FRAC_BASE); +//// int32_t q3 = quantize(num13, FRAC_BASE); +// +// float mag_x = num1; +// float mag_y = num2; +// float mag_z = num3; +// float gyr_x = num4; +// float gyr_y = num5; +// float gyr_z = num6; +// float acc_x = num7; +// float acc_y = num8; +// float acc_z = num9; +// float q0 = num10; +// float q1 = num11; +// float q2 = num12; +// float q3 = num13; +// +// +// u_int64_t time_slots[ITERATION]; +// +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// +// +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// acc_x, acc_y, acc_z, +// mag_x, mag_y, mag_z, +// &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// } +// +// +// +// +// +// u_int64_t average_time = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// average_time += time_slots[idx]; +// } +// average_time /= ITERATION; +// printf("average time = %lu nm\n", average_time); +// +// +//} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_fixmul.c b/applications/newton/llvm-ir/c-files/test_fixmul.c new file mode 100644 index 000000000..f43a8b0da --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_fixmul.c @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#define DATA_SIZE 1000 +#define ITERATIONS 10000 +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) + +/*************************************** +* Timer functions of the test framework +***************************************/ +typedef struct timespec timespec; + +timespec diff(timespec start, timespec end) { + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +void printTimeSpec(timespec t, const char* prefix) { + printf("%s: %ld.%09ld seconds\n", prefix, t.tv_sec, t.tv_nsec); +} + +timespec sum(timespec t1, timespec t2) { + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } else { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +/*************************************** +* Multiplication functions +***************************************/ +int32_t fixmul(int32_t x, int32_t y) { + return ((int64_t)x * y) >> FRAC_Q; +} + +float fpmul(float x, float y) { + return x * y; +} + +/*************************************** +* Main function to test performance +***************************************/ +int main() { + srand(time(NULL)); + + int32_t int_a, int_b; + float fp_a, fp_b; + + timespec start_time, end_time, total_time_int, total_time_fp; + total_time_int.tv_sec = total_time_int.tv_nsec = 0; + total_time_fp.tv_sec = total_time_fp.tv_nsec = 0; + + for (int i = 0; i < ITERATIONS; i++) { + int_a = rand() % FRAC_BASE; + int_b = rand() % FRAC_BASE; + fp_a = (float)int_a / FRAC_BASE; + fp_b = (float)int_b / FRAC_BASE; + + // Test fixed-point multiplication + clock_gettime(CLOCK_MONOTONIC, &start_time); + int32_t result_int = fixmul(int_a, int_b); + clock_gettime(CLOCK_MONOTONIC, &end_time); + total_time_int = sum(total_time_int, diff(start_time, end_time)); + + // Test floating-point multiplication + clock_gettime(CLOCK_MONOTONIC, &start_time); + float result_fp = fpmul(fp_a, fp_b); + clock_gettime(CLOCK_MONOTONIC, &end_time); + total_time_fp = sum(total_time_fp, diff(start_time, end_time)); + } + + printTimeSpec(total_time_int, "Total time for fixed-point multiplication"); + printTimeSpec(total_time_fp, "Total time for floating-point multiplication"); + + return 0; +} diff --git a/applications/newton/llvm-ir/c-files/test_floating_point_operations.c b/applications/newton/llvm-ir/c-files/test_floating_point_operations.c new file mode 100644 index 000000000..8db03e799 --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_floating_point_operations.c @@ -0,0 +1,264 @@ +#include +#include +#include +#include +#include +#include +#include +#define FRAC_Q 10 +#define FRAC_BASE (1 << FRAC_Q) +#define BIT_WIDTH 32 +#define ITERATION 1 + +#include "MadgwickAHRS.h" +/*************************************** + * Timer functions of the test framework + ***************************************/ + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +// Quantization function +int +quantize(float value, int frac_base) +{ + return round(value * frac_base); +} + +// Dequantization function +float +dequantize(int quantized_value) +{ + return (float)(quantized_value >> FRAC_Q); +} + +//extern int perform_addition(float a, float b); +extern int perform_addition(int a ,int b); +// extern int perform_subtraction(float a, float b); +//extern int perform_multiplication(int a, int b); +//extern int perform_multiplication(float a, float b); +//extern void MadgwickAHRSupdate(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t mx, int32_t my, int32_t mz, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); +//extern void MadgwickAHRSupdateIMU(int32_t gx, int32_t gy, int32_t gz, int32_t ax, int32_t ay, int32_t az, int32_t * q0_ptr, int32_t * q1_ptr, int32_t * q2_ptr, int32_t * q3_ptr); + +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} + +int +main() +{ + int num1 = 10; + int num2 = 3; + //extern int perform_multiplication(int a, int b); + //int num3 = perform_multiplication(num1, num2); + int num4 = perform_addition(num1, num2); + //printf("num3: %d\n", num3); + printf("num4: %d\n", num4); + + + + + // float num1 = 8.10; + // printf("num1: %f\n", num1); + // float num2 = 2.25; + // printf("num2: %f\n", num2); + // printf("FRAC_BASE: %d\n", FRAC_BASE); + // + // int a = 10; + // int b = 3; + // + // float num3 = 0; + // float num4 = 0; + // float num5 = 0; + // float num6 = 0; + // + // int quantized_num1 = quantize(num1, FRAC_BASE); + // int quantized_num2 = quantize(num2, FRAC_BASE); + // + // int addition_result = perform_addition(num1, num2); + // float actual_addition_result = dequantize(addition_result); + // + // printf("Addition Result after quantization: %d\n", addition_result); + // printf("expected Addition Result after dequantization: %f\n",dequantize(quantized_num1 + quantized_num2)); + // printf("Actual Addition Result after dequantization: %f\n", actual_addition_result); + // + // + // // Perform original operations to compare results later + // float original_addition = num1 + num2; + // float original_subtraction = num1 - num2; + // float original_multiplication = num1 * num2; + // float original_division = num1 / num2; + // printf("Original Addition: %f\n", original_addition); + // printf("Original Subtraction: %f\n", original_subtraction); + // + // int subtraction_result = perform_subtraction(num1, num2); + // float actual_subtraction_result = dequantize(subtraction_result); + // printf("Subtraction Result after quantization: %d\n", subtraction_result); + // printf("Actual Subtraction Result after dequantization: %f\n", actual_subtraction_result); + + // Quantization of input numbers + + // int subtraction_result = perform_subtraction(a, b); + // printf("Subtraction Result: %d\n", subtraction_result); + + // // Perform operations on the quantized numbers + // float addition = perform_addition(num1, num2); + // + // float subtraction = perform_subtraction(num1, num2); + // + // float multiplication = perform_multiplication(num1, num2); + // + // float division = perform_division(num1, num2); + // + // + // // Dequantization of actual results + // float my_dequantized_addition = dequantize(addition, FRAC_BASE); + // float my_dequantized_subtraction = dequantize(subtraction, FRAC_BASE); + // float my_dequantized_multiplication = dequantize( multiplication, FRAC_BASE * FRAC_BASE); + // float my_dequantized_division = division; + // + // + // num3 = (my_dequantized_addition + my_dequantized_subtraction) / 2; + // num4 = (my_dequantized_addition -my_dequantized_subtraction) / 2; + // printf("num3: %f\n", num3); + // printf("num4: %f\n", num4); + // + // + // num5 = sqrt(my_dequantized_multiplication*my_dequantized_division); + // printf("xdeduced : %f\n", num5); + // num6 = sqrt(my_dequantized_multiplication/my_dequantized_division); + // printf("num6: %f\n", num6); + // + // + // + // + + // printf("Expected after Dequantized Addition: %f\n", dequantize(quantized_num1 + quantized_num2, FRAC_BASE)); + // printf("Actual after Dequantized Addition: %f\n", my_dequantized_addition); + // + // printf("Original Subtraction: %f\n", original_subtraction); + // printf("Expected after Dequantized Subtraction: %f\n", dequantize(quantized_num1 - quantized_num2, FRAC_BASE)); + // printf("Actual after Dequantized Subtraction: %f\n", my_dequantized_subtraction); + // + // printf("Original Multiplication: %f\n", original_multiplication); + // + // printf("Expected after Dequantized Multiplication: %f\n", dequantize((int)((long long)quantized_num1 * quantized_num2), FRAC_BASE * FRAC_BASE)); + // //printf("Expected after Dequantized Multiplication: %f\n", dequantize((quantized_num1 * quantized_num2), (FRAC_BASE * FRAC_BASE))); + // printf("Actual after Dequantized Multiplication: %f\n", my_dequantized_multiplication); + // + // printf("Original Division: %f\n", original_division); + // printf("Expected after Dequantized Division: %f\n", (float)quantized_num1 / quantized_num2 ); + // printf("Actual after Dequantized Division: %f\n", my_dequantized_division); + +// float num1 = -7.249095917; +// float num2 = 26.43893433; +// float num3 = -37.16656494; +// float num4 = -0.1184487343; +// float num5 = 0.001258035656; +// float num6 = 0.008988874033; +// float num7 = -0.3570917249; +// float num8 = 0.3941296637; +// float num9 = 9.963726044; +// +// float num10 = 0.64306622; +// float num11 = 0.02828862; +// float num12 = -0.00567953; +// float num13 = -0.76526684; +// +// int32_t mag_x = quantize(num1, FRAC_BASE); +// int32_t mag_y = quantize(num2, FRAC_BASE); +// int32_t mag_z = quantize(num3, FRAC_BASE); +// int32_t gyr_x = quantize(num4, FRAC_BASE); +// int32_t gyr_y = quantize(num5, FRAC_BASE); +// int32_t gyr_z = quantize(num6, FRAC_BASE); +// int32_t acc_x = quantize(num7, FRAC_BASE); +// int32_t acc_y = quantize(num8, FRAC_BASE); +// int32_t acc_z = quantize(num9, FRAC_BASE); +// int32_t q0 = quantize(num10, FRAC_BASE); +// int32_t q1 = quantize(num11, FRAC_BASE); +// int32_t q2 = quantize(num12, FRAC_BASE); +// int32_t q3 = quantize(num13, FRAC_BASE); +// +// u_int64_t time_slots[ITERATION]; +// +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// timespec timer = tic(); +// // for (size_t ts = 0; ts < DATA_SIZE; ts++) +// //{ +// // MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], +// // acc_x[ts], acc_y[ts], acc_z[ts], +// // mag_x[ts], mag_y[ts], mag_z[ts], +// // &q0[ts], &q1[ts], &q2[ts], &q3[ts]); +// //} +// +// MadgwickAHRSupdate(gyr_x, gyr_y, gyr_z, +// acc_x, acc_y, acc_z, +// mag_x, mag_y, mag_z, +// &q0, &q1, &q2, &q3); +// time_slots[idx] = toc(&timer, "computation delay").tv_nsec; +// } +// +// u_int64_t average_time = 0; +// for (size_t idx = 0; idx < ITERATION; idx++) +// { +// average_time += time_slots[idx]; +// } +// average_time /= ITERATION; +// printf("average time = %lu nm\n", average_time); + + return 0; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/test_sensfusion6.c b/applications/newton/llvm-ir/c-files/test_sensfusion6.c new file mode 100644 index 000000000..57610c3ab --- /dev/null +++ b/applications/newton/llvm-ir/c-files/test_sensfusion6.c @@ -0,0 +1,308 @@ +/* +* Madgwick test case (run locally) +* Compilation command in applications/newton/llvm-ir/performance_test/Makefile +* +* How to compile and run? +* 1. `make perf_madgwick` FP hardware (by default) +* 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) +* 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) +* 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format +* */ + +#include +#include +#include +#include +#include +#include +#include + +#if defined(INT_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#include "sensfusion6.h" +#elif defined(FP_DATA_TYPE) +extern volatile float q0, q1, q2, q3; +#if defined(SOFT_FLOAT_LIB) +#include "MadgwickAHRS_softfloat.h" +#else +#include "sensfusion6.h" +#endif + +#else +#error "Must set data type: FP or INT" +#endif + +#define DATA_SIZE 1000 +#define ITERATION 10 + +/*************************************** +* Timer functions of the test framework +***************************************/ + + +typedef struct timespec timespec; +timespec +diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) + { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } + else + { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec +sum(timespec t1, timespec t2) +{ + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) + { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } + else + { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void +printTimeSpec(timespec t, const char * prefix) +{ + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +timespec +tic() +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec +toc(timespec * start_time, const char * prefix) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_consump = diff(*start_time, current_time); + printTimeSpec(time_consump, prefix); + *start_time = current_time; + return time_consump; +} + +int +main() +{ + FILE * fp = fopen("input.csv", "r"); + + if (!fp) + { + printf("Can't open file\n"); + return -1; + } + + double time[DATA_SIZE]; +#if defined(INT_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + +// q0[i] = 1.0f; +// q1[i] = 0.0f; +// q2[i] = 0.0f; +// q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; + +#elif defined(FP_DATA_TYPE) + float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; + // quaternion of sensor frame relative to auxiliary frame + for (int i = 0; i < DATA_SIZE; i++) + { + q0[i] = 0.64306622f; + q1[i] = 0.02828862f; + q2[i] = -0.00567953f; + q3[i] = -0.76526684f; + + // q0[i] = 1.0f; + // q1[i] = 0.0f; + // q2[i] = 0.0f; + // q3[i] = 0.0f; + } + + float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], + gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], + acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; +#else +#error "Must set data type: FP or INT" +#endif + + char buffer[1024]; + int row = 0, column = 0; + while (fgets(buffer, 1024, fp)) + { + column = 0; + row++; + + if (row == 1) + continue; + + char * value = strtok(buffer, ", "); + + while (value) + { + switch (column) + { + case 0: + time[row - 2] = atof(value); + break; + case 1: +#if defined(INT_DATA_TYPE) + + mag_x[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_x[row - 2] = atof(value); +#endif + break; + case 2: +#if defined(INT_DATA_TYPE) + + mag_y[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_y[row - 2] = atof(value); +#endif + break; + case 3: +#if defined(INT_DATA_TYPE) + + mag_z[row - 2] = atof(value); +#elif defined(FP_DATA_TYPE) + mag_z[row - 2] = atof(value); +#endif + break; + case 4: +#if defined(INT_DATA_TYPE) + // gyr_x[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_x[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_x[row - 2] = atof(value) * 61; +#endif + break; + case 5: +#if defined(INT_DATA_TYPE) + // gyr_y[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_y[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_y[row - 2] = atof(value) * 61; +#endif + break; + case 6: +#if defined(INT_DATA_TYPE) + // gyr_z[row - 2] = round(atof(value) * FRAC_BASE) * 61; + gyr_z[row - 2] = atof(value) * 61; +#elif defined(FP_DATA_TYPE) + gyr_z[row - 2] = atof(value) * 61; +#endif + break; + case 7: +#if defined(INT_DATA_TYPE) + // acc_x[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_x[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_x[row - 2] = atof(value) * 2; +#endif + break; + case 8: +#if defined(INT_DATA_TYPE) + // acc_y[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_y[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_y[row - 2] = atof(value) * 2; +#endif + break; + case 9: +#if defined(INT_DATA_TYPE) + // acc_z[row - 2] = round(atof(value) * FRAC_BASE) * 2; + acc_z[row - 2] = atof(value) * 2; +#elif defined(FP_DATA_TYPE) + acc_z[row - 2] = atof(value) * 2; +#endif + break; + default: + break; + } + + value = strtok(NULL, ", "); + column++; + } + } + + fclose(fp); + + u_int64_t time_slots[ITERATION]; + + for (size_t idx = 0; idx < ITERATION; idx++) + { + + + timespec timer = tic(); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + sensfusion6UpdateQImpl(gyr_x[ts], gyr_y[ts], gyr_z[ts], + acc_x[ts], acc_y[ts], acc_z[ts], + &q0[ts], &q1[ts], &q2[ts], &q3[ts]); + } + + + + time_slots[idx] = toc(&timer, "computation delay").tv_nsec; + } + + u_int64_t average_time = 0; + for (size_t idx = 0; idx < ITERATION; idx++) + { + average_time += time_slots[idx]; + } + average_time /= ITERATION; + printf("average time = %lu nm\n", average_time); + +#if defined(FP_DATA_TYPE) + FILE * fptr = fopen("fp_sensfusion6_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); +#elif defined(INT_DATA_TYPE) + FILE * fptr = fopen("int_sensfusion6_result.txt", "w"); + for (size_t ts = 0; ts < DATA_SIZE; ts++) + { + fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", + ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); + } + fclose(fptr); + + + +#endif + return 0; +} diff --git a/applications/newton/llvm-ir/euler_angle_error.py b/applications/newton/llvm-ir/euler_angle_error.py new file mode 100644 index 000000000..a465dd929 --- /dev/null +++ b/applications/newton/llvm-ir/euler_angle_error.py @@ -0,0 +1,75 @@ +import math + +# Function to convert quaternions to Euler angles +def quaternion_to_euler(q0, q1, q2, q3): + roll = math.atan2(2 * (q0 * q1 + q2 * q3), 1 - 2 * (q1**2 + q2**2)) + pitch = math.asin(max(-1.0, min(1.0, 2 * (q0 * q2 - q3 * q1)))) # Clamp to avoid numerical errors + yaw = math.atan2(2 * (q0 * q3 + q1 * q2), 1 - 2 * (q2**2 + q3**2)) + return roll, pitch, yaw + +# Parse the quaternion values from a file +def parse_quaternions(filename, prefix): + quaternions = [] + with open(filename, 'r') as file: + for line in file: + if line.startswith(prefix): + parts = line.split(',') + q0 = float(parts[0].split('=')[1].strip()) + q1 = float(parts[1].split('=')[1].strip()) + q2 = float(parts[2].split('=')[1].strip()) + q3 = float(parts[3].split('=')[1].strip()) + quaternions.append((q0, q1, q2, q3)) + return quaternions + +# Calculate RMS error between two lists of Euler angles +def calculate_rms_error(fp_euler, int_euler): + errors_roll = [] + errors_pitch = [] + errors_yaw = [] + overall_errors = [] + + for fp, integer in zip(fp_euler, int_euler): + roll_error = math.degrees(fp[0] - integer[0]) # Convert to degrees + pitch_error = math.degrees(fp[1] - integer[1]) + yaw_error = math.degrees(fp[2] - integer[2]) + + errors_roll.append(roll_error**2) + errors_pitch.append(pitch_error**2) + errors_yaw.append(yaw_error**2) + + # Accumulate the overall squared error for all components + overall_errors.append(roll_error**2 + pitch_error**2 + yaw_error**2) + + rms_roll = math.sqrt(sum(errors_roll) / len(errors_roll)) + rms_pitch = math.sqrt(sum(errors_pitch) / len(errors_pitch)) + rms_yaw = math.sqrt(sum(errors_yaw) / len(errors_yaw)) + + # Overall RMS error + rms_overall = math.sqrt(sum(overall_errors) / len(overall_errors)) + + return rms_roll, rms_pitch, rms_yaw, rms_overall + +# Main function to compute the average Euler angle errors +def main(): + fp_quaternions = parse_quaternions('fp_result.txt', 'Original') + int_quaternions = parse_quaternions('int_result.txt', 'FIX') + + if len(fp_quaternions) != len(int_quaternions): + print("Mismatch in the number of quaternions between files!") + return + + # Convert quaternions to Euler angles + fp_euler_angles = [quaternion_to_euler(*q) for q in fp_quaternions] + int_euler_angles = [quaternion_to_euler(*q) for q in int_quaternions] + + # Calculate RMS error for roll, pitch, yaw, and overall + rms_roll, rms_pitch, rms_yaw, rms_overall = calculate_rms_error(fp_euler_angles, int_euler_angles) + + print(f"RMS Error (φₑ - Roll): {rms_roll:.6f} degrees") + print(f"RMS Error (θₑ - Pitch): {rms_pitch:.6f} degrees") + print(f"RMS Error (ψₑ - Yaw): {rms_yaw:.6f} degrees") + print(f"Overall RMS Error: {rms_overall:.6f} degrees") + +# Run the main function +if __name__ == "__main__": + main() diff --git a/applications/newton/llvm-ir/fft.sh b/applications/newton/llvm-ir/fft.sh new file mode 100755 index 000000000..a7f591367 --- /dev/null +++ b/applications/newton/llvm-ir/fft.sh @@ -0,0 +1,28 @@ +USER_HOME=$HOME + + +# Step 1: Generate LLVM IR file +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/fft.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/fft.c + + +# Step 4: Optimize the generated LLVM IR file +opt $HOME/CoSense/applications/newton/llvm-ir/fft.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/fft.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +## Step 9: Compile the test file and link with the static library +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +# +## Step 10: Run the test executable +#$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/fracq.py b/applications/newton/llvm-ir/fracq.py new file mode 100644 index 000000000..c0f13827f --- /dev/null +++ b/applications/newton/llvm-ir/fracq.py @@ -0,0 +1,59 @@ +import numpy as np + +# Function to parse the data from the files after removing the prefix +def parse_data(file_path, prefix_to_remove): + """ + Reads a file and extracts q0, q1, q2, q3 values from each line after removing the specified prefix. + Returns a numpy array of the extracted values. + """ + with open(file_path, 'r') as file: + data = [] + for line in file: + if line.startswith(prefix_to_remove): + # Remove the prefix and strip the line + clean_line = line.replace(prefix_to_remove, "").strip() + values = clean_line.split(", ") + # Extract q0, q1, q2, q3 values and convert them to floats + parsed_values = [float(value.split("=")[1]) for value in values] + data.append(parsed_values) + + # Debug: Print the number of parsed lines and the first few entries + if len(data) == 0: + print(f"No data found in file: {file_path}") + else: + print(f"Parsed {len(data)} lines from {file_path}") + print("First few entries:", data[:3]) + + return np.array(data) + +# Load and clean the data from both files +fp_data = parse_data('fp_result.txt', prefix_to_remove="Original: ") +int_data = parse_data('int_result.txt', prefix_to_remove="FIX: ") + +# Check if data is loaded correctly +if fp_data.size == 0 or int_data.size == 0: + print("Error: One of the datasets is empty. Please check the input files.") +else: + # Calculate the absolute difference between the floating point and integer results + error = np.abs(fp_data - int_data) + + # Calculate the mean and max errors for each q value (q0, q1, q2, q3) + mean_error = np.mean(error, axis=0) + max_error = np.max(error, axis=0) + + # Display the results + print("Mean Error for each q value:") + print(f"q0: {mean_error[0]:.6f}, q1: {mean_error[1]:.6f}, q2: {mean_error[2]:.6f}, q3: {mean_error[3]:.6f}") + + print("\nMax Error for each q value:") + print(f"q0: {max_error[0]:.6f}, q1: {max_error[1]:.6f}, q2: {max_error[2]:.6f}, q3: {max_error[3]:.6f}") + + # Display the summary of errors in a dictionary + error_summary = { + "Mean Error": mean_error, + "Max Error": max_error + } + + # Print the summary dictionary + print("\nError Summary:") + print(error_summary) diff --git a/applications/newton/llvm-ir/input.csv b/applications/newton/llvm-ir/input.csv new file mode 100644 index 000000000..ce7c7c2b3 --- /dev/null +++ b/applications/newton/llvm-ir/input.csv @@ -0,0 +1,1001 @@ +Time (s),Magnetic field x (µT),Magnetic field y (µT),Magnetic field z (µT),Gyroscope x (rad/s),Gyroscope y (rad/s),Gyroscope z (rad/s),Acceleration x (m/s^2),Acceleration y (m/s^2),Acceleration z (m/s^2) +0.07969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.08969094,-6.654678345,26.09465027,-37.29600525,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.09969094,-7.249095917,26.43893433,-37.16656494,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.10969094,-7.548351288,24.87819672,-39.49668884,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.11969094,-7.311084747,23.94067001,-40.19360352,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.12969094,-5.958099365,24.8057251,-37.20587158,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.13969094,-8.100597382,25.37778282,-37.85224915,-0.1184487343,0.001258035656,0.008988874033,-0.3570917249,0.3941296637,9.963726044 +0.14969094,-6.394374847,25.43608284,-37.70759583,-0.1151283234,-0.00251355581,0.01010152325,-0.2793728709,0.4129949808,10.27848434 +0.15969094,-7.610450745,26.11058998,-38.66853333,-0.08246348053,0.00447106408,0.004323757719,-0.2367789149,0.4112356901,10.43229961 +0.16969094,-6.729293823,25.38845825,-38.77174377,-0.03473320603,0.04428362101,0.0009580431506,-0.2995947599,0.416141957,10.24356842 +0.17969094,-6.832756042,26.19889259,-38.67654419,0.002444048412,0.07756645232,-0.003223320469,-0.2556326687,0.3563099205,9.911549568 +0.18969094,-7.576393127,26.12816811,-37.14372253,0.007744635455,0.09112588316,-0.008515236899,-0.1705993563,0.3450968266,9.673620224 +0.19969094,-5.582935333,26.2094841,-38.06962585,-0.007077907212,0.1003924161,-0.01438220032,-0.2904628515,0.4137096405,9.693481445 +0.20969094,-7.26531601,26.16348839,-37.14692688,-0.0214833729,0.07035970688,-0.01663675718,-0.4015436172,0.4240173995,9.765337944 +0.21969094,-7.610450745,26.11058998,-38.66853333,-0.02840357646,0.0412395969,-0.01503071003,-0.4420829713,0.4474136829,9.710318565 +0.22969094,-5.917541504,25.49433899,-37.25494385,-0.04069363698,0.0300014019,-0.01120906137,-0.5065568686,0.4349407554,9.780290604 +0.23969094,-5.917541504,25.49433899,-37.25494385,-0.04557721689,0.01997687295,-0.003380747745,-0.4558943808,0.4060772061,9.982199669 +0.24969094,-7.248287201,26.17227745,-36.38452148,-0.03875789419,-0.002376349643,-0.001149083488,-0.4019374549,0.4131945372,10.07145596 +0.25969094,-5.484790802,27.59550095,-37.40536499,-0.02963040024,-0.007826571353,-0.0005718094762,-0.3436793387,0.4178147614,10.06400871 +0.26969094,-7.59992218,25.44834328,-36.33224487,-0.01951537654,-0.001477465499,-0.004841145594,-0.2960692346,0.3756780624,9.936094284 +0.27969094,-7.536643982,27.08343887,-37.97483826,-0.01999245957,-0.006206189282,-0.007511992939,-0.3121205568,0.3395512402,9.855197906 +0.28969094,-5.894504547,25.77330017,-36.96961975,-0.02960216627,-0.0179349836,-0.009124945849,-0.3137557507,0.3006750047,9.795846939 +0.29969094,-5.791351318,25.63038635,-38.94366455,-0.04506304488,-0.02701679431,-0.006459381431,-0.3230510056,0.3795295656,9.721902847 +0.30969094,-6.642169952,27.03585815,-39.3469696,-0.06745177507,-0.03394386917,-0.005393324886,-0.3160211146,0.3835323155,9.728869438 +0.31969094,-8.179725647,26.60138512,-37.78651428,-0.07811312377,-0.03392977268,-0.003795302706,-0.3117079139,0.3867061734,9.83324337 +0.32969094,-6.671707153,26.08586121,-38.05841064,-0.07162205875,-0.02754923515,-0.002687801374,-0.3693574667,0.4109267592,9.983849525 +0.33969094,-7.483146667,25.31245995,-37.69638062,-0.05240149051,-0.02055237256,0.006894228514,-0.4506206214,0.425347209,10.05073643 +0.34969094,-7.178882599,25.34426498,-38.00453186,-0.01982024126,-0.008340923116,0.01644631103,-0.424624294,0.4272369146,10.15809345 +0.35969094,-5.974441528,27.26355934,-37.68589783,0.01485640649,-0.002841048408,0.0174930077,-0.322455287,0.4224477112,10.03915787 +0.36969094,-5.750793457,26.31900024,-38.99273682,0.03709132969,0.01580360159,0.01747041196,-0.2883752882,0.3374709487,9.90159893 +0.37969094,-5.870170593,26.18646812,-36.99906921,0.03729007766,0.02409679629,0.01276430115,-0.2753283978,0.2819014192,9.6376791 +0.38969094,-7.402835846,26.9563446,-38.57655334,0.01491038781,0.01591248065,0.01207613945,-0.2228651792,0.2963768244,9.694525719 +0.39969094,-8.981754303,26.09991455,-37.74905396,-0.008029373363,-0.004363908432,0.009553089738,-0.3137707412,0.3058240712,9.756849289 +0.40969094,-7.179374695,24.94340134,-36.9077301,-0.0325601548,-0.01107135322,0.008988874033,-0.347237438,0.2963377833,9.893450737 +0.41969094,-6.377658844,26.11239243,-38.82402039,-0.0477651991,-0.02031742781,0.01180266216,-0.2945395708,0.2534381747,10.08242321 +0.42969094,-7.576393127,26.12816811,-37.14372253,-0.03290509433,-0.03066522814,0.01591363735,-0.3064144552,0.2915987968,10.09379768 +0.43969094,-6.654678345,26.09465027,-37.29600525,-0.007921610959,-0.01810567081,0.01840451546,-0.220240429,0.3167188168,10.02454567 +0.44969094,-7.910011292,26.21474838,-38.52267456,0.01434707176,0.005370598286,0.01262276806,-0.2308089137,0.3418518007,9.914393425 +0.45969094,-7.616950989,25.43955421,-37.09465027,0.02976943925,0.02135305665,0.01048317552,-0.2660395503,0.3493344486,9.844152451 +0.46969094,-6.706256866,25.66741943,-38.48641968,0.03813938051,0.02182977088,0.005633147899,-0.216332078,0.3326821923,9.803571701 +0.47969094,-6.727798462,27.58842468,-37.70732117,0.0336618945,0.00669084629,-0.002252099337,-0.3407348692,0.3455455303,9.784972191 +0.48969094,-6.272701263,27.50192642,-37.85481262,0.02714404091,-0.02482098155,-7.733469829e-06,-0.4290958643,0.3308064342,9.846453667 +0.49969094,-7.497264862,24.9045639,-37.20947266,0.02085882053,-0.04777231812,0.005195887294,-0.4415936172,0.3619870543,9.872394562 +0.50969094,-6.638454437,26.37009621,-37.31562805,0.01971525326,-0.06770654023,0.007923526689,-0.3541632593,0.3599542379,9.863398552 +0.51969094,-7.459617615,25.99228477,-38.50785828,0.02144834772,-0.05833188444,0.005967103411,-0.3571015596,0.2838820815,9.942513466 +0.52969094,-6.602794647,24.85698509,-38.581604,0.03338546306,-0.04018680751,0.002523483243,-0.3252090514,0.3449226618,9.981822014 +0.53969094,-6.37815094,25.71152878,-37.72721863,0.0395555906,-0.03105190583,0.002672136296,-0.3542988896,0.3391857445,9.916908264 +0.54969094,-7.507480621,24.89929199,-37.66693115,0.04480022192,-0.009855329059,0.002203115728,-0.2928415239,0.3543523848,9.862686157 +0.55969094,-7.940353394,25.53140831,-38.0161438,0.04905298352,0.03105917946,-0.004327977076,-0.2486828268,0.414686501,9.692631721 +0.56969094,-6.474308014,26.92634201,-38.42385864,0.04452182725,0.05528639257,-0.009296388365,-0.2784487903,0.3446758687,9.670860291 +0.57969094,-6.825942993,26.20240784,-38.37158203,0.0319024995,0.02479611523,-0.006458672695,-0.3158861697,0.380629003,9.788747787 +0.58969094,-6.840065002,25.7945137,-37.88470459,0.02158891037,-0.03473115712,-0.001664606389,-0.3231438696,0.411952734,9.852827072 +0.59969094,-7.162658691,25.61971092,-38.02415466,0.01061961707,-0.06082195044,-0.003890550463,-0.2853017449,0.4193702936,9.816333771 +0.60969094,-6.856781006,25.11820412,-36.76826477,0.007375336252,-0.03639098257,-0.006122777238,-0.2694948912,0.4136307836,9.676003456 +0.61969094,-6.896842957,24.83045387,-37.81599426,0.002672176808,-0.001061701681,-0.01018739119,-0.3234909773,0.3764295876,9.812074661 +0.62969094,-6.792694092,26.48664284,-37.6288147,0.005003871396,0.01006212085,-0.008162576705,-0.4266119301,0.3720042706,10.06733036 +0.63969094,-6.353816986,26.12469673,-37.75666809,0.02046790347,-0.01681301743,0.0007936262991,-0.3262727559,0.3997677267,10.05027294 +0.64969094,-7.610450745,26.11058998,-38.66853333,0.03206149116,-0.02037302963,0.0003540727776,-0.2678869963,0.4512276053,9.878255844 +0.65969094,-6.743099213,24.31304169,-36.40600586,0.0311081633,0.002321796725,-0.003910094965,-0.3650930524,0.3835353553,9.755164146 +0.66969094,-7.426364899,26.27651978,-37.76507568,0.01962944865,0.01086440869,0.0001691966318,-0.2833980918,0.3181568384,9.629114151 +0.67969094,-8.06734848,25.66201782,-37.10948181,-0.003784938715,-0.009464945644,0.004316138569,-0.2693619728,0.3249111176,9.749714851 +0.68969094,-8.06734848,25.66201782,-37.10948181,-0.01996190473,-0.01890161261,0.004569322802,-0.2541345954,0.3612316251,9.785536766 +0.69969094,-6.71957016,24.99286652,-37.21748352,-0.03055388108,-0.002701988444,0.001403293107,-0.2876216173,0.42038095,9.840220451 +0.70969094,-6.71957016,24.99286652,-37.21748352,-0.02469141968,0.02064591087,0.0002037035301,-0.3182426989,0.4196154475,9.82704258 +0.71969094,-6.593383789,25.12891579,-38.90620422,-0.01536074467,0.02345331945,-0.0008636235725,-0.3334631324,0.4254368544,9.891711235 +0.72969094,-6.681922913,26.08058739,-38.51586914,0.0003667054698,0.02293391153,0.00113602588,-0.3242745101,0.4562799931,9.878070831 +0.73969094,-6.498645782,26.51317406,-38.39442444,0.01448426675,0.03026067652,0.000355088152,-0.3454433978,0.4558205009,9.850791931 +0.74969094,-6.310348511,26.41420555,-36.556427,0.02660195902,0.02360223606,0.002740718424,-0.3830767274,0.4391977191,9.813574791 +0.75969094,-7.249095917,26.43893433,-37.16656494,0.03125537932,-0.009130412713,0.006767154206,-0.3287489414,0.4146726429,9.829097748 +0.76969094,-8.217372894,25.51366615,-36.48812866,0.02221588045,-0.04891215265,0.01043653488,-0.2150225341,0.3947498798,9.832125664 +0.77969094,-8.854763031,25.96930504,-38.65571594,0.01269038301,-0.05699107051,0.007309804205,-0.2531299889,0.4025529325,9.883623123 +0.78969094,-7.472930908,25.31773376,-37.23892212,0.009331553243,-0.0362610966,0.007923526689,-0.2762732506,0.3899558783,9.84967804 +0.79969094,-5.784229279,24.96638107,-36.75984192,0.001331721433,-0.02163651586,0.007390852552,-0.234718129,0.4027428627,9.921022415 +0.80969094,-7.957382202,25.52261925,-38.77854919,-0.001178442501,-0.02419010922,0.007390852552,-0.1688663065,0.3537279963,9.899970055 +0.81969094,-7.96679306,25.25068855,-38.45397949,-0.0002859253436,-0.02208984457,0.003662134986,-0.1612079442,0.3765885234,9.860362053 +0.82969094,-6.607620239,27.45429993,-38.91896057,-0.005500952248,-0.00137518486,0.002451392356,-0.2259380519,0.4388239086,9.777145386 +0.83969094,-6.706256866,25.66741943,-38.48641968,-0.008655028418,0.03205142915,0.001516730525,-0.2650729716,0.4043938816,9.713522911 +0.84969094,-6.545207977,25.55438805,-37.86827087,-0.01504812483,0.05091909319,0.004732759669,-0.2684932947,0.3750241399,9.674304008 +0.85969094,-7.96679306,25.25068855,-38.45397949,-0.02855567634,0.04647941142,0.006329850759,-0.2255412191,0.4341191947,9.723275185 +0.86969094,-7.983821869,25.24189949,-39.21638489,-0.03407438844,0.03285332024,0.009157052264,-0.2946357727,0.4097974002,9.758426666 +0.87969094,-6.569046021,25.54208374,-38.93565369,-0.04476465285,0.03424095362,0.007229163777,-0.3785450459,0.4446446896,9.875383377 +0.88969094,-7.569580078,26.13168335,-36.83876038,-0.04716154933,0.02489669435,0.01244034618,-0.3616573513,0.4302352071,10.03662205 +0.89969094,-7.520793915,24.22473907,-36.397995,-0.03513527289,0.009162386879,0.01834226586,-0.3369231224,0.354245156,9.933807373 +0.90969094,-6.706256866,25.66741943,-38.48641968,-0.03387806937,5.956785753e-05,0.01586125046,-0.2603001595,0.413443327,9.827401161 +0.91969094,-8.26845932,25.48729897,-38.77534485,-0.0300873816,0.01511435304,0.008654415607,-0.2779739201,0.3738090396,9.869602203 +0.92969094,-5.997970581,26.58373451,-36.87442017,-0.02735616267,0.04325161129,0.004194807727,-0.3075370789,0.3841251135,9.859921455 +0.93969094,-6.103237152,25.86172104,-39.7224884,-0.02637853473,0.05735059083,0.003129459452,-0.320104301,0.373578757,9.733283043 +0.94969094,-7.90920639,25.94809151,-37.74064636,-0.03651535884,0.0361250788,0.005883715581,-0.3417606056,0.3402200043,9.643527985 +0.95969094,-7.259311676,26.43366051,-37.62400818,-0.05156690255,0.009104821831,0.006214913446,-0.3017233014,0.362526238,9.718540192 +0.96969094,-6.360630035,26.12118149,-38.06161499,-0.05347704142,0.01731107384,0.002591064665,-0.2577753067,0.3784553707,9.827540398 +0.97969094,-6.320072174,26.80979538,-38.11068726,-0.03843300045,0.03169715032,-0.0004796904977,-0.3363775015,0.3911237717,9.874797821 +0.98969094,-8.060348511,26.73391724,-39.78018188,-0.02594162524,0.03712494671,0.0003746603616,-0.4213152528,0.352683723,9.925812721 +0.99969094,-6.769851685,24.69984436,-38.72267151,-0.01860057376,0.03343068063,0.002202911768,-0.4492088258,0.3678621352,10.10691547 +1.00969094,-5.576122284,26.21299934,-37.76467896,-0.001166775823,0.01629613154,0.006246880163,-0.4433344305,0.3575173914,10.19894409 +1.01969094,-7.339931488,25.45729637,-38.62266541,0.02069848031,0.005792571232,0.009291321039,-0.3940068483,0.3785350621,9.979031563 +1.02969094,-4.853103638,25.20479393,-37.23670959,0.02874404564,0.01429859176,0.008222779259,-0.3645473719,0.3816692829,9.829014778 +1.03969094,-6.648670197,26.36482239,-37.77308655,0.02584087476,0.01577451825,0.008752264082,-0.3319935501,0.391680181,9.704668045 +1.04969094,-7.364269257,25.04412842,-38.5932312,0.01320957113,0.02140369639,0.004434604198,-0.2616887093,0.3081431389,9.435370445 +1.05969094,-5.186531067,27.35713577,-37.23646545,-0.01679551601,0.01554101706,0.00070909434,-0.3204491436,0.3072315753,9.469326973 +1.06969094,-6.095619202,25.59858131,-38.63548279,-0.04527019337,-0.02895073779,0.003868098371,-0.3079995215,0.3426241875,9.777582169 +1.07969094,-8.571918488,25.18883705,-37.68516541,-0.05302047729,-0.07532060146,0.007323132362,-0.3083209097,0.3275064826,9.900536537 +1.08969094,-6.433750153,27.6149559,-38.47293091,-0.04696979001,-0.09256293625,0.009241476655,-0.3125772476,0.3005108833,9.937273979 +1.09969094,-7.12210083,26.30832481,-38.07322693,-0.03368562087,-0.08957409114,0.008200418204,-0.2579268813,0.3488419354,9.958529472 +1.10969094,-7.961097717,26.1883812,-40.80989075,-0.01956947893,-0.11456047,0.01081642881,-0.3258158267,0.3308687806,10.03633881 +1.11969094,-6.585765839,24.86577415,-37.81919861,0.004703424871,-0.1317160726,0.01432392001,-0.2255369574,0.315831691,10.02883244 +1.12969094,-6.65839386,26.76041222,-39.3273468,0.03163328022,-0.09282163531,0.007392741274,-0.2747437358,0.4104028642,9.873939514 +1.13969094,-8.267654419,25.22064209,-37.99331665,0.03962652758,-0.04242435843,0.003125044052,-0.2230227441,0.3918129504,9.700381279 +1.14969094,-8.267654419,25.22064209,-37.99331665,0.0334565714,-0.01739601418,-0.0003491293173,-0.2507834136,0.3877517879,9.647592545 +1.15969094,-6.522174835,25.83334923,-37.58294678,0.01399359573,-0.01048641745,-0.0005820093211,-0.2868594825,0.3809897602,9.612992287 +1.16969094,-6.522174835,25.83334923,-37.58294678,-0.009211212397,0.002303284593,-0.002197280526,-0.2456134111,0.3624066412,9.478822708 +1.17969094,-7.220245361,24.92230797,-38.73748779,-0.03906954825,-0.005264308304,-0.002449053805,-0.223413676,0.3052503765,9.675016403 +1.18969094,-8.320838928,26.32410049,-36.39294434,-0.04616601393,-0.01693219133,0.0009987638332,-0.2595686018,0.3547726274,9.831622124 +1.19969094,-6.297035217,27.08875656,-37.82536316,-0.04363083839,-0.01177432761,0.006406869274,-0.3021802008,0.4283012152,9.923987389 +1.20969094,-5.802066803,25.22424889,-38.30429077,-0.03484881297,-0.002998926677,0.005745526869,-0.3050480485,0.3657292128,10.12125111 +1.21969094,-8.227901459,26.17591286,-38.82441711,-0.01179379877,-0.006921730004,0.009910203516,-0.3035609126,0.3256982863,10.10582829 +1.22969094,-6.744827271,27.57963562,-38.46972656,-0.0006591975689,-0.01147360541,0.0132014323,-0.2544778585,0.2881220877,9.779053688 +1.23969094,-7.299373627,26.14591026,-38.67173767,-0.0105999019,-0.00578986574,0.01048056968,-0.1460589617,0.283655107,9.60635376 +1.24969094,-7.473735809,25.58439064,-38.02095032,-0.03279230744,0.02829709277,-0.0002586811315,-0.1317581385,0.2602887452,9.617964745 +1.25969094,-6.430034637,26.94919395,-36.44160461,-0.04902238026,0.0402559936,-0.007753454149,-0.2250817716,0.3019514382,9.776272774 +1.26969094,-6.785385132,26.89102173,-38.4206543,-0.04932562262,0.03157758713,-0.007301884238,-0.2387727499,0.3511135578,9.836483002 +1.27969094,-7.3736763,24.77219772,-38.26863098,-0.04486268014,0.04647804797,-0.007624792866,-0.2069167346,0.3134610355,9.70259285 +1.28969094,-6.866500854,25.51379395,-38.32250977,-0.04943052307,0.0468153283,-0.008807333186,-0.2923846543,0.272872448,9.821870804 +1.29969094,-6.055557251,25.88633156,-37.5877533,-0.04847415164,0.01401153393,3.832438961e-05,-0.2815147936,0.2507653534,9.938878059 +1.30969094,-8.090877533,24.98219299,-36.29800415,-0.0390965566,-0.007415878586,0.002172368579,-0.3992255032,0.3018830121,10.052248 +1.31969094,-6.433750153,27.6149559,-38.47293091,-0.01272503659,0.01556072105,0.006858177949,-0.3525747061,0.3768803477,10.04895401 +1.32969094,-5.842926025,26.20053101,-35.77920532,0.009118192829,0.05159369111,0.003864748403,-0.3418606818,0.3400193453,9.91121769 +1.33969094,-6.688735962,26.07707214,-38.82081604,0.02072456852,0.05234477669,0.00173030328,-0.3416000307,0.3463162482,9.807590485 +1.34969094,-7.080738068,26.73028183,-37.340271,0.02005904168,0.03351046145,0.001531437505,-0.2878206968,0.3056016266,9.60395813 +1.35969094,-8.100597382,25.37778282,-37.85224915,0.005226348527,0.03811069578,-0.003207825124,-0.2599823773,0.2751262188,9.588534355 +1.36969094,-6.607307434,26.7867794,-37.04013062,-0.01296652295,0.01011377014,-0.003455437953,-0.2150486112,0.2730696797,9.68800354 +1.37969094,-7.91601944,25.94457626,-38.04559326,-0.02920342982,-0.03429959714,-0.003262628801,-0.1792165935,0.256131053,9.581644058 +1.38969094,-6.480808258,26.25530624,-36.84997559,-0.05070129409,-0.048120372,-0.005925999023,-0.1703465581,0.202443108,9.596740723 +1.39969094,-5.802066803,25.22424889,-38.30429077,-0.06068585813,-0.06144545227,-0.00397459697,-0.2702684999,0.2518742383,9.811048508 +1.40969094,-6.799507141,26.48312759,-37.93377686,-0.05394846946,-0.08207222819,0.003652619664,-0.3534995615,0.319263339,9.85598278 +1.41969094,-5.760700226,25.6462059,-37.57131958,-0.03585629165,-0.04909897596,0.01270254329,-0.3109368086,0.2954011559,9.816308975 +1.42969094,-7.402030945,26.68968773,-37.79452515,-0.02535336465,-0.01209592633,0.01307334937,-0.3471710384,0.3598883748,9.775583267 +1.43969094,-7.899482727,25.55250168,-36.18638611,-0.01951367408,-0.001619831193,0.0187486317,-0.3248218298,0.3403574526,9.822806358 +1.44969094,-7.186187744,24.93988609,-37.212677,-0.01519426517,-0.008781705052,0.02070770226,-0.3171883821,0.308766067,9.850348473 +1.45969094,-6.514865875,26.23772812,-38.37478638,-0.02264731191,-0.0292735938,0.01713754982,-0.2401963323,0.2161890417,9.744996071 +1.46969094,-7.224758148,26.85210228,-37.19599915,-0.03933265805,-0.05343721062,0.01005422324,-0.0907349363,0.2035675794,9.581429482 +1.47969094,-6.823036194,25.80330276,-37.12229919,-0.06534571946,-0.08001222461,0.001245157328,-0.05353847891,0.21554254,9.619607925 +1.48969094,-5.802066803,25.22424889,-38.30429077,-0.08012887836,-0.1155415028,-0.008874082938,-0.07394784689,0.2216080427,9.712301254 +1.49969094,-7.926235199,25.93930244,-38.50305176,-0.08614850789,-0.1406992674,-0.01109797321,-0.1761151403,0.2405264229,9.942374229 +1.50969094,-8.060848236,26.33305359,-38.68336487,-0.06363983452,-0.131477356,-0.008736706339,-0.1644631177,0.2284149528,10.12247753 +1.51969094,-5.934570312,25.48554993,-38.01734924,-0.03686951846,-0.1067624614,-0.009261385538,-0.2282977104,0.1928204149,10.15298653 +1.52969094,-6.481929779,27.18948364,-39.51086426,-0.01791328192,-0.08309015632,-0.00313524832,-0.2743725479,0.21198605,10.15187454 +1.53969094,-7.875953674,26.23232651,-36.99786377,-0.008983654901,-0.07694666088,0.002590309829,-0.2886238396,0.1790893525,10.07465935 +1.54969094,-7.529022217,26.82029724,-36.88783264,-0.009366864339,-0.07829668373,0.01138180681,-0.1872850209,0.1983563304,9.777256966 +1.55969094,-7.105072021,26.31711388,-37.31082153,-0.03254691884,-0.04712443799,0.01258446462,-0.1739229262,0.2279125303,9.533390045 +1.56969094,-7.194416046,27.53544235,-37.70251465,-0.05746591091,-0.001762664411,0.009799225256,-0.1670654416,0.1957996339,9.661688805 +1.57969094,-7.11448288,26.04518318,-36.98625183,-0.06772604585,0.003953762818,0.0142403841,-0.2083566934,0.2039169073,9.748625755 +1.58969094,-7.978923798,27.4436245,-37.99946594,-0.07317771018,-0.02151427604,0.02289812267,-0.2312756628,0.2643887997,9.760647774 +1.59969094,-5.639026642,27.71204758,-37.71853638,-0.06665766984,-0.04223006219,0.02425481752,-0.1424741298,0.2968740463,9.914733887 +1.60969094,-5.639026642,27.71204758,-37.71853638,-0.04367075861,-0.04056430608,0.0234865211,-0.1608794183,0.3256749511,10.09056568 +1.61969094,-7.868648529,26.6367054,-37.78971863,-0.008530608378,-0.04159038514,0.02200041711,-0.189670369,0.2899492383,10.21168613 +1.62969094,-7.868648529,26.6367054,-37.78971863,0.02628235519,-0.05120419711,0.02283840254,-0.2218288481,0.3283816576,10.20733452 +1.63969094,-6.303535461,26.41772079,-36.2514801,0.05489103496,-0.03783921897,0.02347395197,-0.07787474245,0.2735392451,9.849228859 +1.64969094,-8.169509888,26.60665894,-37.32905579,0.04711384699,-0.003802698106,0.01128105074,-0.0208355207,0.2059793174,9.5058918 +1.65969094,-6.614925385,27.04992104,-38.12710571,0.01760414243,0.01516226307,0.0005188670475,-0.01495215017,0.2007467449,9.477257729 +1.66969094,-7.610450745,26.11058998,-38.66853333,-0.0118032163,0.003432350932,-0.004136003554,-0.1004913151,0.2752893269,9.617282867 +1.67969094,-5.836917877,26.47070312,-36.25628662,-0.03128357977,-0.01314315572,-0.003888061969,-0.1696598381,0.2897028029,9.776549339 +1.68969094,-8.07756424,25.656744,-37.56692505,-0.03902820498,-0.031104194,0.0003777854145,-0.1696388274,0.3016649187,9.855332375 +1.69969094,-8.01266861,26.75852585,-37.64543152,-0.0361819528,-0.04390206188,0.004108215682,-0.08676551282,0.2340180129,9.961453438 +1.70969094,-6.815727234,26.20768166,-37.91413879,-0.02684909478,-0.06165890396,0.004194807727,-0.08061584085,0.2390247434,10.00907612 +1.71969094,-8.774143219,26.94566917,-37.65705872,-0.006290666759,-0.06052558869,0.005260156002,-0.09469732642,0.2780058086,9.99105072 +1.72969094,-6.751327515,26.90859985,-36.89584351,0.009505183436,-0.03102665953,0.002138116863,-0.1390231103,0.2646279931,9.884099007 +1.73969094,-7.415344238,26.01513672,-36.52558899,0.005679448135,0.004524962511,0.001461617649,-0.008892144077,0.1571245342,9.586317062 +1.74969094,-6.648670197,26.36482239,-37.77308655,-0.01966399699,0.02597477287,-0.003588251071,-0.02148039639,0.06291545182,9.521533966 +1.75969094,-7.916511536,25.54371262,-36.9487915,-0.04305823147,0.0223974213,-0.003466613824,-0.0427948162,0.1390912533,9.611943245 +1.76969094,-8.504920959,26.15816879,-37.29640198,-0.05958180875,0.0147998305,-0.003130199853,-0.0402473025,0.1930487752,9.696281433 +1.77969094,-7.408531189,26.01865196,-36.22064209,-0.06862412393,0.02192832902,-0.002607707866,-0.1067857593,0.1676802784,9.801758766 +1.78969094,-7.651504517,25.02111244,-37.5226593,-0.06073463336,0.009087075479,-0.00118764746,-0.1356327534,0.1706100255,9.846673965 +1.79969094,-7.519615173,27.09222794,-37.21243286,-0.05577040464,-0.02627890185,0.006700108293,-0.1201330051,0.2078993767,9.865870476 +1.80969094,-7.292560577,26.14942551,-38.36677551,-0.045699507,-0.05088428408,0.009521549568,-0.1055382341,0.194249332,9.946352005 +1.81969094,-8.941196442,26.78852844,-37.79812622,-0.02589777857,-0.03927788138,0.007972843945,-0.1263555437,0.2195701152,9.982694626 +1.82969094,-8.136257172,26.89089394,-36.58627319,-0.0022355197,-0.007897994481,0.00841078721,-0.13340047,0.2100664824,9.810973167 +1.83969094,-7.91601944,25.94457626,-38.04559326,0.006072483025,0.02714213356,0.008006671444,-0.08557648957,0.1570303142,9.632401466 +1.84969094,-6.8097229,26.47785378,-38.39122009,-0.003367578145,0.0620284602,0.001605473459,-0.1225117221,0.1410100162,9.67570591 +1.85969094,-7.31608963,25.46960068,-37.55529785,-0.0129718259,0.06267061085,0.002064112108,-0.1651064754,0.1498811394,9.766771317 +1.86969094,-8.440029144,27.25995255,-37.37492371,-0.01939735189,0.02667776495,0.001498597208,-0.07774727792,0.1837403625,9.869438171 +1.87969094,-6.269790649,27.10281944,-36.60549927,-0.01915593445,-0.02103452012,-0.003069892991,-0.06515015662,0.1889760047,9.92991066 +1.88969094,-6.245456696,27.5159874,-36.63494873,-0.006940102205,-0.03827885538,-0.005393324886,-0.1165964454,0.1897324771,9.984901428 +1.89969094,-8.234401703,25.50487709,-37.25053406,0.01909381524,-0.03864673525,-0.004890202545,-0.1061906591,0.1626597643,10.02290344 +1.90969094,-6.614612579,26.38240051,-36.24827576,0.04109552875,-0.06041933596,-0.003773062024,-0.1020373926,0.171957165,9.990610123 +1.91969094,-7.17206955,25.34778023,-37.69958496,0.05656939,-0.05699628592,-0.003241203493,-0.09723923355,0.2134001702,9.856469154 +1.92969094,-8.266967773,27.6872654,-37.7109375,0.05981186032,-0.0220338013,-0.0090006385,-0.09895791858,0.1922320724,9.676743507 +1.93969094,-7.080738068,26.73028183,-37.340271,0.04462248087,0.01481336262,-0.01446673647,-0.07513602078,0.1447238624,9.591789246 +1.94969094,-5.692897797,26.34888268,-36.40054321,0.01682722196,0.01619235054,-0.01550634205,-0.04328014329,0.08844234794,9.693587303 +1.95969094,-8.033290863,25.67959595,-35.58467102,-0.004610041622,-0.01159483008,-0.01657947898,-0.06921155751,0.1747955829,9.817505836 +1.96969094,-6.930095673,24.54621887,-38.55877686,-0.01155486237,-0.02273061872,-0.01285894588,-0.1380582452,0.2311763912,9.925704956 +1.97969094,-7.033367157,27.42241287,-37.08439636,-0.007763602771,-0.007799980231,-0.0128642004,-0.08171288669,0.2393242121,9.866889954 +1.98969094,-8.105110168,27.30757713,-36.31077576,0.000226826407,0.007088335231,-0.01391610876,-0.07211033255,0.1989753842,9.786602974 +1.99969094,-8.221088409,26.1794281,-38.51947021,0.007151844911,0.01721406542,-0.0149721168,-0.1541464329,0.2528064251,9.78149128 +2.00969094,-6.014190674,26.30828857,-36.8547821,0.01787129417,0.0273047667,-0.01124131307,-0.2166621983,0.3024373949,9.920562744 +2.01969094,-6.497837067,26.24651718,-37.61238098,0.02909519151,0.02771151997,-0.009713578969,-0.1204545349,0.2341998667,10.05337238 +2.02969094,-7.641288757,25.02638626,-37.06521606,0.03541889042,0.009614607319,-0.01449812017,-0.0005609099171,0.1977932751,9.951425552 +2.03969094,-6.648670197,26.36482239,-37.77308655,0.03854234144,-0.002443780191,-0.02357402071,0.02601853013,0.1741227955,9.71210289 +2.04969094,-6.823036194,25.80330276,-37.12229919,0.03266156837,0.002902421635,-0.03311468288,-0.06614656001,0.2000939846,9.64405632 +2.05969094,-8.481391907,26.83799362,-38.10787964,0.03341981024,0.004892181139,-0.03618502244,-0.1074707657,0.162389487,9.802295685 +2.06969094,-8.481391907,26.83799362,-38.10787964,0.04172888771,-0.004742521793,-0.03478867188,-0.08618438244,0.1765760481,9.902980804 +2.07969094,-9.125282288,26.62259865,-38.70158386,0.04610145465,-0.01111476123,-0.03636684269,-0.1150004193,0.2032267451,9.933595657 +2.08969094,-7.097454071,26.05397224,-36.22384644,0.04376486316,-0.005667108111,-0.03837662563,-0.1557287276,0.1533870399,9.807321548 +2.09969094,-8.169509888,26.60665894,-37.32905579,0.03125278652,-0.001980882138,-0.03598660603,-0.1652607918,0.2026120722,9.855483055 +2.10969094,-7.881961823,25.96215439,-36.52078247,0.0225853771,-0.01276575774,-0.03193943202,-0.1723911315,0.2648062408,9.907776833 +2.11969094,-8.129444122,26.89440918,-36.28132629,0.0193426609,-0.01047625113,-0.02931698225,-0.1844214499,0.2498556525,9.777618408 +2.12969094,-8.090877533,24.98219299,-36.29800415,0.01299724448,0.03212872893,-0.02499904484,-0.1594483554,0.185912326,9.599106789 +2.13969094,-6.583778381,27.46660423,-37.85160828,0.003714879043,0.0774788782,-0.02019474469,-0.1376511902,0.1288460642,9.618491173 +2.14969094,-8.394954681,26.01877022,-38.96549988,-0.004882628098,0.07974929363,-0.01858952455,-0.1264213771,0.2041591108,9.738189697 +2.15969094,-7.352062225,27.65023232,-38.16816711,-0.01172669791,0.06241033226,-0.01871017553,-0.1282311976,0.2556934357,9.854340553 +2.16969094,-6.314064026,27.0799675,-38.58776855,-0.007096226327,0.06087465584,-0.02018002048,-0.140379414,0.2431407869,9.895014763 +2.17969094,-7.603637695,26.11410522,-38.36357117,-0.0009169559926,0.05228283256,-0.02110611834,-0.2296079248,0.2668908536,9.884092331 +2.18969094,-7.875953674,26.23232651,-36.99786377,0.01122065168,0.03513076901,-0.0161176268,-0.2607826889,0.2880160809,9.85874939 +2.19969094,-7.865737915,26.23760033,-36.54040527,0.01991503313,0.01621211134,-0.01369080879,-0.2125987709,0.2349860221,9.828289986 +2.20969094,-7.449401855,25.99755859,-38.05039978,0.02593856305,0.001058021793,-0.01391610876,-0.1139675379,0.1974376589,9.821710587 +2.21969094,-8.971538544,26.10518837,-37.29159546,0.03127063811,-0.00808400847,-0.01858368888,-0.07830224186,0.1740580052,9.894181252 +2.22969094,-8.855258942,25.56844139,-37.55891418,0.04016362131,-0.01468131132,-0.02297156677,-0.08382807672,0.1723902375,9.791069031 +2.23969094,-6.840065002,25.7945137,-37.88470459,0.03510941565,-0.005808715709,-0.02527568862,-0.09655064344,0.2753678262,9.775838852 +2.24969094,-6.344406128,26.39662743,-38.08123779,0.03261594847,-0.001005770639,-0.0267905239,-0.1564678401,0.3032001257,9.954015732 +2.25969094,-6.631149292,26.7744751,-38.10748291,0.03750025481,-0.0004138355143,-0.02738986723,-0.2111902684,0.2499201596,9.906594276 +2.26969094,-5.894012451,26.17416382,-38.06642151,0.03352537006,-0.006534338929,-0.02287421189,-0.185904786,0.256785661,9.880761147 +2.27969094,-7.282344818,26.15469933,-37.90933228,0.02651475742,-0.01083567925,-0.01977552287,-0.1777724028,0.3054589629,9.881853104 +2.28969094,-7.128601074,25.63728905,-36.49934387,0.02215582877,-0.01636308059,-0.01840080321,-0.200542137,0.2789077759,9.947379112 +2.29969094,-8.145980835,27.28648376,-38.14053345,0.0253528133,-0.01774885505,-0.01157522202,-0.2354736328,0.327449441,10.0650425 +2.30969094,-7.559364319,26.13695717,-36.38131714,0.03911292553,-0.01410826668,-0.0002708618995,-0.2110538632,0.3184655607,10.03411579 +2.31969094,-8.043819427,26.34184265,-37.92095947,0.05202747509,0.004895074293,0.007309973706,-0.1512760371,0.3286831975,9.895553589 +2.32969094,-8.00245285,26.76379967,-37.18798828,0.04810490087,0.02173821256,0.008107297122,-0.08198977262,0.3075869679,9.735380173 +2.33969094,-7.408843994,26.68617249,-38.09947205,0.03104375303,0.02506607771,0.008456200361,-0.1293141246,0.3014782965,9.657446861 +2.34969094,-7.115287781,26.31184006,-37.76828003,0.008865046315,0.01515081618,0.01111957058,-0.1489645094,0.3126883209,9.760924339 +2.35969094,-8.885910034,25.55262184,-38.9312439,-0.01044041198,0.005883761216,0.01086863503,-0.1335579604,0.3199432492,9.854743004 +2.36969094,-8.196754456,26.59259605,-38.54891968,-0.01020090934,0.02274373919,0.009521549568,-0.1970662028,0.3293652236,9.818229675 +2.37969094,-8.354896545,26.30652237,-37.91775513,-0.004453454632,0.0365036875,0.01297717914,-0.2366603762,0.3252747357,9.787967682 +2.38969094,-8.15328598,26.88210487,-37.34867859,-0.002847630531,0.03556828201,0.01924502663,-0.2097179592,0.2747732401,9.877931595 +2.39969094,-7.432373047,26.00634766,-37.28799438,0.001555474475,0.03062932193,0.02191158198,-0.1824806482,0.2579643428,9.950833321 +2.40969094,-7.249095917,26.43893433,-37.16656494,0.006780474447,0.03764603287,0.02351267263,-0.1464302391,0.268522054,9.896903038 +2.41969094,-7.10426712,26.050457,-36.52879333,0.01062327344,0.04378296435,0.01857700758,-0.1064542532,0.2074498385,9.702510834 +2.42969094,-7.85161972,26.64549637,-37.02731323,0.002669626847,0.02873747237,0.0129544735,-0.04887576029,0.2210505307,9.567022324 +2.43969094,-7.363460541,24.77747154,-37.81118774,-0.0103612449,0.005181383342,0.009140042588,-0.1335268766,0.2172933221,9.608876228 +2.44969094,-6.644954681,25.69906044,-35.741745,-0.01882448047,-0.004420454614,0.008147323504,-0.09323616326,0.2520536184,9.751316071 +2.45969094,-7.416149139,26.28179359,-37.30761719,-0.02168631367,-0.001290564891,0.004412197508,-0.103556484,0.2825931907,9.850436211 +2.46969094,-8.162696838,26.61017418,-37.02410889,-0.01086263545,0.006069350056,-0.0008704911452,-0.1312154382,0.2413202971,9.851606369 +2.47969094,-7.145629883,25.62849998,-37.26174927,-0.003843489569,0.01436809357,-0.001623692689,-0.1820041239,0.2070912719,9.713765144 +2.48969094,-7.275531769,26.15821457,-37.60437012,-0.009312344715,0.01892914996,-0.001100572641,-0.1220566928,0.1783026159,9.680235863 +2.49969094,-7.507480621,24.89929199,-37.66693115,-0.01717316359,0.02942520566,-0.003816983197,-0.06498123705,0.1999483556,9.725384712 +2.50969094,-7.90920639,25.94809151,-37.74064636,-0.02303904109,0.02805838361,-0.005925999023,-0.1256715059,0.2209081352,9.832509995 +2.51969094,-7.90920639,25.94809151,-37.74064636,-0.02268951386,0.01364582684,-0.003972060047,-0.2051095217,0.225720942,9.763577461 +2.52969094,-6.614120483,26.78326416,-37.34507751,-0.02658636495,0.01437338069,0.002244035713,-0.1648696512,0.2123297453,9.703748703 +2.53969094,-6.614120483,26.78326416,-37.34507751,-0.02037812956,0.02540178038,0.003479044419,-0.1159800962,0.2158698142,9.719527245 +2.54969094,-7.148540497,26.02760506,-38.51106262,-0.0118260067,0.03690432012,0.003315735608,-0.1407853812,0.261910826,9.750429153 +2.55969094,-8.043819427,26.34184265,-37.92095947,-0.006829578429,0.02317268588,0.004384300672,-0.1386529505,0.3286429048,9.795340538 +2.56969094,-8.094593048,25.64795494,-38.32933044,0.003840013407,0.007176350802,0.001100537833,-0.06916922331,0.3458430767,9.878037453 +2.57969094,-6.825942993,26.20240784,-38.37158203,0.008876708336,0.0003457232378,-0.006905544549,-0.08735194057,0.3348065913,9.828860283 +2.58969094,-8.531360626,25.87745094,-37.73423767,0.005340719596,-0.00217997143,-0.01371701993,-0.126806885,0.3439081907,9.871929169 +2.59969094,-6.774856567,26.22877502,-36.08436584,0.0004153251648,-0.01131269149,-0.01645131782,-0.1413100064,0.3308411539,9.938947678 +2.60969094,-7.85161972,26.64549637,-37.02731323,-0.004786434583,-0.003533433191,-0.02084087208,-0.1905422211,0.3367064893,9.995604515 +2.61969094,-6.839752197,25.12699318,-36.00585938,-0.003631051164,0.02111057006,-0.02646948397,-0.1596919447,0.3178349733,9.962801933 +2.62969094,-6.727798462,27.58842468,-37.70732117,-0.002874007449,0.04105066508,-0.02904283628,-0.1408151686,0.3369486332,9.898715019 +2.63969094,-7.459617615,25.99228477,-38.50785828,-0.008197206073,0.04637994617,-0.03437278792,-0.1868165284,0.3002360463,9.948604584 +2.64969094,-7.563079834,26.80271912,-38.41264343,-0.009471255355,0.04070337862,-0.03384329379,-0.1625285,0.3253901303,10.03444958 +2.65969094,-6.765449524,26.50070572,-36.40896606,0.001410008408,0.02944688685,-0.03491182253,-0.1994553506,0.3648633659,9.999612808 +2.66969094,-6.336788177,26.13348579,-36.9942627,0.009185058065,0.03163524717,-0.03544767573,-0.2142654508,0.3202452958,9.846342087 +2.67969094,-7.402030945,26.68968773,-37.79452515,0.01375771593,0.03424095362,-0.03149435297,-0.1759790331,0.2779944837,9.759856224 +2.68969094,-6.60439682,26.38767433,-35.79081726,0.0170276463,0.04406008124,-0.02959464677,-0.1392083019,0.281003803,9.720905304 +2.69969094,-8.084377289,25.65322876,-37.87188721,0.01672596484,0.04845323414,-0.03155862913,-0.1621757448,0.2420970947,9.75135231 +2.70969094,-6.504650116,26.24300194,-37.91734314,0.009563808329,0.04708301276,-0.02664239891,-0.1740728915,0.2218662202,9.769033432 +2.71969094,-7.81186676,27.60076523,-37.8584137,-7.901713252e-05,0.03207566217,-0.02283397503,-0.26397264,0.2458071113,9.813800812 +2.72969094,-7.875461578,26.63319016,-38.09466553,-0.007686767727,0.004682029597,-0.0156044066,-0.2840597332,0.180323258,9.903401375 +2.73969094,-6.792198181,26.88750458,-38.72561646,-0.008024005219,-0.02341856062,-0.004444222432,-0.2712144554,0.2306922525,9.956364632 +2.74969094,-7.576889038,25.72730446,-36.04692078,0.001746371388,-0.02758159675,0.001749235205,-0.2706443071,0.2705234885,9.986332893 +2.75969094,-7.259311676,26.43366051,-37.62400818,0.01849942282,-0.009009528905,0.01289303042,-0.2955932617,0.2673327327,9.963009834 +2.76969094,-7.399120331,26.29058266,-36.54521179,0.03639570996,0.008357273415,0.01962275989,-0.2361007482,0.2875300348,9.913796425 +2.77969094,-7.133922577,27.83374023,-35.73989868,0.04758138955,0.0147917401,0.02016180754,-0.1789459288,0.2186049521,9.904644012 +2.78969094,-7.132316589,26.30305099,-38.53068542,0.05693943799,0.003899033181,0.02071453631,-0.09112010896,0.210464716,9.896371841 +2.79969094,-7.495277405,27.50539398,-37.24186707,0.0550789088,-0.01156693138,0.01165349409,-0.1010814682,0.2988709211,9.751430511 +2.80969094,-7.276340485,26.42487144,-38.38641357,0.04573579505,-0.02328784391,0.003650189843,-0.1644459963,0.2800166309,9.548920631 +2.81969094,-7.063709259,26.73907089,-36.5778656,0.01850998029,-0.01182917133,-0.004340337589,-0.1469102055,0.2510396242,9.466347694 +2.82969094,-6.741111755,26.91387177,-36.43840027,-0.004324021749,-0.01375587657,-0.008865067735,-0.1834320128,0.2701443136,9.711741447 +2.83969094,-6.631641388,26.37361145,-37.01068115,-0.001867462881,-0.02513966337,-0.0110240262,-0.1669787318,0.3215048611,9.896787643 +2.84969094,-7.425559998,26.0098629,-36.98304749,0.01628770307,-0.02488586679,-0.01466791332,-0.2579631507,0.3567141593,9.860242844 +2.85969094,-8.243812561,25.2329464,-36.92596436,0.02688464522,-0.004169604741,-0.01387823559,-0.2582223117,0.2771977484,9.736753464 +2.86969094,-8.193843842,26.19349098,-37.29960632,0.02200842649,0.03450661153,-0.01309495978,-0.272795558,0.2449849695,9.676970482 +2.87969094,-6.823036194,25.80330276,-37.12229919,0.006453162991,0.05085517466,-0.006890017539,-0.3061911166,0.2199545354,9.631800652 +2.88969094,-7.40933609,26.28530884,-37.00267029,-0.01606200263,0.04909879714,0.003005686682,-0.2995644808,0.201525405,9.73643589 +2.89969094,-7.641288757,25.02638626,-37.06521606,-0.02808204293,0.04346767068,0.006027473602,-0.1448681653,0.272339046,9.628669739 +2.90969094,-7.46692276,25.58790588,-37.71600342,-0.04177628458,0.04816025496,0.002225785982,-0.116304405,0.2411215901,9.453680038 +2.91969094,-6.624336243,26.77799034,-37.80253601,-0.05434793979,0.06665814668,-0.006078326143,-0.2214545161,0.2327720374,9.527388573 +2.92969094,-5.72095108,26.60147667,-38.4024353,-0.06135207415,0.06435563415,-0.004860650748,-0.2514836788,0.2456260026,9.681171417 +2.93969094,-7.189098358,25.33899117,-38.46199036,-0.05704882741,0.0275369063,0.0003332057968,-0.2850147784,0.2860251069,9.833475113 +2.94969094,-7.529335022,27.48781586,-38.76667786,-0.04562484473,0.002622469561,0.00259678578,-0.2471337616,0.2965653837,9.949759483 +2.95969094,-8.473773956,26.5748539,-37.02090454,-0.02654643729,0.0005687286612,0.001860674936,-0.2698851526,0.3216832578,10.0168438 +2.96969094,-7.249095917,26.43893433,-37.16656494,-0.006175159011,-6.634369493e-05,0.001427219715,-0.2811920643,0.2962718606,10.01611328 +2.97969094,-7.450206757,26.26421547,-38.83242798,0.006415885873,-0.007441923022,0.002711253706,-0.2567391992,0.20896779,9.862981796 +2.98969094,-7.450206757,26.26421547,-38.83242798,0.0108665498,-0.009281657636,0.00197905954,-0.1455981433,0.2174957097,9.758725166 +2.99969094,-6.447872162,27.20706177,-37.98605347,0.004828017205,0.01278313715,-0.00765129365,-0.09340839088,0.2501848638,9.700984001 +3.00969094,-6.447872162,27.20706177,-37.98605347,-0.003581494559,0.02978005633,-0.01544828713,-0.1543234587,0.2795874476,9.821495056 +3.01969094,-8.49810791,26.16168594,-36.99145508,-0.009767705575,0.02843782678,-0.01857014373,-0.2008584142,0.3034571409,9.921107292 +3.02969094,-8.06734848,25.66201782,-37.10948181,-0.01092633884,0.01583724841,-0.01798436232,-0.2255139798,0.2753494382,9.935453415 +3.03969094,-6.695236206,25.40603638,-37.24693298,-0.01146858186,0.002507640515,-0.01536135934,-0.2470640689,0.2939111292,9.952013969 +3.04969094,-7.316898346,25.73625755,-38.33734131,-0.01291702595,-0.005841163918,-0.01194457337,-0.2390746474,0.2554622889,9.902705193 +3.05969094,-7.374786377,26.70375061,-36.57466125,-0.01152374595,-0.02042293362,-0.009471164085,-0.2179525644,0.2555968761,9.93608284 +3.06969094,-7.333118439,25.46081161,-38.31770325,-0.008825613186,-0.03340864927,-0.007532409392,-0.2380346656,0.3015532494,9.908273697 +3.07969094,-8.468456268,24.37840271,-37.78034973,-0.005647540092,-0.02399409376,-0.006282707676,-0.1862966716,0.2948144078,9.761417389 +3.08969094,-6.474308014,26.92634201,-38.42385864,-0.008505098522,0.01370482426,-0.008589369245,-0.2072754353,0.290933311,9.65144825 +3.09969094,-7.194416046,27.53544235,-37.70251465,-0.01222948264,0.04708583653,-0.009292855859,-0.1612217426,0.2714120448,9.722549438 +3.10969094,-7.488464355,27.50891113,-36.93690491,-0.01520463172,0.05751100183,-0.009822325781,-0.2179809064,0.2705034912,9.771174431 +3.11969094,-7.842208862,26.91742516,-37.35188293,-0.01752957702,0.04657791555,-0.01018739119,-0.20446226,0.3115225434,9.746862411 +3.12969094,-6.607307434,26.7867794,-37.04013062,-0.02082055621,0.03212425858,-0.01252835803,-0.284610033,0.3318831623,9.818239212 +3.13969094,-8.050319672,25.67080688,-36.34707642,-0.02162656561,0.006840092596,-0.01146937907,-0.2710173726,0.335179776,9.85300827 +3.14969094,-6.631641388,26.37361145,-37.01068115,-0.01494333707,-0.003114380408,-0.01216324605,-0.293641299,0.3532663584,9.856822014 +3.15969094,-8.227588654,25.50839233,-36.94558716,-0.00701501593,0.003271889873,-0.01338343509,-0.3079952896,0.2997109592,9.897614479 +3.16969094,-7.115287781,26.31184006,-37.76828003,0.0001519322395,0.005005017854,-0.01125273854,-0.2283413708,0.3019617796,9.842337608 +3.17969094,-7.956577301,25.25596237,-37.996521,0.008300120942,-0.003143170848,-0.01512670144,-0.1571785808,0.2411979139,9.994022369 +3.18969094,-6.794185638,24.2866745,-38.69322205,0.01882113516,-0.01369971037,-0.02005962282,-0.1790753752,0.2787266076,10.03287792 +3.19969094,-7.600730896,25.71500015,-37.11428833,0.0226082243,-0.01807713695,-0.01910396852,-0.2966942191,0.2534326017,9.856265068 +3.20969094,-7.275531769,26.15821457,-37.60437012,0.01845968887,-0.02622262016,-0.01432321966,-0.2260305434,0.2392146587,9.849489212 +3.21969094,-8.507831573,26.55727577,-38.54571533,0.01486025844,-0.02195211686,-0.01205309667,-0.1015152261,0.2238649428,9.757564545 +3.22969094,-8.193843842,26.19349098,-37.29960632,0.007541217841,-0.009552078322,-0.01419021189,-0.1901240647,0.2519818842,9.725886345 +3.23969094,-8.170314789,26.87331581,-38.11108398,-0.002043720335,-0.002106974367,-0.01097225025,-0.3190883398,0.2920241356,9.870922089 +3.24969094,-7.138324738,26.03287888,-38.05360413,-0.002122517675,0.001215161756,-0.002811893821,-0.3439500332,0.3194630742,9.970935822 +3.25969094,-5.583427429,25.80862045,-36.9728241,0.004528208636,0.01517218351,0.0009987638332,-0.246371001,0.294268012,9.926848412 +3.26969094,-6.74079895,26.24635315,-34.55955505,0.01069999952,0.0292138122,0.003129459452,-0.2038469911,0.2858913839,9.858620644 +3.27969094,-7.333118439,25.46081161,-38.31770325,0.01235124376,0.03107888252,0.0003867314663,-0.1960114241,0.3041076958,9.8361063 +3.28969094,-6.695236206,25.40603638,-37.24693298,0.01681809872,0.02263225801,4.35502734e-05,-0.2625229657,0.3344432414,9.877408981 +3.29969094,-8.365112305,26.30124855,-38.37519836,0.01864273101,0.02571816929,-0.001450675773,-0.3248307705,0.3089620173,9.92305851 +3.30969094,-5.703922272,26.61026573,-37.64002991,0.0246123001,0.02688729018,-0.004978229757,-0.1456692666,0.2753237486,9.839014053 +3.31969094,-6.840065002,25.7945137,-37.88470459,0.01809304953,0.03596933186,-0.01544332877,-0.1127016768,0.1825166345,9.618488312 +3.32969094,-7.235782623,27.11348534,-38.43548584,0.0004593646154,0.0279977899,-0.0206976831,-0.2280323654,0.2231822312,9.626499176 +3.33969094,-8.49810791,26.16168594,-36.99145508,-0.01300951093,-0.008212786168,-0.01676802337,-0.2872509062,0.2894925773,9.755972862 +3.34969094,-8.084686279,26.32074928,-39.75073242,-0.01604039222,-0.02611956932,-0.01259132661,-0.3058491349,0.2855690122,9.846001625 +3.35969094,-7.926235199,25.93930244,-38.50305176,-0.01576715335,-0.02967993356,-0.007701023482,-0.2758152187,0.2458155155,9.883844376 +3.36969094,-7.593421936,26.11937904,-37.90612793,-0.0165593382,-0.02559154853,-0.004947154783,-0.2677181363,0.2292198688,9.824416161 +3.37969094,-7.408531189,26.01865196,-36.22064209,-0.01147498749,-0.008676798083,-0.000233884668,-0.288110882,0.2577656209,9.797032356 +3.38969094,-7.148540497,26.02760506,-38.51106262,-0.006633196957,0.01344986074,-0.0008964412846,-0.3024396598,0.3181247711,9.880328178 +3.39969094,-6.470592499,26.26058006,-36.39253235,0.00531923119,0.03014460765,0.0009987638332,-0.2945103049,0.3188195527,9.94841671 +3.40969094,-8.007350922,24.56207466,-38.40490723,0.01756862178,0.03873810172,0.00289370399,-0.2079023421,0.2359249741,9.747755051 +3.41969094,-10.57120132,25.9057312,-39.25784302,0.01010619756,0.05189544708,-0.001664606389,-0.1738738865,0.1913950294,9.680673599 +3.42969094,-7.552864075,26.80799103,-37.9552002,-0.0005048532039,0.03880349547,-0.008658854291,-0.194183588,0.2359473258,9.722563744 +3.43969094,-8.193843842,26.19349098,-37.29960632,-0.01316022221,0.007196725346,-0.008648898453,-0.3240816891,0.297080785,9.751264572 +3.44969094,-8.193843842,26.19349098,-37.29960632,-0.01896319911,0.002648235299,-0.00752402097,-0.348402977,0.307802856,9.702807426 +3.45969094,-7.282344818,26.15469933,-37.90933228,-0.02673238888,0.003566776402,-0.004272747319,-0.3012737632,0.2431114316,9.759227753 +3.46969094,-7.282344818,26.15469933,-37.90933228,-0.02919545397,-0.002234932035,-6.658444181e-05,-0.3082113266,0.2360342592,9.839511871 +3.47969094,-7.601039886,26.38251877,-38.99313354,-0.02647368982,-0.0003828592598,0.004247863777,-0.3241005838,0.2539158165,9.896888733 +3.48969094,-7.70747757,23.79039574,-36.67192078,-0.01391642354,0.005569877103,0.005652846303,-0.3335336745,0.2924467623,9.974705696 +3.49969094,-7.466117859,25.32124901,-36.93397522,-0.002039574087,0.006009228528,0.008374901488,-0.3174402714,0.2626022398,9.936785698 +3.50969094,-6.774856567,26.22877502,-36.08436584,0.005044433288,-0.002327063121,0.009446952492,-0.3287468851,0.2626532614,9.900091171 +3.51969094,-6.638454437,26.37009621,-37.31562805,0.009231860749,-0.0183471106,0.01005422324,-0.2419033647,0.2037884295,9.751329422 +3.52969094,-8.237312317,25.90398216,-38.49984741,-0.0002129487693,-0.0351838246,0.006396608893,-0.2397907376,0.2444081306,9.79660511 +3.53969094,-7.794345856,28.01041794,-38.19281006,-0.001384953968,-0.05765299499,0.004162475467,-0.2784003317,0.2528136969,9.876300812 +3.54969094,-6.01499939,26.57494545,-37.63682556,0.01022412907,-0.04971811175,0.004277116619,-0.2988288105,0.2813760936,9.848402023 +3.55969094,-7.399120331,26.29058266,-36.54521179,0.02244941518,-0.01563763618,0.004172147717,-0.3169019222,0.3055851161,9.799267769 +3.56969094,-7.289653778,25.75032043,-37.11749268,0.0274567008,0.02185425535,0.001589378342,-0.233324185,0.2500683963,9.69851017 +3.57969094,-7.165569305,26.01881599,-39.27346802,0.02480689436,0.02791698091,-0.003749892116,-0.3112936914,0.2143467516,9.725136757 +3.58969094,-6.753314972,24.30776978,-36.86346436,0.01731849089,0.004670565017,-0.003262628801,-0.3077295125,0.2493745536,9.750574112 +3.59969094,-6.712265015,25.39724731,-38.00933838,0.004657322541,-0.01840234734,0.002560213674,-0.3492403328,0.2538228631,9.572223663 +3.60969094,-8.330558777,26.71968842,-37.94718933,-0.01725715771,-0.02274436876,0.00684737647,-0.3248106241,0.2503971457,9.603363991 +3.61969094,-6.785385132,26.89102173,-38.4206543,-0.02854960784,-0.01157763973,0.008985424414,-0.313901335,0.2334174663,9.79224205 +3.62969094,-6.785385132,26.89102173,-38.4206543,-0.03440004215,0.01084966771,0.01005065627,-0.2761927247,0.2305997759,9.864484787 +3.63969094,-7.546051025,26.81150818,-37.65023804,-0.02321261168,0.03533025831,0.0105829034,-0.2884257436,0.258186996,9.792014122 +3.64969094,-7.489959717,25.3089447,-38.00132751,-0.01417162456,0.04542586207,0.01005422324,-0.3501100838,0.2739596069,9.894083023 +3.65969094,-7.131511688,26.03639412,-37.74865723,-0.0024527614,0.04169920832,0.01005422324,-0.3122108281,0.283588022,9.821797371 +3.66969094,-8.243812561,25.2329464,-36.92596436,0.001286050305,0.04755017161,0.006820009556,-0.3645493984,0.2892889977,9.780883789 +3.67969094,-8.11762619,25.36899376,-38.61465454,0.002876439132,0.03516138345,0.006307389122,-0.3239471614,0.2848519981,9.735665321 +3.68969094,-6.440250397,26.94392014,-36.89904785,-0.0002477457747,0.0002992372029,0.007414806169,-0.2544038296,0.3007819951,9.809139252 +3.69969094,-7.536643982,27.08343887,-37.97483826,0.003989266232,-0.05071535707,0.00947406888,-0.2319714129,0.3080909848,9.878863335 +3.70969094,-6.672199249,25.68499756,-36.96160889,0.008822058327,-0.06696711481,0.008434649557,-0.2179051787,0.2917186916,9.934447289 +3.71969094,-7.923324585,25.54019737,-37.2537384,0.0168600902,-0.05562448502,0.00521544693,-0.1962778419,0.2554365098,9.943925858 +3.72969094,-7.080738068,26.73028183,-37.340271,0.02695737779,-0.04445654154,0.00677522039,-0.2058451921,0.2460807711,9.855082512 +3.73969094,-7.258815765,26.83452415,-38.72080994,0.0257118158,-0.03880500048,0.009023677558,-0.2229058892,0.2702342272,9.8239851 +3.74969094,-8.029697418,26.74973679,-38.40783691,0.01767116413,-0.03354083747,0.01173234172,-0.1730276793,0.293876797,9.859820366 +3.75969094,-8.277870178,25.21536827,-38.45077515,0.00750257913,-0.0134898033,0.01595561951,-0.2170909047,0.2744894028,9.763518333 +3.76969094,-7.784622192,27.61482811,-36.6385498,-0.008115497418,0.004707356449,0.01857700758,-0.1322013289,0.2862198949,9.706252098 +3.77969094,-7.408531189,26.01865196,-36.22064209,-0.01740895212,0.02163187601,0.02066392824,-0.200186044,0.2886405289,9.775326729 +3.78969094,-7.276340485,26.42487144,-38.38641357,-0.01349348295,0.02156052925,0.0227907449,-0.05037189275,0.2798060477,10.02560616 +3.79969094,-8.060535431,25.66553307,-36.80451965,-0.006179019809,0.01282461267,0.01740583777,-0.1050582752,0.304669559,9.953327179 +3.80969094,-6.792694092,26.48664284,-37.6288147,-0.008539781906,0.04109317809,0.01307771914,-0.1367159933,0.2985886037,9.855304718 +3.81969094,-6.528675079,25.16231346,-36.00906372,-0.01666204073,0.06206073612,0.009280072525,-0.1876604408,0.2774381042,9.739678383 +3.82969094,-6.840065002,25.7945137,-37.88470459,-0.03033562005,0.04966237396,0.009583637118,-0.1894158125,0.2180558145,9.681078911 +3.83969094,-8.82151413,26.25354004,-37.91294861,-0.0442554988,0.02925441973,0.007262540516,-0.1306591481,0.2705550194,9.748282433 +3.84969094,-7.875461578,26.63319016,-38.09466553,-0.04479436576,0.01652778126,0.002259462141,-0.1970492601,0.2646980286,9.908373833 +3.85969094,-8.313529968,26.72847748,-37.18478394,-0.03184996918,0.02280884422,-0.00311926892,-0.2717116773,0.2997603416,9.914752007 +3.86969094,-6.546016693,25.82104492,-38.65031433,-0.02147505246,0.03477363288,-0.0003719876986,-0.347087115,0.2509671748,9.840457916 +3.87969094,-5.744480133,25.92165184,-37.59095764,-0.01409151033,0.03734870255,0.003584513441,-0.2748141289,0.2386694402,9.767773628 +3.88969094,-7.956577301,25.25596237,-37.996521,-0.005113671999,0.02281517908,0.005340028089,-0.303516686,0.2496834397,9.829120636 +3.89969094,-7.333118439,25.46081161,-38.31770325,0.003993850201,-0.002983287908,0.00775645813,-0.1925490499,0.3337683082,9.893627167 +3.90969094,-7.333118439,25.46081161,-38.31770325,0.01015300211,-0.02382051758,0.0009108139202,-0.2402744293,0.2796029747,9.859054565 +3.91969094,-8.14566803,26.61896324,-36.26170349,0.01105037984,-0.01546274498,-0.003354544286,-0.2564902306,0.254522115,9.739465714 +3.92969094,-8.180530548,26.86804199,-38.56854248,0.005029072054,-0.003389192745,-0.003890158376,-0.2920319736,0.2168327123,9.626190186 +3.93969094,-8.244125366,25.90046692,-38.80479431,-0.0132009536,-0.01026250795,-0.003068285529,-0.2643445432,0.226190418,9.728908539 +3.94969094,-7.898990631,25.95336533,-37.28318787,-0.01736517623,-0.01826028526,0.0005658168811,-0.2710779905,0.2723842859,9.839493752 +3.95969094,-7.899795532,26.2200222,-38.06521606,-0.01492538489,-0.01595095545,0.0008957325481,-0.2685719728,0.2795063257,9.843836784 +3.96969094,-7.566673279,25.73257828,-35.58947754,-0.01235895604,0.003454227,0.001958799548,-0.3106808662,0.2576106787,9.792434692 +3.97969094,-6.982593536,28.11629868,-36.67601013,-0.01192790549,0.006320843473,0.00643603364,-0.2947559953,0.2017954588,9.794699669 +3.98969094,-7.617759705,25.70621109,-37.87669373,-0.01215213537,-0.005099052563,0.00940784812,-0.251095444,0.2411850542,9.778581619 +3.99969094,-6.648178101,26.76568604,-38.86988831,-0.01203843486,-0.008605884388,0.007923526689,-0.2290615439,0.2813150585,9.819487572 +4.00969094,-7.11448288,26.04518318,-36.98625183,-0.006584511138,-0.001146110706,0.008336946368,-0.2567182481,0.3083212674,9.838685036 +4.01969094,-7.385807037,26.96513367,-37.81414795,0.004388485104,0.01485629193,0.002841629088,-0.2343084961,0.2451559454,9.847247124 +4.02969094,-7.097766876,26.72149277,-38.10267639,0.008203179576,0.02133086324,0.00259678578,-0.2072534859,0.2421934903,9.698073387 +4.03969094,-8.01266861,26.75852585,-37.64543152,-0.0003045490012,0.01985875331,-0.003262628801,-0.1555505842,0.1782817245,9.711576462 +4.04969094,-6.823036194,25.80330276,-37.12229919,-0.01336695347,0.005080743693,-0.006058639847,-0.1943382621,0.2094749659,9.714632034 +4.05969094,-8.01997757,26.35414696,-36.85359192,-0.03109871596,-0.01130758598,-0.008986420929,-0.2392482162,0.2315007001,9.763028145 +4.06969094,-7.651504517,25.02111244,-37.5226593,-0.0400146544,-0.004739507101,-0.01046456024,-0.2673299909,0.2497864366,9.792757988 +4.07969094,-7.242595673,27.10997009,-38.740448,-0.03906309232,0.006150948815,-0.01100350544,-0.2492612153,0.2543903887,9.845363617 +4.08969094,-7.074729919,27.00045395,-37.81735229,-0.03663897514,0.004508738406,-0.01430373639,-0.2727160156,0.224393189,9.859981537 +4.09969094,-6.320072174,26.80979538,-38.11068726,-0.02902301773,-0.007368606515,-0.01376769505,-0.2551684082,0.249186188,9.86097908 +4.10969094,-7.587417603,26.38955116,-38.38320923,-0.02162656561,-0.005713349208,-0.0148298014,-0.2201159149,0.2390990704,9.782792091 +4.11969094,-7.859237671,26.90863609,-38.11428833,-0.02336135507,0.003191094613,-0.01513622142,-0.2662590444,0.2411587536,9.972793579 +4.12969094,-7.305873871,25.4748745,-37.09785461,-0.009118622169,-0.002612174489,-0.01077926904,-0.282861501,0.2332253903,9.922781944 +4.13969094,-6.574367523,27.73853493,-38.17617798,-0.005163447931,-0.009760250337,-0.007363054901,-0.211932838,0.212636292,9.728738785 +4.14969094,-8.435512543,25.33015633,-38.91642761,-0.007328464184,0.003674271284,-0.008220901713,-0.20717749,0.1956729591,9.695152283 +4.15969094,-6.055557251,25.88633156,-37.5877533,-0.01590443403,0.01273629628,-0.009684925899,-0.2054186314,0.205473423,9.685699463 +4.16969094,-7.834903717,27.32180405,-38.14373779,-0.02339531854,-0.001536996569,-0.005243080668,-0.2339800149,0.2187104374,9.73903656 +4.17969094,-8.187030792,26.19700623,-36.99465942,-0.02800761536,-0.02326576225,0.001716356725,-0.2859913707,0.2686445117,9.796771049 +4.18969094,-7.242282867,26.44244957,-36.86160278,-0.02908233926,-0.03056327812,0.003839137033,-0.2978691161,0.2407368124,9.87355423 +4.19969094,-7.145629883,25.62849998,-37.26174927,-0.02445448563,-0.04781426489,0.01024218835,-0.2982214987,0.2228002399,9.95925045 +4.20969094,-8.521144867,25.88272476,-37.27677917,-0.01644831337,-0.06270572543,0.01859445311,-0.3007469773,0.2295433432,9.89234066 +4.21969094,-7.385002136,26.69847679,-37.03211975,-0.01185182575,-0.04533778131,0.02409035899,-0.2922000289,0.2343971729,9.830682755 +4.22969094,-7.899482727,25.55250168,-36.18638611,-0.009681108408,-0.02127336524,0.02458944358,-0.1705660373,0.2193717957,9.817026138 +4.23969094,-6.296230316,26.82210159,-37.04333496,-0.008695719764,-0.004451250657,0.01799800247,-0.1621389687,0.2197939306,9.757343292 +4.24969094,-6.509967804,28.43945312,-37.15789795,-0.01495374739,0.002140143886,0.01305411384,-0.2275846601,0.2546940744,9.810265541 +4.25969094,-7.916511536,25.54371262,-36.9487915,-0.02169186994,8.451147005e-05,0.008854851127,-0.2209734768,0.2321222872,9.868536949 +4.26969094,-7.392620087,26.96161842,-38.11909485,-0.02849860862,-0.001703454647,0.006528029684,-0.1220632419,0.1541925073,9.71141243 +4.27969094,-6.614120483,26.78326416,-37.34507751,-0.04428917542,0.01810290664,-0.002197280526,-0.2801389396,0.1237404421,9.710455894 +4.28969094,-7.392307281,26.2940979,-36.24026489,-0.04955516383,0.02923793532,0.0008839904331,-0.2690411806,0.03041800112,9.910918236 +4.29969094,-7.131511688,26.03639412,-37.74865723,-0.03079982474,0.03252982348,-0.002942085499,-0.226893425,0.06968209893,10.19825363 +4.30969094,-8.054035187,26.33656883,-38.37840271,0.008452025242,0.07177698612,0.002925334033,-0.2675082386,0.2150973082,10.1730938 +4.31969094,-7.242595673,27.10997009,-38.740448,0.04162428528,0.1026687622,0.005137396511,-0.3166666329,0.2443601489,10.15234661 +4.32969094,-7.234973907,26.84682846,-37.65344238,0.07101240754,0.09248439223,0.005703500006,-0.2230912894,0.2666797042,9.999241829 +4.33969094,-9.125778198,26.221735,-37.6047821,0.0769181326,0.04123746604,0.003129459452,-0.1260693818,0.2381351441,9.691967964 +4.34969094,-6.775665283,26.4954319,-36.8664093,0.05713566393,-0.01057719067,-0.007675570901,-0.1283408254,0.208796829,9.449485779 +4.35969094,-7.322902679,25.46608543,-37.86026001,0.021940846,-0.03525879979,-0.0164392069,-0.2080651373,0.2594524622,9.481989861 +4.36969094,-7.322902679,25.46608543,-37.86026001,-0.012359038,-0.04465859383,-0.02090466022,-0.33076033,0.2426670343,9.544941902 +4.37969094,-6.873313904,25.5102787,-38.62747192,-0.03849985823,-0.04358688742,-0.01385076717,-0.4688956439,0.2643616199,9.787895203 +4.38969094,-6.873313904,25.5102787,-38.62747192,-0.04186818004,-0.04309883714,-0.0009931600653,-0.4567618966,0.2665080726,9.873948097 +4.39969094,-6.815727234,26.20768166,-37.91413879,-0.03789544106,-0.02908178978,0.01127431542,-0.4167534411,0.3044225276,9.863181114 +4.40969094,-6.782478333,26.49191666,-37.17137146,-0.03473445773,-0.001430485398,0.01775887236,-0.3834793866,0.3153073788,9.803701401 +4.41969094,-8.043819427,26.34184265,-37.92095947,-0.03422413021,0.01866585761,0.01907779463,-0.2802246213,0.277962029,9.730218887 +4.42969094,-7.369091034,27.64144325,-38.93057251,-0.03385256976,0.02039143257,0.01885609329,-0.2379066348,0.2587588131,9.743079185 +4.43969094,-8.297309875,27.00392532,-37.204422,-0.04078369215,0.01425610762,0.01538096368,-0.2097538263,0.237317726,9.7883358 +4.44969094,-6.798698425,26.21647072,-37.1517334,-0.04215364158,0.007632749155,0.0129903052,-0.2162558734,0.2124440819,9.842716217 +4.45969094,-6.679012299,25.68148232,-37.26655579,-0.03894165531,0.002023714595,0.01219128072,-0.21327281,0.2051690519,9.822431564 +4.46969094,-7.881961823,25.96215439,-36.52078247,-0.03920482472,-0.004644265398,0.01271759532,-0.1758301556,0.1789844632,9.79445076 +4.47969094,-9.782485962,25.73265076,-38.02636719,-0.04025738314,-0.007038090378,0.006575869862,-0.1418743432,0.1429290026,9.867178917 +4.48969094,-8.773334503,26.6790123,-36.87501526,-0.03090364486,-0.01158179156,0.001804163679,-0.1442653984,0.1189981252,9.951881409 +4.49969094,-6.671707153,26.08586121,-38.05841064,-0.01513878815,-0.02521918528,-6.658444181e-05,-0.1738433093,0.1544630975,9.960613251 +4.50969094,-6.0623703,25.88281631,-37.89271545,-0.0008522793651,-0.02589283884,0.0004660896957,-0.1996041238,0.1565450281,9.95832634 +4.51969094,-7.868648529,26.6367054,-37.78971863,0.01431652624,-0.004012350924,-0.002729954664,-0.2186203599,0.1420396417,9.912900925 +4.52969094,-7.892982483,26.22353745,-37.76026917,0.018323984,0.02168132737,-0.0004264845047,-0.2232411653,0.1151062325,9.794009209 +4.53969094,-8.274959564,24.81626129,-37.20146179,0.008296172135,0.03482315689,0.0001749894582,-0.1810871363,0.09140643477,9.71138382 +4.54969094,-6.775169373,26.89629364,-37.96321106,-0.0138578536,0.0290421769,0.0007601014804,-0.1903544068,0.133223936,9.736455917 +4.55969094,-6.423221588,26.9527092,-36.13664246,-0.03204216808,0.02157061175,0.001588354819,-0.215617761,0.2027130872,9.723331451 +4.56969094,-8.013477325,27.02518272,-38.42747498,-0.04305369407,0.02441652864,0.00265686959,-0.21561867,0.1740983278,9.768782616 +4.57969094,-8.473773956,26.5748539,-37.02090454,-0.04656537622,0.01722446829,0.005860616919,-0.2287943214,0.1416486502,9.826238632 +4.58969094,-7.249095917,26.43893433,-37.16656494,-0.0395822376,0.01232369244,0.008911250159,-0.2248601615,0.1444966495,9.854561806 +4.59969094,-7.443393707,26.26773071,-38.52748108,-0.03166176751,0.00800199993,0.008988874033,-0.2275328785,0.1459973603,9.916668892 +4.60969094,-8.831729889,26.24826622,-38.37039185,-0.0206013117,0.009386268444,0.008898375556,-0.2268878073,0.1908883601,9.917303085 +4.61969094,-5.772026062,27.57248497,-36.33480835,-0.01100313012,0.01832520589,0.006325504277,-0.2854395807,0.2194937319,9.911237717 +4.62969094,-7.928646088,27.73664856,-36.49429321,-0.003731844481,0.04050150514,0.003878329415,-0.3024586439,0.1961129755,9.810222626 +4.63969094,-6.263290405,27.77385521,-38.17938232,-0.0005319332704,0.05672128499,-0.001344260294,-0.22144261,0.1550287455,9.70290947 +4.64969094,-8.314025879,26.32761574,-36.08798218,-0.002774584573,0.05401900411,-0.006458672695,-0.1730443388,0.1541241705,9.64126873 +4.65969094,-6.654678345,26.09465027,-37.29600525,-0.01445338782,0.03875076026,-0.01371094957,-0.2011986077,0.1363305449,9.544471741 +4.66969094,-7.570388794,26.39834023,-37.62080383,-0.02568543702,0.01708991826,-0.01378848217,-0.29481107,0.1495176256,9.718362808 +4.67969094,-6.825942993,26.20240784,-38.37158203,-0.02655274048,-0.0216173958,-0.01138909534,-0.296559602,0.1946366876,9.950929642 +4.68969094,-7.906295776,25.54898643,-36.49133301,-0.01781709492,-0.04153759778,-0.005925999023,-0.3151355982,0.1484638304,10.02096558 +4.69969094,-7.559364319,26.13695717,-36.38131714,-0.002749661915,-0.03404572606,-0.003847503802,-0.3055528998,0.1347839087,10.00041008 +4.70969094,-8.094593048,25.64795494,-38.32933044,0.00945944991,-0.008453158662,-0.0002174130641,-0.3440961838,0.1186381653,9.896161079 +4.71969094,-6.487621307,26.251791,-37.15493774,0.01406259369,-0.001046618447,0.005578093696,-0.3545964956,0.1277017146,9.871976852 +4.72969094,-7.586608887,26.12289429,-37.60116577,0.007003881969,-0.01737609878,0.01359666139,-0.3624377251,0.1338391006,9.84253788 +4.73969094,-6.487621307,26.251791,-37.15493774,-0.007547943387,-0.01550474204,0.01591363735,-0.2639065683,0.1462768465,9.776966095 +4.74969094,-6.751327515,26.90859985,-36.89584351,-0.02195086703,-0.001110700425,0.01218491793,-0.2222563624,0.1621421576,9.738617897 +4.75969094,-7.249095917,26.43893433,-37.16656494,-0.03246286511,-0.008031003177,0.01146942563,-0.248240903,0.115946807,9.743156433 +4.76969094,-6.457279205,26.93513107,-37.66145325,-0.03635413572,-0.02225174196,0.01254490949,-0.2521977723,0.102270402,9.834392548 +4.77969094,-5.980941772,26.59252357,-36.11201477,-0.02922541276,-0.03147359192,0.01022274233,-0.190122053,0.1274154633,9.831977844 +4.78969094,-7.479057312,27.78084183,-37.26150513,-0.01552046463,-0.0149680879,0.005340931471,-0.2009503394,0.1568839103,9.832667351 +4.79969094,-7.26581192,25.76262474,-36.05012512,-0.0007052477449,0.01664851978,0.001047774218,-0.2484272271,0.1699940115,9.815591812 +4.80969094,-9.155311584,25.27173805,-36.31622314,0.006605156697,0.02571816929,9.230547585e-05,-0.2894585431,0.1414472759,9.803195 +4.81969094,-9.155311584,25.27173805,-36.31622314,0.003320282325,0.007851457223,0.0008433877956,-0.2730394006,0.1397527158,9.857546806 +4.82969094,-7.358562469,26.97919655,-36.59428406,-0.003061691765,-0.01043106336,0.0001573381014,-0.258321166,0.1311504543,9.84120369 +4.83969094,-7.358562469,26.97919655,-36.59428406,-0.004729580134,-0.01538466103,-0.0008347684052,-0.263558358,0.1183589473,9.855064392 +4.84969094,-8.036197662,26.07870102,-36.83395386,-0.004580997862,-0.01142347418,-0.002197280526,-0.2746455669,0.1134366319,9.814276695 +4.85969094,-6.672199249,25.68499756,-36.96160889,-0.007733293343,-0.0003262530081,-0.0008874703199,-0.1970769614,0.08623953164,9.628912926 +4.86969094,-8.81470108,26.25705528,-37.60798645,-0.01761743426,0.01848483458,-0.005533532239,-0.259354353,0.09551355988,9.633827209 +4.87969094,-7.85843277,26.64197922,-37.33226013,-0.02588795871,0.01906336285,-0.0006064993795,-0.2534437478,0.1723184586,9.753379822 +4.88969094,-7.25850296,26.16700363,-36.84196472,-0.03109328821,0.003598091658,0.001400619745,-0.2143057436,0.1092486531,9.908551216 +4.89969094,-7.898990631,25.95336533,-37.28318787,-0.02612610161,-0.01706181467,-0.001536136726,-0.2153220028,0.1003462896,10.02652073 +4.90969094,-6.45356369,26.26936913,-35.63012695,-0.01295110211,-0.02876726352,-0.002070615999,-0.2522668839,0.1105454043,10.04345798 +4.91969094,-9.015007019,25.81567955,-38.49183655,0.002009376884,-0.0287384484,-0.001379664754,-0.2668540478,0.1403126717,9.930277824 +4.92969094,-7.378501892,27.36951256,-38.60600281,0.01198495831,-0.00673567038,-0.003675399348,-0.2398301959,0.1118507236,9.92348671 +4.93969094,-7.471748352,28.18522072,-38.05334473,0.02573502436,0.02284619026,-0.004629021976,-0.224476397,0.1067397967,9.81465435 +4.94969094,-6.574054718,27.0710144,-36.29734802,0.02536131069,0.03054238297,-0.004635522142,-0.2303179353,0.0717818737,9.722005844 +4.95969094,-6.296230316,26.82210159,-37.04333496,0.01556797791,0.004868612625,-0.002214650391,-0.2246153355,0.1043654531,9.742161751 +4.96969094,-7.560173035,26.40361404,-37.1633606,2.473592758e-06,-0.0173169449,-0.0008139780257,-0.2097203881,0.1449587941,9.785895348 +4.97969094,-6.471401215,26.52723694,-37.17457581,-0.008842390031,-0.02489814535,-0.0002747424878,-0.3028791845,0.1854895651,9.777700424 +4.98969094,-7.569580078,26.13168335,-36.83876038,-0.01287231874,-0.02937872149,0.003762538079,-0.271209687,0.129128173,9.908959389 +4.99969094,-6.624336243,26.77799034,-37.80253601,-0.004194155335,-0.04173757136,0.004533623811,-0.1912932694,0.1316759884,10.00805187 +5.00969094,-6.457279205,26.93513107,-37.66145325,0.01475238521,-0.03149639815,0.003035570029,-0.2443001717,0.1331238598,9.971121788 +5.01969094,-8.210559845,25.5171814,-36.18318176,0.03028877825,-0.006620025262,-0.0008588049095,-0.2268170714,0.1420782506,9.910667419 +5.02969094,-7.385002136,26.69847679,-37.03211975,0.04052030668,0.01364735421,-0.004151983652,-0.237571016,0.158927694,9.847308159 +5.03969094,-7.442901611,26.66859436,-39.62428284,0.03705263138,0.0003825714812,-0.006458672695,-0.2139079124,0.1738214344,9.855632782 +5.04969094,-7.299869537,25.74504662,-37.57493591,0.03569614887,-0.0260367766,-0.007442372385,-0.2120704055,0.182754904,9.782229424 +5.05969094,-7.995639801,26.76731491,-36.88302612,0.03453819454,-0.03720206767,-0.00965471752,-0.2041972578,0.1758445352,9.763034821 +5.06969094,-8.423805237,27.53539658,-37.39454651,0.03948170319,-0.04894115031,-0.007673261221,-0.2060015351,0.1358015239,9.880899429 +5.07969094,-7.416149139,26.28179359,-37.30761719,0.04444842041,-0.05113239586,-0.00752402097,-0.2362748682,0.1675562263,9.965304375 +5.08969094,-8.899833679,27.21048546,-37.06517029,0.06157614291,-0.0353282243,-0.003937673289,-0.2722059488,0.193922624,9.950833321 +5.09969094,-7.559364319,26.13695717,-36.38131714,0.07159139216,-0.007121328264,-0.002865193645,-0.2545877993,0.1594867259,9.833031654 +5.10969094,-8.280281067,27.01271439,-36.4420166,0.06986922771,0.0109635517,-0.002792024519,-0.2140764892,0.1569391638,9.850317955 +5.11969094,-8.217372894,25.51366615,-36.48812866,0.06253593415,0.002094432712,-0.007235190365,-0.1947746426,0.2032549828,9.859428406 +5.12969094,-6.286819458,27.09403038,-37.36790466,0.06216876209,-0.008317800239,-0.009709886275,-0.2290931046,0.2211895436,9.904157639 +5.13969094,-8.077068329,26.05760765,-38.66372681,0.06461351365,-0.004909839481,-0.01018739119,-0.2145729065,0.2156002671,9.762316704 +5.14969094,-6.850280762,25.78923988,-38.34214783,0.05910928547,0.001064125914,-0.0116847232,-0.167883575,0.2063707858,9.694931984 +5.15969094,-7.17206955,25.34778023,-37.69958496,0.05363702774,0.01853247173,-0.01631854475,-0.156190142,0.1726144552,9.716248512 +5.16969094,-8.053226471,26.06991196,-37.59635925,0.05348046869,0.02741134167,-0.02060299739,-0.2086635679,0.2122661322,9.849188805 +5.17969094,-7.995639801,26.76731491,-36.88302612,0.053559497,0.02173338085,-0.01817750186,-0.3160272539,0.2369009852,9.909678459 +5.18969094,-7.801651001,27.60603905,-37.4009552,0.06455555558,0.02400907688,-0.01444878429,-0.3371770084,0.280346781,9.874191284 +5.19969094,-8.561702728,25.19411087,-37.22770691,0.07946416736,0.04503597319,-0.01281164959,-0.2494072318,0.2802865803,9.811919212 +5.20969094,-7.282344818,26.15469933,-37.90933228,0.08607848734,0.05437766761,-0.0133135058,-0.2424967289,0.2851865292,9.795174599 +5.21969094,-5.599964142,26.20069504,-38.83203125,0.09813603014,0.05297361314,-0.01501114666,-0.2714679539,0.2680800259,9.75396347 +5.22969094,-8.371612549,25.63021278,-36.80131531,0.09730117023,0.03567400575,-0.0161033757,-0.2618784308,0.2831623554,9.753846169 +5.23969094,-5.85345459,26.86277771,-38.11549377,0.1009470299,0.0124598816,-0.01290932298,-0.2666765749,0.3234401941,9.780581474 +5.24969094,-6.471401215,26.52723694,-37.17457581,0.1109568328,0.01610381715,-0.01498145796,-0.1893724501,0.3073857427,9.646884918 +5.25969094,-7.305873871,25.4748745,-37.09785461,0.09680746496,0.01358146127,-0.01551413164,-0.1401528865,0.2132546306,9.622281075 +5.26969094,-8.179725647,26.60138512,-37.78651428,0.07704415172,-0.02511924133,-0.01607831009,-0.2199209034,0.2536352575,9.718344688 +5.27969094,-7.139129639,26.29953575,-38.83563232,0.06522310525,-0.05784024298,-0.01338343509,-0.2406560779,0.289178431,9.901301384 +5.28969094,-7.139129639,26.29953575,-38.83563232,0.07847820967,-0.06008450687,-0.01284654625,-0.2131624073,0.3604699373,9.955468178 +5.29969094,-5.710235596,27.00761414,-39.04180908,0.09397917241,-0.03338656574,-0.01444436796,-0.2175605744,0.3692127168,9.880940437 +5.30969094,-5.710235596,27.00761414,-39.04180908,0.1072839499,-0.00890467409,-0.0123171173,-0.1554846913,0.338521868,9.983216286 +5.31969094,-8.555202484,25.86514664,-38.80158997,0.1280505955,-0.00145151047,-0.01125509851,-0.1884938926,0.3345931768,9.971286774 +5.32969094,-8.36139679,25.6354866,-36.34387207,0.1440444738,0.001271591522,-0.01606091298,-0.1713260263,0.4138234556,9.948322296 +5.33969094,-7.552864075,26.80799103,-37.9552002,0.1595576257,0.02187692188,-0.02512727119,-0.1057918295,0.3753095269,9.983222008 +5.34969094,-7.186187744,24.93988609,-37.212677,0.1767290533,0.005366987083,-0.0303742215,-0.2102942914,0.3615303338,9.934126854 +5.35969094,-7.898990631,25.95336533,-37.28318787,0.1936744601,0.02417157032,-0.02678085119,-0.1176252067,0.2720018625,10.04296494 +5.36969094,-7.449893951,25.59669495,-36.95359802,0.2405296117,0.06736565381,-0.03421706706,-0.1225322634,0.4234299064,10.36811256 +5.37969094,-8.820705414,25.98688316,-37.13090515,0.3403091133,0.08081629127,-0.03518076614,-0.107458882,0.4686262608,10.39834785 +5.38969094,-7.990322113,24.57086372,-37.64250183,0.4398308396,0.1258682907,-0.0389775075,-0.1128015518,0.5142008662,10.45117283 +5.39969094,-7.16185379,25.35305405,-37.24212646,0.5284068584,0.1784391701,-0.0449366197,-0.2035734504,0.5788798332,10.56637478 +5.40969094,-7.356647491,24.78098679,-37.50622559,0.6149481535,0.1743725836,-0.04687128216,-0.2832054794,0.6244323254,10.58554649 +5.41969094,-7.33052063,25.72922707,-38.94726562,0.7036159635,0.1237051487,-0.0392241925,-0.4429421723,0.6916079521,10.61947918 +5.42969094,-6.578952789,24.8692894,-37.51423645,0.8085047603,0.08715923876,-0.0232714992,-0.5601879358,0.8304725885,10.81384563 +5.43969094,-7.531322479,24.88698578,-38.73428345,0.938151896,0.08443415165,-0.007361578289,-0.6205343008,0.9866380692,10.99567223 +5.44969094,-8.279857635,22.61453819,-38.41836548,1.076931596,0.09234847128,0.01138667203,-0.7176730633,1.141917586,11.07694626 +5.45969094,-6.797901154,24.95243835,-40.7245636,1.21220386,0.142701149,0.01992633007,-0.5940642953,1.237163305,10.96197701 +5.46969094,-6.786567688,24.02353477,-37.60624695,1.331775427,0.2211852372,0.01148944534,-0.5794842839,1.406719089,10.83598232 +5.47969094,-6.683105469,23.21310043,-37.70143127,1.424222708,0.2731072903,0.001702721231,-0.6970831752,1.580493093,10.72273731 +5.48969094,-5.19291687,22.95544434,-39.51779175,1.489971876,0.2497478575,0.005910233129,-0.7620835304,1.679367185,10.78293896 +5.49969094,-7.543029785,22.68174744,-40.25616455,1.567991257,0.1823883057,0.01639992744,-0.7109425664,1.702432513,10.92615986 +5.50969094,-6.331970215,22.53879547,-41.01174927,1.654090762,0.1048861593,0.02590598166,-0.5987703204,1.934044361,10.9778614 +5.51969094,-6.872894287,21.11210251,-40.60383606,1.738529205,0.02646159567,0.03096565418,-0.4815279543,2.036860704,11.03164959 +5.52969094,-6.464668274,20.73433876,-42.10400391,1.831183553,-0.0371132046,0.03252334148,-0.2581321895,2.053145409,11.02364445 +5.53969094,-6.585845947,19.06935883,-43.05360413,1.945584893,-0.0588221848,0.02755689435,-0.03785359487,2.144049168,11.44700241 +5.54969094,-7.916595459,19.74729919,-42.18318176,2.122190714,-0.06661680341,-0.002210137434,-0.2162174582,2.293878794,11.10333824 +5.55969094,-7.169239044,19.15226173,-41.68467712,2.242797375,0.02907058969,0.01810688525,-0.6900045872,2.465847731,10.61191368 +5.56969094,-7.007083893,17.10767746,-42.76051331,2.365107298,0.07657755166,0.04666646942,-0.6271196008,2.857951403,11.23185539 +5.57969094,-7.567134857,15.80464458,-43.58227539,2.533013105,0.009588560089,0.06934992969,-0.6376622319,3.23949194,11.46477699 +5.58969094,-6.512111664,15.24316883,-43.23944092,2.682348967,-0.03812664747,0.1032418609,-1.011867642,3.570411205,11.33907413 +5.59969094,-6.6783638,14.81937218,-42.59846497,2.810805321,-0.02873550914,0.1526294798,-1.132974863,3.754922628,10.92838478 +5.60969094,-6.983627319,12.9884634,-44.45155334,2.896809578,0.02345578931,0.1785736531,-0.7919716239,3.861896276,10.60241222 +5.61969094,-8.335494995,11.18923187,-44.77839661,2.964149952,0.1522061974,0.1822080016,-0.3903231025,3.914002657,10.22689247 +5.62969094,-6.875339508,9.580715179,-44.20939636,3.014033556,0.314350307,0.1683944762,-0.1859156489,4.113828659,9.747583389 +5.63969094,-6.25868988,9.782049179,-44.83552551,3.032514095,0.4121232927,0.1518512666,-0.2616799474,4.384209156,9.284766197 +5.64969094,-5.253143311,8.660894394,-45.21585083,3.02897954,0.3880666196,0.1631702632,-0.4669449329,4.571393967,8.911646843 +5.65969094,-5.458652496,7.682689667,-44.84060669,2.998725176,0.1711447984,0.1893278956,-0.6494381428,4.91509676,8.661911964 +5.66969094,-4.898784637,6.919963837,-45.39804077,2.934651613,-0.1005303711,0.2150487304,-0.6185181737,5.301094055,9.058558464 +5.67969094,-6.250656128,5.120733261,-45.72488403,2.910330772,-0.2587017715,0.2318211645,-0.3392679691,5.375423908,9.396467209 +5.68969094,-5.879581451,4.056110382,-47.02351379,2.922665358,-0.2760679126,0.2243542522,-0.4822314382,5.503026485,9.01198101 +5.69969094,-7.555839539,1.547006607,-46.07824707,2.943333626,-0.2170758992,0.2124996334,-0.4549106658,5.76497364,8.254803658 +5.70969094,-7.756641388,0.7047691345,-45.86528015,2.945270777,-0.1781668216,0.206626907,-0.3903495967,5.746593952,7.337949276 +5.71969094,-7.067371368,-0.9885349274,-44.98329163,2.901698112,-0.208113566,0.1915666908,-0.1816526204,5.731616974,6.997162819 +5.72969094,-6.940868378,-1.520008087,-44.79315186,2.863831282,-0.2202323377,0.1786220968,0.2177357078,5.766057968,6.696406841 +5.73969094,-6.940868378,-1.520008087,-44.79315186,2.802832127,-0.2372323573,0.1332490295,0.4498233199,5.980806351,6.113747597 +5.74969094,-6.167266846,-3.900087357,-45.23603821,2.685050011,-0.3130146265,0.08385125548,0.2968164086,6.279119015,5.801239014 +5.75969094,-6.167266846,-3.900087357,-45.23603821,2.537742376,-0.4306736588,0.05320386589,-0.04167699441,6.515561581,5.803927898 +5.76969094,-6.593517303,-5.330215454,-46.65950012,2.41191411,-0.5239192843,0.04438148439,-0.1330732852,6.619808674,5.855363846 +5.77969094,-8.595588684,-5.950117111,-44.62696838,2.333110571,-0.5195303559,0.03090674616,-0.2117768675,6.696700573,5.642460823 +5.78969094,-8.628837585,-6.234352112,-45.36975098,2.297567368,-0.414770335,0.02381796204,-0.1566102207,6.806136608,5.84990263 +5.79969094,-7.396728516,-8.699171066,-45.80760193,2.302094221,-0.2918973565,0.008515642956,-0.2365027964,7.161421299,5.973455906 +5.80969094,-6.642562866,-9.290693283,-45.00413513,2.342935801,-0.1979716867,-0.003534507239,-0.3914331794,7.542293072,6.224736691 +5.81969094,-7.828483582,-9.00123024,-43.49595642,2.388193846,-0.1110058427,-0.01067978889,-0.2491026968,7.591664314,5.918228626 +5.82969094,-8.313922882,-9.598073959,-42.84197998,2.408067226,-0.01837301813,-0.0253147278,0.03190075234,7.624960423,5.320220947 +5.83969094,-8.342350006,-12.47961998,-43.24739075,2.358142614,0.03511781245,-0.05030852184,-0.1354587525,7.447844982,3.707296848 +5.84969094,-7.461193085,-13.20175362,-43.3506012,2.204473257,0.08235535771,-0.04534388334,-0.007207999472,7.318314552,3.282621861 +5.85969094,-7.363540649,-12.21660042,-41.58953857,2.055655241,0.08076247573,-0.0373577401,0.06377741694,7.392906189,3.514599085 +5.86969094,-7.110240936,-14.94444084,-43.68527222,1.952906013,0.0455320403,-0.03147932887,-0.04667448997,7.750877857,3.611784935 +5.87969094,-7.37494278,-15.08936119,-41.23254395,1.861353755,-0.06208071858,-0.02312056161,-0.1080982238,8.078206062,3.719119072 +5.88969094,-6.948883057,-15.72499657,-41.1882782,1.785762429,-0.1727042198,-0.0151411742,-0.1235443279,8.370152473,4.199866295 +5.89969094,-8.64748764,-16.0464344,-40.24595642,1.783481121,-0.1971811503,-0.02015489712,-0.1364535242,8.50699234,4.808917046 +5.90969094,-8.216724396,-16.54610634,-40.36398315,1.838792682,-0.07802446187,-0.03536086157,-0.06082001328,8.485049248,4.451487541 +5.91969094,-7.526954651,-17.83854485,-40.57881165,1.857732654,0.1300803274,-0.06967652589,-0.01184685621,8.531123161,3.72614336 +5.92969094,-8.303844452,-18.19350243,-39.78877258,1.814050555,0.2553527653,-0.08196844906,-0.06807902455,8.328253746,2.931307793 +5.93969094,-8.504646301,-19.03573799,-39.57580566,1.716382623,0.2538946271,-0.06764163077,0.005141078494,8.346729279,2.556385756 +5.94969094,-6.853099823,-20.07394981,-38.89517212,1.598608613,0.152466163,-0.05751124769,0.003872290719,8.425172806,2.134254694 +5.95969094,-7.664543152,-20.84735298,-38.53311157,1.460810065,0.05854824185,-0.04427852854,-0.007188765798,8.432364464,2.211527348 +5.96969094,-7.158172607,-19.83910179,-39.36903381,1.353563786,0.002514161868,-0.03926705942,0.1740697026,8.347842216,2.068900824 +5.97969094,-6.903877258,-20.76783943,-39.30352783,1.247846365,-0.0405799076,-0.04407382011,0.04491699114,8.569720268,1.811930776 +5.98969094,-7.849121094,-21.41414452,-38.33978271,1.123071313,-0.06583211571,-0.04931728542,0.1047966853,8.705016136,1.846391082 +5.99969094,-6.214603424,-22.4611454,-38.42155457,1.026422024,-0.04438321292,-0.07139761746,0.05338488892,8.847383499,2.146280289 +6.00969094,-8.489295959,-22.29530525,-36.90214539,0.9891057611,0.03876886517,-0.0867260173,-0.1419169158,9.085100174,2.551529169 +6.01969094,-7.745658875,-22.22458076,-38.43496704,0.9823154211,0.1017291918,-0.106370911,-0.1593304873,9.150740623,2.281132936 +6.02969094,-7.793209076,-23.98509407,-35.71522522,0.9616700411,0.1661918014,-0.1270233244,0.0574805215,9.073641777,2.047967672 +6.03969094,-6.944122314,-22.12397194,-37.37562561,0.9382100105,0.1993058175,-0.1547543406,0.01744905487,8.995657921,1.867096186 +6.04969094,-7.567459106,-25.06209755,-36.55479431,0.9021967649,0.1762719601,-0.1718251109,-0.1406121552,9.002772331,1.730701089 +6.05969094,-7.148822784,-23.36882973,-36.21832275,0.8519908786,0.1081412658,-0.1755056828,-0.3042657971,9.020177841,1.228797078 +6.06969094,-6.28729248,-24.36816597,-36.45439148,0.7535880208,0.05081130564,-0.1780438125,-0.07804404944,8.942026138,0.0622860454 +6.07969094,-6.615398407,-24.41227531,-37.21359253,0.618732214,0.1305778027,-0.1905168593,-0.1119370759,8.954662323,0.6950393319 +6.08969094,-6.468467712,-24.93319893,-36.10856628,0.5215619206,0.097046718,-0.2001017928,-0.1604000777,9.188315392,0.8780340552 +6.09969094,-8.278152466,-24.18106651,-36.15803528,0.4290464222,-0.01206605136,-0.1879246235,-0.4243336916,9.332118034,1.622178555 +6.10969094,-6.327850342,-25.05677986,-36.40531921,0.405993849,-0.07996894419,-0.1790645421,-0.552688241,9.564300537,2.605819941 +6.11969094,-7.102638245,-25.54419136,-35.147995,0.4912712276,-0.04473871738,-0.1838391423,-0.6350327134,9.846169472,3.193631172 +6.12969094,-6.550388336,-26.04377174,-36.79244995,0.6150547266,0.02244179323,-0.1928945929,-0.5246245861,10.11047459,2.911791801 +6.13969094,-6.669765472,-26.17630577,-34.79878235,0.6870937347,0.05554625392,-0.2030147314,-0.6148418188,9.993902206,2.150117159 +6.14969094,-5.846191406,-27.59583855,-35.61532593,0.6883915663,0.03711918741,-0.1955397874,-0.5256439447,9.68167305,1.30617404 +6.15969094,-5.604026794,-26.33164406,-35.09532166,0.6299076676,-0.03041126765,-0.1765921712,-0.3788752854,9.399337769,0.6339784861 +6.16969094,-8.064910889,-26.19928551,-34.94665527,0.5348986387,-0.09692849219,-0.1555133909,-0.3197306395,9.323526382,0.3968338072 +6.17969094,-7.614517212,-26.42174721,-34.93183899,0.4100218713,-0.1200562641,-0.1371923983,-0.2656853199,9.336148262,0.2756545842 +6.18969094,-7.253158569,-26.09340477,-33.42985535,0.2929925919,-0.1655155718,-0.119509913,-0.5380512476,9.51198101,1.004795432 +6.19969094,-7.253158569,-26.09340477,-33.42985535,0.2704642713,-0.2962380052,-0.09054698795,-0.5876882672,9.83070755,2.051366806 +6.20969094,-7.109138489,-26.21522713,-33.57411194,0.3218514025,-0.3892082572,-0.07513003796,-0.7501949668,10.15983486,1.972729325 +6.21969094,-7.109138489,-26.21522713,-33.57411194,0.3665290177,-0.3839081526,-0.07275354117,-0.8437544703,10.24689865,1.666616678 +6.22969094,-6.416275024,-26.83838844,-35.51531982,0.395961076,-0.362869978,-0.07692997903,-0.6962200403,10.24797153,1.79286015 +6.23969094,-6.924747467,-27.71418571,-35.14666748,0.4236700237,-0.2611028552,-0.08759007603,-0.5691984892,10.19368839,1.651011705 +6.24969094,-5.731018066,-26.20103264,-34.18865967,0.4270048141,-0.06775772572,-0.1009764075,-0.5052172542,10.05403423,1.090990543 +6.25969094,-5.955661774,-27.05557823,-35.04304504,0.3786482811,0.09604948014,-0.1041087434,-0.4335639477,9.900503159,0.7057359815 +6.26969094,-6.411750793,-27.77080727,-32.70195007,0.3053478301,0.1541505158,-0.09603412449,-0.3152265251,9.683514595,0.2176062912 +6.27969094,-7.398361206,-28.83907127,-32.47116089,0.2221469581,0.1793556064,-0.09244065732,-0.3450230062,9.64139843,0.03580936044 +6.28969094,-6.884685516,-27.42643929,-34.09892273,0.1321454048,0.1524278224,-0.07998012006,-0.4099330306,9.661970139,0.1432226747 +6.29969094,-5.973186493,-27.46523094,-34.70864868,0.06124486029,0.006182244048,-0.05034258217,-0.6426629424,9.841538429,0.8780437112 +6.30969094,-5.79510498,-27.56947136,-33.32810974,0.02109704167,-0.1478120834,-0.01445469074,-0.8502721786,10.12072086,1.450038433 +6.31969094,-6.100177765,-27.33461952,-33.80197144,0.02866151184,-0.1757359207,0.02049909532,-1.059395909,10.29624081,1.798726797 +6.32969094,-5.74773407,-26.87734032,-33.07220459,0.07471268624,-0.1548956931,0.04094931856,-0.6283707023,10.32818985,2.137936592 +6.33969094,-7.230003357,-28.54772377,-32.64486694,0.1274963319,-0.03663658351,0.03674643859,-0.6844533682,10.25029659,1.625554085 +6.34969094,-5.938632965,-27.04678917,-34.28063965,0.1503894925,0.09812860191,0.0308074709,-0.5885108113,10.05522156,1.04837501 +6.35969094,-6.94877243,-28.79487801,-33.2383728,0.141530484,0.1597932577,0.0212403778,-0.4865935445,9.90356636,0.6102195382 +6.36969094,-7.224311829,-27.61003304,-35.0007782,0.09064454585,0.1466042995,0.02438522689,-0.4241021872,9.743000031,0.1843403578 +6.37969094,-7.368019104,-28.15572929,-32.97767639,0.01164131891,0.07730738074,0.03112574108,-0.3331443667,9.599993706,0.2176162302 +6.38969094,-6.503890991,-28.88665199,-33.84329224,-0.05099488795,-0.004641776904,0.03543535247,-0.252383858,9.633149147,0.59193331 +6.39969094,-5.962162018,-27.726614,-33.46916199,-0.08003503084,-0.1150991321,0.04340262339,-0.4005373716,9.868338585,1.204803705 +6.40969094,-6.21163559,-29.39518929,-33.1973114,-0.07899376005,-0.1858911067,0.05741436034,-0.7340079546,10.20933914,1.619800568 +6.41969094,-5.45368576,-26.8508091,-33.83781433,-0.05616622418,-0.1541773379,0.07039058954,-1.079650044,10.29853725,1.836036086 +6.42969094,-5.764762878,-26.88612938,-33.83460999,-0.01166790351,-0.04989422113,0.08693824708,-0.7219535708,10.20710182,1.849469781 +6.43969094,-7.55210495,-28.321661,-33.88114929,0.04423505068,0.07162962109,0.07621535659,-0.5237618089,10.04287529,1.466649294 +6.44969094,-6.139930725,-28.28988838,-32.97085571,0.06462156773,0.1659917086,0.06487458199,-0.2693444788,9.804961205,1.003296137 +6.45969094,-6.900909424,-27.70188332,-34.07929993,0.03726745769,0.1448267251,0.05426616967,-0.3218414485,9.69636631,0.6625964046 +6.46969094,-7.200469971,-27.59772682,-33.9334259,-0.01479796506,0.08245497942,0.05262492597,-0.2694553733,9.55301857,0.2537037134 +6.47969094,-7.517551422,-27.90321922,-33.45314026,-0.08166196942,0.04908277839,0.05063142255,-0.2769313157,9.516850471,0.3263844252 +6.48969094,-6.802764893,-26.31586647,-33.41503906,-0.129857108,-0.02138678916,0.04803046212,-0.2548482716,9.549121857,0.8685886264 +6.49969094,-6.453117371,-28.19276619,-33.43490601,-0.1322575063,-0.1084113792,0.04938947782,-0.3309167624,9.744350433,1.420860767 +6.50969094,-6.421966553,-27.77607918,-33.15940857,-0.1049313694,-0.1264381558,0.046568349,-0.5277194381,9.95321846,1.722908378 +6.51969094,-5.795597076,-27.97033501,-32.23129272,-0.05866263807,-0.1087544486,0.05005582422,-0.7864149809,10.11928844,1.995217443 +6.52969094,-6.756572723,-28.49122429,-32.34472656,-0.003624816425,-0.06324600428,0.05518781021,-0.6161818504,10.06101227,1.923961639 +6.53969094,-7.125358582,-26.49067116,-33.55448914,0.04583896324,0.0206390284,0.04705470428,-0.5417268872,9.938585281,1.376885653 +6.54969094,-7.384243011,-28.43117332,-32.95803833,0.05019680783,0.09256529063,0.04150801897,-0.4252683222,9.765837669,0.8409014344 +6.55969094,-4.91444397,-28.69246864,-32.33450317,0.01591055095,0.1238697395,0.0377532728,-0.2604678869,9.612735748,0.5489522815 +6.56969094,-7.374832153,-28.15924644,-33.28263855,-0.03896613047,0.1192587167,0.03361805156,-0.2545866966,9.425396919,0.2876189351 +6.57969094,-7.552417755,-27.65414238,-35.75997925,-0.115347065,0.05623481423,0.03676060215,-0.2348897308,9.471121788,0.4286818206 +6.58969094,-5.604522705,-26.73250771,-33.9985199,-0.1771036536,-0.04492631555,0.04680872709,-0.2702373564,9.532647133,0.8573217988 +6.59969094,-6.436088562,-28.18397713,-32.67250061,-0.2030239701,-0.1211520582,0.05939222872,-0.3787891567,9.717459679,1.445369482 +6.60969094,-6.883880615,-27.69309425,-33.31689453,-0.1903610229,-0.1436105818,0.06776238978,-0.558552444,9.869405746,1.720998168 +6.61969094,-6.573608398,-27.391119,-34.10212708,-0.1610814184,-0.08932308853,0.07173191011,-0.6367165446,9.939030647,1.911174417 +6.62969094,-7.794269562,-29.58585548,-34.40113831,-0.1116924211,0.006465011742,0.06918103993,-0.5721738338,10.05697632,1.982402325 +6.63969094,-6.814472198,-28.52110863,-34.93688965,-0.06562864035,0.08441458642,0.07163098454,-0.5598268509,10.11370468,1.724575639 +6.64969094,-8.08763504,-27.1457653,-33.35314941,-0.04420860112,0.1142552868,0.07546826452,-0.5372830629,10.04286575,1.392329931 +6.65969094,-6.123214722,-27.6135807,-34.08731079,-0.0459260419,0.1182540208,0.07836209983,-0.4197402894,9.951440811,1.140536189 +6.66969094,-6.123214722,-27.6135807,-34.08731079,-0.05532723293,0.1020412371,0.07906202972,-0.3231778741,9.863641739,0.9318038821 +6.67969094,-6.894096375,-27.69836617,-33.77432251,-0.07831289619,0.07124064118,0.07983452082,-0.3302566707,9.701453209,0.7482259274 +6.68969094,-6.894096375,-27.69836617,-33.77432251,-0.1181097478,0.01705702953,0.08364310861,-0.3872544169,9.722757339,1.051870346 +6.69969094,-7.967948914,-27.68075752,-33.46795654,-0.1227426454,-0.05970519036,0.08897811919,-0.277425617,9.779336929,1.489115953 +6.70969094,-7.40127182,-28.43996239,-33.72044373,-0.0898701027,-0.0761885494,0.08000918478,-0.3281622827,9.89973259,1.708828926 +6.71969094,-7.944419861,-27.00093269,-34.2794342,-0.05175863951,-0.01853408106,0.06382262707,-0.288962692,9.960288048,1.617121935 +6.72969094,-7.350498199,-27.74607658,-33.31208801,-0.01620602794,0.05397556722,0.04388963431,-0.2301783711,9.98184967,1.649990201 +6.73969094,-6.850627899,-27.40886116,-32.57411194,0.02669902518,0.1087433919,0.02283645235,-0.2193958163,9.986762047,1.493821502 +6.74969094,-6.276763916,-25.03041267,-34.11810303,0.05053491145,0.1415589154,0.006427761633,-0.186055243,9.888465881,1.22418046 +6.75969094,-5.76556778,-26.61947441,-34.61665344,0.05468331277,0.1356552541,-0.004396722652,-0.2582173944,9.880655289,0.8945252299 +6.76969094,-6.412559509,-27.5041523,-33.48397827,0.02108596638,0.1019207016,-0.00492911879,-0.1788927615,9.746808052,0.8333583474 +6.77969094,-6.438995361,-27.78486824,-33.92181396,-0.01197361574,0.05054997653,-0.004860650748,-0.214070335,9.705836296,0.8822153211 +6.78969094,-7.23532486,-26.35127068,-31.88542175,-0.04006820545,0.009633807465,-0.007401582785,-0.2310826182,9.749238968,0.9326863885 +6.79969094,-5.922412872,-26.77134514,-34.30026245,-0.05779248476,-0.005850811489,-0.01066414267,-0.3249489665,9.765732765,1.428793669 +6.80969094,-6.624382019,-28.08500481,-34.51049805,-0.03070151433,-0.02131212316,-0.01599514671,-0.3499325216,9.840603828,1.784139752 +6.81969094,-7.216690063,-27.87317085,-33.9138031,0.01399721112,0.04281678796,-0.02650093473,-0.3876112998,10.02012634,1.671920061 +6.82969094,-7.334274292,-27.47063255,-33.33171082,0.03933909908,0.1012582257,-0.03255970404,-0.4020133018,9.993823051,1.556693912 +6.83969094,-6.422775269,-27.50942421,-33.94143677,0.04864115268,0.1026042551,-0.03411246836,-0.3927852213,9.927248001,1.404534221 +6.84969094,-6.099685669,-26.93375587,-34.89878845,0.04725361243,0.08464277536,-0.02633284219,-0.3983494043,9.882896423,1.130617261 +6.85969094,-6.709514618,-27.13157463,-33.96768188,0.03037458286,0.05510923266,-0.02556121349,-0.2641792893,9.814121246,1.064528465 +6.86969094,-8.128997803,-27.56772423,-34.08610535,0.01483618375,0.003363851458,-0.02506784908,-0.187411204,9.8035326,1.080051899 +6.87969094,-7.206474304,-27.86789894,-33.4563446,-0.002668799367,-0.01789128594,-0.02890080027,-0.320317775,9.793665886,1.104911685 +6.88969094,-6.774410248,-28.23335838,-33.88916016,-0.01878705993,-0.02112191916,-0.02776563354,-0.4035662711,9.821204185,1.327342987 +6.89969094,-5.018215179,-27.21451378,-34.1181488,-0.02795302868,-0.03217923641,-0.01834156923,-0.5014348626,9.811349869,1.555790424 +6.90969094,-7.20728302,-27.60124397,-34.2383728,-0.008743250743,-0.01979034208,-0.01190102287,-0.58278054,9.913967133,1.847940207 +6.91969094,-7.614204407,-27.08926582,-33.05297852,0.02492422983,0.0008906451985,-0.00287973159,-0.5595668554,10.02017021,1.774408937 +6.92969094,-7.233718872,-27.88195992,-34.6762085,0.04376917332,0.04712672532,-4.605692811e-05,-0.4242864549,9.926309586,1.59119153 +6.93969094,-6.364692688,-26.41115761,-34.3249054,0.05611082911,0.08474594355,0.001481917687,-0.3794151247,9.889209747,1.309559703 +6.94969094,-6.492179871,-26.68140984,-32.32142639,0.04988059774,0.0568838492,0.004740366712,-0.3320913613,9.857626915,1.161911249 +6.95969094,-6.750072479,-27.82018852,-33.91860962,0.03539325669,0.001818951452,0.01166409627,-0.2974179685,9.810185432,1.090349078 +6.96969094,-7.654762268,-27.77787971,-33.00390625,0.01885271445,-0.02488981374,0.01005619578,-0.2859608829,9.783588409,1.121622562 +6.97969094,-6.388221741,-27.09098244,-33.51342773,0.01086804736,-0.04192594439,0.01164975017,-0.3405780494,9.779755592,1.130196571 +6.98969094,-8.289241791,-27.72134972,-33.92221069,-0.001383066177,-0.04618999362,0.01165413111,-0.3816172481,9.75190258,1.178019404 +6.99969094,-7.193969727,-26.92669106,-35.50730896,-0.0115212854,-0.04669833183,0.01270983368,-0.328725487,9.718396187,1.172267914 +7.00969094,-7.662380219,-27.5147419,-34.09091187,-0.02117509581,-0.01347930171,0.006302303169,-0.3198303878,9.751620293,1.187064886 +7.01969094,-6.630390167,-28.35517693,-34.03343201,-0.03068202361,0.03722409159,-0.002729954664,-0.2822543681,9.780727386,1.300859928 +7.02969094,-5.79510498,-27.56947136,-33.32810974,-0.03604897112,0.08230222762,-0.009162247181,-0.2794805765,9.743423462,1.304435372 +7.03969094,-5.177967072,-26.96727562,-35.05105591,-0.03218114376,0.0754532665,-0.01454768702,-0.2370035499,9.778010368,1.412263632 +7.04969094,-7.679096222,-28.19104958,-32.97447205,-0.01724203117,0.04264059663,-0.01922232471,-0.3165919483,9.784212112,1.443166137 +7.05969094,-6.45602417,-27.7936573,-34.68421936,0.001956798136,0.03951912373,-0.01594966836,-0.4240784943,9.821266174,1.450664759 +7.06969094,-6.479553223,-28.47348213,-33.8727417,0.008233220316,0.02018114552,-0.008499246091,-0.3593118489,9.874123573,1.357286453 +7.07969094,-6.767910004,-27.56232262,-35.46304321,0.009127783589,-0.001021089964,-0.004468719475,-0.3498756886,9.895945549,1.244823337 +7.08969094,-6.692485809,-27.12278557,-33.20527649,-0.0003577759489,0.005629235413,-0.005507835187,-0.3065412641,9.852435112,1.167840958 +7.09969094,-6.924747467,-27.71418571,-35.14666748,-0.01182218362,0.001805877313,-0.01178541407,-0.3422503471,9.787656784,1.151168823 +7.10969094,-7.233718872,-27.88195992,-34.6762085,-0.02720049769,-0.03890308738,-0.006950148847,-0.3069644272,9.728578568,1.21667695 +7.11969094,-7.233718872,-27.88195992,-34.6762085,-0.03472160175,-0.08361277729,-0.002064191969,-0.3741668761,9.828631401,1.381771445 +7.12969094,-6.566795349,-27.38760185,-33.79716492,-0.02443219163,-0.1106951907,0.003613332286,-0.414747417,9.909883499,1.478235364 +7.13969094,-6.566795349,-27.38760185,-33.79716492,-0.009322667494,-0.08561228961,0.001425060909,-0.3364252448,9.950445175,1.520546079 +7.14969094,-6.894901276,-27.4317112,-34.55636597,0.01297305804,-0.02708912455,-0.01052638143,-0.2902875245,9.916857719,1.579128742 +7.15969094,-6.756885529,-27.82370567,-34.22355652,0.03051350266,0.02103862539,-0.02036703378,-0.3130198121,9.968650818,1.473050833 +7.16969094,-5.754547119,-26.88085747,-33.37718201,0.0321297273,0.03274782375,-0.02557382733,-0.2421809435,9.896290779,1.491528034 +7.17969094,-6.583019257,-27.66304588,-33.77752686,0.03285422921,0.03309166431,-0.0193908941,-0.3465489447,10.00986385,1.124078274 +7.18969094,-6.140735626,-28.02323341,-33.75289917,0.006991864182,0.03043413348,-0.01644375175,-0.3370473981,9.83026123,1.150320888 +7.19969094,-6.395530701,-27.49536324,-32.72157288,-0.008524950594,-0.003374490887,-0.008978554048,-0.3312490284,9.773057938,1.049988508 +7.20969094,-6.388221741,-27.09098244,-33.51342773,-0.02062888816,-0.03278618306,-0.003952762112,-0.3668813407,9.767832756,1.011610508 +7.21969094,-7.223503113,-27.876688,-34.21875,-0.04249082133,-0.0584564656,0.0001669055782,-0.3540610373,9.73944664,1.033432245 +7.22969094,-6.600048065,-27.67183495,-34.53993225,-0.05800957978,-0.07085700333,0.003791878931,-0.2489045262,9.71612072,1.233810782 +7.23969094,-5.110660553,-26.6654644,-32.78346252,-0.05563404411,-0.05858933181,-0.002280948451,-0.3057807386,9.793344498,1.323033094 +7.24969094,-7.309940338,-27.05746269,-33.36116028,-0.04836731032,-0.03795132041,-0.005654682405,-0.3357924521,10.0036459,1.809297442 +7.25969094,-6.61416626,-28.07973289,-34.05305481,-0.01423756406,0.006586294156,-0.01188713871,-0.3840130866,10.07378864,1.766835213 +7.26969094,-7.343997955,-27.07504082,-34.88597107,0.01365469862,0.07387618721,-0.01604680531,-0.4060422778,9.988751411,1.480861068 +7.27969094,-7.777240753,-29.57706642,-33.63873291,0.02453222126,0.1045350581,-0.01727712527,-0.4209952354,9.866679192,1.274744511 +7.28969094,-5.620742798,-27.00795174,-33.97889709,0.01804178953,0.06112404913,-0.005017064512,-0.3968960345,9.788542747,1.140993834 +7.29969094,-7.377742767,-27.76013756,-34.53192139,0.0008723521605,0.004055330064,0.007162726019,-0.3800239861,9.755570412,1.057734847 +7.30969094,-7.672595978,-27.52001381,-34.5483551,-0.01781889051,-0.02779907547,0.01464441419,-0.3330850601,9.757546425,1.02295351 +7.31969094,-7.233718872,-27.88195992,-34.6762085,-0.03380134702,-0.05228021741,0.01153825596,-0.3335008323,9.738282204,1.145313025 +7.32969094,-7.320156097,-27.0627346,-33.81858826,-0.04232557118,-0.0432979241,0.008302789181,-0.3039984107,9.838270187,1.414606929 +7.33969094,-7.190254211,-27.59245491,-33.47596741,-0.02935238183,-0.006282945164,-0.002957660705,-0.3414013386,9.905882835,1.408320904 +7.34969094,-7.477802277,-26.94795036,-34.28424072,-0.02123269252,0.01367532462,-0.01296658069,-0.4141909778,9.940719604,1.555158734 +7.35969094,-7.60969162,-29.01906395,-34.59448242,-0.001434227452,0.0439722389,-0.01752397791,-0.3848665655,9.939268112,1.577160716 +7.36969094,-6.877067566,-27.6895771,-33.01191711,0.01578740031,0.06906723976,-0.01977552287,-0.4487940073,9.894701958,1.458930373 +7.37969094,-7.281089783,-28.57409096,-34.93208313,0.01979922131,0.06542313844,-0.01498976164,-0.4290300012,9.838287354,1.364023685 +7.38969094,-8.161937714,-28.51947594,-32.95002747,0.01419570576,0.05275142938,-0.007790469099,-0.4366999269,9.831308365,1.314301133 +7.39969094,-5.692451477,-28.11325264,-34.20532227,0.006470880471,0.05087926984,-0.0004649830516,-0.4752577841,9.859540939,1.219009399 +7.40969094,-7.608882904,-29.28571892,-33.81245422,-0.007653800771,0.03567602485,0.007800285239,-0.4810006022,9.848795891,1.221743464 +7.41969094,-7.368019104,-28.15572929,-32.97767639,-0.01515169907,0.01380138658,0.012325285,-0.4593022466,9.826662064,1.251535416 +7.42969094,-7.377742767,-27.76013756,-34.53192139,-0.01542961784,0.001022662269,0.0169789847,-0.4755968451,9.779748917,1.37317872 +7.43969094,-7.240531921,-27.88547707,-34.9811554,-0.001768577844,-0.009118218906,0.0176607091,-0.4284687936,9.814570427,1.428025246 +7.44969094,-5.634056091,-27.68250465,-32.70996094,0.01253924798,-0.02427603118,0.01522912644,-0.4111814201,9.89552021,1.414922833 +7.45969094,-5.822349548,-27.58353233,-34.54794312,0.016501192,-0.02373738401,0.01423248276,-0.3876463473,9.8126688,1.245267987 +7.46969094,-4.953323364,-26.11273003,-34.19667053,0.01277748775,-0.00871932786,0.006232345942,-0.3258767724,9.745279312,1.207792163 +7.47969094,-8.162742615,-28.25282097,-33.73207092,0.008416143246,0.01032425929,-0.002889809664,-0.3021071851,9.780869484,1.24430275 +7.48969094,-7.350498199,-27.74607658,-33.31208801,0.002876439132,0.02547182329,-0.01436623745,-0.2963513732,9.847862244,1.273426533 +7.49969094,-7.305427551,-28.98726082,-34.90263367,-0.002952933777,0.0436315015,-0.02437200397,-0.324506253,9.83515358,1.275908351 +7.50969094,-6.436088562,-28.18397713,-32.67250061,-0.005305088125,0.0420396477,-0.02915145457,-0.347178787,9.725076675,1.296135187 +7.51969094,-6.781223297,-28.23687553,-34.19410706,-0.002636422403,0.03410620987,-0.03221314773,-0.3757326901,9.717552185,1.36000824 +7.52969094,-5.242053986,-28.33571434,-34.19050598,0.0007457425818,-0.01242156141,-0.02688090876,-0.4411390722,9.801218033,1.417131782 +7.53969094,-6.767910004,-27.56232262,-35.46304321,-0.001384953968,-0.05899711698,-0.01799323969,-0.4971121252,9.86701107,1.508045077 +7.54969094,-6.147857666,-27.35922813,-35.93670654,0.005357009359,-0.04358646274,-0.01161833853,-0.4639644921,9.821174622,1.404155374 +7.55969094,-4.976852417,-26.79255486,-33.38519287,0.0151279429,0.0234514568,-0.01500775106,-0.3858245611,9.831782341,1.389816284 +7.56969094,-7.960639954,-27.27637672,-34.2598114,0.0150560895,0.07433521748,-0.02114808559,-0.4165942073,9.863568306,1.26608634 +7.57969094,-7.960639954,-27.27637672,-34.2598114,-0.00113414228,0.0747378394,-0.01512921415,-0.4706613719,9.822451591,1.127860188 +7.58969094,-6.924438477,-28.38170815,-33.26782227,-0.01949587092,0.04672278464,-0.004416399635,-0.445856601,9.810827255,1.171207547 +7.59969094,-6.924438477,-28.38170815,-33.26782227,-0.02355873771,0.02254176326,0.001991390716,-0.4345932901,9.865891457,1.256605029 +7.60969094,-7.360713959,-27.7513485,-33.76951599,-0.01876131445,-0.01338596083,0.01086175442,-0.4302533865,9.892401695,1.399819732 +7.61969094,-6.099372864,-27.60127449,-33.01992798,-0.01351431571,-0.02890297584,0.01611890458,-0.4486133754,9.895947456,1.357090831 +7.62969094,-6.023464203,-27.7582531,-36.21383667,-0.006224627607,-0.03095378913,0.02272274345,-0.393653661,9.873376846,1.453101158 +7.63969094,-4.95381546,-26.51359367,-33.09985352,0.005430291407,-0.03075273894,0.02272888087,-0.3597887158,9.917237282,1.505781412 +7.64969094,-7.662380219,-27.5147419,-34.09091187,0.01662251353,-0.01261894777,0.02528715879,-0.3409255147,9.983776093,1.455637813 +7.65969094,-5.956157684,-27.45644188,-33.94624329,0.02580481023,0.01031739917,0.02337107435,-0.3538760841,9.961380005,1.392374516 +7.66969094,-5.945941925,-27.45116997,-33.48878479,0.02733973414,0.02243147232,0.02270804159,-0.3228459656,9.875902176,1.254824042 +7.67969094,-6.699794769,-27.52716637,-32.41342163,0.01501560863,0.04744583368,0.0235953778,-0.2549906671,9.883605957,1.205357552 +7.68969094,-5.870212555,-28.67652702,-33.70703125,-0.004503405653,0.07281056046,0.02109043114,-0.2139221281,9.83890152,1.22555697 +7.69969094,-6.106185913,-27.60479164,-33.3249054,-0.02125325799,0.07326879352,0.01964235678,-0.3086723089,9.874782562,1.160287142 +7.70969094,-5.586685181,-26.99037361,-32.4540863,-0.03068202361,0.07159326226,0.0173430033,-0.2877425849,9.871712685,1.175171494 +7.71969094,-4.659275055,-26.08619881,-34.96228027,-0.02949244902,0.07909663022,0.009707890451,-0.2861399055,9.862574577,1.186464429 +7.72969094,-6.55688858,-26.71480751,-35.21856689,-0.03317952901,0.09336777031,0.006730064284,-0.2911436856,9.858118057,1.322293639 +7.73969094,-5.501056671,-27.54294014,-34.09371948,-0.02859684452,0.08231816441,0.006812664215,-0.3232996762,9.870845795,1.490914941 +7.74969094,-6.781223297,-28.23687553,-34.19410706,-0.02269191667,0.05310622603,0.01530707069,-0.4537217319,9.846877098,1.393212676 +7.75969094,-6.124019623,-27.34692574,-34.86933899,-0.0262016058,0.02659603208,0.02568925545,-0.4360646009,9.820919991,1.411521673 +7.76969094,-5.696960449,-26.18345451,-32.66384888,-0.01927095279,0.02992433496,0.02766008116,-0.3731211722,9.859240532,1.493628025 +7.77969094,-6.423088074,-26.84190559,-35.82026672,-0.006756332237,0.06467999518,0.02580158599,-0.3479112685,9.877393723,1.410155177 +7.78969094,-6.002719879,-28.41522789,-33.42008972,-0.008050819859,0.1007210612,0.02127013728,-0.3521136343,9.825234413,1.321725845 +7.79969094,-7.713153839,-28.2086277,-34.49928284,-0.008586347103,0.1213297322,0.01726247743,-0.3854017854,9.753717422,1.385715365 +7.80969094,-6.133430481,-27.61885262,-34.54473877,0.006605016999,0.1322527677,0.01431554556,-0.3959612548,9.918157578,1.32136023 +7.81969094,-5.668609619,-28.10094643,-33.13796997,0.01407197956,0.1031682789,0.02043901943,-0.3845593333,9.779145241,1.264665008 +7.82969094,-7.165916443,-27.17928505,-33.50541687,0.01219318714,0.0370747447,0.02418522909,-0.3916911483,9.846901894,1.245322227 +7.83969094,-6.130828857,-27.35043907,-35.17430115,0.009833314456,0.002732901601,0.03058625944,-0.3888401389,9.822085381,1.323127627 +7.84969094,-6.675769806,-26.44647789,-34.32170105,0.01811700687,-0.005176926032,0.03133744374,-0.3480463326,9.801818848,1.435973644 +7.85969094,-6.683078766,-26.85085869,-33.52984619,0.0284155868,-0.004169987515,0.03186465427,-0.3630813062,9.866709709,1.449119687 +7.86969094,-5.778076172,-27.5606823,-32.56570435,0.03512119129,-0.008301069029,0.02844945155,-0.3229779601,9.843298912,1.415961981 +7.87969094,-4.823112488,-27.30996895,-31.97518921,0.0376309827,0.01536973473,0.02377301455,-0.2518242002,9.83313942,1.337736964 +7.88969094,-6.597137451,-28.07094383,-33.29064941,0.03478628397,0.03389872238,0.01552080922,-0.2680309415,9.835711479,1.253291488 +7.89969094,-6.525432587,-26.96564293,-33.06419373,0.0338845104,0.04535847902,0.006745200139,-0.2180101275,9.819023132,1.17667377 +7.90969094,-5.127689362,-26.67425346,-33.54586792,0.03140550107,0.03659430891,-0.001788613503,-0.1975850165,9.744873047,1.114180207 +7.91969094,-5.986499786,-28.13978386,-33.43971252,0.02867707983,0.01808444411,-0.009790910408,-0.2553076744,9.766321182,1.183923244 +7.92969094,-6.099372864,-27.60127449,-33.01992798,0.03164083511,-0.009051376022,-0.01414498687,-0.2725343108,9.800502777,1.266273379 +7.93969094,-7.190254211,-27.59245491,-33.47596741,0.03399694338,-0.03510866314,-0.01080191508,-0.3454096317,9.859103203,1.313657284 +7.94969094,-6.692485809,-27.12278557,-33.20527649,0.03248399124,-0.05040859431,-0.009788624942,-0.3541353345,9.83678627,1.314404488 +7.95969094,-5.701858521,-28.38517952,-33.88075256,0.02853907272,-0.04782148451,-0.009216323495,-0.3199444115,9.823339462,1.327843428 +7.96969094,-7.233718872,-27.88195992,-34.6762085,0.02739499882,-0.0317414254,-0.01145336591,-0.3068087101,9.891061783,1.347099781 +7.97969094,-5.948848724,-27.05206108,-34.73809814,0.02311804891,0.002707405714,-0.01562085561,-0.3483853638,9.85543251,1.401388407 +7.98969094,-6.965305328,-28.40279961,-35.09759521,0.02524874732,0.03096579574,-0.01465842687,-0.391481787,9.854180336,1.335046649 +7.99969094,-6.503890991,-28.88665199,-33.84329224,0.01820404828,0.03465368971,-0.005479752086,-0.3856081367,9.825998306,1.251428127 +8.00969094,-6.600048065,-27.67183495,-34.53993225,0.007291789167,0.007053038105,0.001987132709,-0.3335992694,9.810012817,1.201320887 +8.01969094,-6.163772583,-28.3021946,-34.03823853,-0.004180718213,-0.01456182078,0.01051915064,-0.3299972117,9.774536133,1.128297806 +8.02969094,-6.740352631,-28.21578026,-32.36434937,-0.01397203282,-0.03909219801,0.01338874549,-0.2939029634,9.800624847,1.181418419 +8.03969094,-6.740352631,-28.21578026,-32.36434937,-0.01755905338,-0.06164037436,0.01571976021,-0.3166171908,9.827580452,1.220766783 +8.04969094,-6.462524414,-28.46469307,-33.1103363,-0.02299508452,-0.07050529122,0.0169789847,-0.3287492394,9.846059799,1.265481591 +8.05969094,-6.462524414,-28.46469307,-33.1103363,-0.02086721361,-0.05611816049,0.01313410886,-0.3127599657,9.857430458,1.293400407 +8.06969094,-7.729377747,-28.48407173,-34.47964478,-0.01981415227,-0.02539752796,0.006699037272,-0.2972289622,9.890842438,1.254536748 +8.07969094,-7.350498199,-27.74607658,-33.31208801,-0.01718049124,0.008935067803,-0.003795302706,-0.2668244243,9.924371719,1.326421261 +8.08969094,-5.829654694,-27.98791313,-33.75610352,-0.009907738306,0.0356521979,-0.01852735132,-0.2779456675,9.923431396,1.423228025 +8.09969094,-7.517551422,-27.90321922,-33.45314026,0.006071233191,0.05359268934,-0.02314954251,-0.3708066344,9.927910805,1.405539036 +8.10969094,-6.399246216,-26.82959938,-34.75291443,0.01424688939,0.05802699178,-0.02333005331,-0.3841352463,9.899069786,1.362089515 +8.11969094,-6.709514618,-27.13157463,-33.96768188,0.01599690691,0.02361442149,-0.01315881126,-0.3848026097,9.839741707,1.206048846 +8.12969094,-4.843856812,-26.65299416,-34.76893616,0.002254735678,-0.01309334487,-0.003706273623,-0.3823072016,9.808428764,1.156294227 +8.13969094,-6.504203796,-28.21913338,-35.72212219,-0.0131037822,-0.02041620202,0.002272384241,-0.3847629428,9.844836235,1.307581306 +8.14969094,-6.73985672,-27.81491661,-33.46115112,-0.0191719532,-0.008650093339,0.006325504277,-0.3688550293,9.877473831,1.353151679 +8.15969094,-6.791439056,-28.24214745,-34.65156555,-0.01912099682,0.009797222912,0.006641102489,-0.3635028005,9.896050453,1.36118114 +8.16969094,-5.996219635,-27.74419212,-34.99397278,-0.01667619683,0.02679310925,0.003280967008,-0.3678926826,9.887942314,1.416732907 +8.17969094,-6.496582031,-28.48227119,-34.63514709,-0.004689696245,0.06497299671,0.005499317776,-0.3481041789,9.858240128,1.296657085 +8.18969094,-6.429588318,-27.51294136,-34.24638367,-0.004146645777,0.07617202401,0.001241869759,-0.3353248835,9.83586216,1.141688943 +8.19969094,-5.587493896,-26.72371864,-33.2361145,-0.01000920963,0.02543907054,0.004583748057,-0.3017803431,9.841454506,1.160948873 +8.20969094,-6.705497742,-29.46223259,-34.41235352,-0.01565810107,-0.03551977128,0.01044568978,-0.3462574482,9.866422653,1.207236767 +8.21969094,-6.877872467,-27.42292213,-33.79396057,-0.01856961846,-0.07280281931,0.01563544571,-0.3439301848,9.821113586,1.283706784 +8.22969094,-5.754547119,-26.88085747,-33.37718201,-0.01776750758,-0.05113984644,0.01072100736,-0.3152393103,9.849270821,1.368287325 +8.23969094,-6.790630341,-28.50880241,-33.86953735,-0.007758837193,0.0131536331,0.00110917585,-0.2948393226,9.888804436,1.375244021 +8.24969094,-6.883880615,-27.69309425,-33.31689453,0.0006529949605,0.05003567785,-0.007681441959,-0.3519371152,9.929426193,1.400822401 +8.25969094,-6.733852386,-27.54474449,-33.93823242,0.006397298537,0.0646423623,-0.01796964183,-0.3227308393,9.871684074,1.478527665 +8.26969094,-6.389030457,-26.82432747,-34.29545593,0.01365157124,0.08304973692,-0.02444793843,-0.285761565,9.80264473,1.286781788 +8.27969094,-5.979190826,-27.73540306,-34.23156738,0.01110325288,0.1031337157,-0.02889024839,-0.3004356325,9.809235573,1.281821251 +8.28969094,-7.199661255,-27.86438179,-33.15139771,0.01459526736,0.09770360589,-0.02966837026,-0.3339990079,9.804094315,1.184403181 +8.29969094,-6.924930573,-28.78257179,-32.17100525,0.001203035004,0.04889506102,-0.0212808568,-0.4237344265,9.790711403,1.148083925 +8.30969094,-6.60735321,-28.07621574,-33.74809265,-0.009375064634,0.01187157445,-0.01253232919,-0.3896429539,9.781116486,1.272933602 +8.31969094,-7.638046265,-27.10157204,-34.12036133,-0.00883369986,-0.007211517543,-0.005917307455,-0.4066072404,9.739402771,1.226894975 +8.32969094,-7.719966888,-28.21214485,-34.804245,-0.00851373747,-0.04851602763,0.003152085003,-0.4191360176,9.802324295,1.162682176 +8.33969094,-6.123214722,-27.6135807,-34.08731079,-0.01866781525,-0.08298770338,0.004629021045,-0.3646979332,9.822608948,1.243217111 +8.34969094,-6.573608398,-27.391119,-34.10212708,-0.01661939919,-0.05272687972,-0.001723809517,-0.3448917866,9.821504593,1.550055861 +8.35969094,-5.922412872,-26.77134514,-34.30026245,0.01482299808,0.01570285112,-0.01347478852,-0.4482457042,9.877696037,1.567859054 +8.36969094,-5.51127243,-27.54821205,-34.55114746,0.0364352949,0.05439424515,-0.01533631422,-0.5011907816,9.884627342,1.447217703 +8.37969094,-6.45602417,-27.7936573,-34.68421936,0.03358461335,0.05259715021,-0.007164225448,-0.4079911113,9.869935989,1.386832952 +8.38969094,-5.282611847,-29.02432823,-34.14143372,0.03009070083,0.02654754184,-0.0008480702527,-0.3597754538,9.862734795,1.254638791 +8.39969094,-6.476646423,-28.87259102,-32.62342834,0.02441980317,0.01135490555,0.004194807727,-0.3759404719,9.843818665,1.162875891 +8.40969094,-5.877025604,-28.68004417,-34.01199341,0.01330524031,-0.007301305421,0.01035590097,-0.272282958,9.803807259,1.169050694 +8.41969094,-7.271369934,-28.96968269,-33.37782288,0.004843432456,-0.02468059771,0.0119635351,-0.2605700195,9.832016945,1.192519903 +8.42969094,-6.976016998,-28.80893898,-34.45822144,-0.006202861667,-0.0435533002,0.01424292661,-0.2615356743,9.865633011,1.344079614 +8.43969094,-6.147548676,-28.02675056,-34.05786133,-0.004407898989,-0.04488246143,0.013322182,-0.2936043739,9.890439034,1.490942717 +8.44969094,-6.826293945,-26.9956913,-32.6035614,0.005539809354,-0.004841507412,0.0106570404,-0.2814545333,9.921515465,1.381560922 +8.45969094,-5.945941925,-27.45116997,-33.48878479,0.009740176611,0.0336917229,0.005443233531,-0.2583817542,9.897677422,1.307276845 +8.46969094,-8.233951569,-28.95725441,-35.05532837,0.006820478477,0.03775998205,0.0009987638332,-0.188798517,9.863088608,1.290988088 +8.47969094,-5.508361816,-27.94732094,-33.30186462,0.002396757714,0.02465282194,-0.002144287806,-0.2838674486,9.874014854,1.326351166 +8.48969094,-6.843322754,-27.00448036,-33.3659668,-0.005590040237,0.03597750515,-0.002197280526,-0.3121668994,9.947696686,1.387628078 +8.49969094,-6.843322754,-27.00448036,-33.3659668,6.277114153e-07,0.05783951283,-0.008056694642,-0.286522001,9.934962273,1.414525747 +8.50969094,-6.222164154,-28.73294258,-35.53359985,0.003852193244,0.06198479235,-0.01023218781,-0.3322080374,9.870047569,1.257705212 +8.51969094,-6.222164154,-28.73294258,-35.53359985,-0.007020583376,0.03898721188,-0.007140536793,-0.354721427,9.797861099,1.161445856 +8.52969094,-6.894096375,-27.69836617,-33.77432251,-0.02465378493,0.003852344118,-0.001664606389,-0.3568671644,9.820009232,1.262687206 +8.53969094,-6.123214722,-27.6135807,-34.08731079,-0.0191527456,-0.0115311034,0.00301573053,-0.4283187389,9.78444767,1.281093597 +8.54969094,-5.668922424,-27.43342781,-35.01679993,-0.02239307947,-0.01610159874,0.009372130036,-0.4523630142,9.761321068,1.180357814 +8.55969094,-5.701858521,-28.38517952,-33.88075256,-0.02748598531,-0.02391372249,0.01481722109,-0.3728437424,9.760118484,1.258087873 +8.56969094,-5.822349548,-27.58353233,-34.54794312,-0.02905605733,-0.02079217322,0.01221286692,-0.3384934366,9.839878082,1.264950633 +8.57969094,-7.391365051,-27.76716805,-35.14186096,-0.02644769847,0.004086437169,0.003770391457,-0.3486168683,9.821240425,1.333070636 +8.58969094,-6.013744354,-28.15384483,-34.65957642,-0.01537995227,0.01251769904,0.0009696688503,-0.3741804063,9.827664375,1.279482841 +8.59969094,-8.104663849,-27.15455437,-34.11555481,-0.01356030069,-0.002310473472,-0.002780725248,-0.3859480917,9.867848396,1.242322564 +8.60969094,-6.163772583,-28.3021946,-34.03823853,-0.02265218273,-0.0157774277,-0.00271670986,-0.3495922685,9.865961075,1.364074707 +8.61969094,-7.311927795,-29.65829659,-33.32875061,-0.01418382674,0.0006659564096,-0.005384140648,-0.3531033099,9.862417221,1.514775276 +8.62969094,-4.850357056,-27.32402992,-33.1950531,0.003424083814,0.03531191498,-0.006987604313,-0.4141439497,9.91368103,1.393144369 +8.63969094,-4.049133301,-26.55590248,-34.01454163,0.005545211025,0.02093706839,-0.003268030239,-0.388307482,9.895030975,1.247608185 +8.64969094,-6.623577118,-28.35165977,-33.72845459,-0.0003154044971,-0.02269215137,0.0004618884996,-0.3421718478,9.894777298,1.317503333 +8.65969094,-5.627555847,-27.01146889,-34.28384399,-0.001384953968,-0.04192937911,0.004192750901,-0.3671855927,9.879576683,1.335730672 +8.66969094,-6.40574646,-27.50063515,-33.17903137,0.006623237394,-0.02268284559,0.003680216148,-0.4353603423,9.886388779,1.367411256 +8.67969094,-7.719654083,-28.87966347,-32.92539978,0.01196025219,0.010412395,0.001041296404,-0.3877769411,9.898147583,1.325113058 +8.68969094,-6.09287262,-26.93023872,-34.59381104,0.01560688298,0.03164923191,-0.002251014113,-0.3476009667,9.874887466,1.177082181 +8.69969094,-5.185276031,-27.37165642,-34.25920105,0.007014569826,0.0320574306,-0.006441064179,-0.3255999684,9.796077728,1.179525852 +8.70969094,-5.887241364,-28.68531609,-34.46943665,-0.004665999673,0.02300379425,-0.007558021694,-0.3302562535,9.838270187,1.29654789 +8.71969094,-6.934654236,-28.38698006,-33.72525024,-0.0008134776726,0.01394113898,-0.008628170937,-0.4091928303,9.864145279,1.397688985 +8.72969094,-6.61447525,-27.41221046,-35.93190002,0.01304710936,0.0058097695,-0.01178541407,-0.3761585057,9.922281265,1.419453979 +8.73969094,-7.247840881,-28.28985786,-34.18930054,0.01882565767,-0.01711278409,-0.009561704472,-0.4335117936,9.864617348,1.383633018 +8.74969094,-6.140735626,-28.02323341,-33.75289917,0.01722362638,-0.01998676173,-0.004755612463,-0.3925135732,9.847726822,1.352542996 +8.75969094,-5.692451477,-28.11325264,-34.20532227,0.01413588692,-0.004737165757,-0.003758656094,-0.3975400031,9.840883255,1.424174428 +8.76969094,-6.374908447,-26.41642952,-34.78236389,0.01665059105,0.01422545873,0.001111822668,-0.4121716917,9.825641632,1.308867693 +8.77969094,-6.872554779,-29.61937523,-34.55342102,0.0079627214,0.00683412049,0.008576428518,-0.3998596966,9.780550003,1.15374434 +8.78969094,-8.128192902,-27.8343792,-33.30407715,-0.00830971729,-0.0006057955325,0.0137829408,-0.3708814979,9.795752525,1.336443067 +8.79969094,-6.156959534,-28.29867744,-33.73326111,-0.003319519572,0.03192749619,0.008939841762,-0.3640524149,9.828931808,1.51407516 +8.80969094,-4.970039368,-26.7890377,-33.08021545,0.0151279429,0.08334953338,0.004141870886,-0.40965572,9.86701107,1.33130312 +8.81969094,-6.123214722,-27.6135807,-34.08731079,0.01143238787,0.08572150022,0.005260156002,-0.3747372925,9.777318001,1.236672163 +8.82969094,-5.484832764,-27.26749611,-34.11334229,-0.0006647165865,0.04545711726,0.00868627429,-0.3375897408,9.728914261,1.210252285 +8.83969094,-5.79510498,-27.56947136,-33.32810974,-0.004408819135,0.004282972775,0.01372285746,-0.3406184614,9.721815109,1.191379905 +8.84969094,-6.901714325,-27.43522835,-34.86132812,-0.01009862404,-0.02514038049,0.0141247306,-0.3459747136,9.810904503,1.276666164 +8.85969094,-5.484027863,-27.53415108,-33.33131409,-0.01250343956,-0.009691631421,0.009318539873,-0.3982391655,9.863305092,1.43898356 +8.86969094,-5.594306946,-26.72723579,-33.5410614,-0.005750786047,0.02927107364,0.008384827524,-0.3999786675,9.804018974,1.47284627 +8.87969094,-5.6510849,-27.69129372,-33.47236633,0.001811090857,0.02968214266,0.01073560491,-0.4115684032,9.764409065,1.312410116 +8.88969094,-6.262722015,-29.42155647,-35.48452759,-0.007250172552,0.009495561942,0.01774243638,-0.3444252312,9.76662159,1.203037024 +8.89969094,-5.819438934,-27.98264122,-33.29866028,-0.01225235127,-0.03034259938,0.01879092306,-0.2967675924,9.789928436,1.379716516 +8.90969094,-6.181606293,-28.04432869,-35.58267212,0.003125378862,-0.04652474821,0.0152979847,-0.3290558457,9.828923225,1.316641212 +8.91969094,-7.777553558,-28.90954781,-35.51757812,0.008289773948,-0.05507510155,0.006511804182,-0.3192000985,9.854446411,1.306584358 +8.92969094,-6.726543427,-27.14036369,-34.73008728,0.006072483025,-0.03125399351,-0.001492372714,-0.2490886152,9.842761993,1.329090953 +8.93969094,-6.530639648,-28.49984932,-36.15995789,0.005286928266,0.004717913922,-0.01269114576,-0.2787717581,9.871692657,1.240097642 +8.94969094,-6.156959534,-28.29867744,-33.73326111,0.007722427137,0.00421890663,-0.01890247688,-0.341399461,9.860766411,1.212909102 +8.95969094,-5.195491791,-27.37692833,-34.71665955,-0.0005177315325,-0.02194965258,-0.01977552287,-0.3430656791,9.841578484,1.20852685 +8.96969094,-5.195491791,-27.37692833,-34.71665955,-0.004397911951,-0.01808477193,-0.02489776164,-0.2913993001,9.817040443,1.284796357 +8.97969094,-7.726776123,-28.21565819,-35.10920715,0.002665152773,0.001532091293,-0.02840395272,-0.3493477702,9.777391434,1.309384704 +8.98969094,-7.726776123,-28.21565819,-35.10920715,0.00724685844,-0.006472785026,-0.02936365642,-0.3285253942,9.861621857,1.359132767 +8.99969094,-6.80475235,-28.91670036,-33.38262939,0.007950174622,-0.02342860959,-0.02639209293,-0.4434536099,9.895025253,1.340307117 +9.00969094,-4.424610138,-27.29214287,-35.02961731,0.006259321235,-0.02674228698,-0.02167566307,-0.4321332276,9.925757408,1.370373964 +9.01969094,-5.539627075,-25.63072395,-34.07704163,0.01441753004,-0.003579446115,-0.02427353896,-0.3899079859,9.913691521,1.459052444 +9.02969094,-8.179279327,-27.86074638,-35.59129333,0.02454777062,0.02349377796,-0.02628907003,-0.4051021338,9.94682312,1.378842115 +9.03969094,-4.857170105,-27.32754707,-33.5,0.01992200688,0.02365491912,-0.02259735391,-0.429605186,9.887137413,1.335855365 +9.04969094,-5.644271851,-27.68777657,-33.16741943,0.00781902764,0.003750482807,-0.0154065527,-0.3775611222,9.861557007,1.329430461 +9.05969094,-4.808990479,-26.902071,-32.46209717,0.002876439132,0.002535451669,-0.01032625698,-0.3763277531,9.841564178,1.236484885 +9.06969094,-6.058815002,-26.9126606,-33.06900024,-0.002186387777,0.02572786249,-0.007384793833,-0.3992110789,9.797712326,1.18870461 +9.07969094,-4.641933441,-26.74492836,-32.3210144,-0.01085284352,0.03625140339,7.089274004e-05,-0.3808373511,9.743421555,1.078503132 +9.08969094,-5.76556778,-26.61947441,-34.61665344,-0.03301307559,0.01188983768,0.006888038013,-0.3624552786,9.697451591,1.048014522 +9.09969094,-6.100177765,-27.33461952,-33.80197144,-0.05799226463,-0.02245188132,0.01368184574,-0.3520770371,9.722191811,1.232402325 +9.10969094,-6.076648712,-26.65479469,-34.6134491,-0.05832531676,-0.02201962285,0.01837413386,-0.4136171639,9.800364494,1.344371796 +9.11969094,-7.182945251,-27.18807411,-34.26782227,-0.04887463897,-0.006774947047,0.01895935461,-0.4178379476,9.859547615,1.376471162 +9.12969094,-6.918430328,-28.11153603,-33.74488831,-0.03943052515,-0.01560469344,0.01933539659,-0.4469336271,9.893157959,1.409580708 +9.13969094,-6.422775269,-27.50942421,-33.94143677,-0.03463018686,-0.008655243553,0.01995558664,-0.3561683893,9.847137451,1.53296864 +9.14969094,-5.195491791,-27.37692833,-34.71665955,-0.01544791646,0.02348045073,0.0137829408,-0.3908901513,9.918141365,1.499109507 +9.15969094,-6.600048065,-27.67183495,-34.53993225,0.001441395842,0.03940471262,0.0152179841,-0.4149687886,9.856978416,1.277093053 +9.16969094,-5.989406586,-27.74067497,-34.68902588,-0.006609489676,0.004615615588,0.02280433103,-0.3392545581,9.758050919,1.146429181 +9.17969094,-5.484832764,-27.26749611,-34.11334229,-0.02656558156,-0.03210052848,0.02782632969,-0.2474892884,9.751603127,1.163014531 +9.18969094,-5.819747925,-27.31511879,-35.17750549,-0.02908400446,-0.04421976209,0.02460899949,-0.2521186173,9.782547951,1.296428919 +9.19969094,-5.76556778,-26.61947441,-34.61665344,-0.02535528317,-0.05329878628,0.02000474744,-0.2538339794,9.860228539,1.281494498 +9.20969094,-5.551830292,-28.23682594,-34.5020752,-0.0306779705,-0.04690035433,0.01591363735,-0.259737134,9.867933273,1.315501928 +9.21969094,-6.381721497,-26.41994667,-35.08731079,-0.03282633424,-0.03781130165,0.01075538062,-0.2662158608,9.842382431,1.342554212 +9.22969094,-5.979686737,-28.13626671,-33.13476562,-0.03420222551,-0.0125182569,0.005236934405,-0.2183810771,9.868021965,1.407692909 +9.23969094,-6.100177765,-27.33461952,-33.80197144,-0.02300329134,0.02829145826,-0.00561462203,-0.2840327621,9.901955605,1.485758662 +9.24969094,-4.625217438,-26.06862068,-33.43746948,-0.01911250688,0.04374434054,-0.008972733282,-0.3135461211,9.874196053,1.319430351 +9.25969094,-8.288433075,-27.98800468,-33.1401825,-0.02691411227,0.02197675779,-0.01033035852,-0.299005717,9.850079536,1.237532496 +9.26969094,-6.509208679,-26.6901989,-33.08383179,-0.03162460029,0.001549815293,-0.01018739119,-0.3111849427,9.796510696,1.378709555 +9.27969094,-6.473861694,-27.5357914,-36.22865295,-0.02315361984,-0.002986954525,-0.009323257953,-0.3509652317,9.8478508,1.414524198 +9.28969094,-7.360713959,-27.7513485,-33.76951599,-0.02178758755,-0.01910696179,-0.009859112091,-0.3926713765,9.908628464,1.373457074 +9.29969094,-6.951683044,-28.39576912,-34.48765564,-0.02419984341,-0.01715814695,-0.01178541407,-0.3376707137,9.898147583,1.401946902 +9.30969094,-6.590637207,-27.39990807,-34.86453247,-0.01450248435,0.01003692858,-0.01763336919,-0.3839448094,9.886221886,1.38350749 +9.31969094,-7.36151886,-27.48469353,-34.55155945,-0.01278505847,0.01857505366,-0.01977552287,-0.3988118172,9.814868927,1.286329269 +9.32969094,-6.484874725,-26.27702904,-33.11326599,-0.0293417424,-0.004705166444,-0.01006867364,-0.4420841634,9.828910828,1.158545732 +9.33969094,-6.422775269,-27.50942421,-33.94143677,-0.04848059639,-0.02031212486,-0.001976955682,-0.3750244081,9.772753716,1.376981616 +9.34969094,-6.510704041,-28.89016914,-34.14823914,-0.04332835972,-0.01426587068,-0.00082276389,-0.4337833822,9.7676754,1.373581648 +9.35969094,-7.736190796,-28.48758888,-34.78462219,-0.0376179181,-0.01224911399,0.002211536281,-0.4449275136,9.839629173,1.465065002 +9.36969094,-7.217002869,-27.20565224,-35.79263306,-0.02085287496,-0.03022374399,0.009145841002,-0.4059441388,9.8934021,1.544756889 +9.37969094,-5.001186371,-27.20572472,-33.35574341,-0.01117274538,-0.04885619879,0.01271759532,-0.3361937106,9.898633957,1.443880439 +9.38969094,-6.46333313,-28.1980381,-33.8923645,0.02227536216,-0.04157933593,-0.001544222352,-0.383582741,10.06635571,1.592002034 +9.39969094,-6.45602417,-27.7936573,-34.68421936,0.02985722572,0.008457273245,0.003183291759,-0.4190266728,10.04802799,1.19617939 +9.40969094,-6.100177765,-27.33461952,-33.80197144,0.02161491662,-0.01152697392,0.004680039361,-0.3310981691,9.888607979,1.307484984 +9.41969094,-6.100177765,-27.33461952,-33.80197144,0.02274901047,-0.02205886319,0.003949012142,-0.2644232512,9.895751953,1.281700253 +9.42969094,-8.001693726,-28.36585426,-33.11393738,0.0270957537,0.008429657668,-0.004258564673,-0.3135220706,9.886099815,1.322879076 +9.43969094,-8.001693726,-28.36585426,-33.11393738,0.03116482496,0.03424095362,-0.01175708137,-0.3184842765,9.87495327,1.208269715 +9.44969094,-5.972377777,-27.73188591,-33.92662048,0.0162592046,0.01626186259,-0.0105086565,-0.335937798,9.807515144,1.067642093 +9.45969094,-5.161434174,-27.3593502,-33.19184875,-0.01620616391,-0.01655917056,-0.004570898134,-0.3055139184,9.73563385,1.092912316 +9.46969094,-7.310745239,-26.79080772,-34.14318848,-0.02988763899,-0.03499742597,-0.003262628801,-0.2359486967,9.825713158,1.553684115 +9.47969094,-6.573112488,-26.99025154,-35.19894409,-0.01365076192,-0.01185967028,-0.008056694642,-0.3714031279,9.954780579,1.606928349 +9.48969094,-6.388221741,-27.09098244,-33.51342773,-0.0008522793651,0.03052273393,-0.008053194731,-0.4348410964,9.923695564,1.438187957 +9.49969094,-5.524894714,-27.55524254,-35.16108704,-0.002721569035,0.01259369962,-0.0008508043829,-0.3950788081,9.823553085,1.357761741 +9.50969094,-4.90422821,-28.68719673,-31.87704468,-0.009681989439,-0.01744466461,0.007116397377,-0.3847718537,9.837177277,1.353380203 +9.51969094,-5.419940948,-26.16571236,-34.19186401,-0.01018538047,-0.0034658527,0.009544158354,-0.4182123542,9.833841324,1.346598744 +9.52969094,-7.343997955,-27.07504082,-34.88597107,-0.005646346137,0.01587818377,0.009550519288,-0.3948255181,9.812287331,1.296076536 +9.53969094,-6.710323334,-26.86491966,-34.74971008,-0.006285015494,0.003204530338,0.009770220146,-0.3175331056,9.817604065,1.234001398 +9.54969094,-8.449485779,-27.87497139,-33.75830078,-0.02056121826,-0.02370020375,0.01402841881,-0.310777247,9.741884232,1.292146921 +9.55969094,-5.525390625,-27.95611,-34.06427002,-0.0177536048,-0.04929082096,0.01203878596,-0.3341468275,9.749916077,1.421624541 +9.56969094,-7.351303101,-27.47942162,-34.09411621,-0.005059229676,-0.07262051105,0.01338049769,-0.3519653678,9.822608948,1.402941823 +9.57969094,-6.428779602,-27.77959633,-33.46435547,-0.004816933535,-0.09561718255,0.01041176356,-0.3689287603,9.827497482,1.293786645 +9.58969094,-6.60735321,-28.07621574,-33.74809265,-0.006380548701,-0.09612983465,0.008321847767,-0.2538587749,9.772336006,1.353903651 +9.59969094,-6.934654236,-28.38698006,-33.72525024,0.002941088751,-0.0783181861,-0.002114825416,-0.2645348608,9.775900841,1.470979691 +9.60969094,-7.518360138,-27.63656425,-34.23516846,0.0110929301,-0.0527979359,-0.009588164277,-0.2304200828,9.846688271,1.339077234 +9.61969094,-6.428779602,-27.77959633,-33.46435547,0.00417023059,-0.02648497,-0.01985131018,-0.286404103,9.793714523,1.367927313 +9.62969094,-5.990215302,-27.47402,-35.47105408,0.0009704809636,-0.01529773325,-0.02053293586,-0.3193401098,9.735281944,1.396854877 +9.63969094,-5.915599823,-26.76782799,-33.99531555,0.002660098486,-0.01091999374,-0.02030819841,-0.3742132783,9.726406097,1.408692479 +9.64969094,-5.836467743,-27.99143028,-34.06106567,0.009704994969,0.00570339663,-0.02030819841,-0.396333158,9.782100677,1.37638545 +9.65969094,-5.075801849,-27.91191673,-34.83148193,0.01480308268,0.01089406572,-0.01835904643,-0.383464247,9.801027298,1.287954688 +9.66969094,-7.527767181,-27.90849113,-33.91059875,0.004126793705,-0.01640985347,-0.01433289424,-0.3254973888,9.808070183,1.299425244 +9.67969094,-5.753753662,-28.14489174,-36.94999695,-0.01064739004,-0.03583458811,-0.01477447897,-0.3483581841,9.893923759,1.421264291 +9.68969094,-7.688819885,-27.79545784,-34.52871704,-0.01350821275,-0.01939630695,-0.01784704253,-0.4041688144,9.913567543,1.498895407 +9.69969094,-5.795909882,-27.30281639,-34.11013794,-0.001301977783,0.01890260912,-0.02151327021,-0.4571124613,9.889979362,1.583774805 +9.70969094,-6.037273407,-28.83366966,-33.84809875,0.02016665787,0.03482097015,-0.02001620457,-0.3828421235,9.923582077,1.550175071 +9.71969094,-7.368331909,-27.48821068,-34.85652161,0.02689192817,0.01481494214,-0.01628457569,-0.4274457693,9.955366135,1.418853045 +9.72969094,-5.846683502,-27.99670219,-34.51850891,0.01808453351,0.0007289100904,-0.007230801042,-0.4414436519,9.913903236,1.362712622 +9.73969094,-7.233718872,-27.88195992,-34.6762085,0.006072483025,0.005902021192,-0.00551132299,-0.4905114472,9.913440704,1.355573893 +9.74969094,-7.385047913,-28.16451836,-33.74008179,-0.001548105851,0.03948125988,0.001368286088,-0.3877508342,9.898666382,1.246912837 +9.75969094,-6.509895325,-29.15682411,-33.36621094,-0.01327420678,0.06441155076,-0.002559530549,-0.3439078927,9.951795578,1.185796857 +9.76969094,-7.19997406,-27.19686317,-35.03022766,-0.02978560328,0.05697054416,-0.00752402097,-0.4116650224,9.954465866,1.257131696 +9.77969094,-6.428779602,-27.77959633,-33.46435547,-0.03334539384,0.07115680724,-0.006260839291,-0.5033277869,9.958221436,1.41427815 +9.78969094,-5.668113708,-27.70008278,-34.23477173,-0.02409242094,0.08291181177,-0.003795302706,-0.5041429996,9.987658501,1.488123059 +9.79969094,-5.535114288,-27.56051826,-35.61853027,-0.01179026719,0.08431231976,0.001658534165,-0.5291974545,9.954752922,1.494916201 +9.80969094,-6.589519501,-28.33408165,-32.2036438,0.003343363293,0.07458043098,0.00850411877,-0.4825110435,9.898130417,1.412799358 +9.81969094,-5.997028351,-27.47753716,-35.77600098,0.006553829648,0.05965533108,0.01489961706,-0.4234584868,9.782516479,1.332983136 +9.82969094,-6.059619904,-26.64600563,-33.8510437,0.01331350673,0.04850659519,0.01735352911,-0.3877936006,9.777862549,1.266014695 +9.83969094,-6.503890991,-28.88665199,-33.84329224,0.01824017987,0.03485743701,0.01993710175,-0.3487462103,9.775895119,1.266067386 +9.84969094,-7.19997406,-27.19686317,-35.03022766,0.02160441503,0.02122296393,0.01546534896,-0.311239928,9.760976791,1.272807956 +9.85969094,-6.55657959,-27.38232994,-33.33972168,0.01953481883,0.01390312053,0.01141053811,-0.3209119439,9.78553009,1.278277993 +9.86969094,-5.979190826,-27.73540306,-34.23156738,0.01899946108,0.01229704265,0.006715378258,-0.2684645653,9.816758156,1.335773826 +9.87969094,-7.206474304,-27.86789894,-33.4563446,0.02045468241,0.005114794243,0.002737503033,-0.3115752637,9.813545227,1.378985286 +9.88969094,-7.206474304,-27.86789894,-33.4563446,0.02057313174,-0.009556769393,0.003524060361,-0.2928525209,9.85481739,1.433280349 +9.89969094,-5.709480286,-28.1220417,-34.96772766,0.02471607178,-0.02593976445,-0.000195460394,-0.289963156,9.882286072,1.459529281 +9.90969094,-5.709480286,-28.1220417,-34.96772766,0.0349977091,-0.03940877318,-6.658444181e-05,-0.3231247663,9.801194191,1.448393464 +9.91969094,-6.100177765,-27.33461952,-33.80197144,0.04312978685,-0.04885619879,0.0009987638332,-0.32735309,9.733083725,1.397290349 +9.92969094,-5.444274902,-26.57888222,-34.16241455,0.05012412742,-0.04050931334,-0.001043979544,-0.2132692337,9.657939911,1.322166562 +9.93969094,-6.124019623,-27.34692574,-34.86933899,0.05335898697,-0.02532506175,-0.005103618838,-0.2339841872,9.683965683,1.298429251 +9.94969094,-5.822349548,-27.58353233,-34.54794312,0.04862793908,-0.005474109203,-0.009122043848,-0.2713183165,9.604672432,1.183119655 +9.95969094,-5.028430939,-27.21978569,-34.57559204,0.03629063815,-0.004140705802,-0.01083520055,-0.2865132093,9.572835922,1.108448267 +9.96969094,-6.019748688,-28.42401695,-34.18249512,0.01961249486,0.02772274427,-0.009543137625,-0.2350679785,9.573115349,1.162874699 +9.97969094,-5.610527039,-27.00267982,-33.5214386,0.008852970786,0.05212645978,-0.01561361179,-0.2903158665,9.632687569,1.220809221 +9.98969094,-4.843048096,-26.91964912,-33.98690796,-0.006395697594,0.07004146278,-0.01498145796,-0.3082864285,9.775126457,1.420338035 +9.99969094,-6.510704041,-28.89016914,-34.14823914,-0.01554161683,0.08230477571,-0.01358819567,-0.3808072507,9.88078022,1.6931777 +10.00969094,-5.558643341,-28.24034309,-34.80705261,-0.01750100963,0.09559768438,-0.003126793541,-0.4694359601,9.878305435,1.681256294 +10.01969094,-5.948848724,-27.05206108,-34.73809814,-0.01223013736,0.0947631076,0.01301613264,-0.520768702,9.835087776,1.616311669 +10.02969094,-5.524585724,-28.22276497,-33.28224182,-0.01229349431,0.08211163431,0.03143116459,-0.5171135068,9.89792347,1.515195131 +10.03969094,-5.634864807,-27.41584969,-33.49198914,-0.02304166183,0.07002359629,0.04795950279,-0.4607204795,9.916221619,1.462448239 +10.04969094,-5.86290741,-28.27214622,-34.49887085,-0.03202089667,0.06247267872,0.05862831324,-0.3976040781,9.907452583,1.388126731 +10.05969094,-5.812625885,-27.97912407,-32.99369812,-0.04750698432,0.0630890578,0.06102353334,-0.3217801452,9.843457222,1.314891696 +10.06969094,-6.058815002,-26.9126606,-33.06900024,-0.04895600677,0.05808627605,0.05451076478,-0.3295866549,9.846390724,1.25166893 diff --git a/applications/newton/llvm-ir/kalman.sh b/applications/newton/llvm-ir/kalman.sh new file mode 100755 index 000000000..1109ff2c0 --- /dev/null +++ b/applications/newton/llvm-ir/kalman.sh @@ -0,0 +1,28 @@ +USER_HOME=$HOME + + +# Step 1: Generate LLVM IR file +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/Kalman.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/Kalman.cpp +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/lu.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/lu_openmp.c + +# Step 4: Optimize the generated LLVM IR file +opt $HOME/CoSense/applications/newton/llvm-ir/Kalman.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $HOME/CoSense/applications/newton/llvm-ir/Kalman.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +## Step 9: Compile the test file and link with the static library +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm +# +## Step 10: Run the test executable +#$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/original_compile.sh b/applications/newton/llvm-ir/original_compile.sh new file mode 100755 index 000000000..98fe3a0f5 --- /dev/null +++ b/applications/newton/llvm-ir/original_compile.sh @@ -0,0 +1,27 @@ +# Step 1: Generate LLVM IR file from the C file +echo "Step 1: Generate LLVM IR file from the C file" +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o /home/xyf/CoSense/applications/newton/llvm-ir/original.ll /home/xyf/CoSense/applications/newton/llvm-ir/c-files/original.c + +# Step 2: Convert generated LLVM IR file back to bitcode +echo "Step 2: Convert generated LLVM IR file back to bitcode" +llvm-as /home/xyf/CoSense/applications/newton/llvm-ir/original.ll -o /home/xyf/CoSense/applications/newton/llvm-ir/original.bc + +# Step 3: Optimize the LLVM bitcode file +echo "Step 3: Optimize the LLVM bitcode file" +opt /home/xyf/CoSense/applications/newton/llvm-ir/original.bc --simplifycfg --instsimplify -O3 -Os -o /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.bc + +# Step 4: Convert optimized bitcode back to LLVM IR +echo "Step 4: Convert optimized bitcode back to LLVM IR" +llvm-dis /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.bc -o /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.ll + +# Step 5: Compile the optimized LLVM IR file to assembly +echo "Step 5: Compile the optimized LLVM IR file to assembly" +llc /home/xyf/CoSense/applications/newton/llvm-ir/original_opt.ll -o /home/xyf/CoSense/applications/newton/llvm-ir/original.s + +# Step 6: Compile the assembly file to object file +echo "Step 6: Compile the assembly file to object file" +clang -c /home/xyf/CoSense/applications/newton/llvm-ir/original.s -o /home/xyf/CoSense/applications/newton/llvm-ir/original.o + +# Step 7: Package the object file into a static library +echo "Step 7: Package the object file into a static library" +ar -rc /home/xyf/CoSense/applications/newton/llvm-ir/liboriginal.a /home/xyf/CoSense/applications/newton/llvm-ir/original.o diff --git a/applications/newton/llvm-ir/performance_test/Makefile b/applications/newton/llvm-ir/performance_test/Makefile index 25c8a62e5..edb89e4f5 100644 --- a/applications/newton/llvm-ir/performance_test/Makefile +++ b/applications/newton/llvm-ir/performance_test/Makefile @@ -18,7 +18,7 @@ OUT_OBJ = out.o OUT_LIB = libout.a # DEBUG_MODE=true make ... -ifdef DEBUG_MODE +ifdef DEBUG_MODE./ CC_OPT_LEVEL = -O0 -g else CC_OPT_LEVEL = -O3 -Os @@ -40,10 +40,10 @@ endif # auto-quantization configs, FP hardware by default, for Madgwick only currently # AUTO_QUANT=true make ... -ifdef AUTO_QUANT -DATA_TYPE=INT_DATA_TYPE -else -DATA_TYPE=FP_DATA_TYPE +#ifdef AUTO_QUANT +#DATA_TYPE=INT_DATA_TYPE +#else +#DATA_TYPE=FP_DATA_TYPE # SOFT_FLOAT_LIB=true make ... ifdef SOFT_FLOAT_LIB @@ -54,10 +54,11 @@ CC_FP_FLAG = -msoft-float OPT_FP_FLAG = --float-abi=soft endif -endif + # ENABLE_OVERLOAD=true ENABLE_BUILTIN_ASSUME=true make ... NEWTON_FLAG=--llvm-ir-liveness-check +#NEWTON_FLAG=--llvm-ir-liveness-check ifdef ENABLE_OVERLOAD NEWTON_FLAG+=--llvm-ir-enable-overload endif @@ -65,6 +66,10 @@ ifdef ENABLE_BUILTIN_ASSUME NEWTON_FLAG+=--llvm-ir-enable-builtin-assume endif +ifdef AUTO_QUANT +NEWTON_FLAG+=--llvm-ir-auto-quantization +endif + max_opt_fn = opt ../$(1).ll $(OPT_FP_FLAG) -O3 -Os -S -o $(OUT_FILE) non_opt_fn = cp ../$(1).ll $(OUT_FILE) necessary_opt_fn = opt ../$(1).ll --simplifycfg --instsimplify -S -o $(OUT_FILE) @@ -293,17 +298,17 @@ perf_exp_opt: clean make_ll exp_opt compile_lib compile_exp perf_log: clean make_ll log_non_opt compile_lib compile_log -perf_log_opt: clean make_ll log_opt compile_lib compile_log +perf_log_opt: clean make_ll log_opt compile_lib compile_log -perf_acosh: clean make_ll acosh_non_opt compile_lib compile_acosh +perf_acosh: clean make_ll acosh_non_opt compile_lib compile_acosh -perf_acosh_opt: clean make_ll acosh_opt compile_lib compile_acosh +perf_acosh_opt: clean make_ll acosh_opt compile_lib compile_acosh -perf_j0: clean make_ll j0_non_opt compile_lib compile_j0 +perf_j0: clean make_ll j0_non_opt compile_lib compile_j0 -perf_j0_opt: clean make_ll j0_opt compile_lib compile_j0 +perf_j0_opt: clean make_ll j0_opt compile_lib compile_j0 -perf_y0: clean make_ll y0_non_opt compile_lib compile_y0 +perf_y0: clean make_ll y0_non_opt compile_lib compile_y0 perf_y0_opt: clean make_ll y0_opt compile_lib compile_y0 @@ -372,6 +377,9 @@ perf_inferBoundControlFlow_opt: clean make_ll inferBoundControlFlow_opt compile_ auto_test_compile: clang++ auto_test.cpp -g -o auto_test +auto_test_compile_woquant: + clang++ auto_test_woquant.cpp -g -o auto_test_woquant + all: default default: perf_exp perf_exp_opt perf_log perf_log_opt perf_acosh perf_acosh_opt perf_j0 perf_j0_opt perf_y0 perf_y0_opt perf_rem_pio2 perf_rem_pio2_opt perf_sincosf perf_sincosf_opt perf_float64_add perf_float64_add_opt perf_float64_div perf_float64_div_opt perf_float64_mul perf_float64_mul_opt perf_float64_sin perf_float64_sin_opt perf_arm_sqrt_q15 perf_arm_sqrt_q15_opt auto_test_compile @@ -379,4 +387,4 @@ default: perf_exp perf_exp_opt perf_log perf_log_opt perf_acosh perf_acosh_opt p clean: $(QUIET)rm -f *.ll *.o *.s *.txt out.* libout.a main_out auto_test cd $(CHStone_DIR) && $(MAKE_CLEAN) - cd $(SUBDIR) && $(MAKE_CLEAN) + cd $(SUBDIR) && $(MAKE_CLEAN) \ No newline at end of file diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index b1e4d6f66..96202c8d3 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -16,8 +16,9 @@ #include #include #include +#include -const size_t iteration_num = 5; +const size_t iteration_num = 1; struct perfData { int64_t inst_count_avg; @@ -30,12 +31,56 @@ struct perfData { struct timerData { int64_t inst_count_avg = -1; double time_consumption_avg; + double compile_time_avg; std::vector ms_time_consumption; int64_t ir_lines; int64_t library_size; std::vector function_results; + std::vector compile_time; }; +#define TLOG_TIMESPEC_NSEC_PER_SEC 1000000000 + +typedef struct timespec timespec; + +timespec diff(timespec start, timespec end) { + timespec temp; + if ((end.tv_nsec - start.tv_nsec) < 0) { + temp.tv_sec = end.tv_sec - start.tv_sec - 1; + temp.tv_nsec = 1000000000 + end.tv_nsec - start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec - start.tv_sec; + temp.tv_nsec = end.tv_nsec - start.tv_nsec; + } + return temp; +} + +timespec tic() { + timespec t; + clock_gettime(CLOCK_REALTIME, &t); + return t; +} + +timespec toc(timespec* start_time, const char* prefix, bool print) { + timespec end_time; + clock_gettime(CLOCK_REALTIME, &end_time); + timespec diff_time = diff(*start_time, end_time); + *start_time = end_time; + return diff_time; +} + + +double compileTargetCode(const std::string& test_case) { + timespec compile_timer = tic(); + std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; + int ret = system(cmd.c_str()); + timespec compile_time = toc(&compile_timer, "compile time", false); + if (ret != 0) exit(-1); + return compile_time.tv_sec + compile_time.tv_nsec / (double)TLOG_TIMESPEC_NSEC_PER_SEC; +} + + + /* * Get number from: * 36,200,478 instructions @@ -91,7 +136,11 @@ std::pair processDataPerf(const std::string test_case, const s int64_t inst_count, time_consumption; // perf command - std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + //todo Quantization prefix + std::string quant_prefix = (test_case.find("_opt") != std::string::npos) ? "AUTO_QUANT=1 " : ""; + std::string cmd = "bash -c '" + quant_prefix + "ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + std::cout << "[DEBUG] Running command: " << cmd << std::endl; + // std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; int command_return = system(cmd.c_str()); if (command_return != 0) { return std::make_pair(0, 0); @@ -99,7 +148,8 @@ std::pair processDataPerf(const std::string test_case, const s cmd.clear(); cmd = "bash -c 'perf stat -B ./main_out " + params; - cmd += "if=/dev/zero of=/dev/null count=1000000"; +// cmd += "if=/dev/zero of=/dev/null count=1000000"; + cmd += "if=/dev/zero of=/dev/null count=1"; cmd += " 2>&1 | tee tmp.log'"; command_return = system(cmd.c_str()); if (command_return != 0) { @@ -138,7 +188,18 @@ std::pair> processDataTimer(const std::string test_c std::vector function_results; // perf command - std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; + //TODO Addd Quantization prefix +// std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; +// std::string quant_prefix = "AUTO_QUANT=1 "; + +// std::string cmd = "bash -c '" + quant_prefix + "make " + test_case + " >& compile.log'"; +// std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; +// std::cout << "[DEBUG] Running command: " << cmd << std::endl; + std::string quant_prefix = (test_case.find("_opt") != std::string::npos) ? "AUTO_QUANT=1 " : ""; + std::string cmd = "bash -c '" + quant_prefix + "make " + test_case + " >& compile.log'"; + std::cout << "[DEBUG] Running command: " << cmd << std::endl; + + int command_return = system(cmd.c_str()); if (command_return != 0) { return std::make_pair(0, std::vector(0)); @@ -256,13 +317,17 @@ struct perfData recordData(const std::string& test_cases, const std::string& par ofs << test_cases << "\t" << param_str << "\t" << perf_data.inst_count_avg << "\t" << perf_data.time_consumption_avg << "\t" << perf_data.ir_lines << "\t" << perf_data.library_size << std::endl; + + return perf_data; } -struct timerData recordTimerData(const std::string& test_cases, const std::string& param_str, std::ofstream& ofs) { +struct timerData recordTimerData(const std::string& test_cases, const std::string& param_str, int precision_bits, std::ofstream& ofs){ timerData timer_data; for (size_t idx = 0; idx < iteration_num; idx++) { + double compile_time = compileTargetCode(test_cases); + timer_data.compile_time.emplace_back(compile_time); const std::pair> data_timer_res = processDataTimer(test_cases, param_str); timer_data.ms_time_consumption.emplace_back(data_timer_res.first); std::copy_if(data_timer_res.second.begin(), data_timer_res.second.end(), @@ -281,193 +346,302 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin timer_data.ir_lines = getIrLines(); timer_data.library_size = getLibSize(); - ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg - << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), - timer_data.ms_time_consumption.end(), - 0.0) / timer_data.ms_time_consumption.size() - << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size << std::endl; +ofs << test_cases << "\t" << param_str << "\t" << precision_bits << "\t" + << timer_data.inst_count_avg << "\t" + << std::accumulate(timer_data.ms_time_consumption.begin(), timer_data.ms_time_consumption.end(), 0.0) / timer_data.ms_time_consumption.size() << "\t" + << timer_data.ir_lines << "\t" << timer_data.library_size << "\t" + << std::accumulate(timer_data.compile_time.begin(), timer_data.compile_time.end(), 0.0) / timer_data.compile_time.size() + << std::endl; return timer_data; } -int main(int argc, char** argv) { - std::vector test_cases{ - "perf_exp", "perf_log", - "perf_acosh", "perf_j0", - "perf_y0", "perf_rem_pio2", "perf_sincosf", - "perf_float64_add", "perf_float64_div", - "perf_float64_mul"}; - - if (argc >= 2) { - test_cases.clear(); - test_cases.emplace_back(argv[1]); - } - - std::ofstream ofs("perf.log"); - if (!ofs.is_open()) { - std::cout << "error opening perf.log"; - return -1; - } - - std::ofstream avg_speedup("average_speedup.log"); - if (!avg_speedup.is_open()) { - std::cout << "error opening perf.log"; - return -1; - } - - std::vector> normalParameters{ - // BMX055 acceleration - {-2, 2}, - {-4, 4}, - {-8, 8}, - {-16, 16}, - // BMX055 gyroscope - {-125, 125}, - // LM35 Centigrade Temperature Sensor - {-40, 110}, - {-55, 150}, - {0, 100}, - {0, 70}, - // LPS25H crazyflie - {260, 1260}, - // MAX31820 1-Wire Ambient Temperature Sensor - {10, 45}, - {-55, 125}, - // DHT11 Humidity Sensor - {20, 80}, - {0, 50}, - // LMP82064 Current Sensor and Voltage Monitor with SPI - {-0.2, 2}, - // PCE-353 LEQ Sound Level Meter - {30, 130}, - // LLS05-A Linear Light Sensor - {1, 200} - }; - - std::vector> trigonometricParams{ - {0, 0.17453292519943295}, // (0, pi/18) - {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) - {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) - {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) - {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) - {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) - {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) - {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) - {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) - {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) - }; - - if (argc == 4) { - normalParameters.clear(); - std::vector input_param{strtod(argv[2], nullptr), strtod(argv[3], nullptr)}; - normalParameters.emplace_back(input_param); - - trigonometricParams.clear(); - trigonometricParams.emplace_back(input_param); - } - - ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; - avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size" << std::endl; - - for (size_t case_id = 0; case_id < test_cases.size(); case_id++) { - int avg_inst_speedup = 0; - int avg_time_speedup = 0; - int avg_ir_reduce = 0; - int avg_lib_size_reduce = 0; - const std::vector> parameters = - test_cases[case_id] == "perf_float64_sin" ? trigonometricParams : normalParameters; - for (const auto& p : parameters) { - const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", - "/g' ../../sensors/test.nt", - {p.front(), p.back()}); - const double p1 = p.front() + 0.6; - const double p2 = p.back() + 0.3; - change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); - - // perfData ori_perf_data = recordData(test_cases[case_id], param_str, ofs); - // perfData opt_perf_data = recordData(test_cases[case_id] + "_opt", param_str, ofs); - timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); - timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); - - // check function results - if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), - opt_perf_data.function_results.begin())) { - std::cerr << "result error: " << test_cases[case_id] << " with parameters: " << param_str << - "ori: " << ori_perf_data.function_results[0] << ", opt: " << opt_perf_data.function_results[0] << - std::endl; - } - - // remove element if ori < opt - assert(ori_perf_data.ms_time_consumption.size() == opt_perf_data.ms_time_consumption.size()); - auto itOri = ori_perf_data.ms_time_consumption.begin(); - for (auto itOpt = opt_perf_data.ms_time_consumption.begin(); - itOpt != opt_perf_data.ms_time_consumption.end();) { - if (*itOri < *itOpt) { - // assert(false && "Need to check why this case slow down!!!!!!"); - itOri = ori_perf_data.ms_time_consumption.erase(itOri); - itOpt = opt_perf_data.ms_time_consumption.erase(itOpt); - } else { - itOri++; - itOpt++; - } - } - - int inst_speedup, time_speedup, ir_reduce, lib_size_reduce; - if (ori_perf_data.ms_time_consumption.empty()) { - assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); - inst_speedup = 0; - time_speedup = 0; - } else { - ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), - ori_perf_data.ms_time_consumption.end(), - 0.0) / ori_perf_data.ms_time_consumption.size(); - opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), - opt_perf_data.ms_time_consumption.end(), - 0.0) / opt_perf_data.ms_time_consumption.size(); - - inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) - * 100 / opt_perf_data.inst_count_avg); - time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) - * 100 / opt_perf_data.time_consumption_avg); - } - - if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) { - ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); - lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); - } else { - // assert(false && "Need to check why this case increase size!!!!!!"); - ir_reduce = 0; - lib_size_reduce = 0; - } - ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" - << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; - std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" - << ir_reduce << "%\t" << lib_size_reduce << "%" << std::endl; - - avg_inst_speedup += inst_speedup; - avg_time_speedup += time_speedup; - avg_ir_reduce += ir_reduce; - avg_lib_size_reduce += lib_size_reduce; - - // reset test.nt - change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", - {p.front(), p.back()}); - change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); - } - avg_inst_speedup = round(avg_inst_speedup / parameters.size()); - avg_time_speedup = round(avg_time_speedup / parameters.size()); - avg_ir_reduce = round(avg_ir_reduce / parameters.size()); - avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); - avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" - << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%" << std::endl; - - if (test_cases[case_id] == "perf_float64_sin") { - // trigonometricParams cannot have extent - break; - } - } - - ofs.close(); - - return 0; +int main(int argc, char** argv) +{ + std::vector test_cases{ + // "perf_exp", "perf_log", + // "perf_acosh", "perf_j0", + // "perf_y0", "perf_rem_pio2", "perf_sincosf", + // "perf_float64_add", "perf_float64_div", + // "perf_float64_mul"}; + "perf_j0","perf_y0"}; + + + if (argc >= 2) + { + test_cases.clear(); + test_cases.emplace_back(argv[1]); + } + + std::ofstream ofs("perf_quant.log"); + if (!ofs.is_open()) + { + std::cout << "error opening perf_quant.log"; + return -1; + } + + std::ofstream avg_speedup("average_speedup_quant.log"); + if (!avg_speedup.is_open()) + { + std::cout << "error opening perf_quant.log"; + return -1; + } + + // std::vector> normalParameters{ + // // BMX055 acceleration + // {-2, 2}, + // {-4, 4}, + // {-8, 8}, + // {-16, 16}, + // // BMX055 gyroscope + // {-125, 125}, + // // LM35 Centigrade Temperature Sensor + // {-40, 110}, + // {-55, 150}, + // {0, 100}, + // {0, 70}, + // // LPS25H + // {260, 1260}, + // // MAX31820 1-Wire Ambient Temperature Sensor + // {10, 45}, + // {-55, 125}, + // // DHT11 Humidity Sensor + // {20, 80}, + // {0, 50}, + // // LMP82064 Current Sensor and Voltage Monitor with SPI + // {-0.2, 2}, + // // PCE-353 LEQ Sound Level Meter + // {30, 130}, + // // LLS05-A Linear Light Sensor + // {1, 200} + // }; + + // param_range, precision_bits, frac_q + std::vector, int, int>> normalParameters = { + // BMX055 acceleration + {{-2, 2}, 12, 9}, + {{-4, 4}, 12, 8}, + {{-8, 8}, 12, 7}, + {{-16, 16}, 12, 6}, + + // BMX055 gyroscope + {{-125, 125}, 16, 8}, + + // LM35 Centigrade Temperature Sensor + {{-40, 110}, 10, 2}, + {{-55, 150}, 11, 3}, + {{0, 100}, 10, 3}, + {{0, 70}, 10, 3}, + + // LPS25H Pressure Sensor + {{260, 1260}, 14, 4}, + + // MAX31820 1-Wire Ambient Temperature Sensor + {{10, 45}, 12, 6}, + {{-55, 125}, 11, 3}, + + // DHT11 Humidity Sensor + {{20, 80}, 8, 2}, + {{0, 50}, 8, 2}, + + // LMP82064 Current Sensor and Voltage Monitor with SPI + {{-0.2, 2}, 14, 12}, + + // PCE-353 LEQ Sound Level Meter + {{30, 130}, 8, 1}, + + // LLS05-A Linear Light Sensor + {{1, 200}, 10, 2}}; + + std::vector> trigonometricParams{ + {0, 0.17453292519943295}, // (0, pi/18) + {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) + {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) + {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) + {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) + {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) + {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) + {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) + {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) + {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) + }; + + if (argc == 4) + { + normalParameters.clear(); + std::vector input_param{strtod(argv[2], nullptr), strtod(argv[3], nullptr)}; + int default_frac_q = 8; + int default_precision_bits = 12; + normalParameters.emplace_back(std::make_tuple(input_param, default_precision_bits, default_frac_q)); + //normalParameters.emplace_back(input_param); + + + trigonometricParams.clear(); + trigonometricParams.emplace_back(input_param); + } + + + ofs << "test case\tparam\tprecision_bits\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + + for (size_t case_id = 0; case_id < test_cases.size(); case_id++) + { + int avg_inst_speedup = 0; + int avg_time_speedup = 0; + int avg_compile_time_speedup = 0; + int avg_ir_reduce = 0; + int avg_lib_size_reduce = 0; + // const std::vector> parameters = + // test_cases[case_id] == "perf_float64_sin" ? trigonometricParams : normalParameters; + // TODO + const bool is_trig = test_cases[case_id] == "perf_float64_sin"; + // 对于普通测试(非trig),使用 normalParameters + if (!is_trig) + { + for (const auto & entry : normalParameters) + { + const std::vector & range = std::get<0>(entry); + int frac_q = std::get<2>(entry); + + // 自动重编译 newton 以更新 MAX_PRECISION_BITS=frac_q + std::string frac_str = std::to_string(frac_q); + std::string rebuild_cmd = + "bash -c '" + "echo \"[DEBUG] Cleaning object files...\" && " + "rm -f ../../../../src/newton/newton-irPass-LLVMIR-quantization.o ../../../../src/newton/newton-irPass-LLVMIR-optimizeByRange.o && " + "echo \"[DEBUG] Running make with MAX_PRECISION_BITS=" + frac_str + "\" && " + "make -C ../../../../src/newton MAX_PRECISION_BITS=" + frac_str + " BIT_WIDTH=32 > build_" + frac_str + ".log 2>&1'"; + +// "2>&1 | tee build_" + frac_str + ".log'"; +//\\ std::string rebuild_cmd = "make -C ../../../src/newton MAX_PRECISION_BITS=" + std::to_string(frac_q) + " VERBOSE=1 rebuild-quant-opt"; + std::cout << "[INFO] Rebuilding Newton with MAX_PRECISION_BITS = " << frac_q << std::endl; + int ret = system(rebuild_cmd.c_str()); + if (ret != 0) + { + std::cerr << "[ERROR] Failed to rebuild Newton with MAX_PRECISION_BITS=" << frac_q << std::endl; + continue; + } + + const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", "/g' ../../sensors/test.nt", range); + const double p1 = range.front() + 0.6; + const double p2 = range.back() + 0.3; + change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); + + timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str,std::get<1>(entry), ofs); + timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, std::get<1>(entry), ofs); + + // check function results + if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), + opt_perf_data.function_results.begin())) + { + std::cerr << "result error: " << test_cases[case_id] << " with parameters: " << param_str << "ori: " << ori_perf_data.function_results[0] << ", opt: " << opt_perf_data.function_results[0] << std::endl; + } + + // remove element if ori < opt + assert(ori_perf_data.ms_time_consumption.size() == opt_perf_data.ms_time_consumption.size()); + auto itOri = ori_perf_data.ms_time_consumption.begin(); + for (auto itOpt = opt_perf_data.ms_time_consumption.begin(); + itOpt != opt_perf_data.ms_time_consumption.end();) + { + if (*itOri < *itOpt) + { + // assert(false && "Need to check why this case slow down!!!!!!"); + itOri = ori_perf_data.ms_time_consumption.erase(itOri); + itOpt = opt_perf_data.ms_time_consumption.erase(itOpt); + } + else + { + itOri++; + itOpt++; + } + } + + int inst_speedup, time_speedup, ir_reduce, lib_size_reduce, compile_time_speedup; + if (ori_perf_data.ms_time_consumption.empty()) + { + assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); + inst_speedup = 0; + time_speedup = 0; + } + else + { + ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), + ori_perf_data.ms_time_consumption.end(), + 0.0) / + ori_perf_data.ms_time_consumption.size(); + opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), + opt_perf_data.ms_time_consumption.end(), + 0.0) / + opt_perf_data.ms_time_consumption.size(); + ori_perf_data.compile_time_avg = std::accumulate(ori_perf_data.compile_time.begin(), + ori_perf_data.compile_time.end(), + 0.0) / ori_perf_data.compile_time.size(); + opt_perf_data.compile_time_avg = std::accumulate(opt_perf_data.compile_time.begin(), + opt_perf_data.compile_time.end(), + 0.0) / opt_perf_data.compile_time.size(); + + inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) * 100 / opt_perf_data.inst_count_avg); + time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) * 100 / opt_perf_data.time_consumption_avg); + compile_time_speedup = round((ori_perf_data.compile_time_avg - opt_perf_data.compile_time_avg) + * 100 / opt_perf_data.compile_time_avg); + } + + if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) + { + ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); + lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); + } + else + { + // assert(false && "Need to check why this case increase size!!!!!!"); + ir_reduce = 0; + lib_size_reduce = 0; + } + ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup + << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + + avg_inst_speedup += inst_speedup; + avg_time_speedup += time_speedup; + avg_ir_reduce += ir_reduce; + avg_lib_size_reduce += lib_size_reduce; + avg_compile_time_speedup += compile_time_speedup; + + // reset test.nt + change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", + // {p.front(), p.back()}); + {range.front(), range.back()}); + change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); + } + size_t count = is_trig ? trigonometricParams.size() : normalParameters.size(); + avg_inst_speedup = round(avg_inst_speedup / count); + avg_time_speedup = round(avg_time_speedup / count); + avg_ir_reduce = round(avg_ir_reduce / count); + avg_lib_size_reduce = round(avg_lib_size_reduce / count); + avg_compile_time_speedup = round(avg_compile_time_speedup / count); + // avg_inst_speedup = round(avg_inst_speedup / parameters.size()); + // avg_time_speedup = round(avg_time_speedup / parameters.size()); + // avg_ir_reduce = round(avg_ir_reduce / parameters.size()); + // avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); + avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" + << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%\t" + << avg_compile_time_speedup << "%" << std::endl; + + if (test_cases[case_id] == "perf_float64_sin") + { + // trigonometricParams cannot have extention + break; + } + } + +// ofs.close(); +// +// return 0; + } + ofs.close(); + + return 0; } + diff --git a/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp new file mode 100644 index 000000000..481031320 --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp @@ -0,0 +1,564 @@ +/* +* Auto test framework of performance and correctness. +* +* Run with: `./auto_test 2> err.log` +* */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +const size_t iteration_num = 1; + +struct perfData { + int64_t inst_count_avg; + int64_t time_consumption_avg; + int64_t ir_lines; + int64_t library_size; + std::vector function_results; +}; + +struct timerData { + int64_t inst_count_avg = -1; + double time_consumption_avg; + double compile_time_avg; + std::vector ms_time_consumption; + int64_t ir_lines; + int64_t library_size; + std::vector function_results; + std::vector compile_time; +}; + +#define TLOG_TIMESPEC_NSEC_PER_SEC 1000000000 + +/*************************************** +* Timer functions of the test framework +***************************************/ + +typedef struct timespec timespec; +timespec diff(timespec start, timespec end) +{ + timespec temp; + if ((end.tv_nsec-start.tv_nsec)<0) { + temp.tv_sec = end.tv_sec-start.tv_sec-1; + temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; + } else { + temp.tv_sec = end.tv_sec-start.tv_sec; + temp.tv_nsec = end.tv_nsec-start.tv_nsec; + } + return temp; +} + +timespec sum(timespec t1, timespec t2) { + timespec temp; + if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { + temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; + } else { + temp.tv_sec = t1.tv_sec + t2.tv_sec; + temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; + } + return temp; +} + +void printTimeSpec(timespec t, const char* prefix) { + printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); +} + +timespec tic( ) +{ + timespec start_time; + clock_gettime(CLOCK_REALTIME, &start_time); + return start_time; +} + +timespec toc( timespec* start_time, const char* prefix, bool print ) +{ + timespec current_time; + clock_gettime(CLOCK_REALTIME, ¤t_time); + timespec time_diff = diff( *start_time, current_time ); + if (print) + printTimeSpec( time_diff, prefix ); + *start_time = current_time; + return time_diff; +} + +/* +* Get number from: +* 36,200,478 instructions +* 0.013535825 seconds time elapsed +* */ +int64_t getPerfCount(const std::string& string, size_t position) { + std::string substring; + substring = string.substr(0, position); + substring.erase( + std::remove_if(substring.begin(), + substring.end(), + static_cast(&ispunct)), + substring.end()); + substring.erase( + std::remove_if(substring.begin(), + substring.end(), + static_cast(&isspace)), + substring.end()); + return std::stoi(substring); +} + +/* +* Get number from: +* computation delay: 0.001342399 +* */ +double getTimerConsumption(const std::string& string, size_t position) { + std::string substring; + substring = string.substr(position, string.size()); + return std::stod(substring); +} + +/* +* Get number from: +* results: 0.517104 0.809373 0.043233 -0.805564 -0.973201 +* */ +std::vector getFunctionResults(const std::string& string, size_t position) { + std::vector res; + std::stringstream ss; + std::string tmp; + ss << string; + double number; + while (!ss.eof()) { + ss >> tmp; + if (std::stringstream(tmp) >> number) + res.emplace_back(number); + } + return res; +} + +std::pair processDataPerf(const std::string test_case, const std::string params) { + std::string line; + size_t position; + int64_t inst_count, time_consumption; + + // perf command + std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) { + return std::make_pair(0, 0); + } + cmd.clear(); + cmd = "bash -c 'perf stat -B ./main_out " + params; + + cmd += "if=/dev/zero of=/dev/null count=1000000"; + cmd += " 2>&1 | tee tmp.log'"; + command_return = system(cmd.c_str()); + if (command_return != 0) { + return std::make_pair(0, 0); + } + std::ifstream ifs("tmp.log"); + if (!ifs.is_open()) { + std::cout << "error opening tmp.log"; + assert(false); + } + + // process + while (getline(ifs, line)) { + position = line.find("instructions"); + if (position != std::string::npos) { + inst_count = getPerfCount(line, position); + } + position = line.find("seconds time elapsed"); + if (position != std::string::npos) { + time_consumption = getPerfCount(line, position); + continue; + } + } + + // printf("%lu\t%lu\n", inst_count, time_consumption); + + ifs.close(); + + return std::make_pair(inst_count, time_consumption); +} + +double compileTargetCode(const std::string test_case) { + // measure the compile time + timespec compile_timer = tic(); + // perf command + std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; + int command_return = system(cmd.c_str()); + timespec compile_time = toc(&compile_timer, "compile time", false); + if (command_return != 0) { + exit(-1); + } + + return (double)compile_time.tv_sec + (double)compile_time.tv_nsec / TLOG_TIMESPEC_NSEC_PER_SEC; +} + +std::pair> processDataTimer(const std::string test_case, const std::string params) { + std::string line; + size_t position; + double time_consumption; + std::vector function_results; + + // perf command + std::string cmd = "bash -c './main_out " + params; + cmd += " 2>&1 | tee tmp.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) { + return std::make_pair(0, std::vector(0)); + } + std::ifstream ifs("tmp.log"); + if (!ifs.is_open()) { + std::cout << "error opening tmp.log"; + assert(false); + } + + // process + while (getline(ifs, line)) { + std::string key = "computation delay: "; + position = line.find(key); + if (position != std::string::npos) { + time_consumption = getTimerConsumption(line, position+key.size()); + } + key = "results: "; + position = line.find(key); + if (position != std::string::npos) { + function_results = getFunctionResults(line, position+key.size()); + } + } + + // printf("%f\n", time_consumption); + + ifs.close(); + + return std::make_pair(time_consumption, function_results); +} + +std::string change_nt_range(const std::string& cmd1, const std::string& cmd2, const std::vector& params) { + std::string param_str; + std::string change_nt_cmd; + param_str.clear(); + change_nt_cmd.clear(); + change_nt_cmd = cmd1; + // prepare parameters + for (const auto& pp : params) { + param_str += std::to_string(pp) + " "; + change_nt_cmd += std::to_string(pp) + " mjf, "; + } + + change_nt_cmd.erase(change_nt_cmd.end() - 2); + change_nt_cmd += cmd2; + change_nt_cmd = "bash -c \"" + change_nt_cmd + "\""; + system(change_nt_cmd.c_str()); + + return param_str; +} + +int64_t exactNumber() { + std::ifstream ifs("tmp.log"); + if (!ifs.is_open()) { + std::cout << "error opening tmp.log"; + assert(false); + } + + // process + std::string line; + std::getline(ifs, line); + auto it = std::remove_if(line.begin(), line.end(), [](const char &c){ + return !std::isdigit(c); + }); + + line.erase(it, line.end()); + + ifs.close(); + + char* pEnd; + + return std::strtol(line.c_str(), &pEnd, 10); +} + +int64_t getIrLines() { + std::string cmd = "bash -c 'wc -l out.ll >& tmp.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) + return 0; + + return exactNumber(); +} + +int64_t getLibSize() { + std::string cmd = "bash -c 'wc -c libout.a >& tmp.log'"; + int command_return = system(cmd.c_str()); + if (command_return != 0) + return 0; + + return exactNumber(); +} + +struct perfData recordData(const std::string& test_cases, const std::string& param_str, std::ofstream& ofs) { + perfData perf_data = {0, 0, 0, 0}; + + for (size_t idx = 0; idx < iteration_num; idx++) { + const std::pair inst_time_data = processDataPerf(test_cases, param_str); + perf_data.inst_count_avg += (inst_time_data.first/1000); + perf_data.time_consumption_avg += (inst_time_data.second/1000); + } + perf_data.inst_count_avg /= iteration_num; + perf_data.time_consumption_avg /= iteration_num; + + // check library size + perf_data.ir_lines = getIrLines(); + perf_data.library_size = getLibSize(); + + ofs << test_cases << "\t" << param_str << "\t" << perf_data.inst_count_avg + << "\t" << perf_data.time_consumption_avg << "\t" << perf_data.ir_lines << "\t" << perf_data.library_size << std::endl; + + return perf_data; +} + +struct timerData recordTimerData(const std::string& test_cases, const std::string& param_str, std::ofstream& ofs) { + timerData timer_data; + + for (size_t idx = 0; idx < iteration_num; idx++) { + double compile_time = compileTargetCode(test_cases); + timer_data.compile_time.emplace_back(compile_time); + const std::pair> data_timer_res = processDataTimer(test_cases, param_str); + timer_data.ms_time_consumption.emplace_back(data_timer_res.first); + std::copy_if(data_timer_res.second.begin(), data_timer_res.second.end(), + std::back_inserter(timer_data.function_results), + [test_cases, param_str, timer_data, data_timer_res](double val) { + if (!timer_data.function_results.empty()) { + if (!std::equal(timer_data.function_results.begin(), timer_data.function_results.end(), + data_timer_res.second.begin())) + std::cerr << "result error within iteration: " << test_cases << " with parameters: " << param_str << std::endl; + return false; + } else + return true; + }); + } + // check library size + timer_data.ir_lines = getIrLines(); + timer_data.library_size = getLibSize(); + + ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg + << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), + timer_data.ms_time_consumption.end(), + 0.0) / timer_data.ms_time_consumption.size() + << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + << "\t" << std::accumulate(timer_data.compile_time.begin(), + timer_data.compile_time.end(), + 0.0) / timer_data.compile_time.size() + << std::endl; + + return timer_data; +} + +int main(int argc, char** argv) { +// std::vector test_cases{ +// "perf_exp", "perf_log", +// "perf_acosh", "perf_j0", +// "perf_y0", "perf_rem_pio2", "perf_sincosf", +// "perf_float64_add", "perf_float64_div", +// "perf_float64_mul"}; + std::vector test_cases{ + // "perf_exp", "perf_log", + // "perf_acosh", "perf_j0", + // "perf_y0", "perf_rem_pio2", "perf_sincosf", + // "perf_float64_add", "perf_float64_div", + // "perf_float64_mul"}; + "perf_j0","perf_y0"}; + + if (argc >= 2) { + test_cases.clear(); + test_cases.emplace_back(argv[1]); + } + + std::ofstream ofs("perf_woquant.log"); + if (!ofs.is_open()) { + std::cout << "error opening perf_woquant.log"; + return -1; + } + + std::ofstream avg_speedup("average_speedup_woquant.log"); + if (!avg_speedup.is_open()) { + std::cout << "error opening perf_woquant.log"; + return -1; + } + + std::vector> normalParameters{ + // BMX055 acceleration + {-2, 2}, + {-4, 4}, + {-8, 8}, + {-16, 16}, + // BMX055 gyroscope + {-125, 125}, + // LM35 Centigrade Temperature Sensor + {-40, 110}, + {-55, 150}, + {0, 100}, + {0, 70}, + // LPS25H crazyflie + {260, 1260}, + // MAX31820 1-Wire Ambient Temperature Sensor + {10, 45}, + {-55, 125}, + // DHT11 Humidity Sensor + {20, 80}, + {0, 50}, + // LMP82064 Current Sensor and Voltage Monitor with SPI + {-0.2, 2}, + // PCE-353 LEQ Sound Level Meter + {30, 130}, + // LLS05-A Linear Light Sensor + {1, 200} + }; + + std::vector> trigonometricParams{ + {0, 0.17453292519943295}, // (0, pi/18) + {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) + {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) + {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) + {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) + {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) + {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) + {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) + {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) + {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) + }; + + if (argc == 4) { + normalParameters.clear(); + std::vector input_param{strtod(argv[2], nullptr), strtod(argv[3], nullptr)}; + normalParameters.emplace_back(input_param); + + trigonometricParams.clear(); + trigonometricParams.emplace_back(input_param); + } + + ofs << "test case\tparam\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + avg_speedup << "test cast\tinstruction count\ttime consumption\tir lines\tlibrary size\tcompile time" << std::endl; + + for (size_t case_id = 0; case_id < test_cases.size(); case_id++) { + int avg_inst_speedup = 0; + int avg_time_speedup = 0; + int avg_compile_time_speedup = 0; + int avg_ir_reduce = 0; + int avg_lib_size_reduce = 0; + const std::vector> parameters = + test_cases[case_id] == "perf_float64_sin" ? trigonometricParams : normalParameters; + for (const auto& p : parameters) { + const std::string param_str = change_nt_range("sed -i 's/3 mjf, 10 mjf/", + "/g' ../../sensors/test.nt", + {p.front(), p.back()}); + const double p1 = p.front() + 0.6; + const double p2 = p.back() + 0.3; + change_nt_range("sed -i 's/15 mjf, 36 mjf/", "/g' ../../sensors/test.nt", {p1, p2}); + + // perfData ori_perf_data = recordData(test_cases[case_id], param_str, ofs); + // perfData opt_perf_data = recordData(test_cases[case_id] + "_opt", param_str, ofs); + timerData ori_perf_data = recordTimerData(test_cases[case_id], param_str, ofs); + timerData opt_perf_data = recordTimerData(test_cases[case_id] + "_opt", param_str, ofs); + + // check function results + if (!std::equal(ori_perf_data.function_results.begin(), ori_perf_data.function_results.end(), + opt_perf_data.function_results.begin())) { + std::cerr << "result error: " << test_cases[case_id] << " with parameters: " << param_str << + "ori: " << ori_perf_data.function_results[0] << ", opt: " << opt_perf_data.function_results[0] << + std::endl; + } + + // remove element if ori < opt + assert(ori_perf_data.ms_time_consumption.size() == opt_perf_data.ms_time_consumption.size()); + auto itOri = ori_perf_data.ms_time_consumption.begin(); + for (auto itOpt = opt_perf_data.ms_time_consumption.begin(); + itOpt != opt_perf_data.ms_time_consumption.end();) { + if (*itOri < *itOpt) { + // assert(false && "Need to check why this case slow down!!!!!!"); + itOri = ori_perf_data.ms_time_consumption.erase(itOri); + itOpt = opt_perf_data.ms_time_consumption.erase(itOpt); + } else { + itOri++; + itOpt++; + } + } + + int inst_speedup, time_speedup, ir_reduce, lib_size_reduce, compile_time_speedup; + if (ori_perf_data.ms_time_consumption.empty()) { + assert(opt_perf_data.ms_time_consumption.empty() && "erase mis-match!"); + inst_speedup = 0; + time_speedup = 0; + } else { + ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), + ori_perf_data.ms_time_consumption.end(), + 0.0) / ori_perf_data.ms_time_consumption.size(); + opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), + opt_perf_data.ms_time_consumption.end(), + 0.0) / opt_perf_data.ms_time_consumption.size(); + + ori_perf_data.compile_time_avg = std::accumulate(ori_perf_data.compile_time.begin(), + ori_perf_data.compile_time.end(), + 0.0) / ori_perf_data.compile_time.size(); + opt_perf_data.compile_time_avg = std::accumulate(opt_perf_data.compile_time.begin(), + opt_perf_data.compile_time.end(), + 0.0) / opt_perf_data.compile_time.size(); + + inst_speedup = round((ori_perf_data.inst_count_avg - opt_perf_data.inst_count_avg) + * 100 / opt_perf_data.inst_count_avg); + time_speedup = round((ori_perf_data.time_consumption_avg - opt_perf_data.time_consumption_avg) + * 100 / opt_perf_data.time_consumption_avg); + compile_time_speedup = round((ori_perf_data.compile_time_avg - opt_perf_data.compile_time_avg) + * 100 / opt_perf_data.compile_time_avg); + } + + if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) { + ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); + lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); + } else { + // assert(false && "Need to check why this case increase size!!!!!!"); + ir_reduce = 0; + lib_size_reduce = 0; + } + ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup + << "%\t" << time_speedup << "%\t" + << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; + + avg_inst_speedup += inst_speedup; + avg_time_speedup += time_speedup; + avg_ir_reduce += ir_reduce; + avg_lib_size_reduce += lib_size_reduce; + avg_compile_time_speedup += compile_time_speedup; + + // reset test.nt + change_nt_range("sed -i 's/", "/3 mjf, 10 mjf/g' ../../sensors/test.nt", + {p.front(), p.back()}); + change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); + } + avg_inst_speedup = round(avg_inst_speedup / parameters.size()); + avg_time_speedup = round(avg_time_speedup / parameters.size()); + avg_ir_reduce = round(avg_ir_reduce / parameters.size()); + avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); + avg_compile_time_speedup = round(avg_compile_time_speedup / parameters.size()); + avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" + << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%\t" + << avg_compile_time_speedup << "%" << std::endl; + + if (test_cases[case_id] == "perf_float64_sin") { + // trigonometricParams cannot have extention + break; + } + } + + ofs.close(); + + return 0; +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/performance_test/bar_plot.py b/applications/newton/llvm-ir/performance_test/bar_plot.py index e4d06cef4..d2ed97057 100644 --- a/applications/newton/llvm-ir/performance_test/bar_plot.py +++ b/applications/newton/llvm-ir/performance_test/bar_plot.py @@ -1,220 +1,293 @@ import matplotlib.pyplot as plt import numpy as np import pandas as pd -import os - -breakdown = False - - -def bar_plot_groups(labels, - y1, - y2, - y1_label, - y2_label, - ylabel, - title, - save_fig=''): - """ - A simple wrapper for matplotlib's barplot. - - :param labels: - :param y1: - :param y2: - :param ylabel: - :param xlabel: - :param save_fig: - :return: - """ - x = np.arange(len(labels)) # the label locations - width = 0.35 # the width of the bars - - plt.rc('text', usetex=True) - plt.rc('font', family='serif') - plt.rc('font', size=12) - - fig, ax = plt.subplots() - ax.bar(x[1:len(y1)], y1[1:], width, label=y1_label, color='#c2a5cf') - ax.bar(x, y2, width, label=y2_label, color='#7b3294') - - # Add some text for labels, title and custom x-axis tick labels, etc. - ax.set_ylabel(ylabel) - ax.set_title(title) - ax.set_xticks(x) - ax.set_xticklabels(labels) - - plt.xticks(rotation=45) - ax.yaxis.grid() # horizontal lines - - fig.tight_layout() - - if len(save_fig): - plt.savefig(save_fig) - else: - plt.show() - - -def bar_plot_pairs(labels, - y1, - y2, - y1_label, - y2_label, - ylabel, - title, - save_fig=''): - """ - A simple wrapper for matplotlib's barplot. - - :param labels: - :param y1: - :param y2: - :param ylabel: - :param xlabel: - :param save_fig: - :return: - """ - x = np.arange(len(labels)) # the label locations - width = 0.35 # the width of the bars - - plt.rc('text', usetex=True) - plt.rc('font', family='serif') - plt.rc('font', size=12) - - fig, ax = plt.subplots() - - if breakdown: - ax.bar(x[0:4] - width / 2, y1[0:4], width, label=y1_label+"w/o Overload", color='#fdb863') - ax.bar(x[0:4] + width / 2, y2[0:4], width, label=y2_label+"w/o Overload", color='#e66101') - ax.bar(x[4:] - width / 2, y1[4:], width, label=y1_label+"w/ Overload", color='#c2a4cf') - ax.bar(x[4:] + width / 2, y2[4:], width, label=y2_label+"w/ Overload", color='#7b3294') - - # Add some text for labels, title and custom x-axis tick labels, etc. - if ylabel == 'Speedup': - y_axis_length = [0.9, 2.4] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0]+0.1, y_axis_length[1]+0.1, 0.2)) - elif ylabel == 'Size reduction (\%)': - y_axis_length = [2, 13] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0], y_axis_length[1], 2)) - else: - ax.bar(x - width / 2, y1, width, label=y1_label, color='#c2a4cf') - ax.bar(x + width / 2, y2, width, label=y2_label, color='#7b3294') - - # Add some text for labels, title and custom x-axis tick labels, etc. - if ylabel == 'Speedup': - y_axis_length = [1, 2.1] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0], y_axis_length[1], 0.2)) - elif ylabel == 'Size reduction (\%)': - y_axis_length = [-5, 36] - ax.set_ylim(y_axis_length) - ax.set_yticks(np.arange(y_axis_length[0], y_axis_length[1], 5)) - - ax.set_ylabel(ylabel) - ax.set_title(title) - ax.set_xticks(x) - ax.set_xticklabels(labels) - ax.legend(prop={'size': 9}) - fig.set_figheight(3) - - plt.xticks(rotation=45) - ax.yaxis.grid() # horizontal lines - - fig.tight_layout() - - if len(save_fig) > 0: - plt.savefig(save_fig) - - -if __name__ == '__main__': - if breakdown: - df = pd.read_excel('breakdown_performance.xlsx', - sheet_name='speedup', - nrows=5, - usecols='C:F', - engine='openpyxl') - df.rename(columns={ - "type compression": "Type\nCompression", - "branch elimination": "Condition\nSimplification", - "constant substitution": "Constant\nSubstitution", - "overall": "Overall" - }, - inplace=True) - print(df) - - labels = list(df.columns.values) - labels = labels + labels - - y1 = list(df.iloc[0]) + list(df.iloc[1]) - y2 = list(df.iloc[2]) + list(df.iloc[3]) - - bar_plot_pairs(labels, y1, y2, 'arm', 'x86', 'Speedup', - 'Speedup breakdown - fp_add [0,200]', 'breakdown_speedup.png') - - df = pd.read_excel('breakdown_performance.xlsx', - sheet_name='size', - nrows=5, - usecols='C:F', - engine='openpyxl') - df.rename(columns={ - "type compression": "Type\nCompression", - "branch elimination": "Condition\nSimplification", - "constant substitution": "Constant\nSubstitution", - "overall": "Overall" - }, - inplace=True) - print(df) - - labels = list(df.columns.values) - labels = labels + labels - - y1 = list(map(lambda x: 100*x, list(df.iloc[0]) + list(df.iloc[1]))) - y2 = list(map(lambda x: 100*x, list(df.iloc[2]) + list(df.iloc[3]))) - - bar_plot_pairs(labels, y1, y2, 'arm', 'x86', 'Size reduction (\%)', - 'Size reduction breakdown - fp_add [0,200]', 'breakdown_size.png') - - # bar_plot_groups(labels, y1, y2, 'w/o Function Overload', - # 'w/ Function Overload', 'Speedup', - # 'Performance breakdown of CoSense optimizations', - # 'speedup_breakdown.png') + +# Function to build a map of (benchmark, params_raw) -> precision_bits +# by parsing the primary data lines of the quant_file content. +def build_precision_map_from_quant_file(file_content): + precision_map = {} + current_benchmark_key = None + lines = file_content.strip().split('\n') + + header_skipped = False + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + header_skipped = True + + # parts[0]=test_case, parts[1]=param, parts[2]=precision_bits + precision_bits_col_index = 2 + # Minimum parts needed: test_case, param, precision_bits ... + min_parts_for_precision_line = precision_bits_col_index + 1 + + for line in lines: + parts = line.split('\t') + if not parts: continue + + test_case = parts[0].strip() + + # Only process primary data lines, not "speed up..." lines + if test_case.startswith("perf_j0"): + current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): + current_benchmark_key = "y0" + else: # Skip "speed up..." lines or other unrecognized lines + continue + + if current_benchmark_key and len(parts) > min_parts_for_precision_line: + params_str = parts[1].strip() + # Validate params_str before splitting + if len(params_str.split()) >= 2: + try: + precision_val = parts[precision_bits_col_index].strip() + # Ensure precision_val is not empty and is a valid integer + if precision_val: + int(precision_val) # Test conversion + map_key = (current_benchmark_key, params_str) + if map_key not in precision_map: # Store first encountered for a given param + precision_map[map_key] = precision_val + # else: + # print(f"Debug: Duplicate param '{params_str}' for benchmark '{current_benchmark_key}' in precision map building. Using first value.") + except (IndexError, ValueError) as e: + print(f"Warning: Could not parse precision_bits from line: '{line[:80]}...' Error: {e}") + # else: + # print(f"Debug: Invalid params_str '{params_str}' for precision map building from line: '{line[:80]}...'") + + if not precision_map: + print("Warning: Precision map is empty. Check 'perf_quant.log' format and 'precision_bits_col_index'.") + # else: + # print(f"Debug: Precision map built with {len(precision_map)} entries.") + # for k, v in list(precision_map.items())[:5]: print(f" Map sample: {k} -> {v}") + + return precision_map + +# Helper function to parse the "speed up after optimization" lines +def parse_summary_perf_data(file_content, quantization_type): + data = [] + current_benchmark_key = None + lines = file_content.strip().split('\n') + + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + + for line in lines: + parts = line.split('\t') + if not parts: continue + + test_case = parts[0].strip() + + if test_case.startswith("perf_j0"): + current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): + current_benchmark_key = "y0" + elif test_case == "speed up after optimization": + if current_benchmark_key is None: + print(f"Skipping 'speed up' line (no benchmark context): '{line[:60]}...'") + continue + + # Standard "speed up after optimization" line structure: + # test case(0)|param(1)|instr%(2)|time%(3)|ir%(4)|lib_size%(5)|compile_time%(6) + if len(parts) < 7: + print(f"Skipping 'speed up' line (insufficient columns): '{line[:60]}...'") + continue + + try: + params_str = parts[1].strip() + param_values = params_str.split() + if len(param_values) < 2: + print(f"Skipping 'speed up' line (invalid params format): '{line[:60]}...'") + continue + formatted_params = f"[{float(param_values[0]):.1f}, {float(param_values[1]):.1f}]" + + time_speedup_pct = float(parts[3].replace('%', '')) + library_size_reduction_pct = float(parts[5].replace('%', '')) + speedup_factor = 1 + (time_speedup_pct / 100.0) + + entry = { + "benchmark": current_benchmark_key, + "params_raw": params_str, + "params_formatted": formatted_params, + "quant_type": quantization_type, + "speedup_factor": speedup_factor, + "library_size_reduction_pct": library_size_reduction_pct, + "precision_bits": None # Will be filled in later using the map + } + data.append(entry) + except (IndexError, ValueError) as e: + print(f"Error parsing 'speed up' data line: '{line[:60]}...' due to: {e}") + continue + return data + +# --- Main script execution --- +file_path_wo_quant = "perf_woquant.log" +file_path_w_quant = "perf_quant.log" + +# 1. Build precision_bits map from perf_quant.log +quant_precision_map = {} +try: + with open(file_path_w_quant, 'r', encoding='utf-8') as f: + perf_quant_content_for_map = f.read() + quant_precision_map = build_precision_map_from_quant_file(perf_quant_content_for_map) +except FileNotFoundError: + print(f"Error: File not found '{file_path_w_quant}'. Precision bits cannot be mapped.") + # Continue without it, precision_bits will remain None for all entries +except Exception as e: + print(f"Error building precision map from '{file_path_w_quant}': {e}") + + +# 2. Parse summary performance data for both files +all_data_collected = [] +try: + with open(file_path_wo_quant, 'r', encoding='utf-8') as f: + content = f.read() + parsed_data = parse_summary_perf_data(content, "w/o Auto Quantization") + all_data_collected.extend(parsed_data) +except FileNotFoundError: + print(f"Warning: File not found '{file_path_wo_quant}'.") +except Exception as e: + print(f"Error reading or parsing '{file_path_wo_quant}': {e}") + +try: + with open(file_path_w_quant, 'r', encoding='utf-8') as f: + content = f.read() + # Note: perf_quant.log's "speed up..." lines are parsed like wo_quant's here + parsed_data = parse_summary_perf_data(content, "with Auto Quantization") + all_data_collected.extend(parsed_data) +except FileNotFoundError: + print(f"Warning: File not found '{file_path_w_quant}'.") +except Exception as e: + print(f"Error reading or parsing '{file_path_w_quant}': {e}") + + +if not all_data_collected: + print("No summary data was successfully parsed from any file. Exiting.") + exit() + +# 3. Add precision_bits to all entries using the map +for item in all_data_collected: + key = (item['benchmark'], item['params_raw']) + item['precision_bits'] = quant_precision_map.get(key) + +df_performance = pd.DataFrame(all_data_collected) + +if df_performance.empty: + print("DataFrame is empty after parsing and mapping. No plots will be generated.") + exit() + +# 4. Create the 'range_over_precision_bits' column for X-axis display +def create_range_precision_label(row): + if pd.notna(row['precision_bits']) and str(row['precision_bits']).strip(): + return f"{row['params_formatted']}/{row['precision_bits']}" + return row['params_formatted'] # Fallback if no precision_bits + +df_performance['range_over_precision_bits'] = df_performance.apply(create_range_precision_label, axis=1) + +# Function to create and save grouped bar charts (largely same as before) +def create_and_save_plot(df_benchmark_data, benchmark_id, metric_column, y_axis_label, plot_title_suffix, output_filename): + if df_benchmark_data.empty: + print(f"No data available for {benchmark_id} {plot_title_suffix.lower()}, skipping plot.") + return + + if 'range_over_precision_bits' not in df_benchmark_data.columns: + print(f"Critical error: 'range_over_precision_bits' column missing in data for {benchmark_id}. Skipping plot.") + return + + try: + pivot_table_df = df_benchmark_data.pivot_table(index="range_over_precision_bits", + columns="quant_type", + values=metric_column) + except Exception as e: + print(f"Could not create pivot table for {benchmark_id} {plot_title_suffix.lower()}: {e}") + return + + ordered_quant_types = [qt for qt in ["w/o Auto Quantization", "with Auto Quantization"] if qt in pivot_table_df.columns] + + if not ordered_quant_types: + print(f"Pivot table for {benchmark_id} {plot_title_suffix.lower()} has no quantization type columns (e.g. all NaNs for this metric). Skipping.") + # print(pivot_table_df.head()) # For debugging + return + pivot_table_df = pivot_table_df[ordered_quant_types] + + temp_sort_df = df_benchmark_data[['params_raw', 'range_over_precision_bits']].drop_duplicates().copy() + temp_sort_df['sort_key_params_numeric'] = temp_sort_df['params_raw'].apply(lambda x: tuple(map(float, x.split()))) + temp_sort_df = temp_sort_df.sort_values(by='sort_key_params_numeric') + final_ordered_x_labels = temp_sort_df['range_over_precision_bits'].unique().tolist() + + pivot_table_df = pivot_table_df.reindex(final_ordered_x_labels).dropna(axis=0, how='all') + + if pivot_table_df.empty: + print(f"Pivot table became empty after reindexing for {benchmark_id} {plot_title_suffix.lower()}. Skipping.") + return + + num_param_groups = len(pivot_table_df.index) + bar_item_width = 0.35 + x_positions = np.arange(num_param_groups) + + fig_width = max(12, num_param_groups * 0.8) + fig, ax = plt.subplots(figsize=(fig_width, 7)) + + num_quant_types = len(ordered_quant_types) + for i, quant_type in enumerate(ordered_quant_types): + offset = (i - (num_quant_types - 1) / 2) * bar_item_width + # Ensure data for the bar is numeric, default to 0 if NaN + bar_data = pd.to_numeric(pivot_table_df[quant_type], errors='coerce').fillna(0) + ax.bar(x_positions + offset, bar_data, bar_item_width, + label=quant_type, color=['#C3B1E1', '#6A0DAD'][i % 2]) + + ax.set_xlabel("Range/PrecisionBits") + ax.set_ylabel(y_axis_label) + ax.set_title(f"{benchmark_id.upper()} {plot_title_suffix}") + ax.set_xticks(x_positions) + ax.set_xticklabels(pivot_table_df.index, rotation=45, ha="right") + ax.legend(title="Quantization Type") + ax.grid(True, linestyle='--', alpha=0.7, axis='y') + + # Dynamically set Y limits based on actual plotted data + all_bar_values = [] + for qt in ordered_quant_types: + all_bar_values.extend(pd.to_numeric(pivot_table_df[qt], errors='coerce').fillna(0).tolist()) + + if not all_bar_values: # Should not happen if pivot_table_df is not empty + min_data_val, max_data_val = (0,1) if "Speedup" in y_axis_label else (0,100) else: - # X86 - df = pd.read_excel('overall_speedup_microbenchmarks.xlsx', - sheet_name='x86', - nrows=10, - header=None, - names=['benchmark', 'speedup', 'size'], - usecols='A,B,D', - engine='openpyxl') - df['benchmark'] = df['benchmark'].apply( - lambda x: x.replace(' with time speed up', '')) - - labels = list(df['benchmark'].values) - x86_speedup = list(df['speedup'].values) - x86_size = list(df['size'].values) - x86_speedup = list(map(lambda x: float(x.strip('%'))/100 + 1.0, x86_speedup)) - x86_size = list(map(lambda x: float(x.strip('%')), x86_size)) - - # ARM - df = pd.read_excel('overall_speedup_microbenchmarks.xlsx', - sheet_name='arm64', - nrows=10, - header=None, - names=['benchmark', 'speedup', 'size'], - usecols='A,B,D', - engine='openpyxl') - - arm_speedup = list(df['speedup'].values) - arm_size = list(df['size'].values) - arm_speedup = list(map(lambda x: float(x.strip('%'))/100 + 1.0, arm_speedup)) - arm_size = list(map(lambda x: float(x.strip('%')), arm_size)) - - bar_plot_pairs(labels, arm_speedup, x86_speedup, 'arm', 'x86', 'Speedup', - 'Speedup in microbenchmarks', 'speedup_microbenchmarks.png') - - bar_plot_pairs(labels, arm_size, x86_size, 'arm', 'x86', - 'Size reduction (\%)', 'Library size reduction in microbenchmarks', - 'size_reduction_microbenchmarks.png') - - os.system("mv *.png fig/.") \ No newline at end of file + min_data_val = min(all_bar_values) + max_data_val = max(all_bar_values) + + if "Speedup Factor" in y_axis_label: + ax.axhline(1.0, color='grey', linestyle='-.', linewidth=0.8) + y_bottom = min(0.9, min_data_val - 0.05) if min_data_val < 5 else 0.9 # Ensure 0.9 is a potential floor + y_top = max_data_val + 0.05 + if y_bottom >= y_top : y_bottom = y_top - 0.1 # Ensure valid range + ax.set_ylim(bottom=y_bottom, top=y_top) + + elif "Reduction (%)" in y_axis_label: + y_bottom = min(0, min_data_val - 5) if min_data_val < 100 else 0 + y_top = max_data_val + 5 + if y_bottom >= y_top : y_bottom = y_top - 10 + ax.set_ylim(bottom=y_bottom, top=y_top) + + + plt.tight_layout() + plt.savefig(output_filename) + print(f"Plot saved: {output_filename}") + plt.close(fig) + +# --- Generate and Save Plots --- +df_j0_data = df_performance[df_performance["benchmark"] == "j0"].copy() +if not df_j0_data.empty: + create_and_save_plot(df_j0_data, "e_j0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_j0_speedup_comparison.png") + create_and_save_plot(df_j0_data, "e_j0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_j0_size_reduction_comparison.png") +else: + print("No data processed for e_j0 benchmark.") + +df_y0_data = df_performance[df_performance["benchmark"] == "y0"].copy() +if not df_y0_data.empty: + create_and_save_plot(df_y0_data, "e_y0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_y0_speedup_comparison.png") + create_and_save_plot(df_y0_data, "e_y0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_y0_size_reduction_comparison.png") +else: + print("No data processed for e_y0 benchmark.") + +print("--- Plot generation complete ---") \ No newline at end of file diff --git a/applications/newton/llvm-ir/performance_test/main.c b/applications/newton/llvm-ir/performance_test/main.c index fc89e1b67..718f12d31 100644 --- a/applications/newton/llvm-ir/performance_test/main.c +++ b/applications/newton/llvm-ir/performance_test/main.c @@ -320,7 +320,7 @@ main(int argc, char** argv) } printf("results: %f\t%f\t%f\t%f\t%f\n", result[0], result[1], result[2], result[3], result[4]); - printf("int results: %d\t%d\t%d\t%d\t%d\n", intResult[0], intResult[1], intResult[2], intResult[3], intResult[4]); + //printf("int results: %d\t%d\t%d\t%d\t%d\n", intResult[0], intResult[1], intResult[2], intResult[3], intResult[4]); return 0; } diff --git a/applications/newton/llvm-ir/performance_test/quant_bar.py b/applications/newton/llvm-ir/performance_test/quant_bar.py new file mode 100644 index 000000000..3a1ae32dc --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/quant_bar.py @@ -0,0 +1,209 @@ +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd + +# Helper function to parse the text data from file content +def parse_perf_data(file_content, quantization_type): + data = [] + current_benchmark_key = None + lines = file_content.strip().split('\n') + + # Skip header line if present + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + + for line in lines: + parts = line.split('\t') + if not parts or len(parts) < 2: # Basic check for enough parts + if line.strip(): + print(f"Skipping line due to insufficient parts: '{line[:60]}...'") + continue + + test_case = parts[0].strip() + + if test_case.startswith("perf_j0"): + current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): + current_benchmark_key = "y0" + elif test_case == "speed up after optimization": + if current_benchmark_key is None: + print(f"Skipping 'speed up' line due to missing benchmark context: '{line[:60]}...'") + continue + + try: + # Ensure enough parts for "speed up after optimization" line + if len(parts) < 6: + print(f"Skipping 'speed up' line due to insufficient data columns: '{line[:60]}...'") + continue + + params_str = parts[1].strip() + param_values = params_str.split() + if len(param_values) < 2: + print(f"Skipping 'speed up' line due to invalid params format: '{line[:60]}...'") + continue + # Format params for display: "[val1, val2]" + formatted_params = f"[{float(param_values[0]):.1f}, {float(param_values[1]):.1f}]" + + # Time consumption speedup is at index 3 (4th item based on 0-indexing) + time_speedup_pct = float(parts[3].replace('%', '')) + # Library size reduction is at index 5 (6th item based on 0-indexing) + library_size_reduction_pct = float(parts[5].replace('%', '')) + + # Speedup factor: 1 + (percentage / 100) + # e.g., 4% speedup -> 1.04x; 0% speedup -> 1.00x + speedup_factor = 1 + (time_speedup_pct / 100.0) + + data.append({ + "benchmark": current_benchmark_key, + "params_raw": params_str, # For sorting + "params_formatted": formatted_params, # For display + "quant_type": quantization_type, + "speedup_factor": speedup_factor, + "library_size_reduction_pct": library_size_reduction_pct + }) + except (IndexError, ValueError) as e: + print(f"Error parsing 'speed up' data line: '{line[:60]}...' due to: {e}") + continue + return data + +# --- Main script execution --- +file_path_wo_quant = "perf_woquant.log" +file_path_w_quant = "perf_quant.log" +all_data_collected = [] + +# Process data without quantization +try: + with open(file_path_wo_quant, 'r', encoding='utf-8') as f: + perf_woquant_content_from_file = f.read() + data_wo_quant_parsed = parse_perf_data(perf_woquant_content_from_file, "w/o Auto Quantization") + all_data_collected.extend(data_wo_quant_parsed) +except FileNotFoundError: + print(f"Error: File not found '{file_path_wo_quant}'. Please ensure it's in the correct location.") + exit() +except Exception as e: + print(f"Error reading or parsing '{file_path_wo_quant}': {e}") + exit() + +# Process data with quantization +try: + with open(file_path_w_quant, 'r', encoding='utf-8') as f: + perf_quant_content_from_file = f.read() + data_w_quant_parsed = parse_perf_data(perf_quant_content_from_file, "with Auto Quantization") + all_data_collected.extend(data_w_quant_parsed) +except FileNotFoundError: + print(f"Error: File not found '{file_path_w_quant}'. Please ensure it's in the correct location.") + exit() +except Exception as e: + print(f"Error reading or parsing '{file_path_w_quant}': {e}") + exit() + +if not all_data_collected: + print("No data was successfully parsed from the files. Exiting.") + exit() + +df_performance = pd.DataFrame(all_data_collected) + +if df_performance.empty: + print("DataFrame is empty after parsing. No plots will be generated. Check parsing logs if any.") + exit() + +# Function to create and save grouped bar charts +def create_and_save_plot(df_benchmark_data, benchmark_id, metric_column, y_axis_label, plot_title_suffix, output_filename): + if df_benchmark_data.empty: + print(f"No data available for {benchmark_id} {plot_title_suffix.lower()}, skipping plot.") + return + + try: + pivot_table_df = df_benchmark_data.pivot_table(index="params_formatted", + columns="quant_type", + values=metric_column) + except Exception as e: + print(f"Could not create pivot table for {benchmark_id} {plot_title_suffix.lower()}: {e}") + return + + # Define the desired order of columns for plotting + ordered_quant_types = [qt for qt in ["w/o Auto Quantization", "with Auto Quantization"] if qt in pivot_table_df.columns] + + if not ordered_quant_types: + print(f"Pivot table for {benchmark_id} {plot_title_suffix.lower()} has no quantization type columns. Skipping.") + return + pivot_table_df = pivot_table_df[ordered_quant_types] + + # Sort index (params_formatted) based on the numerical values of params_raw for consistent plotting order + unique_params_temp_df = df_benchmark_data[['params_raw', 'params_formatted']].drop_duplicates().copy() + unique_params_temp_df['sort_key_internal'] = unique_params_temp_df['params_raw'].apply(lambda x: tuple(map(float, x.split()))) + unique_params_temp_df = unique_params_temp_df.sort_values('sort_key_internal') + final_ordered_params = unique_params_temp_df['params_formatted'].tolist() + + pivot_table_df = pivot_table_df.reindex(final_ordered_params).dropna(axis=0, how='all') + + if pivot_table_df.empty: + print(f"Pivot table became empty after reindexing for {benchmark_id} {plot_title_suffix.lower()}. Skipping.") + return + + num_param_groups = len(pivot_table_df.index) + bar_item_width = 0.35 # Width of individual bars + x_positions = np.arange(num_param_groups) # Base positions for groups + + # Determine overall figure width dynamically + fig_width = max(12, num_param_groups * 0.7) + fig, ax = plt.subplots(figsize=(fig_width, 7)) + + # Plotting bars for each quantization type + num_quant_types = len(ordered_quant_types) + for i, quant_type in enumerate(ordered_quant_types): + # Calculate offset for each bar within a group to achieve grouped bar chart + offset = (i - (num_quant_types - 1) / 2) * bar_item_width + ax.bar(x_positions + offset, pivot_table_df[quant_type].fillna(0), bar_item_width, + label=quant_type, color=['#C3B1E1', '#6A0DAD'][i % 2]) # Light purple, Dark purple + + ax.set_xlabel("Parameters (Range)") + ax.set_ylabel(y_axis_label) + ax.set_title(f"{benchmark_id.upper()} {plot_title_suffix}") + ax.set_xticks(x_positions) + ax.set_xticklabels(pivot_table_df.index, rotation=45, ha="right") + ax.legend(title="Quantization Type") + ax.grid(True, linestyle='--', alpha=0.7, axis='y') # Horizontal grid lines only + + # Adjust y-axis limits for better visualization + if "Speedup Factor" in y_axis_label: + ax.axhline(1.0, color='grey', linestyle='-.', linewidth=0.8) # Reference line at 1.0x speedup + min_data_val = pivot_table_df[ordered_quant_types].min().min() + max_data_val = pivot_table_df[ordered_quant_types].max().max() + if pd.notna(min_data_val) and pd.notna(max_data_val): + ax.set_ylim(bottom=min(0.9, min_data_val - 0.05), top=max_data_val + 0.05) + elif "Reduction (%)" in y_axis_label: + min_data_val = pivot_table_df[ordered_quant_types].min().min() + max_data_val = pivot_table_df[ordered_quant_types].max().max() + if pd.notna(min_data_val) and pd.notna(max_data_val): + ax.set_ylim(bottom=min(0, min_data_val - 5), top=max_data_val + 5) # Give some padding + + plt.tight_layout() # Adjust layout to prevent labels from overlapping + plt.savefig(output_filename) + print(f"Plot saved: {output_filename}") + plt.close(fig) # Close the figure to free memory + + +# --- Generate and Save Plots --- + +# For e_j0 benchmark +df_j0_data = df_performance[df_performance["benchmark"] == "j0"].copy() +if not df_j0_data.empty: + create_and_save_plot(df_j0_data, "e_j0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_j0_speedup_comparison.png") + create_and_save_plot(df_j0_data, "e_j0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_j0_size_reduction_comparison.png") +else: + print("No data processed for e_j0 benchmark.") + +# For e_y0 benchmark +df_y0_data = df_performance[df_performance["benchmark"] == "y0"].copy() +if not df_y0_data.empty: + create_and_save_plot(df_y0_data, "e_y0", "speedup_factor", + "Speedup Factor (x)", "Speedup Comparison", "e_y0_speedup_comparison.png") + create_and_save_plot(df_y0_data, "e_y0", "library_size_reduction_pct", + "Library Size Reduction (%)", "Library Size Reduction Comparison", "e_y0_size_reduction_comparison.png") +else: + print("No data processed for e_y0 benchmark.") + +print("--- Plot generation complete ---") \ No newline at end of file diff --git a/applications/newton/llvm-ir/plot_sqnr.py b/applications/newton/llvm-ir/plot_sqnr.py new file mode 100644 index 000000000..bfc3218b1 --- /dev/null +++ b/applications/newton/llvm-ir/plot_sqnr.py @@ -0,0 +1,50 @@ +import matplotlib.pyplot as plt +import sys +import re +import os + +def parse_sqnr_file(filename): + bits = [] + sqnrs = [] + with open(filename, 'r') as f: + for line in f: + match = re.match(r"\|\s*(\d+)\s*\|\s*([\d.]+)\s*\|", line) + if match: + bits.append(int(match.group(1))) + sqnrs.append(float(match.group(2))) + return bits, sqnrs + +def extract_filter_name(filename): + base = os.path.basename(filename) + if "madgwickahrs" in base: + return "MadgwickAHRS" + elif "mahonyahrs" in base: + return "MahonyAHRS" + elif "sensfusion6" in base: + return "Sensfusion6" + else: + return "Unknown" + +if __name__ == "__main__": + if len(sys.argv) != 2: + print("Usage: python3 plot_sqnr.py ") + sys.exit(1) + + filename = sys.argv[1] + bits, sqnrs = parse_sqnr_file(filename) + filter_name = extract_filter_name(filename) + + plt.figure() + plt.plot(bits, sqnrs, marker='o') + + plt.xticks(bits) + + plt.title(f'SQNR vs. MAX_PRECISION_BITS for {filter_name} Filter') + plt.xlabel('MAX_PRECISION_BITS (FRAC_Q)') + plt.ylabel('SQNR (dB)') + plt.grid(True) + plt.tight_layout() + + output_img = filename.replace(".txt", ".png") + plt.savefig(output_img) + print(f"Plot saved to {output_img}") diff --git a/applications/newton/llvm-ir/replace.py b/applications/newton/llvm-ir/replace.py new file mode 100644 index 000000000..89ff3ff79 --- /dev/null +++ b/applications/newton/llvm-ir/replace.py @@ -0,0 +1,34 @@ +import re +import os + +def replace_text_in_file(filepath, patterns_replacements): + # 检查文件是否存在 + if not os.path.isfile(filepath): + print(f"Error: File '{filepath}' does not exist.") + return + + # 读取文件内容 + with open(filepath, 'r', encoding='utf-8') as file: + content = file.read() + + # 对所有模式和替换进行操作 + for pattern, replacement in patterns_replacements: + updated_content = re.sub(pattern, replacement, content) + + # 将更新后的内容写回文件 + with open(filepath, 'w', encoding='utf-8') as file: + file.write(updated_content) + +# 定义文件路径和替换规则 +filepath = 'MadgwickAHRS_opt.ll' +patterns_replacements = [ + (r'declare dso_local i32 @printf\(i8\*\)', 'declare dso_local i32 @printf(i8*, i32)'), + (r'float\*\*', 'i32**'), + (r'float\*', 'i32*'), + (r'call i32 @invSqrt', 'call i32 @fixrsqrt') +] + +# 执行替换 +replace_text_in_file(filepath, patterns_replacements) + +print("Replacement complete.") \ No newline at end of file diff --git a/applications/newton/llvm-ir/replace.sh b/applications/newton/llvm-ir/replace.sh new file mode 100755 index 000000000..d036f676a --- /dev/null +++ b/applications/newton/llvm-ir/replace.sh @@ -0,0 +1,32 @@ +#!/bin/bash + +# Define the file path +file="MadgwickAHRS_opt.ll" + +# Check if the file exists +if [ ! -f "$file" ]; then + echo "Error: File does not exist." + exit 1 +fi + +# Use sed to replace the text +#sed -i 's/declare dso_local i32 @printf(i8\*)/declare dso_local i32 @printf(i8\*, i32)/g' "$file" + +#Replace float** with i32** first to avoid conflicting replacements +sed -i 's/float\*\*/i32**/g' "$file" +# +##Replace float* with i32* +sed -i 's/float\*/i32*/g' "$file" + +# Now replace the call to invSqrt with the call to fixrsqrt +#sed -i 's/call i32 @invSqrt/call i32 @fixrsqrt/g' "$file" + +# Replace the specific call to MadgwickAHRSupdateIMU +#sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(float %0, float %1, float %2, float %3, float %4, float %5, i32* %119, i32* %120, i32* %121, i32* %122)/g' "$file" +#sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(float %0, float %1, float %2, float %3, float %4, float %5)/g' "$file" +#sed -i 's/call void @MadgwickAHRSupdateIMU(.*)/call void @MadgwickAHRSupdateIMU(i32 %83, i32 %84, i32 %85, i32 %86, i32 %87, i32 %88, i32* %89, i32* %90, i32* %91, i32* %92)/g' "$file" + +# Remove all occurrences of "_quantized" in the file +sed -i 's/_quantized//g' "$file" + +echo "Replacement complete." diff --git a/applications/newton/llvm-ir/rms_error.py b/applications/newton/llvm-ir/rms_error.py new file mode 100644 index 000000000..200ff819f --- /dev/null +++ b/applications/newton/llvm-ir/rms_error.py @@ -0,0 +1,153 @@ +import math +import argparse + +def parse_data(file_path, prefix_to_remove): + """ + Reads a file and extracts q0, q1, q2, q3 values from each line after removing the specified prefix. + Returns a list of lists containing the extracted values. + """ + with open(file_path, 'r') as file: + data = [] + for line in file: + if line.startswith(prefix_to_remove): + # Remove the prefix and strip the line + clean_line = line.replace(prefix_to_remove, "").strip() + values = clean_line.split(", ") + # Extract q0, q1, q2, q3 values and convert them to floats + parsed_values = [float(value.split("=")[1]) for value in values] + data.append(parsed_values) + return data + +def calculate_rms_errors(fp_data, int_data): + """ + Calculate RMS errors for each quaternion component (q0, q1, q2, q3) and overall RMS. + Returns (rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms). + """ + errors_q0 = [] + errors_q1 = [] + errors_q2 = [] + errors_q3 = [] + overall_errors = [] + + for fp, integer in zip(fp_data, int_data): + q0_error = fp[0] - integer[0] + q1_error = fp[1] - integer[1] + q2_error = fp[2] - integer[2] + q3_error = fp[3] - integer[3] + + # Square the errors + errors_q0.append(q0_error ** 2) + errors_q1.append(q1_error ** 2) + errors_q2.append(q2_error ** 2) + errors_q3.append(q3_error ** 2) + + # Accumulate the overall squared error for all components + overall_errors.append(q0_error ** 2 + q1_error ** 2 + q2_error ** 2 + q3_error ** 2) + + # RMS for each component + rms_q0 = math.sqrt(sum(errors_q0) / len(errors_q0)) + rms_q1 = math.sqrt(sum(errors_q1) / len(errors_q1)) + rms_q2 = math.sqrt(sum(errors_q2) / len(errors_q2)) + rms_q3 = math.sqrt(sum(errors_q3) / len(errors_q3)) + + # Overall RMS error (combined four components) + rms_overall = math.sqrt(sum(overall_errors) / len(overall_errors)) + + # Average of RMS values (average of q0, q1, q2, q3 RMS) + avg_rms = (rms_q0 + rms_q1 + rms_q2 + rms_q3) / 4 + + return rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms + +def compute_signal_power(data): + """ + Calculate signal power, P_signal = mean(q0^2 + q1^2 + q2^2 + q3^2) + """ + total = 0.0 + for quaternion in data: + q0, q1, q2, q3 = quaternion + total += (q0**2 + q1**2 + q2**2 + q3**2) + return total / len(data) + +def compute_sqnr(fp_data, int_data): + """ + Calculate Signal-to-Quantization-Noise Ratio (SQNR). + Uses floating-point data as the reference signal and quantization error as noise. + SQNR = 10 * log10(signal_power / noise_power) + """ + # Calculate noise power (mean squared quantization error) + _, _, _, _, rms_overall, _ = calculate_rms_errors(fp_data, int_data) + noise_power = rms_overall ** 2 + + # Calculate signal power (using floating-point data) + signal_power = compute_signal_power(fp_data) + + # Handle division by zero case + if noise_power == 0: + return float('inf') + + # Calculate SQNR in dB + sqnr_db = 10 * math.log10(signal_power / noise_power) + return sqnr_db + +def main(): + parser = argparse.ArgumentParser(description="Calculate RMS errors and SQNR for quaternion data.") + parser.add_argument("--filter", choices=["MadgwickAHRS", "MahonyAHRS", "sensfusion6"], default="MadgwickAHRS", + help="Select which algorithm's data to process (default: MadgwickAHRS)") + parser.add_argument("--limit", type=int, default=1000, + help="Limit the number of data lines to process (default: 1000)") + args = parser.parse_args() + + if args.filter == "MadgwickAHRS": + fp_data = parse_data('fp_result.txt', 'Original: ') + int_data = parse_data('int_result.txt', 'FIX: ') + elif args.filter == "MahonyAHRS": + fp_data = parse_data('fp_mahony_result.txt', 'Original: ') + int_data = parse_data('int_mahony_result.txt', 'FIX: ') + + elif args.filter == "sensfusion6": + fp_data = parse_data('fp_sensfusion6_result.txt', 'Original: ') + int_data = parse_data('int_sensfusion6_result.txt', 'FIX: ') + + else: + raise ValueError(f"Unsupported filter: {args.filter}") + + if len(fp_data) != len(int_data): + print("Mismatch in the number of quaternions between files!") + return + + # Use specified number of data lines + data_limit = min(args.limit, len(fp_data)) if args.limit > 0 else len(fp_data) + fp_data = fp_data[:data_limit] + int_data = int_data[:data_limit] + + print(f"Processing {len(fp_data)} lines of data...") + + # Calculate RMS errors + rms_q0, rms_q1, rms_q2, rms_q3, rms_overall, avg_rms = calculate_rms_errors(fp_data, int_data) + print(f"RMS Error (q0): {rms_q0:.6f}") + print(f"RMS Error (q1): {rms_q1:.6f}") + print(f"RMS Error (q2): {rms_q2:.6f}") + print(f"RMS Error (q3): {rms_q3:.6f}") + print(f"Overall RMS Error: {rms_overall:.6f}") + print(f"Average of RMS Values: {avg_rms:.6f}") + + # Calculate SQNR + sqnr_db = compute_sqnr(fp_data, int_data) + print(f"SQNR (dB): {sqnr_db:.2f}") + + # Calculate signal power (for debugging) + signal_power = compute_signal_power(fp_data) + print(f"Signal Power: {signal_power:.6f}") + + # Debug info: print first line error values + if len(fp_data) > 0: + first_fp = fp_data[0] + first_int = int_data[0] + first_error = [first_fp[i] - first_int[i] for i in range(4)] + print("\nDebug Information:") + print(f"First line FP: {first_fp}") + print(f"First line INT: {first_int}") + print(f"First line errors (q0, q1, q2, q3): {first_error}") + +if __name__ == "__main__": + main() \ No newline at end of file diff --git a/applications/newton/llvm-ir/run.sh b/applications/newton/llvm-ir/run.sh new file mode 100755 index 000000000..b2b7721ef --- /dev/null +++ b/applications/newton/llvm-ir/run.sh @@ -0,0 +1,31 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +#opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -O3 -Os -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll +#opt $USER_HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS_opt.ll -S -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as $USER_HOME/CoSense/applications/newton/llvm-ir/out.ll -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc $USER_HOME/CoSense/applications/newton/llvm-ir/out.bc -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +echo "Step 7: Compile the assembly file to object file" +clang -c $USER_HOME/CoSense/applications/newton/llvm-ir/out.s -o $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc $USER_HOME/CoSense/applications/newton/llvm-ir/libout.a $USER_HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang $USER_HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L$USER_HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $USER_HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/stm32f303xc.h b/applications/newton/llvm-ir/stm32f303xc.h new file mode 100644 index 000000000..9a49f5793 --- /dev/null +++ b/applications/newton/llvm-ir/stm32f303xc.h @@ -0,0 +1,13485 @@ +/** +****************************************************************************** +* @file stm32f303xc.h +* @author MCD Application Team +* @brief CMSIS STM32F303xC Devices Peripheral Access Layer Header File. +* +* This file contains: +* - Data structures and the address mapping for all peripherals +* - Peripheral's registers declarations and bits definition +* - Macros to access peripheral's registers hardware +* +****************************************************************************** +* @attention +* +* Copyright (c) 2016 STMicroelectronics. +* All rights reserved. +* +* This software is licensed under terms that can be found in the LICENSE file +* in the root directory of this software component. +* If no LICENSE file comes with this software, it is provided AS-IS. +* +****************************************************************************** +*/ + +/** @addtogroup CMSIS_Device +* @{ +*/ + +/** @addtogroup stm32f303xc +* @{ +*/ + +#ifndef __STM32F303xC_H +#define __STM32F303xC_H + +#ifdef __cplusplus +extern "C" { +#endif /* __cplusplus */ + +/** @addtogroup Configuration_section_for_CMSIS +* @{ +*/ + +/** +* @brief Configuration of the Cortex-M4 Processor and Core Peripherals +*/ +#define __CM4_REV 0x0001U /*!< Core revision r0p1 */ +#define __MPU_PRESENT 1U /*!< STM32F303xC devices provide an MPU */ +#define __NVIC_PRIO_BITS 4U /*!< STM32F303xC devices use 4 Bits for the Priority Levels */ +#define __Vendor_SysTickConfig 0U /*!< Set to 1 if different SysTick Config is used */ +#define __FPU_PRESENT 1U /*!< STM32F303xC devices provide an FPU */ + +/** +* @} +*/ + +/** @addtogroup Peripheral_interrupt_number_definition +* @{ +*/ + +/** +* @brief STM32F303xC devices Interrupt Number Definition, according to the selected device +* in @ref Library_configuration_section +*/ +typedef enum +{ + /****** Cortex-M4 Processor Exceptions Numbers ****************************************************************/ + NonMaskableInt_IRQn = -14, /*!< 2 Non Maskable Interrupt */ + HardFault_IRQn = -13, /*!< 3 Cortex-M4 Hard Fault Interrupt */ + MemoryManagement_IRQn = -12, /*!< 4 Cortex-M4 Memory Management Interrupt */ + BusFault_IRQn = -11, /*!< 5 Cortex-M4 Bus Fault Interrupt */ + UsageFault_IRQn = -10, /*!< 6 Cortex-M4 Usage Fault Interrupt */ + SVCall_IRQn = -5, /*!< 11 Cortex-M4 SV Call Interrupt */ + DebugMonitor_IRQn = -4, /*!< 12 Cortex-M4 Debug Monitor Interrupt */ + PendSV_IRQn = -2, /*!< 14 Cortex-M4 Pend SV Interrupt */ + SysTick_IRQn = -1, /*!< 15 Cortex-M4 System Tick Interrupt */ + /****** STM32 specific Interrupt Numbers **********************************************************************/ + WWDG_IRQn = 0, /*!< Window WatchDog Interrupt */ + PVD_IRQn = 1, /*!< PVD through EXTI Line detection Interrupt */ + TAMP_STAMP_IRQn = 2, /*!< Tamper and TimeStamp interrupts through the EXTI line 19 */ + RTC_WKUP_IRQn = 3, /*!< RTC Wakeup interrupt through the EXTI line 20 */ + FLASH_IRQn = 4, /*!< FLASH global Interrupt */ + RCC_IRQn = 5, /*!< RCC global Interrupt */ + EXTI0_IRQn = 6, /*!< EXTI Line0 Interrupt */ + EXTI1_IRQn = 7, /*!< EXTI Line1 Interrupt */ + EXTI2_TSC_IRQn = 8, /*!< EXTI Line2 Interrupt and Touch Sense Controller Interrupt */ + EXTI3_IRQn = 9, /*!< EXTI Line3 Interrupt */ + EXTI4_IRQn = 10, /*!< EXTI Line4 Interrupt */ + DMA1_Channel1_IRQn = 11, /*!< DMA1 Channel 1 Interrupt */ + DMA1_Channel2_IRQn = 12, /*!< DMA1 Channel 2 Interrupt */ + DMA1_Channel3_IRQn = 13, /*!< DMA1 Channel 3 Interrupt */ + DMA1_Channel4_IRQn = 14, /*!< DMA1 Channel 4 Interrupt */ + DMA1_Channel5_IRQn = 15, /*!< DMA1 Channel 5 Interrupt */ + DMA1_Channel6_IRQn = 16, /*!< DMA1 Channel 6 Interrupt */ + DMA1_Channel7_IRQn = 17, /*!< DMA1 Channel 7 Interrupt */ + ADC1_2_IRQn = 18, /*!< ADC1 & ADC2 Interrupts */ + USB_HP_CAN_TX_IRQn = 19, /*!< USB Device High Priority or CAN TX Interrupts */ + USB_LP_CAN_RX0_IRQn = 20, /*!< USB Device Low Priority or CAN RX0 Interrupts */ + CAN_RX1_IRQn = 21, /*!< CAN RX1 Interrupt */ + CAN_SCE_IRQn = 22, /*!< CAN SCE Interrupt */ + EXTI9_5_IRQn = 23, /*!< External Line[9:5] Interrupts */ + TIM1_BRK_TIM15_IRQn = 24, /*!< TIM1 Break and TIM15 Interrupts */ + TIM1_UP_TIM16_IRQn = 25, /*!< TIM1 Update and TIM16 Interrupts */ + TIM1_TRG_COM_TIM17_IRQn = 26, /*!< TIM1 Trigger and Commutation and TIM17 Interrupt */ + TIM1_CC_IRQn = 27, /*!< TIM1 Capture Compare Interrupt */ + TIM2_IRQn = 28, /*!< TIM2 global Interrupt */ + TIM3_IRQn = 29, /*!< TIM3 global Interrupt */ + TIM4_IRQn = 30, /*!< TIM4 global Interrupt */ + I2C1_EV_IRQn = 31, /*!< I2C1 Event Interrupt & EXTI Line23 Interrupt (I2C1 wakeup) */ + I2C1_ER_IRQn = 32, /*!< I2C1 Error Interrupt */ + I2C2_EV_IRQn = 33, /*!< I2C2 Event Interrupt & EXTI Line24 Interrupt (I2C2 wakeup) */ + I2C2_ER_IRQn = 34, /*!< I2C2 Error Interrupt */ + SPI1_IRQn = 35, /*!< SPI1 global Interrupt */ + SPI2_IRQn = 36, /*!< SPI2 global Interrupt */ + USART1_IRQn = 37, /*!< USART1 global Interrupt & EXTI Line25 Interrupt (USART1 wakeup) */ + USART2_IRQn = 38, /*!< USART2 global Interrupt & EXTI Line26 Interrupt (USART2 wakeup) */ + USART3_IRQn = 39, /*!< USART3 global Interrupt & EXTI Line28 Interrupt (USART3 wakeup) */ + EXTI15_10_IRQn = 40, /*!< External Line[15:10] Interrupts */ + RTC_Alarm_IRQn = 41, /*!< RTC Alarm (A and B) through EXTI Line 17 Interrupt */ + USBWakeUp_IRQn = 42, /*!< USB Wakeup Interrupt */ + TIM8_BRK_IRQn = 43, /*!< TIM8 Break Interrupt */ + TIM8_UP_IRQn = 44, /*!< TIM8 Update Interrupt */ + TIM8_TRG_COM_IRQn = 45, /*!< TIM8 Trigger and Commutation Interrupt */ + TIM8_CC_IRQn = 46, /*!< TIM8 Capture Compare Interrupt */ + ADC3_IRQn = 47, /*!< ADC3 global Interrupt */ + SPI3_IRQn = 51, /*!< SPI3 global Interrupt */ + UART4_IRQn = 52, /*!< UART4 global Interrupt & EXTI Line34 Interrupt (UART4 wakeup) */ + UART5_IRQn = 53, /*!< UART5 global Interrupt & EXTI Line35 Interrupt (UART5 wakeup) */ + TIM6_DAC_IRQn = 54, /*!< TIM6 global and DAC underrun error Interrupt */ + TIM7_IRQn = 55, /*!< TIM7 global Interrupt */ + DMA2_Channel1_IRQn = 56, /*!< DMA2 Channel 1 global Interrupt */ + DMA2_Channel2_IRQn = 57, /*!< DMA2 Channel 2 global Interrupt */ + DMA2_Channel3_IRQn = 58, /*!< DMA2 Channel 3 global Interrupt */ + DMA2_Channel4_IRQn = 59, /*!< DMA2 Channel 4 global Interrupt */ + DMA2_Channel5_IRQn = 60, /*!< DMA2 Channel 5 global Interrupt */ + ADC4_IRQn = 61, /*!< ADC4 global Interrupt */ + COMP1_2_3_IRQn = 64, /*!< COMP1, COMP2 and COMP3 global Interrupt via EXTI Line21, 22 and 29*/ + COMP4_5_6_IRQn = 65, /*!< COMP4, COMP5 and COMP6 global Interrupt via EXTI Line30, 31 and 32*/ + COMP7_IRQn = 66, /*!< COMP7 global Interrupt via EXTI Line33 */ + USB_HP_IRQn = 74, /*!< USB High Priority global Interrupt */ + USB_LP_IRQn = 75, /*!< USB Low Priority global Interrupt */ + USBWakeUp_RMP_IRQn = 76, /*!< USB Wakeup Interrupt remap */ + FPU_IRQn = 81, /*!< Floating point Interrupt */ +} IRQn_Type; + +/** +* @} +*/ + +#include "core_cm4.h" /* Cortex-M4 processor and core peripherals */ +#include "system_stm32f3xx.h" /* STM32F3xx System Header */ +#include + +/** @addtogroup Peripheral_registers_structures +* @{ +*/ + +/** +* @brief Analog to Digital Converter +*/ + +typedef struct +{ + __IO uint32_t ISR; /*!< ADC Interrupt and Status Register, Address offset: 0x00 */ + __IO uint32_t IER; /*!< ADC Interrupt Enable Register, Address offset: 0x04 */ + __IO uint32_t CR; /*!< ADC control register, Address offset: 0x08 */ + __IO uint32_t CFGR; /*!< ADC Configuration register, Address offset: 0x0C */ + uint32_t RESERVED0; /*!< Reserved, 0x010 */ + __IO uint32_t SMPR1; /*!< ADC sample time register 1, Address offset: 0x14 */ + __IO uint32_t SMPR2; /*!< ADC sample time register 2, Address offset: 0x18 */ + uint32_t RESERVED1; /*!< Reserved, 0x01C */ + __IO uint32_t TR1; /*!< ADC watchdog threshold register 1, Address offset: 0x20 */ + __IO uint32_t TR2; /*!< ADC watchdog threshold register 2, Address offset: 0x24 */ + __IO uint32_t TR3; /*!< ADC watchdog threshold register 3, Address offset: 0x28 */ + uint32_t RESERVED2; /*!< Reserved, 0x02C */ + __IO uint32_t SQR1; /*!< ADC regular sequence register 1, Address offset: 0x30 */ + __IO uint32_t SQR2; /*!< ADC regular sequence register 2, Address offset: 0x34 */ + __IO uint32_t SQR3; /*!< ADC regular sequence register 3, Address offset: 0x38 */ + __IO uint32_t SQR4; /*!< ADC regular sequence register 4, Address offset: 0x3C */ + __IO uint32_t DR; /*!< ADC regular data register, Address offset: 0x40 */ + uint32_t RESERVED3; /*!< Reserved, 0x044 */ + uint32_t RESERVED4; /*!< Reserved, 0x048 */ + __IO uint32_t JSQR; /*!< ADC injected sequence register, Address offset: 0x4C */ + uint32_t RESERVED5[4]; /*!< Reserved, 0x050 - 0x05C */ + __IO uint32_t OFR1; /*!< ADC offset register 1, Address offset: 0x60 */ + __IO uint32_t OFR2; /*!< ADC offset register 2, Address offset: 0x64 */ + __IO uint32_t OFR3; /*!< ADC offset register 3, Address offset: 0x68 */ + __IO uint32_t OFR4; /*!< ADC offset register 4, Address offset: 0x6C */ + uint32_t RESERVED6[4]; /*!< Reserved, 0x070 - 0x07C */ + __IO uint32_t JDR1; /*!< ADC injected data register 1, Address offset: 0x80 */ + __IO uint32_t JDR2; /*!< ADC injected data register 2, Address offset: 0x84 */ + __IO uint32_t JDR3; /*!< ADC injected data register 3, Address offset: 0x88 */ + __IO uint32_t JDR4; /*!< ADC injected data register 4, Address offset: 0x8C */ + uint32_t RESERVED7[4]; /*!< Reserved, 0x090 - 0x09C */ + __IO uint32_t AWD2CR; /*!< ADC Analog Watchdog 2 Configuration Register, Address offset: 0xA0 */ + __IO uint32_t AWD3CR; /*!< ADC Analog Watchdog 3 Configuration Register, Address offset: 0xA4 */ + uint32_t RESERVED8; /*!< Reserved, 0x0A8 */ + uint32_t RESERVED9; /*!< Reserved, 0x0AC */ + __IO uint32_t DIFSEL; /*!< ADC Differential Mode Selection Register, Address offset: 0xB0 */ + __IO uint32_t CALFACT; /*!< ADC Calibration Factors, Address offset: 0xB4 */ + +} ADC_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< ADC Common status register, Address offset: ADC1/3 base address + 0x300 */ + uint32_t RESERVED; /*!< Reserved, ADC1/3 base address + 0x304 */ + __IO uint32_t CCR; /*!< ADC common control register, Address offset: ADC1/3 base address + 0x308 */ + __IO uint32_t CDR; /*!< ADC common regular data register for dual + AND triple modes, Address offset: ADC1/3 base address + 0x30C */ +} ADC_Common_TypeDef; + +/** +* @brief Controller Area Network TxMailBox +*/ +typedef struct +{ + __IO uint32_t TIR; /*!< CAN TX mailbox identifier register */ + __IO uint32_t TDTR; /*!< CAN mailbox data length control and time stamp register */ + __IO uint32_t TDLR; /*!< CAN mailbox data low register */ + __IO uint32_t TDHR; /*!< CAN mailbox data high register */ +} CAN_TxMailBox_TypeDef; + +/** +* @brief Controller Area Network FIFOMailBox +*/ +typedef struct +{ + __IO uint32_t RIR; /*!< CAN receive FIFO mailbox identifier register */ + __IO uint32_t RDTR; /*!< CAN receive FIFO mailbox data length control and time stamp register */ + __IO uint32_t RDLR; /*!< CAN receive FIFO mailbox data low register */ + __IO uint32_t RDHR; /*!< CAN receive FIFO mailbox data high register */ +} CAN_FIFOMailBox_TypeDef; + +/** +* @brief Controller Area Network FilterRegister +*/ +typedef struct +{ + __IO uint32_t FR1; /*!< CAN Filter bank register 1 */ + __IO uint32_t FR2; /*!< CAN Filter bank register 1 */ +} CAN_FilterRegister_TypeDef; + +/** +* @brief Controller Area Network +*/ +typedef struct +{ + __IO uint32_t MCR; /*!< CAN master control register, Address offset: 0x00 */ + __IO uint32_t MSR; /*!< CAN master status register, Address offset: 0x04 */ + __IO uint32_t TSR; /*!< CAN transmit status register, Address offset: 0x08 */ + __IO uint32_t RF0R; /*!< CAN receive FIFO 0 register, Address offset: 0x0C */ + __IO uint32_t RF1R; /*!< CAN receive FIFO 1 register, Address offset: 0x10 */ + __IO uint32_t IER; /*!< CAN interrupt enable register, Address offset: 0x14 */ + __IO uint32_t ESR; /*!< CAN error status register, Address offset: 0x18 */ + __IO uint32_t BTR; /*!< CAN bit timing register, Address offset: 0x1C */ + uint32_t RESERVED0[88]; /*!< Reserved, 0x020 - 0x17F */ + CAN_TxMailBox_TypeDef sTxMailBox[3]; /*!< CAN Tx MailBox, Address offset: 0x180 - 0x1AC */ + CAN_FIFOMailBox_TypeDef sFIFOMailBox[2]; /*!< CAN FIFO MailBox, Address offset: 0x1B0 - 0x1CC */ + uint32_t RESERVED1[12]; /*!< Reserved, 0x1D0 - 0x1FF */ + __IO uint32_t FMR; /*!< CAN filter master register, Address offset: 0x200 */ + __IO uint32_t FM1R; /*!< CAN filter mode register, Address offset: 0x204 */ + uint32_t RESERVED2; /*!< Reserved, 0x208 */ + __IO uint32_t FS1R; /*!< CAN filter scale register, Address offset: 0x20C */ + uint32_t RESERVED3; /*!< Reserved, 0x210 */ + __IO uint32_t FFA1R; /*!< CAN filter FIFO assignment register, Address offset: 0x214 */ + uint32_t RESERVED4; /*!< Reserved, 0x218 */ + __IO uint32_t FA1R; /*!< CAN filter activation register, Address offset: 0x21C */ + uint32_t RESERVED5[8]; /*!< Reserved, 0x220-0x23F */ + CAN_FilterRegister_TypeDef sFilterRegister[28]; /*!< CAN Filter Register, Address offset: 0x240-0x31C */ +} CAN_TypeDef; + +/** +* @brief Analog Comparators +*/ +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, Address offset: 0x00 */ +} COMP_TypeDef; + +typedef struct +{ + __IO uint32_t CSR; /*!< COMP control and status register, used for bits common to several COMP instances, Address offset: 0x00 */ +} COMP_Common_TypeDef; + +/** +* @brief CRC calculation unit +*/ + +typedef struct +{ + __IO uint32_t DR; /*!< CRC Data register, Address offset: 0x00 */ + __IO uint8_t IDR; /*!< CRC Independent data register, Address offset: 0x04 */ + uint8_t RESERVED0; /*!< Reserved, 0x05 */ + uint16_t RESERVED1; /*!< Reserved, 0x06 */ + __IO uint32_t CR; /*!< CRC Control register, Address offset: 0x08 */ + uint32_t RESERVED2; /*!< Reserved, 0x0C */ + __IO uint32_t INIT; /*!< Initial CRC value register, Address offset: 0x10 */ + __IO uint32_t POL; /*!< CRC polynomial register, Address offset: 0x14 */ +} CRC_TypeDef; + +/** +* @brief Digital to Analog Converter +*/ + +typedef struct +{ + __IO uint32_t CR; /*!< DAC control register, Address offset: 0x00 */ + __IO uint32_t SWTRIGR; /*!< DAC software trigger register, Address offset: 0x04 */ + __IO uint32_t DHR12R1; /*!< DAC channel1 12-bit right-aligned data holding register, Address offset: 0x08 */ + __IO uint32_t DHR12L1; /*!< DAC channel1 12-bit left aligned data holding register, Address offset: 0x0C */ + __IO uint32_t DHR8R1; /*!< DAC channel1 8-bit right aligned data holding register, Address offset: 0x10 */ + __IO uint32_t DHR12R2; /*!< DAC channel2 12-bit right aligned data holding register, Address offset: 0x14 */ + __IO uint32_t DHR12L2; /*!< DAC channel2 12-bit left aligned data holding register, Address offset: 0x18 */ + __IO uint32_t DHR8R2; /*!< DAC channel2 8-bit right-aligned data holding register, Address offset: 0x1C */ + __IO uint32_t DHR12RD; /*!< Dual DAC 12-bit right-aligned data holding register, Address offset: 0x20 */ + __IO uint32_t DHR12LD; /*!< DUAL DAC 12-bit left aligned data holding register, Address offset: 0x24 */ + __IO uint32_t DHR8RD; /*!< DUAL DAC 8-bit right aligned data holding register, Address offset: 0x28 */ + __IO uint32_t DOR1; /*!< DAC channel1 data output register, Address offset: 0x2C */ + __IO uint32_t DOR2; /*!< DAC channel2 data output register, Address offset: 0x30 */ + __IO uint32_t SR; /*!< DAC status register, Address offset: 0x34 */ +} DAC_TypeDef; + +/** +* @brief Debug MCU +*/ + +typedef struct +{ + __IO uint32_t IDCODE; /*!< MCU device ID code, Address offset: 0x00 */ + __IO uint32_t CR; /*!< Debug MCU configuration register, Address offset: 0x04 */ + __IO uint32_t APB1FZ; /*!< Debug MCU APB1 freeze register, Address offset: 0x08 */ + __IO uint32_t APB2FZ; /*!< Debug MCU APB2 freeze register, Address offset: 0x0C */ +}DBGMCU_TypeDef; + +/** +* @brief DMA Controller +*/ + +typedef struct +{ + __IO uint32_t CCR; /*!< DMA channel x configuration register */ + __IO uint32_t CNDTR; /*!< DMA channel x number of data register */ + __IO uint32_t CPAR; /*!< DMA channel x peripheral address register */ + __IO uint32_t CMAR; /*!< DMA channel x memory address register */ +} DMA_Channel_TypeDef; + +typedef struct +{ + __IO uint32_t ISR; /*!< DMA interrupt status register, Address offset: 0x00 */ + __IO uint32_t IFCR; /*!< DMA interrupt flag clear register, Address offset: 0x04 */ +} DMA_TypeDef; + +/** +* @brief External Interrupt/Event Controller +*/ + +typedef struct +{ + __IO uint32_t IMR; /*! +#include +#include + +extern int _end; + +void _exit(int status) { + while (1) {} +} + +caddr_t _sbrk(int incr) { + static unsigned char *heap = NULL; + unsigned char *prev_heap; + + if (heap == NULL) { + heap = (unsigned char *)&_end; + } + prev_heap = heap; + heap += incr; + + return (caddr_t) prev_heap; +} + +int _write(int file, char *ptr, int len) { + return len; +} + +int _read(int file, char *ptr, int len) { + return 0; +} + +int _close(int file) { + return -1; +} + +int _fstat(int file, struct stat *st) { + st->st_mode = S_IFCHR; + return 0; +} + +int _isatty(int file) { + return 1; +} + +int _lseek(int file, int ptr, int dir) { + return 0; +} + +int _open(const char *name, int flags, int mode) { + return -1; +} + +int _kill(int pid, int sig) { + errno = EINVAL; + return -1; +} + +int _getpid(void) { + return 1; +} diff --git a/applications/newton/llvm-ir/system_stm32f3xx.c b/applications/newton/llvm-ir/system_stm32f3xx.c new file mode 100644 index 000000000..f52fba4e6 --- /dev/null +++ b/applications/newton/llvm-ir/system_stm32f3xx.c @@ -0,0 +1,287 @@ +/** + ****************************************************************************** + * @file system_stm32f3xx.c + * @author MCD Application Team + * @brief CMSIS Cortex-M4 Device Peripheral Access Layer System Source File. + * + * 1. This file provides two functions and one global variable to be called from + * user application: + * - SystemInit(): This function is called at startup just after reset and + * before branch to main program. This call is made inside + * the "startup_stm32f3xx.s" file. + * + * - SystemCoreClock variable: Contains the core clock (HCLK), it can be used + * by the user application to setup the SysTick + * timer or configure other parameters. + * + * - SystemCoreClockUpdate(): Updates the variable SystemCoreClock and must + * be called whenever the core clock is changed + * during program execution. + * + * 2. After each device reset the HSI (8 MHz) is used as system clock source. + * Then SystemInit() function is called, in "startup_stm32f3xx.s" file, to + * configure the system clock before to branch to main program. + * + * 3. This file configures the system clock as follows: + *============================================================================= + * Supported STM32F3xx device + *----------------------------------------------------------------------------- + * System Clock source | HSI + *----------------------------------------------------------------------------- + * SYSCLK(Hz) | 8000000 + *----------------------------------------------------------------------------- + * HCLK(Hz) | 8000000 + *----------------------------------------------------------------------------- + * AHB Prescaler | 1 + *----------------------------------------------------------------------------- + * APB2 Prescaler | 1 + *----------------------------------------------------------------------------- + * APB1 Prescaler | 1 + *----------------------------------------------------------------------------- + * USB Clock | DISABLE + *----------------------------------------------------------------------------- + *============================================================================= + ****************************************************************************** + * @attention + * + * Copyright (c) 2016 STMicroelectronics. + * All rights reserved. + * + * This software is licensed under terms that can be found in the LICENSE file + * in the root directory of this software component. + * If no LICENSE file comes with this software, it is provided AS-IS. + * + ****************************************************************************** + */ + +/** @addtogroup CMSIS + * @{ + */ + +/** @addtogroup stm32f3xx_system + * @{ + */ + +/** @addtogroup STM32F3xx_System_Private_Includes + * @{ + */ + +#include "Include/stm32f3xx.h" + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_TypesDefinitions + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Defines + * @{ + */ +#if !defined (HSE_VALUE) + #define HSE_VALUE ((uint32_t)8000000) /*!< Default value of the External oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSE_VALUE */ + +#if !defined (HSI_VALUE) + #define HSI_VALUE ((uint32_t)8000000) /*!< Default value of the Internal oscillator in Hz. + This value can be provided and adapted by the user application. */ +#endif /* HSI_VALUE */ + +/* Note: Following vector table addresses must be defined in line with linker + configuration. */ +/*!< Uncomment the following line if you need to relocate the vector table + anywhere in Flash or Sram, else the vector table is kept at the automatic + remap of boot address selected */ +/* #define USER_VECT_TAB_ADDRESS */ + +#if defined(USER_VECT_TAB_ADDRESS) +/*!< Uncomment the following line if you need to relocate your vector Table + in Sram else user remap will be done in Flash. */ +/* #define VECT_TAB_SRAM */ +#if defined(VECT_TAB_SRAM) +#define VECT_TAB_BASE_ADDRESS SRAM_BASE /*!< Vector Table base address field. + This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +#else +#define VECT_TAB_BASE_ADDRESS FLASH_BASE /*!< Vector Table base address field. + This value must be a multiple of 0x200. */ +#define VECT_TAB_OFFSET 0x00000000U /*!< Vector Table base offset field. + This value must be a multiple of 0x200. */ +#endif /* VECT_TAB_SRAM */ +#endif /* USER_VECT_TAB_ADDRESS */ + +/******************************************************************************/ +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Macros + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Variables + * @{ + */ + /* This variable is updated in three ways: + 1) by calling CMSIS function SystemCoreClockUpdate() + 2) by calling HAL API function HAL_RCC_GetHCLKFreq() + 3) each time HAL_RCC_ClockConfig() is called to configure the system clock frequency + Note: If you use this function to configure the system clock there is no need to + call the 2 first functions listed above, since SystemCoreClock variable is + updated automatically. + */ +uint32_t SystemCoreClock = 8000000; + +const uint8_t AHBPrescTable[16] = {0, 0, 0, 0, 0, 0, 0, 0, 1, 2, 3, 4, 6, 7, 8, 9}; +const uint8_t APBPrescTable[8] = {0, 0, 0, 0, 1, 2, 3, 4}; + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_FunctionPrototypes + * @{ + */ + +/** + * @} + */ + +/** @addtogroup STM32F3xx_System_Private_Functions + * @{ + */ + +/** + * @brief Setup the microcontroller system + * @param None + * @retval None + */ +void SystemInit(void) +{ +/* FPU settings --------------------------------------------------------------*/ +#if (__FPU_PRESENT == 1) && (__FPU_USED == 1) + SCB->CPACR |= ((3UL << 10*2)|(3UL << 11*2)); /* set CP10 and CP11 Full Access */ +#endif + + /* Configure the Vector Table location -------------------------------------*/ +#if defined(USER_VECT_TAB_ADDRESS) + SCB->VTOR = VECT_TAB_BASE_ADDRESS | VECT_TAB_OFFSET; /* Vector Table Relocation in Internal SRAM */ +#endif /* USER_VECT_TAB_ADDRESS */ +} + +/** + * @brief Update SystemCoreClock variable according to Clock Register Values. + * The SystemCoreClock variable contains the core clock (HCLK), it can + * be used by the user application to setup the SysTick timer or configure + * other parameters. + * + * @note Each time the core clock (HCLK) changes, this function must be called + * to update SystemCoreClock variable value. Otherwise, any configuration + * based on this variable will be incorrect. + * + * @note - The system frequency computed by this function is not the real + * frequency in the chip. It is calculated based on the predefined + * constant and the selected clock source: + * + * - If SYSCLK source is HSI, SystemCoreClock will contain the HSI_VALUE(*) + * + * - If SYSCLK source is HSE, SystemCoreClock will contain the HSE_VALUE(**) + * + * - If SYSCLK source is PLL, SystemCoreClock will contain the HSE_VALUE(**) + * or HSI_VALUE(*) multiplied/divided by the PLL factors. + * + * (*) HSI_VALUE is a constant defined in stm32f3xx_hal.h file (default value + * 8 MHz) but the real value may vary depending on the variations + * in voltage and temperature. + * + * (**) HSE_VALUE is a constant defined in stm32f3xx_hal.h file (default value + * 8 MHz), user has to ensure that HSE_VALUE is same as the real + * frequency of the crystal used. Otherwise, this function may + * have wrong result. + * + * - The result of this function could be not correct when using fractional + * value for HSE crystal. + * + * @param None + * @retval None + */ +void SystemCoreClockUpdate (void) +{ + uint32_t tmp = 0, pllmull = 0, pllsource = 0, predivfactor = 0; + + /* Get SYSCLK source -------------------------------------------------------*/ + tmp = RCC->CFGR & RCC_CFGR_SWS; + + switch (tmp) + { + case RCC_CFGR_SWS_HSI: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + case RCC_CFGR_SWS_HSE: /* HSE used as system clock */ + SystemCoreClock = HSE_VALUE; + break; + case RCC_CFGR_SWS_PLL: /* PLL used as system clock */ + /* Get PLL clock source and multiplication factor ----------------------*/ + pllmull = RCC->CFGR & RCC_CFGR_PLLMUL; + pllsource = RCC->CFGR & RCC_CFGR_PLLSRC; + pllmull = ( pllmull >> 18) + 2; + +#if defined (STM32F302xE) || defined (STM32F303xE) || defined (STM32F398xx) + predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1; + if (pllsource == RCC_CFGR_PLLSRC_HSE_PREDIV) + { + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / predivfactor) * pllmull; + } + else + { + /* HSI oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSI_VALUE / predivfactor) * pllmull; + } +#else + if (pllsource == RCC_CFGR_PLLSRC_HSI_DIV2) + { + /* HSI oscillator clock divided by 2 selected as PLL clock entry */ + SystemCoreClock = (HSI_VALUE >> 1) * pllmull; + } + else + { + predivfactor = (RCC->CFGR2 & RCC_CFGR2_PREDIV) + 1; + /* HSE oscillator clock selected as PREDIV1 clock entry */ + SystemCoreClock = (HSE_VALUE / predivfactor) * pllmull; + } +#endif /* STM32F302xE || STM32F303xE || STM32F398xx */ + break; + default: /* HSI used as system clock */ + SystemCoreClock = HSI_VALUE; + break; + } + /* Compute HCLK clock frequency ----------------*/ + /* Get HCLK prescaler */ + tmp = AHBPrescTable[((RCC->CFGR & RCC_CFGR_HPRE) >> 4)]; + /* HCLK clock frequency */ + SystemCoreClock >>= tmp; +} + +/** + * @} + */ + +/** + * @} + */ + +/** + * @} + */ diff --git a/applications/newton/llvm-ir/test.sh b/applications/newton/llvm-ir/test.sh new file mode 100755 index 000000000..379a6d082 --- /dev/null +++ b/applications/newton/llvm-ir/test.sh @@ -0,0 +1,27 @@ +#!/bin/bash + +# Get the current user's home directory +USER_HOME=$HOME + +FILE_PATH="$HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.ll" + + #Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/floating_point_operations.c + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd $HOME/CoSense/src/newton && ./newton-linux-EN --llvm-ir=$HOME/CoSense/applications/newton/llvm-ir/floating_point_operations.ll --llvm-ir-liveness-check --llvm-ir-auto-quantization $HOME/CoSense/applications/newton/sensors/test.nt +# +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +#llvm-dis $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.bc -o $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.ll +#cd $HOME/CoSense/applications/newton/llvm-ir/&& +#./replace.sh $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_opt.ll + +#python3 replace.py + + +# Step 4: Optimize the generated LLVM IR file +echo "Step 4: Optimize the generated LLVM IR file" +#opt $HOME/CoSense/applications/newton/llvm-ir/floating_point_operations_output.ll -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwick.sh b/applications/newton/llvm-ir/testMadgwick.sh new file mode 100755 index 000000000..70e71cdf1 --- /dev/null +++ b/applications/newton/llvm-ir/testMadgwick.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Usage: ./run_algorithm.sh [MadgwickAHRS|MahonyAHRS] +# If no argument is provided, it defaults to MadgwickAHRS + +# Set the algorithm name based on the first command-line argument, default to MadgwickAHRS +ALGO=${1:-MadgwickAHRS} + +# Validate the input (optional) +if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" && "$ALGO" != "sensfusion6" ]]; then + echo "Error: Unknown algorithm '$ALGO'. Please use 'MadgwickAHRS', 'MahonyAHRS', or 'sensfusion6'." + exit 1 +fi + +echo "Selected algorithm: $ALGO" + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + "$HOME/CoSense/applications/newton/llvm-ir/c-files/${ALGO}.c" + +# Optimize the LLVM IR file with mem2reg +opt "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd "$HOME/CoSense/src/newton" && \ + ./newton-linux-EN --llvm-ir="$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + --llvm-ir-liveness-check --llvm-ir-auto-quantization \ + "$HOME/CoSense/applications/newton/sensors/BMX055.nt" + +## Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +llvm-dis "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.bc" \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" +cd "$HOME/CoSense/applications/newton/llvm-ir" && ./replace.sh "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" + +# Step 4: Optimize the generated LLVM IR file further +echo "Step 4: Optimize the generated LLVM IR file" +opt -inline "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/out.ll" + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as "$HOME/CoSense/applications/newton/llvm-ir/out.ll" -o "$HOME/CoSense/applications/newton/llvm-ir/out.bc" + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc "$HOME/CoSense/applications/newton/llvm-ir/out.bc" -o "$HOME/CoSense/applications/newton/llvm-ir/out.s" + +# Step 7: Compile the assembly file to an object file +echo "Step 7: Compile the assembly file to object file" +clang -c "$HOME/CoSense/applications/newton/llvm-ir/out.s" -o "$HOME/CoSense/applications/newton/llvm-ir/out.o" + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc "$HOME/CoSense/applications/newton/llvm-ir/libout.a" "$HOME/CoSense/applications/newton/llvm-ir/out.o" + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang "$HOME/CoSense/applications/newton/llvm-ir/c-files/test_${ALGO}.c" -D INT_DATA_TYPE -no-pie \ + -L"$HOME/CoSense/applications/newton/llvm-ir" -lout -O3 -Os -g \ + -o "$HOME/CoSense/applications/newton/llvm-ir/main_out" -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +"$HOME/CoSense/applications/newton/llvm-ir/main_out" \ No newline at end of file diff --git a/applications/newton/llvm-ir/testMadgwickfix.sh b/applications/newton/llvm-ir/testMadgwickfix.sh new file mode 100755 index 000000000..19d770ced --- /dev/null +++ b/applications/newton/llvm-ir/testMadgwickfix.sh @@ -0,0 +1,38 @@ +USER_HOME=$HOME + +# Step 1: Generate LLVM IR file +#clang -g -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + +#clang -g -O0 -Xclang -disable-O0-optnone -fno-math-errno -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + + +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + + +#clang -O0 -ffast-math -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRSfix.c + + +# Step 4: Optimize the generated LLVM IR file +#opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRSfix.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRSfix.c -no-pie -L $HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + + +#clang $HOME/CoSense/applications/newton/llvm-ir/test_madgwick.c -D INT_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +$HOME/CoSense/applications/newton/llvm-ir/main_out diff --git a/applications/newton/llvm-ir/testOriginal.sh b/applications/newton/llvm-ir/testOriginal.sh new file mode 100755 index 000000000..20de5c460 --- /dev/null +++ b/applications/newton/llvm-ir/testOriginal.sh @@ -0,0 +1,25 @@ +# Step 1: Generate LLVM IR file +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra -o $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll $HOME/CoSense/applications/newton/llvm-ir/c-files/MadgwickAHRS.c + + +# Step 4: Optimize the generated LLVM IR file +#opt -inline $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll --mem2reg -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll +opt $HOME/CoSense/applications/newton/llvm-ir/MadgwickAHRS.ll -O3 -Os -S -o $HOME/CoSense/applications/newton/llvm-ir/out.ll + +# Step 5: Compile the optimized LLVM IR file to bitcode +llvm-as $HOME/CoSense/applications/newton/llvm-ir/out.ll -o $HOME/CoSense/applications/newton/llvm-ir/out.bc + +# Step 6: Compile the bitcode file to assembly +llc $HOME/CoSense/applications/newton/llvm-ir/out.bc -o $HOME/CoSense/applications/newton/llvm-ir/out.s + +# Step 7: Compile the assembly file to object file +clang -c $HOME/CoSense/applications/newton/llvm-ir/out.s -o $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 8: Package the object file into a static library +ar -rc $HOME/CoSense/applications/newton/llvm-ir/libout.a $HOME/CoSense/applications/newton/llvm-ir/out.o + +# Step 9: Compile the test file and link with the static library +clang $HOME/CoSense/applications/newton/llvm-ir/c-files/test_MadgwickAHRS.c -D FP_DATA_TYPE -no-pie -L$HOME/CoSense/applications/newton/llvm-ir -lout -O3 -Os -g -fno-builtin -o $HOME/CoSense/applications/newton/llvm-ir/main_out -lm + +# Step 10: Run the test executable +$HOME/CoSense/applications/newton/llvm-ir/main_out \ No newline at end of file diff --git a/applications/newton/llvm-ir/testSensfusion6.sh b/applications/newton/llvm-ir/testSensfusion6.sh new file mode 100755 index 000000000..3c50c3cae --- /dev/null +++ b/applications/newton/llvm-ir/testSensfusion6.sh @@ -0,0 +1,68 @@ +#!/bin/bash +# Usage: ./run_algorithm.sh [MadgwickAHRS|MahonyAHRS] +# If no argument is provided, it defaults to MadgwickAHRS + +# Set the algorithm name based on the first command-line argument, default to MadgwickAHRS +#ALGO=${1:-MadgwickAHRS} +# +## Validate the input (optional) +#if [[ "$ALGO" != "MadgwickAHRS" && "$ALGO" != "MahonyAHRS" ]]; then +# echo "Error: Unknown algorithm '$ALGO'. Please use either 'MadgwickAHRS' or 'MahonyAHRS'." +# exit 1 +#fi +ALGO="sensfusion6" +echo "Selected algorithm: $ALGO" + +# Step 1: Generate LLVM IR file +echo "Step 1: Generate LLVM IR file" +clang -g0 -O0 -Xclang -disable-O0-optnone -S -emit-llvm -Wall -Wextra \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + "$HOME/CoSense/applications/newton/llvm-ir/c-files/${ALGO}.c" + +# Optimize the LLVM IR file with mem2reg +opt "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" + +# Step 2: Use newton for optimization and quantization +echo "Step 2: Use newton for optimization and quantization" +cd "$HOME/CoSense/src/newton" && \ + ./newton-linux-EN --llvm-ir="$HOME/CoSense/applications/newton/llvm-ir/${ALGO}.ll" \ + --llvm-ir-liveness-check --llvm-ir-auto-quantization \ + "$HOME/CoSense/applications/newton/sensors/BMX055.nt" + +# Step 3: Convert generated bytecode file to LLVM IR file +echo "Step 3: Convert generated bytecode file to LLVM IR file" +llvm-dis "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.bc" \ + -o "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" +cd "$HOME/CoSense/applications/newton/llvm-ir" && ./replace.sh "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_opt.ll" + +# Step 4: Optimize the generated LLVM IR file further +echo "Step 4: Optimize the generated LLVM IR file" +opt -inline "$HOME/CoSense/applications/newton/llvm-ir/${ALGO}_output.ll" --mem2reg -S \ + -o "$HOME/CoSense/applications/newton/llvm-ir/out.ll" + +# Step 5: Compile the optimized LLVM IR file to bitcode +echo "Step 5: Compile the optimized LLVM IR file to bitcode" +llvm-as "$HOME/CoSense/applications/newton/llvm-ir/out.ll" -o "$HOME/CoSense/applications/newton/llvm-ir/out.bc" + +# Step 6: Compile the bitcode file to assembly +echo "Step 6: Compile the bitcode file to assembly" +llc "$HOME/CoSense/applications/newton/llvm-ir/out.bc" -o "$HOME/CoSense/applications/newton/llvm-ir/out.s" + +# Step 7: Compile the assembly file to an object file +echo "Step 7: Compile the assembly file to object file" +clang -c "$HOME/CoSense/applications/newton/llvm-ir/out.s" -o "$HOME/CoSense/applications/newton/llvm-ir/out.o" + +# Step 8: Package the object file into a static library +echo "Step 8: Package the object file into a static library" +ar -rc "$HOME/CoSense/applications/newton/llvm-ir/libout.a" "$HOME/CoSense/applications/newton/llvm-ir/out.o" + +# Step 9: Compile the test file and link with the static library +echo "Step 9: Compile the test file and link with the static library" +clang "$HOME/CoSense/applications/newton/llvm-ir/c-files/test_${ALGO}.c" -D INT_DATA_TYPE -no-pie \ + -L"$HOME/CoSense/applications/newton/llvm-ir" -lout -O3 -Os -g \ + -o "$HOME/CoSense/applications/newton/llvm-ir/main_out" -lm + +# Step 10: Run the test executable +echo "Step 10: Run the test executable" +"$HOME/CoSense/applications/newton/llvm-ir/main_out" \ No newline at end of file diff --git a/applications/newton/llvm-ir/test_madgwick.c b/applications/newton/llvm-ir/test_madgwick.c deleted file mode 100644 index 808254695..000000000 --- a/applications/newton/llvm-ir/test_madgwick.c +++ /dev/null @@ -1,269 +0,0 @@ -/* - * Madgwick test case (run locally) - * Compilation command in applications/newton/llvm-ir/performance_test/Makefile - * - * How to compile and run? - * 1. `make perf_madgwick` FP hardware (by default) - * 2. `SOFT_FLOAT=1 make perf_madgwick` FP software (clang -msoft-float and opt --float-abi=soft) - * 3. `SOFT_FLOAT_LIB=1 make perf_madgwick` FP soft-float lib (from CHStone) - * 4. `AUTO_QUANT=1 make perf_madgwick` INT fixed Q number format - * */ - -#include -#include -#include -#include -#include -#include - -#if defined(INT_DATA_TYPE) -extern volatile int32_t q0, q1, q2, q3; -#include "c-files/MadgwickAHRSfix.h" -#elif defined(FP_DATA_TYPE) -extern volatile float q0, q1, q2, q3; -#if defined(SOFT_FLOAT_LIB) -#include "c-files/MadgwickAHRS_softfloat.h" -#else -#include "c-files/MadgwickAHRS.h" -#endif - -#else -#error "Must set data type: FP or INT" -#endif - -#define DATA_SIZE 1000 - -#define ITERATION 10 - -/*************************************** - * Timer functions of the test framework - ***************************************/ - -typedef struct timespec timespec; -timespec diff(timespec start, timespec end) -{ - timespec temp; - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - return temp; -} - -timespec sum(timespec t1, timespec t2) { - timespec temp; - if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } else { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; -} - -void printTimeSpec(timespec t, const char* prefix) { - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); -} - -timespec tic( ) -{ - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; -} - -timespec toc( timespec* start_time, const char* prefix ) -{ - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - timespec time_consump = diff( *start_time, current_time ); - printTimeSpec(time_consump, prefix ); - *start_time = current_time; - return time_consump; -} - -int main() { - FILE* fp = fopen("input.csv", "r"); - - if (!fp) { - printf("Can't open file\n"); - return -1; - } - - double time[DATA_SIZE]; -#if defined(INT_DATA_TYPE) - int32_t q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = (0.64306622f*FRAC_BASE); - q1[i] = (0.02828862f*FRAC_BASE); - q2[i] = (-0.00567953f*FRAC_BASE); - q3[i] = (-0.76526684f*FRAC_BASE); - } - - int32_t mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; -#elif defined(FP_DATA_TYPE) - float q0[DATA_SIZE], q1[DATA_SIZE], q2[DATA_SIZE], q3[DATA_SIZE]; - // quaternion of sensor frame relative to auxiliary frame - for (int i = 0; i < DATA_SIZE; i++) { - q0[i] = 0.64306622f; - q1[i] = 0.02828862f; - q2[i] = -0.00567953f; - q3[i] = -0.76526684f; - } - - float mag_x[DATA_SIZE], mag_y[DATA_SIZE], mag_z[DATA_SIZE], - gyr_x[DATA_SIZE], gyr_y[DATA_SIZE], gyr_z[DATA_SIZE], - acc_x[DATA_SIZE], acc_y[DATA_SIZE], acc_z[DATA_SIZE]; -#else -#error "Must set data type: FP or INT" -#endif - - char buffer[1024]; - int row = 0, column = 0; - while (fgets(buffer, 1024, fp)) { - column = 0; - row++; - - if (row == 1) - continue; - - char* value = strtok(buffer, ", "); - - while (value) { - switch (column) { - case 0: - time[row-2] = atof(value); - break; - case 1: -#if defined(INT_DATA_TYPE) - mag_x[row-2] = round(atof(value)*FRAC_BASE); -#elif defined(FP_DATA_TYPE) - mag_x[row-2] = atof(value); -#endif - break; - case 2: -#if defined(INT_DATA_TYPE) - mag_y[row-2] = round(atof(value)*FRAC_BASE); -#elif defined(FP_DATA_TYPE) - mag_y[row-2] = atof(value); -#endif - break; - case 3: -#if defined(INT_DATA_TYPE) - mag_z[row-2] = round(atof(value)*FRAC_BASE); -#elif defined(FP_DATA_TYPE) - mag_z[row-2] = atof(value); -#endif - break; - case 4: -#if defined(INT_DATA_TYPE) - gyr_x[row-2] = round(atof(value)*FRAC_BASE)*61; -#elif defined(FP_DATA_TYPE) - gyr_x[row-2] = atof(value)*61; -#endif - break; - case 5: -#if defined(INT_DATA_TYPE) - gyr_y[row-2] = round(atof(value)*FRAC_BASE)*61; -#elif defined(FP_DATA_TYPE) - gyr_y[row-2] = atof(value)*61; -#endif - break; - case 6: -#if defined(INT_DATA_TYPE) - gyr_z[row-2] = round(atof(value)*FRAC_BASE)*61; -#elif defined(FP_DATA_TYPE) - gyr_z[row-2] = atof(value)*61; -#endif - break; - case 7: -#if defined(INT_DATA_TYPE) - acc_x[row-2] = round(atof(value)*FRAC_BASE)*2; -#elif defined(FP_DATA_TYPE) - acc_x[row-2] = atof(value)*2; -#endif - break; - case 8: -#if defined(INT_DATA_TYPE) - acc_y[row-2] = round(atof(value)*FRAC_BASE)*2; -#elif defined(FP_DATA_TYPE) - acc_y[row-2] = atof(value)*2; -#endif - break; - case 9: -#if defined(INT_DATA_TYPE) - acc_z[row-2] = round(atof(value)*FRAC_BASE)*2; -#elif defined(FP_DATA_TYPE) - acc_z[row-2] = atof(value)*2; -#endif - break; - default: - break; - } - - value = strtok(NULL, ", "); - column++; - } - } - - fclose(fp); - - u_int64_t time_slots[ITERATION]; - - for (size_t idx = 0; idx < ITERATION; idx++) { - timespec timer = tic(); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { - MadgwickAHRSupdate(gyr_x[ts], gyr_y[ts], gyr_z[ts], - acc_x[ts], acc_y[ts], acc_z[ts], - mag_x[ts], mag_y[ts], mag_z[ts], - &q0[ts], &q1[ts], &q2[ts], &q3[ts]); - } - time_slots[idx] = toc(&timer, "computation delay").tv_nsec; - } - - u_int64_t average_time = 0; - for (size_t idx = 0; idx < ITERATION; idx++) { - average_time += time_slots[idx]; - } - average_time /= ITERATION; - printf("average time = %lu nm\n", average_time); - -#if defined(FP_DATA_TYPE) - FILE *fptr = fopen("fp_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { -// printf("Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", -// ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - fprintf(fptr, "Original: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, q0[ts], ts, q1[ts], ts, q2[ts], ts, q3[ts]); - } - fclose(fptr); -#elif defined(INT_DATA_TYPE) - FILE *fptr = fopen("int_result.txt", "w"); - for (size_t ts = 0; ts < DATA_SIZE; ts++) { -// printf("FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", -// ts, (double)q0[ts]/FRAC_BASE, -// ts, (double)q1[ts]/FRAC_BASE, -// ts, (double)q2[ts]/FRAC_BASE, -// ts, (double)q3[ts]/FRAC_BASE); - fprintf(fptr, "FIX: q0[%d]=%f, q1[%d]=%f, q2[%d]=%f, q3[%d]=%f\n", - ts, (double)q0[ts]/FRAC_BASE, - ts, (double)q1[ts]/FRAC_BASE, - ts, (double)q2[ts]/FRAC_BASE, - ts, (double)q3[ts]/FRAC_BASE); - } - fclose(fptr); -// printf("FIX: q0 = %d.%04d, q1 = %d.%04d, q2 = %d.%04d, q3 = %d.%04d\n", -// DISPLAY_INT(q0), DISPLAY_FRAC(q0), -// DISPLAY_INT(q1), DISPLAY_FRAC(q1), -// DISPLAY_INT(q2), DISPLAY_FRAC(q2), -// DISPLAY_INT(q3), DISPLAY_FRAC(q3)); -#endif - return 0; -} diff --git a/applications/newton/sensors/BMX055.nt b/applications/newton/sensors/BMX055.nt index c87828bf6..e03b07cd8 100644 --- a/applications/newton/sensors/BMX055.nt +++ b/applications/newton/sensors/BMX055.nt @@ -37,7 +37,7 @@ # # Description: Base signals used in most Newton descriptions. # -include "applications/newton/include/NewtonBaseSignals.nt" +include "NewtonBaseSignals.nt" # # Description: Additional signals used in this particular newton description. @@ -142,9 +142,13 @@ bmx055: sensor ( # # Range of Acceleration can be found in Table 2 on page 11. # - range bmx055xAcceleration == [-16 mjf, 16 mjf], - range bmx055yAcceleration == [-16 mjf, 16 mjf], - range bmx055zAcceleration == [-16 mjf, 16 mjf], + #range bmx055xAcceleration == [-16 mjf, 16 mjf], + #range bmx055yAcceleration == [-16 mjf, 16 mjf], + #range bmx055zAcceleration == [-16 mjf, 16 mjf], + + range bmx055xAcceleration == [-2 mjf, 2 mjf], + range bmx055yAcceleration == [-2 mjf, 2 mjf], + range bmx055zAcceleration == [-2 mjf, 2 mjf], # # Range of Temperature can be found in Table 2 on page 11. @@ -161,9 +165,9 @@ bmx055: sensor ( # # Range of Angular Rate can be found in Table 3 on page 14. # - range bmx055xAngularRate == [0 ajf, 2000 ajf], - range bmx055yAngularRate == [0 ajf, 2000 ajf], - range bmx055zAngularRate == [0 ajf, 2000 ajf], + range bmx055xAngularRate == [-2000 ajf, 2000 ajf], + range bmx055yAngularRate == [-2000 ajf, 2000 ajf], + range bmx055zAngularRate == [-2000 ajf, 2000 ajf], # # The operation sequence needed to get a sample @@ -205,7 +209,7 @@ bmx055: sensor ( # # Get MSB and LSB of zAccel data... # - + zAccelHigh := read 16r07; zAccelLow := read 16r06; bmx055zAcceleration = (zAccelHigh << 4) | (zAccelLow >> 4); @@ -218,9 +222,9 @@ bmx055: sensor ( # interface bmx055Temperature == i2c (address: 16r18) { - # + # # Start up at normal mode, modify (write value register) if set to other modes ... - # + # # Get Temp data... # bmx055Temperature = read 16r08; @@ -235,7 +239,7 @@ bmx055: sensor ( { # # Start up at normal mode, modify (write value register) if set to other modes ... - # + # # Get MSB and LSB of xMagneto data... # xMagnetoHigh := read 16r43; @@ -289,7 +293,7 @@ bmx055: sensor ( # # Start up at normal mode, modify (write value register) if set to other modes ... # - # Get MSB and LSB of yAngularRate data... + # Get MSB and LSB of yAngularRate data... # yAngularRateHigh := read 16r05; yAngularRateLow := read 16r04; @@ -391,6 +395,11 @@ bmx055: sensor ( (16, -1) }, + + # @sensitivity bmx055xAcceleration = 1024 LSB/g + + + # # Accuracy settings list for the sensor. This is a list of (accuracy, cost) pairs. # @@ -473,4 +482,4 @@ bmx055: sensor ( accuracy bmx055xAngularRate == { (0.1 ajf, 0.012) } -} +} \ No newline at end of file diff --git a/src/common/common-data-structures.h b/src/common/common-data-structures.h index d1e728af8..561ffa946 100644 --- a/src/common/common-data-structures.h +++ b/src/common/common-data-structures.h @@ -664,6 +664,7 @@ typedef enum kNewtonIrNodeType_PruleList, kNewtonIrNodeType_PnewtonDescription, kNewtonIrNodeType_PnumericConst, + kNewtonIrNodeType_PresolutionStatement, /* * Code depends on this bringing up the rear for Newton Productions. @@ -897,6 +898,7 @@ typedef struct Signal Signal; typedef struct Sensor Sensor; typedef struct Modality Modality; + typedef struct NoisyType NoisyType; enum @@ -1011,6 +1013,7 @@ struct Modality { double accuracy; double accuracyCost; // Signal * accuracySignal; + double resolution; SensorInterfaceType interfaceType; /* WiP */ /* Missing register address for modality */ diff --git a/src/newton/Makefile b/src/newton/Makefile index 31e249390..a4f119402 100644 --- a/src/newton/Makefile +++ b/src/newton/Makefile @@ -1,8 +1,16 @@ TREEROOT = ../.. +ifndef MAX_PRECISION_BITS +MAX_PRECISION_BITS = 13 +endif + +ifndef BIT_WIDTH +BIT_WIDTH = 32 +endif + include $(TREEROOT)/config.local -PRECOMMITHOOK = precommitStatisticsHook-$(OSTYPE).sh +PRECOMMITHOOK = precommitStatisticsHook-$(OSTYPE).sh COMMONPATH = ../common NOISYPATH = ../noisy @@ -26,6 +34,17 @@ CXXFLAGS = $(PLATFORM_DBGFLAGS) $(PLATFORM_CFLAGS) $(PLATFORM_DFLAGS) $(PLATFORM CCFLAGS = $(PLATFORM_DBGFLAGS) $(PLATFORM_CFLAGS) $(PLATFORM_DFLAGS) $(PLATFORM_OPTFLAGS) LDFLAGS = $(PLATFORM_DBGFLAGS) -lm $(PLATFORM_LFLAGS) `pkg-config --libs 'libprotobuf-c >= 1.0.0'` + +#Quantization setting +CCFLAGS += -DMAX_PRECISION_BITS=$(MAX_PRECISION_BITS) +CXXFLAGS += -DMAX_PRECISION_BITS=$(MAX_PRECISION_BITS) + + +CCFLAGS += -DBIT_WIDTH=$(BIT_WIDTH) +CXXFLAGS += -DBIT_WIDTH=$(BIT_WIDTH) + + + CCFLAGS+=$(shell $(LLVM_CONFIG) --cflags) LDFLAGS+=$(shell $(LLVM_CONFIG) --ldflags) CXXFLAGS+=$(COMMON_FLAGS) $(shell $(LLVM_CONFIG) --cxxflags) -fno-rtti @@ -313,24 +332,28 @@ installhooks: # Libraries # -lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a: $(LIBNEWTONOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a: $(LIBNEWTONOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(AR) $(ARFLAGS) $@ $(LIBNEWTONOBJS) # # Executables # -target: $(OBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +target: $(OBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(LD) $(LINKDIRS) $(LDFLAGS) $(OBJS) $(LLVMLIBS) $(SYSTEMLIBS) -lflex-$(OSTYPE) -lm $(LINKDIRS) $(LDFLAGS) -o $(TARGET) -lstdc++ -cgi:lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a $(CGIOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +cgi:lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a $(CGIOBJS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(LD) $(LINKDIRS) $(LDFLAGS) $(CGIOBJS) $(LLVMLIBS) $(SYSTEMLIBS) -lflex-$(OSTYPE) $(LINKDIRS) $(LDFLAGS) -o $(CGI_TARGET) -lstdc++ + +maxprec.cfg: + @echo $(MAX_PRECISION_BITS) > maxprec.cfg + # # Objects # -%.$(OBJECTEXTENSION): %.c $(HEADERS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile +%.$(OBJECTEXTENSION): %.c $(HEADERS) $(CONFIGPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) $(COMMONPATH)/config.$(OSTYPE)-$(MACHTYPE).$(COMPILERVARIANT) Makefile $(CC) $(FLEXFLAGS) $(INCDIRS) $(CCFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CC) $(FLEXFLAGS) $(INCDIRS) $(CCFLAGS) $(WFLAGS) $(OPTFLAGS) $< @@ -366,18 +389,27 @@ newton-irPass-LLVMIR-shrinkTypeByRange.$(OBJECTEXTENSION): newton-irPass-LLVMIR- $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< -newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-quantization.cpp +newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION): newton-irPass-LLVMIR-quantization.cpp maxprec.cfg $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< -newton-irPass-LLVMIR-memoryAlignment.$(OBJECTEXTENSION): newton-irPass-LLVMIR-memoryAlignment.cpp +newton-irPass-LLVMIR-memoryAlignment.$(OBJECTEXTENSION): newton-irPass-LLVMIR-memoryAlignment.cpp maxprec.cfg $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< + + newton-irPass-LLVMIR-emitAssume.$(OBJECTEXTENSION): newton-irPass-LLVMIR-emitAssume.cpp $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $(LINTFLAGS) $< $(CXX) $(FLEXFLAGS) $(INCDIRS) $(CXXFLAGS) $(WFLAGS) $(OPTFLAGS) $< + +.PHONY: rebuild-quant-opt + +rebuild-quant-opt: + rm -f newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION) newton-irPass-LLVMIR-optimizeByRange.$(OBJECTEXTENSION) + $(MAKE) newton-irPass-LLVMIR-quantization.$(OBJECTEXTENSION) newton-irPass-LLVMIR-optimizeByRange.$(OBJECTEXTENSION) + version.c: $(HEADERS) Makefile echo 'char kNewtonVersion[] = "0.3-alpha-'`git rev-list --count HEAD`' ('`git rev-parse HEAD`') (build '`date '+%m-%d-%Y-%H:%M'`-`whoami`@`hostname -s`-`uname -s`-`uname -r`-`uname -m`\)\"\; > version.c @@ -437,14 +469,14 @@ examples: $(EXAMPLES) target installcgi: cgi sudo cp $(CGI_TARGET) $(CGI_BIN) sudo mkdir -p $(HTMLBASEPATH) - sudo cp ../icons/* $(HTMLBASEPATH)/ - sudo chmod 777 $(HTMLBASEPATH) + sudo cp ../icons/* $(HTMLBASEPATH)/ + sudo chmod 777 $(HTMLBASEPATH) sudo chmod 755 $(HTMLBASEPATH)/*.png test: - ./$(TARGET) --dot 0 $(EXAMPLESPATH)/invariants.nt | dot -Tpdf -O ; open noname.gv.pdf + ./$(TARGET) --dot 0 $(EXAMPLESPATH)/invariants.nt | dot -Tpdf -O ; open noname.gv.pdf clean: rm -rf version.c $(OBJS) $(CGIOBJS) $(LIBNEWTONOBJS) $(CGI_TARGET) $(CGI_TARGET).dSYM $(TARGET) $(TARGET).dSYM $(CGI_TARGET) $(CGI_TARGET).dsym lib$(LIBNEWTON)-$(OSTYPE)-$(NEWTON_L10N).a *.o *.plist - cd ../common && make clean + cd ../common && make clean \ No newline at end of file diff --git a/src/newton/Range.h b/src/newton/Range.h new file mode 100644 index 000000000..139b2fc66 --- /dev/null +++ b/src/newton/Range.h @@ -0,0 +1,65 @@ +#ifndef RANGE_H +#define RANGE_H + +#include "llvm/ADT/APInt.h" +using llvm::APInt; + +#include "llvm/Support/raw_ostream.h" +using llvm::raw_ostream; + +enum RangeType { Unknown, Regular, Empty }; + +class Range { + private: + APInt l; // The lower bound of the range. + APInt u; // The upper bound of the range. + RangeType type{Regular}; + + public: + Range(); + Range(const APInt &lb, const APInt &ub, RangeType rType = Regular); + ~Range() = default; + Range(const Range &other) = default; + Range(Range &&) = default; + Range &operator=(const Range &other) = default; + Range &operator=(Range &&) = default; + + const APInt& getLower() const { return l; } + const APInt& getUpper() const { return u; } + void setLower(const APInt &newl) { this->l = newl; } + void setUpper(const APInt &newu) { this->u = newu; } + bool isUnknown() const { return type == Unknown; } + void setUnknown() { type = Unknown; } + bool isRegular() const { return type == Regular; } + void setRegular() { type = Regular; } + bool isEmpty() const { return type == Empty; } + void setEmpty() { type = Empty; } + bool isMaxRange() const; + void print(raw_ostream &OS) const; + Range add(const Range &other) const; + Range sub(const Range &other) const; + Range mul(const Range &other) const; + Range udiv(const Range &other) const; + Range sdiv(const Range &other) const; + Range urem(const Range &other) const; + Range srem(const Range &other) const; + Range shl(const Range &other) const; + Range lshr(const Range &other) const; + Range ashr(const Range &other) const; + Range And(const Range &other) const; + Range And_conservative(const Range &other) const; + Range Or(const Range &other) const; + Range Or_conservative(const Range &other) const; + Range Xor(const Range &other) const; + Range truncate(unsigned bitwidth) const; + // Range signExtend(unsigned bitwidth) const; + // Range zeroExtend(unsigned bitwidth) const; + Range sextOrTrunc(unsigned bitwidth) const; + Range zextOrTrunc(unsigned bitwidth) const; + Range intersectWith(const Range &other) const; + Range unionWith(const Range &other) const; + bool operator==(const Range &other) const; + bool operator!=(const Range &other) const; +}; + +#endif // RANGE_H \ No newline at end of file diff --git a/src/newton/SimplePass.cpp b/src/newton/SimplePass.cpp new file mode 100644 index 000000000..723c0e236 --- /dev/null +++ b/src/newton/SimplePass.cpp @@ -0,0 +1,5 @@ +// +// Created by 13862 on 2024/8/2. +// + +#include "SimplePass.h" diff --git a/src/newton/SimplePass.h b/src/newton/SimplePass.h new file mode 100644 index 000000000..f99acee54 --- /dev/null +++ b/src/newton/SimplePass.h @@ -0,0 +1,33 @@ +#include "llvm/Pass.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/IR/PassManager.h" + +using namespace llvm; + +namespace { +class SimplePass : public PassInfoMixin { + public: + PreservedAnalyses run(Function &F, FunctionAnalysisManager &) { + errs() << "SimplePass: " << F.getName() << "\n"; + return PreservedAnalyses::all(); + } +}; + +extern "C" ::llvm::PassPluginLibraryInfo llvmGetPassPluginInfo() { + return { + LLVM_PLUGIN_API_VERSION, "SimplePass", LLVM_VERSION_STRING, + [](PassBuilder &PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager &FPM, + ArrayRef) { + if (Name == "simple-pass") { + FPM.addPass(SimplePass()); + return true; + } + return false; + }); + } + }; +} +} diff --git a/src/newton/config.h b/src/newton/config.h new file mode 100644 index 000000000..a61df6b81 --- /dev/null +++ b/src/newton/config.h @@ -0,0 +1,16 @@ +#ifndef CONFIG_H +#define CONFIG_H + +#ifndef BIT_WIDTH +#define BIT_WIDTH 32 +#endif + +#define AUTO_QUANTIZATION 1 +#define IS_POINTER 1 +#define IS_MATRIX 0 + +#ifndef MAX_PRECISION_BITS +#define MAX_PRECISION_BITS 16 +#endif + +#endif // CONFIG_H diff --git a/src/newton/myRangeAnalysis.cpp b/src/newton/myRangeAnalysis.cpp new file mode 100644 index 000000000..0c3a28508 --- /dev/null +++ b/src/newton/myRangeAnalysis.cpp @@ -0,0 +1,921 @@ +#include "llvm/Pass.h" +#include "llvm/IR/Function.h" +#include "llvm/IR/Instructions.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/KnownBits.h" +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/BasicBlock.h" +#include "llvm/IR/InstIterator.h" +#include "llvm/Passes/PassBuilder.h" +#include "llvm/Passes/PassPlugin.h" +#include "llvm/IR/PassManager.h" +#include "llvm/ADT/APInt.h" +#include "Range.h" +#include +#include + +using namespace llvm; + +namespace +{ + +// The number of bits needed to store the largest variable of the function +// (APInt). +unsigned MAX_BIT_INT = 1; +// 声明用于表示整数常量的全局变量 +APInt Min; +APInt Max; +APInt Zero; +APInt One; + +// 宏定义 +#define MUL_HELPER(x, y) \ + ((x).eq(Max) ? ((x).slt(Zero) ? Min : ((x).eq(Zero) ? Zero : Max)) \ + : ((x).eq(Min) ? ((x).slt(Zero) ? Max : ((x).eq(Zero) ? Zero : Min)) \ + : ((x) * (y)))) + +#define MUL_OV(x, y, xy) \ + ((x).isStrictlyPositive() == (y).isStrictlyPositive() \ + ? ((xy).isNegative() ? Max : (xy)) \ + : ((xy).isStrictlyPositive() ? Min : (xy))) + + +// Range 类定义 +class Range { + public: + // 构造函数 + Range(); + Range(const APInt &lb, const APInt &ub, RangeType rType = Regular); + + // 成员函数 + bool isMaxRange() const; + Range add(const Range &other) const; + Range sub(const Range &other) const; + Range mul(const Range &other) const; + Range urem(const Range &other) const; + Range srem(const Range &other) const; + Range shl(const Range &other) const; + Range lshr(const Range &other) const; + Range ashr(const Range &other) const; + Range And(const Range &other) const; + Range And_conservative(const Range &other) const; + Range Or(const Range &other) const; + + // 获取范围下界 + const APInt &getLower() const { return l; } + + // 获取范围上界 + const APInt &getUpper() const { return u; } + + // 判断范围是否未知 + bool isUnknown() const { return type == Unknown; } + + // 判断范围是否为空 + bool isEmpty() const { return type == Empty; } + + // 操作符 + bool operator==(const Range &other) const; + + + private: + APInt l, u; + RangeType type; +}; + + + +// Range 类的默认构造函数,初始化为 [Min, Max] +Range::Range() : l(Min), u(Max), type(Regular) {} +// 使用下界和上界以及范围类型初始化 Range +Range::Range(const APInt & lb, const APInt & ub, RangeType rType) + : l(lb), u(ub), type(rType) +{ + if (lb.sgt(ub)) + { + type = Empty; // 如果下界大于上界,则认为范围为空 + } +} + + +Range Range::Or(const Range &other) const { + APInt lower = l | other.l; + APInt upper = u | other.u; + return Range(lower, upper); +} + +// 判断是否为最大范围 [Min, Max] +bool +Range::isMaxRange() const +{ + return this->getLower().eq(Min) && this->getUpper().eq(Max); +} + +// 加法运算 [a, b] + [c, d] = [a + c, b + d] +Range +Range::add(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + APInt l = Min, u = Max; + + if (a.ne(Min) && c.ne(Min)) + { + l = a + c; + + // 溢出处理 + if (a.isNegative() == c.isNegative() && a.isNegative() != l.isNegative()) + { + l = Min; + } + } + + if (b.ne(Max) && d.ne(Max)) + { + u = b + d; + + // 溢出处理 + if (b.isNegative() == d.isNegative() && b.isNegative() != u.isNegative()) + { + u = Max; + } + } + + return Range(l, u); +} + +// 减法运算 [a, b] - [c, d] = [a - d, b - c] +Range +Range::sub(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + APInt l, u; + + // 计算下界 + if (a.eq(Min) || d.eq(Max)) + { + l = Min; + } + else + { + l = a - d; + + // 溢出处理 + if (a.isNegative() != d.isNegative() && d.isNegative() == l.isNegative()) + { + l = Min; + } + } + + // 计算上界 + if (b.eq(Max) || c.eq(Min)) + { + u = Max; + } + else + { + u = b - c; + + // 溢出处理 + if (b.isNegative() != c.isNegative() && c.isNegative() == u.isNegative()) + { + u = Max; + } + } + + return Range(l, u); +} + +// 乘法运算 [a, b] * [c, d] = [min(a*c, a*d, b*c, b*d), max(a*c, a*d, b*c, b*d)] +Range +Range::mul(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + if (this->isMaxRange() || other.isMaxRange()) + { + return Range(Min, Max); // 如果任一范围为最大范围,则结果为最大范围 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + APInt candidates[4]; + candidates[0] = MUL_OV(a, c, MUL_HELPER(a, c)); + candidates[1] = MUL_OV(a, d, MUL_HELPER(a, d)); + candidates[2] = MUL_OV(b, c, MUL_HELPER(b, c)); + candidates[3] = MUL_OV(b, d, MUL_HELPER(b, d)); + + // 找到最小和最大值 + APInt * min = &candidates[0]; + APInt * max = &candidates[0]; + + for (unsigned i = 1; i < 4; ++i) + { + if (candidates[i].sgt(*max)) + { + max = &candidates[i]; + } + else if (candidates[i].slt(*min)) + { + min = &candidates[i]; + } + } + + return Range(*min, *max); +} + +// 无符号取模运算 [a, b] % [c, d] +Range +Range::urem(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + // 处理除以0的情况 + if (c.ule(Zero) && d.uge(Zero)) + { + return Range(Min, Max); + } + + APInt candidates[4]; + candidates[0] = Min; + candidates[1] = Min; + candidates[2] = Max; + candidates[3] = Max; + + if (a.ne(Min) && c.ne(Min)) + { + candidates[0] = a.urem(c); // lower lower + } + + if (a.ne(Min) && d.ne(Max)) + { + candidates[1] = a.urem(d); // lower upper + } + + if (b.ne(Max) && c.ne(Min)) + { + candidates[2] = b.urem(c); // upper lower + } + + if (b.ne(Max) && d.ne(Max)) + { + candidates[3] = b.urem(d); // upper upper + } + + // 找到最小和最大值 + APInt * min = &candidates[0]; + APInt * max = &candidates[0]; + + for (unsigned i = 1; i < 4; ++i) + { + if (candidates[i].sgt(*max)) + { + max = &candidates[i]; + } + else if (candidates[i].slt(*min)) + { + min = &candidates[i]; + } + } + + return Range(*min, *max); +} + +// 有符号取模运算 [a, b] % [c, d] +Range +Range::srem(const Range & other) const +{ + if (other == Range(Zero, Zero) || other == Range(Min, Max, Empty)) + { + return Range(Min, Max, Empty); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); // 如果任一范围未知,则结果未知 + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + // 处理除以0的情况 + if (c.sle(Zero) && d.sge(Zero)) + { + return Range(Min, Max); + } + + APInt candidates[4]; + candidates[0] = MUL_OV(a, c, MUL_HELPER(a, c)); + candidates[1] = MUL_OV(a, d, MUL_HELPER(a, d)); + candidates[2] = MUL_OV(b, c, MUL_HELPER(b, c)); + candidates[3] = MUL_OV(b, d, MUL_HELPER(b, d)); + + // 找到最小和最大值 + APInt * min = &candidates[0]; + APInt * max = &candidates[0]; + + for (unsigned i = 1; i < 4; ++i) + { + if (candidates[i].sgt(*max)) + { + max = &candidates[i]; + } + else if (candidates[i].slt(*min)) + { + min = &candidates[i]; + } + } + + return Range(*min, *max); +} + +// 左移运算 +Range +Range::shl(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || c.eq(Min) || b.eq(Max) || d.eq(Max)) + { + return Range(Min, Max); + } + + APInt min = a.shl(c); + APInt max = b.shl(d); + + APInt Zeros(MAX_BIT_INT, b.countLeadingZeros()); + if (Zeros.ugt(d)) + { + return Range(min, max); + } + + // 返回最大范围 [-inf, +inf] + return Range(Min, Max); +} + +// 逻辑右移运算 +Range +Range::lshr(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || c.eq(Min) || b.eq(Max) || d.eq(Max)) + { + return Range(Min, Max); + } + + APInt max = b.lshr(c); + APInt min = a.lshr(d); + + return Range(min, max); +} + +// 算术右移运算 +Range +Range::ashr(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + if (this->isUnknown() || other.isUnknown()) + { + return Range(Min, Max, Unknown); + } + + const APInt & a = this->getLower(); + const APInt & b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || c.eq(Min) || b.eq(Max) || d.eq(Max)) + { + return Range(Min, Max); + } + + APInt max = b.ashr(c); + APInt min = a.ashr(d); + + return Range(min, max); +} + +// 与操作,Hacker's Delight算法 +Range +Range::And(const Range & other) const +{ + if (this->isUnknown() || other.isUnknown()) + { + const APInt & umin = llvm::APIntOps::umin(getUpper(), other.getUpper()); + if (umin.isAllOnesValue()) + { + return Range(Min, Max); + } + return Range(APInt::getNullValue(MAX_BIT_INT), umin); + } + + APInt a = this->getLower(); + APInt b = this->getUpper(); + const APInt & c = other.getLower(); + const APInt & d = other.getUpper(); + + if (a.eq(Min) || b.eq(Max) || c.eq(Min) || d.eq(Max)) + { + return Range(Min, Max); + } + + // 取反所有位 + APInt negA = APInt(a); + negA.flipAllBits(); + APInt negB = APInt(b); + negB.flipAllBits(); + APInt negC = APInt(c); + negC.flipAllBits(); + APInt negD = APInt(d); + negD.flipAllBits(); + + Range inv1 = Range(negB, negA); + Range inv2 = Range(negD, negC); + + Range invres = inv1.Or(inv2); + + // 取反结果 + APInt invLower = invres.getUpper(); + invLower.flipAllBits(); + + APInt invUpper = invres.getLower(); + invUpper.flipAllBits(); + + return Range(invLower, invUpper); +} + +// 保守的与操作,适用于大于64位的值 +Range +Range::And_conservative(const Range & other) const +{ + if (isEmpty()) + { + return Range(*this); + } + if (other.isEmpty()) + { + return Range(other); + } + + const APInt & umin = llvm::APIntOps::umin(other.getUpper(), getUpper()); + if (umin.isAllOnesValue()) + { + return Range(Min, Max); + } + return Range(APInt::getNullValue(MAX_BIT_INT), umin); +} + + + + + +// 范围表示和管理 +std::map valueRanges; + +// 更新常量整数值 +void +updateConstantIntegers(unsigned maxBitWidth) +{ + // 使用给定的最大位宽设置最小值和最大值 + Min = APInt::getSignedMinValue(maxBitWidth); + Max = APInt::getSignedMaxValue(maxBitWidth); + Zero = APInt(maxBitWidth, 0UL, true); // 确保使用maxBitWidth来表示Zero + One = APInt(maxBitWidth, 1UL, true); // 确保使用maxBitWidth来表示One +} + +// 初始化分析 +void +initializeAnalysis(Function & F) +{ + // 清除前一次分析的范围信息 + valueRanges.clear(); + errs() << "Starting range analysis for function: " << F.getName() << "\n"; +} + +// 结束分析 +void +finalizeAnalysis(Function & F) +{ + // 结束分析的逻辑 + errs() << "Finished range analysis for function: " << F.getName() << "\n"; +} + +// 判断指令是否有效 +bool +isValidInstruction(const Instruction * I) +{ + switch (I->getOpcode()) + { + // 算术运算 + case Instruction::Add: + case Instruction::Sub: + case Instruction::Mul: + case Instruction::UDiv: + case Instruction::SDiv: + case Instruction::URem: + case Instruction::SRem: + // 位操作 + case Instruction::Shl: + case Instruction::LShr: + case Instruction::AShr: + case Instruction::And: + case Instruction::Or: + case Instruction::Xor: + // 类型转换 + case Instruction::Trunc: + case Instruction::ZExt: + case Instruction::SExt: + // 其他操作 + case Instruction::PHI: + case Instruction::Select: + return true; + default: + return false; + } +} + +// 获取函数中最大位宽 +unsigned +getMaxBitWidth(const Function & F) +{ + unsigned max = 0; + for (const Instruction & I : instructions(F)) + { + if (I.getType()->isIntegerTy()) + { + max = std::max(max, static_cast(I.getType()->getPrimitiveSizeInBits().getFixedSize())); + } + + // 获得指令操作数的最大位宽 + for (const Value * operand : I.operands()) + { + if (operand->getType()->isIntegerTy()) + { + max = std::max(max, static_cast(operand->getType()->getPrimitiveSizeInBits().getFixedSize())); + } + } + } + // 确保位宽最小为1 + return max == 0 ? 1 : max; +} + +void handleBinaryOperator(BinaryOperator * binOp); + +// 分析每个指令 +void +analyzeInstruction(Instruction & I) +{ + if (auto * binOp = dyn_cast(&I)) + { + handleBinaryOperator(binOp); + } +} + +void +insertOverflowCheck(BinaryOperator * binOp, Value * op1, Value * op2, Range & resultRange) +{ + Function * F = binOp->getFunction(); + LLVMContext & Context = F->getContext(); + IRBuilder<> Builder(binOp); + + // 检查 x 和 y 是否都是非负数 + Value * xNonNegative = Builder.CreateICmpSGE(op1, ConstantInt::get(op1->getType(), 0)); + Value * yNonNegative = Builder.CreateICmpSGE(op2, ConstantInt::get(op2->getType(), 0)); + Value * bothNonNegative = Builder.CreateAnd(xNonNegative, yNonNegative); + + // 如果是,则检查结果是否也是非负数 + Value * addNegative = Builder.CreateICmpSLT(binOp, ConstantInt::get(binOp->getType(), 0)); + Value * nonNegativeOverflow = Builder.CreateAnd(bothNonNegative, addNegative); + + // 检查 x 和 y 是否都是负数 + Value * xNegative = Builder.CreateICmpSLT(op1, ConstantInt::get(op1->getType(), 0)); + Value * yNegative = Builder.CreateICmpSLT(op2, ConstantInt::get(op2->getType(), 0)); + Value * bothNegative = Builder.CreateAnd(xNegative, yNegative); + + // 如果是,则检查结果是否也是负数 + Value * addNonNegative = Builder.CreateICmpSGE(binOp, ConstantInt::get(binOp->getType(), 0)); + Value * negativeOverflow = Builder.CreateAnd(bothNegative, addNonNegative); + + // 最终检查是否发生溢出 + Value * overflow = Builder.CreateOr(nonNegativeOverflow, negativeOverflow); + + // 根据溢出情况创建分支 + BasicBlock * CurrentBB = binOp->getParent(); + BasicBlock * OverflowBB = BasicBlock::Create(Context, "overflow", F); + BasicBlock * ContinueBB = BasicBlock::Create(Context, "continue", F); + + Builder.CreateCondBr(overflow, OverflowBB, ContinueBB); + + // 在 OverflowBB 中处理溢出 + Builder.SetInsertPoint(OverflowBB); + // 调用处理溢出的函数 + Function * HandleOverflowFunc = F->getParent()->getFunction("handle_overflow"); + if (HandleOverflowFunc) + { + Builder.CreateCall(HandleOverflowFunc); + } + Builder.CreateBr(ContinueBB); + + // 在 ContinueBB 中继续执行 + Builder.SetInsertPoint(ContinueBB); +} + +// 处理二元操作指令 +void +handleBinaryOperator(BinaryOperator * binOp) +{ + auto op1 = binOp->getOperand(0); + auto op2 = binOp->getOperand(1); + + // 获取操作数的范围 + Range range1 = valueRanges.count(op1) ? valueRanges[op1] : Range(Min, Max); + Range range2 = valueRanges.count(op2) ? valueRanges[op2] : Range(Min, Max); + + // 计算新范围 + Range resultRange; + + if (binOp->getOpcode() == Instruction::Add) + { + resultRange = range1.add(range2); + + // 插入溢出检测逻辑 + insertOverflowCheck(binOp, op1, op2, resultRange); + } + + // switch (binOp->getOpcode()) { + // case Instruction::Add: + // // 调用 Range::add 方法 + // resultRange = range1.add(range2); + // break; + // case Instruction::Sub: + // resultRange = range1.sub(range2); + // break; + // case Instruction::Mul: + // resultRange = range1.mul(range2); + // break; + // case Instruction::UDiv: + // resultRange = range1.udiv(range2); + // break; + // case Instruction::SDiv: + // resultRange = range1.sdiv(range2); + // break; + // case Instruction::URem: + // resultRange = range1.urem(range2); + // break; + // case Instruction::SRem: + // resultRange = range1.srem(range2); + // break; + // // 处理其他运算符,如位移、位操作等 + // default: + // resultRange = Range(Min, Max, Unknown); + // break; + // } + + // 存储结果范围 + valueRanges[binOp] = resultRange; +} + +// 处理函数调用指令 +void +handleCallInst(CallInst * callInst) +{ + // 处理函数调用的范围分析逻辑 +} + +// 获取某个值的范围 +std::pair +getValueRange(Value * val) +{ + if (isa(val)) + { + auto cInt = dyn_cast(val); + double v = cInt->getSExtValue(); + return std::make_pair(v, v); + } + else if (valueRanges.find(val) != valueRanges.end()) + { + Range range = valueRanges[val]; + return std::make_pair(range.getLower().getSExtValue(), range.getUpper().getSExtValue()); + } + else + { + return std::make_pair(-INFINITY, INFINITY); + } +} + +// 加法的范围分析 +std::pair +analyzeAddition(std::pair range1, std::pair range2) +{ + return std::make_pair(range1.first + range2.first, range1.second + range2.second); +} + +// 减法的范围分析 +std::pair +analyzeSubtraction(std::pair range1, std::pair range2) +{ + return std::make_pair(range1.first - range2.second, range1.second - range2.first); +} + +// 乘法的范围分析 +std::pair +analyzeMultiplication(std::pair range1, std::pair range2) +{ + double lz = std::min({range1.first * range2.first, range1.first * range2.second, range1.second * range2.first, range1.second * range2.second}); + double uz = std::max({range1.first * range2.first, range1.first * range2.second, range1.second * range2.first, range1.second * range2.second}); + return std::make_pair(lz, uz); +} + +// 除法的范围分析 +std::pair +analyzeDivision(std::pair range1, std::pair range2) +{ + if (range2.first > 0 || range2.second < 0) + { + double lz = std::min({range1.first / range2.first, range1.first / range2.second, range1.second / range2.first, range1.second / range2.second}); + double uz = std::max({range1.first / range2.first, range1.first / range2.second, range1.second / range2.first, range1.second / range2.second}); + return std::make_pair(lz, uz); + } + // 处理除零的情况 + return std::make_pair(-INFINITY, INFINITY); +} + +// 打印变量名 +// void printVarName(const Value *V, raw_ostream &OS) { +// if (const Argument *A = dyn_cast(V)) { +// OS << A->getParent()->getName() << "." << A->getName(); +// } else if (const Instruction *I = dyn_cast(V)) { +// OS << I->getParent()->getParent()->getName() << "." +// << I->getParent()->getName() << "." << I->getName(); +// } else { +// OS << V->getName(); +// } +//} + +// SymbInterval 打印函数 +// void printSymbInterval(raw_ostream &OS, const SymbInterval &SI) { +// switch (SI.getOperation()) { +// case ICmpInst::ICMP_EQ: // equal +// OS << "[lb("; +// printVarName(SI.getBound(), OS); +// OS << "), ub("; +// printVarName(SI.getBound(), OS); +// OS << ")]"; +// break; +// case ICmpInst::ICMP_SLE: // sign less or equal +// OS << "[-inf, ub("; +// printVarName(SI.getBound(), OS); +// OS << ")]"; +// break; +// case ICmpInst::ICMP_SLT: // sign less than +// OS << "[-inf, ub("; +// printVarName(SI.getBound(), OS); +// OS << ") - 1]"; +// break; +// case ICmpInst::ICMP_SGE: // sign greater or equal +// OS << "[lb("; +// printVarName(SI.getBound(), OS); +// OS << "), +inf]"; +// break; +// case ICmpInst::ICMP_SGT: // sign greater than +// OS << "[lb("; +// printVarName(SI.getBound(), OS); +// OS << " - 1), +inf]"; +// break; +// default: +// OS << "Unknown Instruction.\n"; +// } +//} + +// 主方法:运行范围分析 +void +runRangeAnalysis(Function & F) +{ + initializeAnalysis(F); + + // 获取函数的最大位宽 + unsigned maxBitWidth = getMaxBitWidth(F); + + // 更新常量整数值 + updateConstantIntegers(maxBitWidth); + + for (auto & BB : F) + { + for (auto & I : BB) + { + if (isValidInstruction(&I)) + { + analyzeInstruction(I); + } + } + } + finalizeAnalysis(F); +} + +class RangeAnalysisPass : public PassInfoMixin { + public: + PreservedAnalyses + run(Function & F, FunctionAnalysisManager & FAM) + { + runRangeAnalysis(F); + return PreservedAnalyses::all(); + } +}; + +// 注册新的 Pass 机制 +extern "C" ::llvm::PassPluginLibraryInfo +llvmGetPassPluginInfo() +{ + return {LLVM_PLUGIN_API_VERSION, "RangeAnalysisPass", LLVM_VERSION_STRING, + [](PassBuilder & PB) { + PB.registerPipelineParsingCallback( + [](StringRef Name, FunctionPassManager & FPM, + ArrayRef) { + if (Name == "range-analysis") + { + FPM.addPass(RangeAnalysisPass()); + return true; + } + return false; + }); + }}; +} + +} // namespace + +// end of anonymous namespace diff --git a/src/newton/myRangeAnalysis.h b/src/newton/myRangeAnalysis.h new file mode 100644 index 000000000..66de50819 --- /dev/null +++ b/src/newton/myRangeAnalysis.h @@ -0,0 +1,12 @@ +// +// Created by 13862 on 2024/8/1. +// + +#ifndef COSENSE_MYRANGEANALYSIS_H +#define COSENSE_MYRANGEANALYSIS_H + +#endif // COSENSE_MYRANGEANALYSIS_H + + + +void runRangeAnalysis(llvm::Module &module); diff --git a/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp b/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp index 8f72bd51e..fcd1a6651 100644 --- a/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp +++ b/src/newton/newton-irPass-LLVMIR-constantSubstitution.cpp @@ -34,6 +34,26 @@ using namespace llvm; extern "C" { + +static bool isFromQuantizedGlobal(Value *V) { + if (auto *loadInst = dyn_cast(V)) { + if (auto *gv = dyn_cast(loadInst->getPointerOperand())) { + if (gv->getName().contains("_quantized")) { + return true; + } + } + } + if (auto *inst = dyn_cast(V)) { + for (auto &op : inst->operands()) { + if (isFromQuantizedGlobal(op)) { + return true; + } + } + } + return false; +} + + /* * Steps of constantSubstitution: * 1. for each instruction (that is the case statement), get the range of current instruction from boundInfo @@ -132,6 +152,10 @@ constantSubstitution(State * N, BoundInfo * boundInfo, llvm::Function & llvmIrFu * */ if (fabs(lowerBound - upperBound) < DBL_EPSILON) { + if (isFromQuantizedGlobal(llvmIrInstruction)) { + break; + } + /* * check the type of instruction * */ diff --git a/src/newton/newton-irPass-LLVMIR-dequantization.h b/src/newton/newton-irPass-LLVMIR-dequantization.h new file mode 100644 index 000000000..a5e4e2166 --- /dev/null +++ b/src/newton/newton-irPass-LLVMIR-dequantization.h @@ -0,0 +1,26 @@ +// +// Created by 13862 on 2024/7/19. +// + + + +#include "newton-irPass-LLVMIR-rangeAnalysis.h" + +#ifdef __cplusplus +extern "C" +{ +#endif /* __cplusplus */ +extern std::vector functionsToErase; +extern std::vector globalsToErase; +void +irPassLLVMIRAutoDequantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); + + + +extern + void eraseOldFunctions(); +extern + void eraseOldGlobals(); +#ifdef __cplusplus +} /* extern "C" */ +#endif /* __cplusplus */ \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 81c4f9f3a..cb8edc9a4 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -1,39 +1,39 @@ /* - Authored 2022. Pei Mu. +Authored 2022. Pei Mu. - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are met: - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. + * Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. - * Redistributions in binary form must reproduce the above + * Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials - provided with the distribution. + provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written permission. THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, + INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER + CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT + LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN + ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. -*/ + */ #ifdef __cplusplus #include "newton-irPass-LLVMIR-rangeAnalysis.h" @@ -41,8 +41,11 @@ #include "newton-irPass-LLVMIR-constantSubstitution.h" #include "newton-irPass-LLVMIR-shrinkTypeByRange.h" #include "newton-irPass-LLVMIR-quantization.h" +#include "newton-irPass-LLVMIR-optimizeByRange.h" #include "newton-irPass-LLVMIR-memoryAlignment.h" #include "newton-irPass-LLVMIR-emitAssume.h" + + #endif /* __cplusplus */ #include @@ -66,9 +69,629 @@ #include "llvm/Support/raw_ostream.h" #include "llvm/Support/Path.h" -using namespace llvm; +#include "llvm/IR/Module.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Support/FileSystem.h" +#include "llvm/IR/Function.h" + +#include "config.h" + +#include +#include +#include + + using namespace llvm; +// #define FRAC_BASE (1 << maxPrecisionBits) +#define FRAC_BASE (1 << MAX_PRECISION_BITS) + +void +checkOverflow(State * N, BoundInfo * boundInfo, int FRAC_Q) +{ + int maxVal, minVal; + if (BIT_WIDTH == 16) + { + maxVal = INT16_MAX; + minVal = INT16_MIN; + } + else if (BIT_WIDTH == 32) + { + maxVal = INT32_MAX; + minVal = INT32_MIN; + } + else + { + flexprint(N->Fe, N->Fm, N->Fperr, "Unsupported BIT_WIDTH: %d\n", BIT_WIDTH); + return; + } + + for (const auto & entry : boundInfo->virtualRegisterRange) + { + double scaledMin = entry.second.first * FRAC_BASE; + double scaledMax = entry.second.second * FRAC_BASE; + + std::string instStr = "unknown"; + if (Instruction * inst = dyn_cast(entry.first)) + { + instStr = inst->getOpcodeName(); + } + + if ((scaledMin > maxVal && scaledMax > maxVal) || (scaledMin < minVal && scaledMax < minVal)) + { + flexprint(N->Fe, N->Fm, N->Fperr, + "Definite overflow detected: %s range [%f, %f] when scaled by 2^%d is completely outside int%d bounds\n", + instStr.c_str(), entry.second.first, entry.second.second, + FRAC_Q, BIT_WIDTH); + } + else if (scaledMax > maxVal || scaledMin < minVal) + { + flexprint(N->Fe, N->Fm, N->Fperr, + "Possible overflow detected: %s range [%f, %f] when scaled by 2^%d partially exceeds int%d bounds\n", + instStr.c_str(), entry.second.first, entry.second.second, + FRAC_Q, BIT_WIDTH); + } + } +} + +void +autoWhitelistFunctions(Module &Mod, std::set &whitelist) +{ + for (Function &F : Mod) + { + if (F.isDeclaration()) continue; + + bool hasSensorParams = false; + + for (auto &Arg : F.args()) + { + if (Arg.hasName()) + { + std::string argName = Arg.getName().str(); + // 这里你可以加更丰富的匹配,比如查 typedef 类型名(需要调试信息) + if (argName.find("bmx055") != std::string::npos) + { + hasSensorParams = true; + break; + } + } + } + + if (hasSensorParams) + { + llvm::errs() << "Auto-Whitelisting function based on sensor params: " << F.getName() << "\n"; + whitelist.insert(F.getName().str()); + } + } +} + + + +// #define IS_POINTER 1 +std::set whitelist = { + "MadgwickAHRSupdate", + "MahonyAHRSupdate", + "sensfusion6UpdateQImpl", + "matrixMul", + "pzero", + "qzero", + "pone", + "qone", + "__ieee754_exp"}; + + +void eraseUnusedConstant(Module &M) { + std::set quantizedBaseNames; + for (auto &GV : M.globals()) { + if (GV.getName().endswith("_quantized")) { + std::string baseName = GV.getName().str().substr(0, GV.getName().size() - 10); // remove "_quantized" + quantizedBaseNames.insert(baseName); + } + } + std::vector toDelete; + for (auto &GV : M.globals()) { + std::string name = GV.getName().str(); + + if (GV.use_empty() && quantizedBaseNames.count(name)) { + toDelete.push_back(&GV); + } + if (GV.use_empty() && name.size() > 10 && name.substr(name.size() - 10) == "_quantized") { + toDelete.push_back(&GV); + } + } + for (auto *GV : toDelete) { + GV->eraseFromParent(); + } +} + + + + +void +handleGlobalStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) +{ + auto * pointerOperand = storeInst->getPointerOperand(); + + // Ensure the operation is on a global variable + if (auto * quantizedGlobalVar = dyn_cast(pointerOperand)) + { + llvm::errs() << "Processing quantized global variable: " << quantizedGlobalVar->getName() << "\n"; + + // Identify the corresponding original global variable (e.g., remove "_quantized" suffix) + std::string originalName = quantizedGlobalVar->getName().str(); + if (originalName.size() > 10 && originalName.compare(originalName.size() - 10, 10, "_quantized") == 0) + { + originalName = originalName.substr(0, originalName.size() - 10); + } + else + { + llvm::errs() << "Skipping: No matching original global for " << quantizedGlobalVar->getName() << "\n"; + return; + } + + // Find the original global variable + GlobalVariable * originalGlobalVar = quantizedGlobalVar->getParent()->getNamedGlobal(originalName); + if (!originalGlobalVar || !originalGlobalVar->getType()->getElementType()->isFloatingPointTy()) + { + llvm::errs() << "Skipping: Original global variable not found or not floating-point: " << originalName << "\n"; + return; + } + + llvm::errs() << "Found corresponding original global variable: " << originalGlobalVar->getName() << "\n"; + + // Check if the previous instruction is `trunc` + Instruction * prevInst = storeInst->getPrevNode(); + if (!prevInst || !isa(prevInst)) + { + llvm::errs() << "Skipping: Previous instruction is not trunc.\n"; + return; + } + + // Load the integer value from the quantized global variable + auto * loadInst = Builder.CreateLoad(quantizedGlobalVar->getType()->getPointerElementType(), quantizedGlobalVar); + + // Convert the integer value to a floating-point value + Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); + + // Perform dequantization + Value * dequantizedValue = Builder.CreateFMul( + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE)); + + // Store the dequantized floating-point value back into the original global variable + Builder.CreateStore(dequantizedValue, originalGlobalVar); + + llvm::errs() << "Dequantized and stored value for original global variable: " << originalGlobalVar->getName() << "\n"; + } + else + { + llvm::errs() << "Pointer operand is not a global variable. Skipping.\n"; + } +} + +void +handlePointerStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) +{ + auto * pointerOperand = storeInst->getPointerOperand(); + + if (!pointerOperand->getType()->getPointerElementType()->isIntegerTy(BIT_WIDTH)) + { + llvm::errs() << "Pointer operand type is not an integer of expected bit width.\n"; + return; + } + + auto * loadInst = Builder.CreateLoad(pointerOperand->getType()->getPointerElementType(), pointerOperand); + if (isa(loadInst->getPointerOperand())) + { + llvm::errs() << "Skipping StoreInst due to global variable in load operand.\n"; + return; + } + + Value * convertedFloat = Builder.CreateSIToFP(loadInst, Type::getFloatTy(storeInst->getContext())); + Value * dividedValue = Builder.CreateFMul( + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE)); + + if (auto * bitcastInst = dyn_cast(pointerOperand)) + { + Value * finalStorePtr = nullptr; + bool isValidSource = false; + llvm::errs() << "BIT_WIDTH: " << BIT_WIDTH << "\n"; + // Determine the final store pointer based on bit width + switch (BIT_WIDTH) + + { + case 16: + if (bitcastInst->getSrcTy()->getPointerElementType()->isIntegerTy(32)) + { + auto * i32Ptr = bitcastInst->getOperand(0); + if (auto * floatBitcast = dyn_cast(i32Ptr)) + { + if (floatBitcast->getSrcTy()->getPointerElementType()->isFloatTy()) + { + finalStorePtr = floatBitcast->getOperand(0); // Original float* + isValidSource = true; + } + } + } + break; + + case 32: + if (bitcastInst->getSrcTy()->getPointerElementType()->isFloatTy()) + { + finalStorePtr = bitcastInst->getOperand(0); // Original float* + isValidSource = true; + } + break; + + default: + llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; + return; + } + + if (isValidSource && finalStorePtr) + { + Builder.CreateStore(dividedValue, finalStorePtr); + llvm::errs() << "Dequantized and stored value for pointer.\n"; + } + else + { + llvm::errs() << "Invalid source for StoreInst: " << *storeInst << "\n"; + } + } +} + +void +handleMatrixStore(StoreInst * storeInst, IRBuilder<> & Builder, int maxPrecisionBits) +{ + Value * valueOperand = storeInst->getValueOperand(); + Value * pointerOperand = storeInst->getPointerOperand(); + + // Ensure the stored value is an integer and the destination is a float pointer + Type * valueType = valueOperand->getType(); + Type * pointerElementType = pointerOperand->getType()->getPointerElementType(); + + if (valueType->isIntegerTy() && pointerElementType->isFloatingPointTy()) + { + llvm::errs() << "Processing matrix store (quantized to dequantized): " << *storeInst << "\n"; + + // Convert integer value to floating-point (dequantization step 1) + llvm::errs() << "Converting integer to float: " << *valueOperand << "\n"; + Value * convertedFloat = Builder.CreateSIToFP(valueOperand, Type::getFloatTy(storeInst->getContext()), storeInst->getName() + ".dequantized"); + + // Perform dequantization by multiplying by (1 / fracBase) + Value * dequantizedValue = Builder.CreateFMul( + convertedFloat, ConstantFP::get(Type::getFloatTy(storeInst->getContext()), 1.0 / FRAC_BASE), storeInst->getName() + ".scaled_back"); + + // Store the dequantized floating-point value back to the original float memory location + Builder.CreateStore(dequantizedValue, pointerOperand); + + llvm::errs() << "Dequantized and stored float value at: " << *pointerOperand << "\n"; + + // Remove the original store instruction + storeInst->eraseFromParent(); + } + else + { + llvm::errs() << "Skipping store: Not storing i32 into float*.\n"; + } +} + +void +handleReturnValue(ReturnInst * retInst, int maxPrecisionBits) +{ + if (!retInst->getReturnValue()) + return; + + Value * retVal = retInst->getReturnValue(); + + if (!retVal->getType()->isIntegerTy()) + { + errs() << "Return value is not integer type, skipping dequantization.\n"; + return; + } + + IRBuilder<> Builder(retInst); + Type * targetType = Type::getDoubleTy(retInst->getContext()); + + Value * fpVal = Builder.CreateSIToFP(retVal, targetType); + + llvm::Constant * oneDivFrac = llvm::ConstantFP::get(targetType, 1.0 / FRAC_BASE); + + Value * dequantizedVal = Builder.CreateFMul(fpVal, oneDivFrac); + ReturnInst * newRet = ReturnInst::Create(retInst->getContext(), dequantizedVal, retInst); + + retInst->eraseFromParent(); + + errs() << "Replaced return with dequantized value: " << *newRet << "\n"; +} + +void +dequantizeResults(StoreInst * storeInst, Function & F, int maxPrecisionBits) +{ + IRBuilder<> Builder(storeInst->getNextNode()); + llvm::errs() << "Processing StoreInst in function: " << F.getName() << " | Store instruction: " << *storeInst << "\n"; + +#if IS_MATRIX + handleMatrixStore(storeInst, Builder, maxPrecisionBits); +#elif IS_POINTER + llvm::errs() << "Handling pointer store.\n"; + handlePointerStore(storeInst, Builder, maxPrecisionBits); + +#else + handleGlobalStore(storeInst, Builder, maxPrecisionBits); +#endif + +} + +//////////////////////////////////////////////////////////////////////////////////////////////////////////////////////// + +bool enableAutoQuantization = false; + +void +detectFloatingPointOps(Module & Mod) +{ + bool hasFloatOps = false; + std::map floatOpCounts; // Map to store the count of floating-point operations per function + + for (auto & F : Mod) + { + int functionFloatOpCount = 0; // Counter for floating-point operations in the current function + + // Analyze function parameters + int paramCount = 0; + int returnCount = 0; + std::vector paramTypes; // To store parameter types + for (auto & Arg : F.args()) + { + paramCount++; + if (Arg.getType()->isPointerTy()) + { + paramTypes.push_back("pointer"); + } + else if (Arg.getType()->isFloatingPointTy()) + { + paramTypes.push_back("floating-point"); + } + else if (Arg.getType()->isIntegerTy()) + { + paramTypes.push_back("integer"); + } + else + { + paramTypes.push_back("unknown"); + } + } + + // Analyze function return type + std::string returnType = "void"; // Default return type + if (!F.getReturnType()->isVoidTy()) + { + if (F.getReturnType()->isPointerTy()) + { + returnType = "pointer"; + } + else if (F.getReturnType()->isFloatingPointTy()) + { + returnType = "floating-point"; + } + else if (F.getReturnType()->isIntegerTy()) + { + returnType = "integer"; + } + else + { + returnType = "unknown"; + } + } + + for (auto & BB : F) + { + for (auto & I : BB) + { + // Check if the instruction is a floating-point operation + if (I.getOpcode() == Instruction::FAdd || + I.getOpcode() == Instruction::FMul || + I.getOpcode() == Instruction::FSub || + I.getOpcode() == Instruction::FDiv) + { + hasFloatOps = true; + functionFloatOpCount++; + } + + // Check if the instruction is a return + if (isa(I)) + { + returnCount++; + } + } + } + + // Store the count for this function + if (functionFloatOpCount > 0) + { + floatOpCounts[F.getName().str()] = functionFloatOpCount; + } + + // Output function details + llvm::errs() << "Function: " << F.getName() << "\n"; + llvm::errs() << " Return Type: " << returnType << "\n"; + llvm::errs() << " Parameter Count: " << paramCount << "\n"; + llvm::errs() << " Parameter Types: "; + for (const auto & type : paramTypes) + { + llvm::errs() << type << " "; + } + llvm::errs() << "\n"; + // f + } + + // Output the results + if (hasFloatOps) + { + llvm::errs() << "Floating-point operations detected in the module.\n"; + for (const auto & entry : floatOpCounts) + { + llvm::errs() << "Function: " << entry.first + << " - Floating-point operations: " << entry.second << "\n"; + } + llvm::errs() << "Enabling Auto-Quantization.\n"; + enableAutoQuantization = true; + } + else + { + llvm::errs() << "No floating-point operations detected. Skipping Auto-Quantization.\n"; + } +} + +void checkFPUAvailability(Module &Mod) +{ + bool hasFPU = false; + std::set detectedFeatures; + + // 1. Check target-features from function attributes + for (auto &F : Mod) + { + if (F.hasFnAttribute("target-features")) + { + std::string features = F.getFnAttribute("target-features").getValueAsString().str(); + detectedFeatures.insert(features); + + // x86 FPU features + if (features.find("+sse") != std::string::npos || + features.find("+sse2") != std::string::npos || + features.find("+avx") != std::string::npos || + features.find("+x87") != std::string::npos || + features.find("+fma") != std::string::npos) + { + hasFPU = true; + } + + // ARM FPU features + if (features.find("+vfp") != std::string::npos || + features.find("+neon") != std::string::npos || + features.find("+fp-armv8") != std::string::npos || + features.find("+fp16") != std::string::npos) + { + hasFPU = true; + } + } + } + + // 2. Supplementary check: parse target triple for architecture + std::string triple = Mod.getTargetTriple(); + llvm::errs() << "Target Triple: " << triple << "\n"; + + if (triple.find("armv7e-m") != std::string::npos || + triple.find("armv8-m.main") != std::string::npos || + triple.find("aarch64") != std::string::npos) + { + hasFPU = true; + llvm::errs() << "Triple indicates hardware FPU support.\n"; + } + else if (triple.find("armv6-m") != std::string::npos || + triple.find("armv7-m") != std::string::npos) + { + hasFPU = false; + llvm::errs() << "Triple indicates no FPU support.\n"; + } + else + { + llvm::errs() << "Triple is not recognized. Assuming conservative FPU support = false.\n"; + } + + // 3. (Removed hardcoded CPU name database for better generality) + + // Summary + if (!detectedFeatures.empty()) + { + llvm::errs() << "Target Features: "; + for (const auto &feature : detectedFeatures) + { + llvm::errs() << feature << " "; + } + llvm::errs() << "\n"; + } + + if (hasFPU) + { + llvm::errs() << "FPU detected (from features and/or triple).\n"; + } + else + { + llvm::errs() << "No FPU detected. Enabling Auto-Quantization.\n"; + enableAutoQuantization = true; + } +} + + + +/////////////////////////////////////////////////////////////////////////////////////////////////////////////// -extern "C" { +// Process functions that are whitelisted for dequantization + +void +processWhitelistedFunctions(Module & module, const std::set & whitelist, int maxPrecisionBits) +{ + for (Function & F : module) + { + if (whitelist.find(F.getName().str()) != whitelist.end()) + { + llvm::errs() << "Found whitelisted function: " << F.getName() << "\n"; + std::vector retWorkList; + for (BasicBlock & BB : F) + { + for (Instruction & I : BB) + { + if (ReturnInst * retInst = dyn_cast(&I)) + { + if (retInst->getReturnValue() && retInst->getReturnValue()->getType()->isIntegerTy()) + { + retWorkList.push_back(retInst); + } + } + } + } + for (ReturnInst * retInst : retWorkList) + { + handleReturnValue(retInst, maxPrecisionBits); + } + + for (BasicBlock & BB : F) + { + for (Instruction & I : BB) + { + // llvm::errs() << "Processing instruction: " << I << "\n"; + if (auto * storeInst = dyn_cast(&I)) + { + llvm::errs() << "Found valid StoreInst.\n"; + dequantizeResults(storeInst, F, maxPrecisionBits); + } + } + } + } + } +} + + + +// Function to save the IR of a module to a file +void +saveModuleIR(llvm::Module & M, const std::string & fileName) +{ + std::error_code EC; + llvm::raw_fd_ostream file(fileName, EC, llvm::sys::fs::OF_Text); + if (EC) + { + llvm::errs() << "Error opening file " << fileName << " for writing: " << EC.message() << "\n"; + return; + } + M.print(file, nullptr); + llvm::errs() << "IR saved to " << fileName << "\n"; + file.close(); +} + +double +computeResolution(Modality * mod) +{ + return (mod->rangeUpperBound - mod->rangeLowerBound) / (1 << mod->precisionBits); +} void dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) @@ -86,6 +709,43 @@ dumpIR(State * N, std::string fileSuffix, const std::unique_ptr & Mod) dumpedFile.close(); } +// void dumpIR(State *N, std::string fileSuffix, const std::unique_ptr &Mod) { +// StringRef filePath(N->llvmIR); +// std::string dirPath = std::string(sys::path::parent_path(filePath)) + "/"; +// std::string fileName = std::string(sys::path::stem(filePath)) + "_" + fileSuffix + ".bc"; +// std::string filePathStr = dirPath + fileName; +// filePath = StringRef(filePathStr); +// +// // 输出调试信息:目标 IR 文件路径 +// flexprint(N->Fe, N->Fm, N->Fpinfo, "Dump IR of: %s\n", filePath.str().c_str()); +// llvm::errs() << "DumpIR: File path = " << filePath.str() << "\n"; +// +// // 使用 errorCode 检查创建文件是否成功 +// std::error_code errorCode; +// raw_fd_ostream dumpedFile(filePath, errorCode); +// if (errorCode) { +// // 输出错误信息 +// flexprint(N->Fe, N->Fm, N->Fpinfo, "Error opening file %s: %s\n", filePath.str().c_str(), errorCode.message().c_str()); +// llvm::errs() << "DumpIR: Failed to open file: " << filePath.str() << " (" << errorCode.message() << ")\n"; +// return; +// } else { +// llvm::errs() << "DumpIR: File opened successfully.\n"; +// } +// +// // 写入 IR 并检查写入过程 +// WriteBitcodeToFile(*Mod, dumpedFile); +// dumpedFile.flush(); +// if (dumpedFile.has_error()) { +// llvm::errs() << "DumpIR: Error during WriteBitcodeToFile: " << dumpedFile.error().message() << "\n"; +// flexprint(N->Fe, N->Fm, N->Fpinfo, "Error during WriteBitcodeToFile: %s\n", dumpedFile.error().message().c_str()); +// } else { +// llvm::errs() << "DumpIR: WriteBitcodeToFile completed successfully.\n"; +// } +// +// dumpedFile.close(); +// llvm::errs() << "DumpIR: File closed.\n"; +// } + void mergeBoundInfo(BoundInfo * dst, const BoundInfo * src) { @@ -207,9 +867,11 @@ overloadFunc(std::unique_ptr & Mod, std::map & void irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverload, bool enableBuiltinAssume) { + llvm::errs() << "Entering irPassLLVMIROptimizeByRange\n"; if (N->llvmIR == nullptr) { flexprint(N->Fe, N->Fm, N->Fperr, "Please specify the LLVM IR input file\n"); + llvm::errs() << "Error: llvmIR is nullptr\n"; fatal(N, Esanity); } @@ -219,9 +881,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl if (!Mod) { flexprint(N->Fe, N->Fm, N->Fperr, "Error: Couldn't parse IR file."); + llvm::errs() << "Error: Couldn't parse IR file: " << N->llvmIR << "\n"; fatal(N, Esanity); } - + llvm::errs() << "Module successfully parsed: " << N->llvmIR << "\n"; auto globalBoundInfo = new BoundInfo(); std::map funcBoundInfo; @@ -237,9 +900,94 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeLowerBound: %f\n", currentModality->rangeLowerBound); flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\trangeUpperBound: %f\n", currentModality->rangeUpperBound); typeRange.emplace(currentModality->identifier, std::make_pair(currentModality->rangeLowerBound, currentModality->rangeUpperBound)); + double resolution = computeResolution(currentModality); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tresolution: %.10f\n", resolution); } } + /* + *TODO get sensor info, we only concern the id and precisionBits here + * */ + std::map typePrecisionBits; + if (N->sensorList != NULL) + { + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + { + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tModality: %s\n", currentModality->identifier); + flexprint(N->Fe, N->Fm, N->Fpinfo, "\t\tprecisionBits: %d\n", currentModality->precisionBits); + typePrecisionBits.emplace(currentModality->identifier, currentModality->precisionBits); + + // resolution + } + } + + // int maxPrecisionBits = 0; + // int maxPrecisionBits = MAX_PRECISION_BITS; + // for (auto & typePrecisionBit : typePrecisionBits) + // { + // if (typePrecisionBit.second > maxPrecisionBits) + // { + // maxPrecisionBits = typePrecisionBit.second; + // } + // } + // + + // int maxPrecisionBits = 0; + // for (auto & typePrecisionBit : typePrecisionBits) + // { + // if (typePrecisionBit.second > maxPrecisionBits) + // { + // maxPrecisionBits = typePrecisionBit.second; + // } + // } + // int MAX_PRECISION_BITS = maxPrecisionBits; + + + + int maxPrecisionBits = MAX_PRECISION_BITS; + + /** + * Precision Analysis + */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "Precision Analysis"); + + double minResolution = 0.0; + bool isFirstSensor = true; + + for (Modality * currentModality = N->sensorList->modalityList; currentModality != NULL; currentModality = currentModality->next) + { + // Calculate resolution + double resolution = (currentModality->rangeUpperBound - currentModality->rangeLowerBound) / + (1 << currentModality->precisionBits); + + // Store and print + currentModality->resolution = resolution; + + // Initialize or compare for minimum + if (isFirstSensor) + { + minResolution = resolution; + isFirstSensor = false; + } + else if (resolution < minResolution) + { + minResolution = resolution; + } + } + + double fracQ_exact = -log2(minResolution) - 1.0; + int fracQ = (int)ceil(fracQ_exact); + + flexprint(N->Fe, N->Fm, N->Fpinfo, "Minimum resolution across all sensors: %f\n", minResolution); + flexprint(N->Fe, N->Fm, N->Fpinfo, "Required FRAC_Q: ceil(-log2(minResolution) - 1) = ceil(%f) = %d\n", + fracQ_exact, fracQ); + + /** + * Config + */ + // int BIT_WIDTH = 32; + // maxPrecisionBits = 16; + /* * get const global variables * */ @@ -248,8 +996,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl { if (!globalVar.hasInitializer()) { + llvm::errs() << "Global variable " << globalVar.getName() << " has no initializer\n"; continue; } + llvm::errs() << "Processing global variable: " << globalVar.getName() << "\n"; auto constValue = globalVar.getInitializer(); if (ConstantFP * constFp = llvm::dyn_cast(constValue)) { @@ -312,13 +1062,61 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } + detectFloatingPointOps(*Mod); + checkFPUAvailability(*Mod); + + /* + * analyze the range of all local variables in each function + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + std::map callerMap; + callerMap.clear(); + funcBoundInfo.clear(); + bool useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } + + /** + * Check for potential overflows + */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); + for (auto & funcPair : funcBoundInfo) + { + checkOverflow(N, funcPair.second, maxPrecisionBits); + } + + + flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + shrinkType(N, boundInfoIt->second, mi); + } + // else + // { + // memoryAlignment + // } + } + if (enableQuantization) { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + llvm::errs() << "Auto quantization enabled\n"; std::vector functionsToInsert; for (auto & mi : *Mod) { - irPassLLVMIRAutoQuantization(N, mi, functionsToInsert); + llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + + irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); } for (auto mi : functionsToInsert) { @@ -327,146 +1125,160 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } -// /* -// * analyze the range of all local variables in each function -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// std::map callerMap; -// callerMap.clear(); -// funcBoundInfo.clear(); -// bool useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "shrink data type by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// shrinkType(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// memoryAlignment(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = true; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// /* -// * simplify the condition of each branch -// * */ -// flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// simplifyControlFlow(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// legacy::PassManager passManager; -// passManager.add(createCFGSimplificationPass()); -// passManager.add(createInstSimplifyLegacyPass()); -// passManager.add(createGlobalDCEPass()); -// passManager.run(*Mod); -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); -// callerMap.clear(); -// funcBoundInfo.clear(); -// useOverLoad = false; -// for (auto & mi : *Mod) -// { -// auto boundInfo = new BoundInfo(); -// mergeBoundInfo(boundInfo, globalBoundInfo); -// rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); -// funcBoundInfo.emplace(mi.getName().str(), boundInfo); -// std::vector calleeNames; -// collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); -// } -// -// flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); -// for (auto & mi : *Mod) -// { -// auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); -// if (boundInfoIt != funcBoundInfo.end()) -// { -// constantSubstitution(N, boundInfoIt->second, mi); -// } -// // else -// // { -// // assert(false); -// // } -// } -// -// /* -// * remove the functions that are optimized by passes. -// * */ -// if (useOverLoad) -// cleanFunctionMap(Mod, callerMap); -// -// if (useOverLoad) -// overloadFunc(Mod, callerMap); + /** + * Check for potential overflows + */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "checking for potential overflows\n"); + for (auto & funcPair : funcBoundInfo) { + checkOverflow(N, funcPair.second, maxPrecisionBits); + } + + flexprint(N->Fe, N->Fm, N->Fpinfo, "memory alignment\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + memoryAlignment(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = true; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + + /* + * simplify the condition of each branch + * */ + flexprint(N->Fe, N->Fm, N->Fpinfo, "simplify control flow by range\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + simplifyControlFlow(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + + legacy::PassManager passManager; + passManager.add(createCFGSimplificationPass()); + passManager.add(createInstSimplifyLegacyPass()); + passManager.add(createGlobalDCEPass()); + passManager.run(*Mod); + + + + + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = false; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } + // + flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); + for (auto & mi : *Mod) + { + auto boundInfoIt = funcBoundInfo.find(mi.getName().str()); + if (boundInfoIt != funcBoundInfo.end()) + { + constantSubstitution(N, boundInfoIt->second, mi); + } + // else + // { + // assert(false); + // } + } + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + + + + // Finally, erase old functions + + // eraseOldFunctions(); + + // eraseOldGlobals(); + eraseUnusedConstant(*Mod); + +// eraseOldFunctions(); +// eraseOldFunctions(); + + + processWhitelistedFunctions(*Mod, whitelist, maxPrecisionBits); + +// eraseOldFunctions(); + eraseOldFunctions(*Mod); + + + + + const char * homeDir = getenv("HOME"); + if (!homeDir) + { + llvm::errs() << "Error: HOME environment variable not set.\n"; + return; + } + // Save the optimized IR to a file + // std::string fileName = std::string(homeDir) + "/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"; + // saveModuleIR(*Mod, fileName); + // Save the optimized IR to a file + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MadgwickAHRS_output.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/MahonyAHRS_output.ll"); + saveModuleIR(*Mod, "/home/xyf/CoSense/applications/newton/llvm-ir/sensfusion6_output.ll"); /* * Dump BC file to a file. * */ dumpIR(N, "output", Mod); -} -} + llvm::errs() << "Exiting irPassLLVMIROptimizeByRange\n"; +} \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.h b/src/newton/newton-irPass-LLVMIR-optimizeByRange.h index 5d38eccbf..92a49ad09 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.h +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.h @@ -1,39 +1,39 @@ /* - Authored 2022. Pei Mu. +Authored 2022. Pei Mu. - All rights reserved. +All rights reserved. - Redistribution and use in source and binary forms, with or without +Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions - are met: - - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ +are met: + +* Redistributions of source code must retain the above +copyright notice, this list of conditions and the following +disclaimer. + + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. + + * Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. + +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE + COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, + BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; + LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. + */ #ifndef NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE #define NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE @@ -50,4 +50,4 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } /* extern "C" */ #endif /* __cplusplus */ -#endif /* NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE */ +#endif /* NEWTON_IR_PASS_LLVM_IR_OPTIMIZE_BY_RANGE */ \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index 687ba8c1d..bad060b5f 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -1,440 +1,2194 @@ -/* - Authored 2022. Pei Mu. - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. -*/ +#include "llvm/IR/IRBuilder.h" +#include "llvm/IR/Module.h" +#include "llvm/IR/Constant.h" +#include "llvm/IR/Constants.h" +#include "llvm/IR/GlobalVariable.h" +#include +#include +#include "newton-irPass-LLVMIR-quantization.h" +#include "llvm/Support/raw_ostream.h" +#include +#include "llvm/IR/Metadata.h" +#include "config.h" +#include +#include +using namespace llvm; + +unsigned int FRAC_Q; + +#define FRAC_BASE (1 << FRAC_Q) + + +llvm::Value * +performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned int FRAC_Q) +{ + llvm::Value * result = nullptr; + + switch (BIT_WIDTH) + { + case 16: + { + // Sign extend the 16-bit operands to 32-bit integers + llvm::Value * lhs32 = Builder.CreateSExt(lhs, llvm::Type::getInt32Ty(Builder.getContext())); + llvm::Value * rhs32 = Builder.CreateSExt(rhs, llvm::Type::getInt32Ty(Builder.getContext())); + + // Perform 32-bit multiplication + llvm::Value * mulResult32 = Builder.CreateNSWMul(lhs32, rhs32); + + // Right shift the result to simulate fixed-point division by FRAC_Q + // llvm::Value * divResult32 = Builder.CreateLShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult32 = Builder.CreateAShr(mulResult32, llvm::ConstantInt::get(llvm::Type::getInt32Ty(Builder.getContext()), FRAC_Q)); + + // Truncate the result back to 16-bit integer + result = Builder.CreateTrunc(divResult32, llvm::Type::getInt16Ty(Builder.getContext())); + break; + } + case 32: + { + // Sign extend the 32-bit operands to 64-bit integers + llvm::Value * lhs64 = Builder.CreateSExt(lhs, llvm::Type::getInt64Ty(Builder.getContext())); + llvm::Value * rhs64 = Builder.CreateSExt(rhs, llvm::Type::getInt64Ty(Builder.getContext())); + + // Perform 64-bit multiplication + llvm::Value * mulResult64 = Builder.CreateNSWMul(lhs64, rhs64); + + // Right shift the result to simulate fixed-point division by FRAC_Q + // llvm::Value * divResult64 = Builder.CreateLShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + llvm::Value * divResult64 = Builder.CreateAShr(mulResult64, llvm::ConstantInt::get(llvm::Type::getInt64Ty(Builder.getContext()), FRAC_Q)); + + // Truncate the 64-bit result back to 32-bit integer + result = Builder.CreateTrunc(divResult64, llvm::Type::getInt32Ty(Builder.getContext())); + break; + + } + default: + { + llvm::errs() << "Unsupported BIT_WIDTH: " << BIT_WIDTH << "\n"; + break; + } + } + + return result; +} + +extern "C" { +// TODO : float version rsqrt +llvm::Function * +createFixSqrt(llvm::Module * irModule, Type * quantizedType, std::vector & functionsToInsert) +{ + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixSqrtFuncName = "fixsqrt"; + for (auto & function : *irModule) + { + if (function.getName() == fixSqrtFuncName) + { + llvm::errs() << "fixsqrt already exists\n"; + return &function; + } + } + + llvm::LLVMContext & context = irModule->getContext(); + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixSqrtFuncName, irModule); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(context, "entry", func); + llvm::IRBuilder<> builder(entryBB); + + llvm::Function::arg_iterator args = func->arg_begin(); + llvm::Value * x = &*args++; + + // Convert the fixed-point integer to a floating-point number for sqrt computation + llvm::Value * fp_x = builder.CreateSIToFP(x, llvm::Type::getFloatTy(context)); + + // Call sqrt on the floating-point value + llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fp_x}); + + // Convert the result back to a fixed-point integer + llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); + + // Perform a left shift to scale the result + llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); + + // Apply compensation if FRAC_Q is odd + llvm::Value * finalRes = shlRes; + if (FRAC_Q % 2 != 0) + { + llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562); + llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); + llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); + finalRes = builder.CreateFPToSI(compensated, quantizedType); + } + + builder.CreateRet(finalRes); + functionsToInsert.push_back(func); + llvm::errs() << "Created fixsqrt function: " << func->getName() << "\n"; + + return func; +} + + + +llvm::Function * +createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector & functionsToInsert) +{ + llvm::errs() << "Entering createFixRsqrt\n"; + + // Check if irModule is valid + if (!irModule) + { + llvm::errs() << "Error: irModule is nullptr\n"; + return nullptr; + } + + std::string fixrsqrtFuncName = "fixrsqrt"; + for (auto & function : *irModule) + { + if (function.getName() == fixrsqrtFuncName) + { + llvm::errs() << "fixrsqrt already exists\n"; + return &function; + } + } + + // Define the function type: int16_t/int32_t fixrsqrt(int16_t/int32_t x) + llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); + llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixrsqrtFuncName, irModule); + + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "", func); + llvm::IRBuilder<> builder(entryBB); + + // Get the function argument (x) + llvm::Function::arg_iterator args = func->arg_begin(); + llvm::Value * x = &*args++; + + llvm::Value *fpX = nullptr, *scaledFpX = nullptr, *approx = nullptr, *intApprox = nullptr, *shiftedX = nullptr, *result = nullptr; + + // Switch based on BIT_WIDTH + switch (BIT_WIDTH) + { + case 16: + { + // Step 1: Shift x to compute %1 (x >> 1) + shiftedX = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); + + // Step 2: Convert x to float and scale + llvm::Value * sextX = builder.CreateSExt(x, llvm::Type::getInt32Ty(irModule->getContext())); + fpX = builder.CreateSIToFP(sextX, llvm::Type::getFloatTy(irModule->getContext())); + scaledFpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + + // Step 3: Approximation using magic number + llvm::Value * bitcastFpX = builder.CreateBitCast(scaledFpX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * shiftedFpX = builder.CreateLShr(bitcastFpX, 1); +// llvm::Value * shiftedFpX = builder.CreateAShr(bitcastFpX, 1); + llvm::Value * magicNumber = llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df); + approx = builder.CreateSub(magicNumber, shiftedFpX); + llvm::Value * approxFp = builder.CreateBitCast(approx, llvm::Type::getFloatTy(irModule->getContext())); + llvm::Value * scaledApprox = builder.CreateFMul(approxFp, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)); + intApprox = builder.CreateFPToSI(scaledApprox, llvm::Type::getInt32Ty(irModule->getContext())); + + // Step 4: Newton-Raphson refinement + llvm::Value * sextShiftedX = builder.CreateSExt(shiftedX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * mul1 = builder.CreateMul(sextShiftedX, intApprox); + llvm::Value * mul1Shifted = builder.CreateLShr(mul1, FRAC_Q); +// llvm::Value * mul1Shifted = builder.CreateAShr(mul1, FRAC_Q); + + llvm::Value * mul2 = builder.CreateMul(mul1Shifted, intApprox); + // llvm::Value * mul2Shifted = builder.CreateLShr(mul2, FRAC_Q); + llvm::Value * mul2Shifted = builder.CreateAShr(mul2, FRAC_Q); + + int correctionValue = static_cast(1.5f * FRAC_BASE); + llvm::Value * correction = builder.CreateSub( + llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), correctionValue), + mul2Shifted); + llvm::Value * finalMul = builder.CreateMul(intApprox, correction); + llvm::Value * finalShifted = builder.CreateLShr(finalMul, FRAC_Q); +// llvm::Value * finalShifted = builder.CreateAShr(finalMul, FRAC_Q); + + // Step 5: Truncate the result back to i16 + result = builder.CreateTrunc(finalShifted, quantizedType); + break; + } + case 32: + default: + { + // Step 1: Shift x to compute %1 (x >> 1) +// llvm::Value * halfBase = builder.CreateLShr(x, llvm::ConstantInt::get(quantizedType, 1)); + llvm::Value * halfBase = builder.CreateAShr(x, llvm::ConstantInt::get(quantizedType, 1)); + + // Step 2: Convert x to floating-point and perform the initial approximation + fpX = builder.CreateSIToFP(x, llvm::Type::getFloatTy(irModule->getContext())); + fpX = builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), 1.0f / FRAC_BASE)); + + // llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); + llvm::Value * i = builder.CreateBitCast(fpX, llvm::Type::getInt32Ty(irModule->getContext())); + // i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateLShr(i, 1)); + i = builder.CreateNSWSub(llvm::ConstantInt::get(llvm::Type::getInt32Ty(irModule->getContext()), 0x5f3759df), builder.CreateAShr(i, 1)); + fpX = builder.CreateBitCast(i, llvm::Type::getFloatTy(irModule->getContext())); + + llvm::Value * int_y = builder.CreateFPToSI(builder.CreateFMul(fpX, llvm::ConstantFP::get(llvm::Type::getFloatTy(irModule->getContext()), FRAC_BASE)), quantizedType); + llvm::Value * mulfix1 = performFixedPointMul(builder, halfBase, int_y, FRAC_Q); + llvm::Value * mulfix2 = performFixedPointMul(builder, mulfix1, int_y, FRAC_Q); + llvm::Value * correction = builder.CreateSub(llvm::ConstantInt::get(quantizedType, static_cast(1.5f * FRAC_BASE)), mulfix2); + llvm::Value * final_y = performFixedPointMul(builder, int_y, correction, FRAC_Q); + + result = final_y; + break; + } + } + + // Return the result + builder.CreateRet(result); + functionsToInsert.emplace_back(func); + + return func; +} + +// A list of global variables to erase after processing +std::vector globalsToErase; +void +eraseOldGlobals() +{ + std::set uniqueGlobals(globalsToErase.begin(), globalsToErase.end()); + for (auto * global : uniqueGlobals) + { + if (global) + { + llvm::errs() << "Erasing old global variable: " << global->getName() << "\n"; + global->eraseFromParent(); + } + else + { + llvm::errs() << "Skipping null global variable\n"; + } + } + globalsToErase.clear(); +} + +// A list of functions to erase after processing +std::vector functionsToErase; + +// Function to actually erase functions after processing +void eraseOldFunctions(Module &M) { + llvm::errs() << "Entering eraseOldFunctions\n"; + + std::vector toErase; + for (auto *func : functionsToErase) { + if (!func) continue; + + if (M.getFunction(func->getName()) != func) { + llvm::errs() << "Skipping dangling or already removed function.\n"; + continue; + } + + toErase.push_back(func); + } + + functionsToErase.clear(); + for (Function *func : toErase) { + llvm::errs() << "Erasing old function: " << func->getName() << "\n"; + func->eraseFromParent(); + } + llvm::errs() << "Exiting eraseOldFunctions\n"; +} + + + + + +bool +isWhitelistedGlobal(const std::string & globalName) +{ + // Define the whitelist of global variables + // static const std::set whitelist = {"beta", "qw", "qx", "qy", "qz","zero","q0","q1","q2","q3"}; + // return whitelist.find(globalName) != whitelist.end(); + return true; +} + +//bool +//shouldProcessFunction(Function &F) +//{ +// if (F.isDeclaration()) +// return false; +// +// +// for (auto &BB : F) +// { +// for (auto &I : BB) +// { +// +// if (auto *DbgValue = dyn_cast(&I)) +// { +// Value *Val = DbgValue->getValue(); +// if (!Val) +// continue; +// if (auto *Arg = dyn_cast(Val)) +// { +// auto *DIVar = DbgValue->getVariable(); +// if (DIVar) +// { +// std::string typeName = DIVar->getType()->getName().str(); +// if (typeName.find("bmx055") != std::string::npos) +// { +// +// return true; +// } +// } +// } +// } +// } +// } +// +// return false; +//} + + + +bool +shouldProcessFunction(Function & F) +{ + // List of function names to process + static const std::set targetFunctions = { + "sensfusion6UpdateQImpl", + "MadgwickAHRSupdate", + "MadgwickAHRSupdateIMU", + "MahonyAHRSupdate", + "MahonyAHRSupdateIMU", + "matrixMul", + "matrixAdd", + "matrixSub", + "pzero", + "qzero", + "pone", + "qone", + "__ieee754_exp", + "twofft" + // "__ieee754_log", + }; + + // Check if the function name is in the set + return targetFunctions.find(F.getName().str()) != targetFunctions.end(); +} + +// Helper to create or retrieve the quantized global variable +GlobalVariable * +getOrCreateQuantizedGlobal(Module * module, GlobalVariable & globalVar, Type * quantizedType) +{ + std::string quantizedName = globalVar.getName().str() + "_quantized"; + + // Check if the quantized version already exists + if (GlobalVariable * existingGlobal = module->getNamedGlobal(quantizedName)) + { + llvm::errs() << "Quantized global already exists: " << quantizedName << "\n"; + return existingGlobal; + } + + // // Calculate initializer for the quantized global + // llvm::Constant *initializer = nullptr; + // if (llvm::Constant *init = globalVar.getInitializer()) { + // if (llvm::ConstantFP *constFp = llvm::dyn_cast(init)) { + // double value = constFp->getValueAPF().convertToDouble(); + // int64_t quantizedValue = static_cast(round((value * FRAC_BASE) + 0.5)); + // initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); + // } + // } + + // Calculate initializer for the quantized global + llvm::Constant * initializer = nullptr; + if (llvm::Constant * init = globalVar.getInitializer()) + { + if (llvm::ConstantFP * constFp = llvm::dyn_cast(init)) + { + double value = constFp->getValueAPF().convertToDouble(); + int64_t quantizedValue = static_cast(round((value * FRAC_BASE))); + initializer = llvm::ConstantInt::get(quantizedType, quantizedValue); + } + } + + // Create the quantized global variable + GlobalVariable * newGlobalVar = new GlobalVariable( + *module, + quantizedType, + globalVar.isConstant(), + globalVar.getLinkage(), + initializer, + quantizedName); + + // Set alignment based on bit width + switch (BIT_WIDTH) + { + case 16: + newGlobalVar->setAlignment(llvm::MaybeAlign(2)); + break; + case 32: + newGlobalVar->setAlignment(llvm::MaybeAlign(4)); + break; + default: + llvm::errs() << "Unsupported bit width: " << BIT_WIDTH << "\n"; + break; + } + + newGlobalVar->setDSOLocal(true); + llvm::errs() << "Created quantized global: " << quantizedName << "\n"; + return newGlobalVar; +} + + +// Helper to create or retrieve the quantized internal constant +llvm::GlobalVariable * +getOrCreateQuantizedConstant(llvm::Module *module, + llvm::GlobalVariable &origConst, + llvm::Type *quantizedType) +{ + std::string quantizedName = origConst.getName().str() + "_quantized"; + + + if (llvm::GlobalVariable *existingConst = module->getNamedGlobal(quantizedName)) + { + llvm::errs() << "Quantized internal constant already exists: " << quantizedName << "\n"; + return existingConst; + } + + + if (!origConst.hasInitializer()) + { + llvm::errs() << "Skipping quantization: constant has no initializer: " << origConst.getName() << "\n"; + return nullptr; + } + + llvm::Constant *init = origConst.getInitializer(); + llvm::ArrayType *origArrayType = llvm::dyn_cast(origConst.getType()->getElementType()); + + if (!origArrayType || + (!origArrayType->getArrayElementType()->isFloatTy() && + !origArrayType->getArrayElementType()->isDoubleTy())) + { + llvm::errs() << "Skipping non-float internal constant: " << origConst.getName() << "\n"; + return nullptr; + } + + llvm::errs() << "Quantizing internal constant: " << origConst.getName() << "\n"; + + std::vector quantizedValues; + + llvm::Type *intType = llvm::Type::getInt32Ty(module->getContext()); + + const double epsilon = 1e-9; + + for (unsigned i = 0; i < origArrayType->getNumElements(); ++i) + { + llvm::ConstantFP *fpVal = llvm::dyn_cast(init->getAggregateElement(i)); + if (!fpVal) + continue; + + double floatValue = fpVal->getValueAPF().convertToDouble(); + int quantizedValue = 0; + if (fabs(floatValue) < epsilon) + { + llvm::errs() << "Quantizing value below epsilon: " << floatValue << "\n"; + quantizedValue = 0; + } + else + { + quantizedValue = static_cast(round(floatValue * FRAC_BASE)); + } + quantizedValues.push_back(llvm::ConstantInt::get(intType, quantizedValue)); + } + + llvm::ArrayType *quantizedArrayType = llvm::ArrayType::get(intType, quantizedValues.size()); + llvm::Constant *newInit = llvm::ConstantArray::get(quantizedArrayType, quantizedValues); + + llvm::GlobalVariable *quantizedConst = new llvm::GlobalVariable( + *module, + quantizedArrayType, + true, + origConst.getLinkage(), + newInit, + quantizedName); + + quantizedConst->setAlignment(llvm::MaybeAlign(4)); + quantizedConst->setDSOLocal(true); + llvm::errs() << "Created quantized internal constant: " << quantizedName << "\n"; + + return quantizedConst; +} + + +// Helper to replace uses of the original global in whitelisted functions +void +replaceUsesInWhitelistedFunctions(GlobalVariable & originalGlobal, GlobalVariable & quantizedGlobal) +{ + for (auto it = originalGlobal.use_begin(), end = originalGlobal.use_end(); it != end;) + { + Use & use = *it++; + if (Instruction * inst = dyn_cast(use.getUser())) + { + Function * parentFunc = inst->getFunction(); + if (parentFunc && shouldProcessFunction(*parentFunc)) + { + llvm::errs() << "Replacing use of " << originalGlobal.getName() + << " in function: " << parentFunc->getName() << "\n"; + use.set(&quantizedGlobal); + } + } + } +} +void +updateGlobalVariables(Module * module, Type * quantizedType) +{ + llvm::errs() << "Updating global variables\n"; + + for (GlobalVariable & globalVar : module->globals()) + { + std::string globalName = globalVar.getName().str(); + + // Skip non-whitelisted globals + if (!isWhitelistedGlobal(globalName)) + { + llvm::errs() << "Skipping global variable not in whitelist: " << globalName << "\n"; + continue; + } + + // Process only float or double global variables + if (!globalVar.getType()->getElementType()->isFloatTy() && + !globalVar.getType()->getElementType()->isDoubleTy()) + { + llvm::errs() << "Skipping non-float global variable: " << globalName << "\n"; + continue; + } + + llvm::errs() << "Quantizing global variable: " << globalName << "\n"; + + // Create or retrieve the quantized version of the global variable + GlobalVariable * quantizedGlobal = getOrCreateQuantizedGlobal(module, globalVar, quantizedType); + + // Replace uses of the original global in whitelisted functions + replaceUsesInWhitelistedFunctions(globalVar, *quantizedGlobal); + + // The original global variable remains untouched + llvm::errs() << "Original global variable preserved: " << globalName << "\n"; + } +} + +// 辅助函数:对于 ConstantExpr 类型的使用,如果它是 GEP,则尝试构造新的 constant GEP 表达式 +static void +handleConstantExprUse(llvm::ConstantExpr * constExpr, + llvm::GlobalVariable & origConst, + llvm::GlobalVariable & quantizedConst) +{ + // 如果 constant-expression 是 getelementptr,则重建一个新的 constant-expression + if (constExpr->getOpcode() == llvm::Instruction::GetElementPtr) + { + llvm::SmallVector Indices; + // 从操作数1开始(operand0 是指针) + for (unsigned i = 1, e = constExpr->getNumOperands(); i < e; ++i) + { + if (llvm::Constant * C = llvm::dyn_cast(constExpr->getOperand(i))) + Indices.push_back(C); + } + // 直接使用量化后的全局变量,不做 bitcast + llvm::Constant * newGEP = llvm::ConstantExpr::getGetElementPtr( + quantizedConst.getType()->getPointerElementType(), &quantizedConst, Indices); + // 用新构造的 constant 替换所有对该 constant-expression 的使用 + constExpr->replaceAllUsesWith(newGEP); + llvm::errs() << "Replaced constant GEP for " << origConst.getName() << "\n"; + } + else + { + // 对于非 GEP 的 constant-expression,则转换为指令 + llvm::Instruction * insertPt = nullptr; + for (llvm::Use & U : constExpr->uses()) + { + if (llvm::Instruction * inst = llvm::dyn_cast(U.getUser())) + { + insertPt = inst; + break; + } + } + if (insertPt) + { + llvm::Instruction * newInst = constExpr->getAsInstruction(); + newInst->insertBefore(insertPt); + constExpr->replaceAllUsesWith(newInst); + llvm::errs() << "Converted constant expr to instruction for " << origConst.getName() << "\n"; + } + } +} + +static void +handleGEPInstruction(llvm::GetElementPtrInst * gep, + llvm::GlobalVariable & origConst, + llvm::GlobalVariable & quantizedConst) +{ + llvm::IRBuilder<> builder(gep); + llvm::SmallVector Indices; + for (llvm::Value * idx : gep->indices()) + Indices.push_back(idx); + llvm::Value * newGEP = builder.CreateGEP(quantizedConst.getType()->getPointerElementType(), + &quantizedConst, Indices, + gep->getName() + ".quantized_gep"); + gep->replaceAllUsesWith(newGEP); + gep->eraseFromParent(); + llvm::errs() << "Replaced GEP instruction for " << origConst.getName() << "\n"; +} + + + +void +replaceInternalConstantUses(llvm::Module *module, + llvm::GlobalVariable &origConst, + llvm::GlobalVariable &quantizedConst) +{ + std::vector usesToReplace; + + for (auto it = origConst.use_begin(), end = origConst.use_end(); it != end; ) + { + llvm::Use &use = *it++; + llvm::Value *user = use.getUser(); + + if (llvm::ConstantExpr *constExpr = llvm::dyn_cast(user)) + { + bool replace = false; + for (auto &CEUse : constExpr->uses()) + { + if (llvm::Instruction *inst = llvm::dyn_cast(CEUse.getUser())) + { + if (inst->getFunction() && shouldProcessFunction(*inst->getFunction())) + { + replace = true; + break; + } + } + } + if (replace) + { + handleConstantExprUse(constExpr, origConst, quantizedConst); + } + continue; + } + + + if (llvm::Instruction *inst = llvm::dyn_cast(user)) + { + if (!inst->getFunction() || !shouldProcessFunction(*inst->getFunction())) + continue; + + + if (llvm::GetElementPtrInst *gep = llvm::dyn_cast(inst)) + { + if (gep->getResultElementType()->isDoubleTy()) + { + handleGEPInstruction(gep, origConst, quantizedConst); + continue; + } + } + + usesToReplace.push_back(&use); + } + } + + for (llvm::Use *use : usesToReplace) + use->set(&quantizedConst); + + llvm::errs() << "Replaced all uses of " << origConst.getName() + << " with " << quantizedConst.getName() + << " in whitelisted functions.\n"; +} + +void +updateInternalConstants(Module * module, Type * quantizedType) +{ + llvm::errs() << "Updating internal constants\n"; + + for (GlobalVariable & globalVar : module->globals()) + { + std::string globalName = globalVar.getName().str(); + + if (!globalVar.isConstant() || !globalVar.hasInitializer()) + { + llvm::errs() << "Skipping non-constant or uninitialized global: " << globalName << "\n"; + continue; + } + + Type * elementType = globalVar.getType()->getElementType(); + if (!elementType->isArrayTy() || + (!elementType->getArrayElementType()->isFloatTy() && + !elementType->getArrayElementType()->isDoubleTy())) + { + llvm::errs() << "Skipping non-float internal constant: " << globalName << "\n"; + continue; + } + + llvm::errs() << "Quantizing internal constant: " << globalName << "\n"; + + GlobalVariable * quantizedConst = getOrCreateQuantizedConstant(module, globalVar, quantizedType); + + if (quantizedConst) + { + replaceInternalConstantUses(module, globalVar, *quantizedConst); + } + + llvm::errs() << "Original internal constant preserved: " << globalName << "\n"; + } +} + +//void +//quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +//{ +// Value * pointerOperand = loadInst->getPointerOperand(); +// llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; +// +// Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); +// +// Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); +// +// Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized_ptr"); +// +// loadInst->replaceAllUsesWith(quantizedValue); +// loadInst->eraseFromParent(); +// +// llvm::errs() << "Replaced load with quantized integer value.\n"; +//} + +void +quantizePointer(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +{ + Value * pointerOperand = loadInst->getPointerOperand(); + llvm::errs() << "Quantizing load from local pointer: " << *pointerOperand << "\n"; + + + Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); + + + Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled_ptr"); + + + Value * roundingOffset = ConstantFP::get(loadedType, 0.5); + Value * roundedValue = Builder.CreateFAdd(scaledValue, roundingOffset, loadInst->getName() + ".rounded_ptr"); + + Value * quantizedValue = Builder.CreateFPToSI(roundedValue, quantizedType, loadInst->getName() + ".quantized_ptr"); + + loadInst->replaceAllUsesWith(quantizedValue); + loadInst->eraseFromParent(); + + llvm::errs() << "Replaced load with quantized integer value with rounding.\n"; +} + +void +quantizeMatrixFloat(LoadInst * loadInst, IRBuilder<> & Builder, Type * quantizedType, Type * loadedType) +{ + // Get the pointer operand of the load instruction + Value * pointerOperand = loadInst->getPointerOperand(); + Type * pointerElementType = pointerOperand->getType()->getPointerElementType(); + + if (pointerElementType->isFloatingPointTy()) + { + llvm::errs() << "Quantizing load from local float pointer: " << *pointerOperand << "\n"; + + Value * loadedValue = Builder.CreateLoad(loadedType, pointerOperand, loadInst->getName() + ".p"); + Value * scaledValue = Builder.CreateFMul(loadedValue, ConstantFP::get(loadedType, FRAC_BASE), loadInst->getName() + ".scaled"); + Value * quantizedValue = Builder.CreateFPToSI(scaledValue, quantizedType, loadInst->getName() + ".quantized"); + loadInst->replaceAllUsesWith(quantizedValue); + loadInst->eraseFromParent(); + + llvm::errs() << "Replaced load with quantized integer value.\n"; + } + else + { + llvm::errs() << "Skipping quantization for load: " << *loadInst << " (Not a float load)\n"; + } +} + +/** + * handleLoad + */ +void +handleLoad(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto * loadInst = dyn_cast(llvmIrInstruction)) + { + IRBuilder<> Builder(loadInst); + Type * loadedType = loadInst->getType(); + if (loadedType->isFloatingPointTy()) + { + Value * pointerOperand = loadInst->getPointerOperand(); + Function * parentFunc = loadInst->getFunction(); + + // if (isa(pointerOperand) || + // parentFunc->getName() == "MadgwickAHRSupdateIMU" || + // parentFunc->getName() == "MahonyAHRSupdateIMU" || + // parentFunc->getName() == "pzero" || + // parentFunc->getName() == "qzero") || + // parentFunc->getName() == "ieee754_exp" + if (isa(pointerOperand) || + parentFunc->getName() == "MadgwickAHRSupdateIMU" || + parentFunc->getName() == "MahonyAHRSupdateIMU" || + parentFunc->getName() == "pzero" || + parentFunc->getName() == "qzero" || + parentFunc->getName() == "pone" || + parentFunc->getName() == "qone" || + parentFunc->getName() == "__ieee754_exp"|| + parentFunc->getName() == "__ieee754_log"|| + parentFunc->getName() == "twofft") + // ... + + { + llvm::errs() << "Handling load from global variable: " << *pointerOperand << "\n"; + LoadInst * newLoadInst = Builder.CreateLoad(quantizedType, pointerOperand, loadInst->getName() + ".global_quantized"); + llvm::errs() << "New load instruction: " << *newLoadInst << "\n"; + loadInst->replaceAllUsesWith(newLoadInst); + loadInst->eraseFromParent(); + } + + // Quantize local pointers + // #ifndef IS_MATRIX + // else if (!isa(pointerOperand)) + // { + // quantizePointer(loadInst, Builder, quantizedType, loadedType); + // } + // #else + // else if (!isa(pointerOperand)) + // { + // quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); + // } + // #endif + +#ifdef IS_POINTER + else if (!isa(pointerOperand)) + { + quantizePointer(loadInst, Builder, quantizedType, loadedType); + } + +#else + else if (!isa(pointerOperand)) + { + quantizeMatrixFloat(loadInst, Builder, quantizedType, loadedType); + } +#endif + } + } +} + +void +handleFAdd(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FAdd\n"; + IRBuilder<> Builder(inInstruction); + + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + if (ConstantFP * constFp = dyn_cast(op0)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + + if (ConstantFP * constFp = dyn_cast(op1)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + + // Check if this instruction has metadata indicating it's quantized + if (inInstruction->getMetadata("quantized")) + { + llvm::errs() << "Skipping quantized instruction\n"; + return; + } + + // Create fixed-point addition + Value * newInst = Builder.CreateNSWAdd(op0, op1); + // Value * newInst = Builder.CreateAdd(op0, op1); + + // Replace the original FAdd instruction with the new fixed-point addition + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FAdd\n"; +} + +void +handleFSub(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FSub\n"; + IRBuilder<> Builder(inInstruction); + + Value * op0 = inInstruction->getOperand(0); + Value * op1 = inInstruction->getOperand(1); + + // Check if one of the operands is a floating-point constant that needs to be multiplied by FRAC_BASE + if (ConstantFP * constFp = dyn_cast(op0)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op0 = ConstantInt::get(quantizedType, quantizedValue); + } + + if (ConstantFP * constFp = dyn_cast(op1)) + { + // Multiply the constant by FRAC_BASE and convert to integer + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + op1 = ConstantInt::get(quantizedType, quantizedValue); + } + + // Create fixed-point subtraction + Value * newInst = Builder.CreateNSWSub(op0, op1); + // Value * newInst = Builder.CreateSub(op0, op1); + + // Replace the original FSub instruction with the new fixed-point subtraction + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FSub\n"; +} + +void +handleFNeg(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FNeg\n"; + IRBuilder<> Builder(inInstruction); + + // Get the operand for the FNeg operation + Value * operand = inInstruction->getOperand(0); + llvm::errs() << "Operand: " << *operand << "\n"; + + // Create a constant zero of the same integer type as the operand + Value * zero = ConstantInt::get(operand->getType(), 0); + + // Perform the integer subtraction: sub 0, operand + Value * newInst = Builder.CreateSub(zero, operand); + + // Replace the original FNeg instruction with the new subtraction + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FNeg with integer subtraction\n"; +} + +// Quantize simple floating-point instructions +CmpInst::Predicate +quantizePredict(CmpInst::Predicate predict) +{ + llvm::errs() << "Entering quantizePredict with predicate: " << predict << "\n"; + switch (predict) + { + case FCmpInst::FCMP_OEQ: // equal + case FCmpInst::FCMP_UEQ: + return ICmpInst::ICMP_EQ; // greater than + case FCmpInst::FCMP_OGT: + case FCmpInst::FCMP_UGT: + return ICmpInst::ICMP_SGT; + case FCmpInst::FCMP_OGE: // greater than or equal + case FCmpInst::FCMP_UGE: + return ICmpInst::ICMP_SGE; + case FCmpInst::FCMP_OLT: // less than + case FCmpInst::FCMP_ULT: + return ICmpInst::ICMP_SLT; + case FCmpInst::FCMP_OLE: // less than or equal + case FCmpInst::FCMP_ULE: + return ICmpInst::ICMP_SLE; + case FCmpInst::FCMP_ONE: // not equal + case FCmpInst::FCMP_UNE: + return ICmpInst::ICMP_NE; + default: + llvm::errs() << "Unhandled floating point predicate\n"; + return ICmpInst::ICMP_EQ; // Default to equal for safety + } +} + +void +handleFCmp(Instruction * inInstruction, Type * quantizedType) +{ + IRBuilder<> Builder(inInstruction); + + if (auto fcmp_inst = dyn_cast(inInstruction)) + { + CmpInst::Predicate pred = quantizePredict(fcmp_inst->getPredicate()); + if (fcmp_inst->getPredicate() == FCmpInst::FCMP_TRUE) + { + Value * newInst = ConstantInt::getTrue(quantizedType); + inInstruction->replaceAllUsesWith(newInst); + } + else if (fcmp_inst->getPredicate() == FCmpInst::FCMP_FALSE) + { + Value * newInst = ConstantInt::getFalse(quantizedType); + inInstruction->replaceAllUsesWith(newInst); + } + else + { + Value * fpOp0 = fcmp_inst->getOperand(0); + Value * fpOp1 = fcmp_inst->getOperand(1); + + Value * intOp0 = Builder.CreateFPToSI(fpOp0, quantizedType); + Value * intOp1 = Builder.CreateFPToSI(fpOp1, quantizedType); + + Value * newInst = Builder.CreateICmp(pred, intOp0, intOp1); + inInstruction->replaceAllUsesWith(newInst); + } + inInstruction->eraseFromParent(); + } +} + +void +handleSelect(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling Select\n"; + + if (inInstruction->getNumOperands() < 3) + { + llvm::errs() << "Error: Select instruction does not have 3 operands!\n"; + return; + } + IRBuilder<> Builder(inInstruction); + + Value * condition = inInstruction->getOperand(0); + Value * opTrue = inInstruction->getOperand(1); + Value * opFalse = inInstruction->getOperand(2); + + llvm::errs() << "Original condition: " << *condition << "\n"; + llvm::errs() << "Original true branch: " << *opTrue << "\n"; + llvm::errs() << "Original false branch: " << *opFalse << "\n"; + + if (ConstantFP * constFp = dyn_cast(opTrue)) + { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + opTrue = ConstantInt::get(quantizedType, quantizedValue); + } + + if (ConstantFP * constFp = dyn_cast(opFalse)) + { + float constValue = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(constValue * FRAC_BASE)); + opFalse = ConstantInt::get(quantizedType, quantizedValue); + } + + Value * newInst = Builder.CreateSelect(condition, opTrue, opFalse); + llvm::errs() << "Created new select instruction: " << *newInst << "\n"; + + inInstruction->replaceAllUsesWith(newInst); + inInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling Select\n"; +} + +void +simplifyConstant(Instruction * inInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling constant operand\n"; + auto checkDecimal = [](float decimalNum) { + int digits = 0; + /* + * Since the max value of `int16` is 32767, + * we maximum multiply with 1,000 to make sure it won't exceed max_int16 + * */ + while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) + { + decimalNum *= 10; + digits++; + } + return decimalNum; + }; + + auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { + /* + * 3333.3 / 3.3333 = 1000 + * ===> + * Example 1: + * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 + * + * Example 2: + * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 + * + * Example 3: + * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 + * */ + float compensateNum = quantizedNum / decimalNum; + + Value * constOperand, *nonConstOperand; + unsigned constIdx, nonConstIdx; + if (isa(inInstruction->getOperand(0))) + { + constIdx = 0; + nonConstIdx = 1; + constOperand = inInstruction->getOperand(0); + nonConstOperand = inInstruction->getOperand(1); + } + else + { + constIdx = 1; + nonConstIdx = 0; + constOperand = inInstruction->getOperand(1); + nonConstOperand = inInstruction->getOperand(0); + } + + auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); + + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newFisrtInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + + if (compensateNum == 1) + { + llvm::errs() << "Compensation factor is 1, directly setting the quantized value\n"; + // newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + inInstruction->setOperand(constIdx, quantizeNumValue); + // inInstruction->replaceAllUsesWith(newSecondInst); + // //inInstruction->removeFromParent(); + // instructionsToErase.push_back(inInstruction); + } + else + { + llvm::errs() << "Applying compensation to the fixed-point arithmetic\n"; + auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); + + IRBuilder<> Builder(inInstruction); + Instruction * insertPoint = inInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newFisrtInst = nullptr; + Value * newSecondInst = nullptr; + auto instOpCode = inInstruction->getOpcode(); + if (instOpCode == Instruction::FMul) + { + llvm::errs() << "Handling FMul instruction\n"; + newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) + { + llvm::errs() << "Handling FDiv instruction with constant denominator\n"; + newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); + newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); + } + else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) + { + llvm::errs() << "Handling FDiv instruction with constant numerator\n"; + newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); + newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); + } + + inInstruction->replaceAllUsesWith(newSecondInst); + inInstruction->removeFromParent(); + } + }; + + for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) + { + Value * inValue = inInstruction->getOperand(idx); + + if (!isa(inValue)) + { + continue; + } + + ConstantFP * constFp = llvm::dyn_cast(inValue); + Value * newValue = nullptr; + if (inValue->getType()->isFloatTy()) + { + float constValue = constFp->getValueAPF().convertToFloat(); + compensateFP(checkDecimal(constValue), constValue); + } + else if (inValue->getType()->isDoubleTy()) + { + double constValue = constFp->getValueAPF().convertToDouble(); + compensateFP(checkDecimal(constValue), constValue); + } + else + { + assert(false && "unknown floating type"); + } + } + llvm::errs() << "Exiting Simplifying Constant\n"; +} +} + +void +simplifyUsingShift(Value * operand, Instruction * instruction, int shiftAmount, bool isRightShift) +{ + llvm::IRBuilder<> Builder(instruction); + // Type * intType = Type::getInt32Ty(instruction->getContext()); + llvm::Type * intType = nullptr; + + switch (BIT_WIDTH) + { + case 16: + intType = llvm::Type::getInt16Ty(instruction->getContext()); // Use 16-bit integer type + break; + + default: + intType = llvm::Type::getInt32Ty(instruction->getContext()); // Use 32-bit integer type + break; + } + Value * shiftValue = ConstantInt::get(intType, shiftAmount); + Instruction * shiftInst; + + if (isRightShift) + { + shiftInst = BinaryOperator::CreateAShr(operand, shiftValue, "", instruction); + } + else + { + shiftInst = BinaryOperator::CreateShl(operand, shiftValue, "", instruction); + } + + instruction->replaceAllUsesWith(shiftInst); + instruction->eraseFromParent(); +} + +bool +checkAndSimplifyForConstant(ConstantFP * constFP, Value * otherOperand, Instruction * instruction) +{ + static const std::map> constantsMap = { + {0.5, {1, true}}, + {2.0, {1, false}}, + {4.0, {2, false}}, + {8.0, {3, false}}, + {16.0, {4, false}}, + {32.0, {5, false}}, + {64.0, {6, false}}, + {128.0, {7, false}}, + {256.0, {8, false}}, + {512.0, {9, false}}, + {1 / 2.0, {1, true}}, + {1 / 4.0, {2, true}}, + {1 / 8.0, {3, true}}, + {1 / 16.0, {4, true}}, + {1 / 32.0, {5, true}}, + {1 / 64.0, {6, true}}, + {1 / 128.0, {7, true}}, + {1 / 256.0, {8, true}}, + {1 / 512.0, {9, true}}}; + double value = constFP->getValueAPF().convertToDouble(); + auto it = constantsMap.find(value); + if (it != constantsMap.end()) + { + int shiftAmount = it->second.first; + bool isRightShift = it->second.second; + simplifyUsingShift(otherOperand, instruction, shiftAmount, isRightShift); + return true; + } + return false; +} + + +void +handleFMul(Instruction * llvmIrInstruction, Type * quantizedType) +{ + llvm::errs() << "Handling FMul\n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + // Check if this instruction has metadata indicating it's quantized + if (llvmIrInstruction->getMetadata("quantized")) + { + llvm::errs() << "Skipping already quantized instruction.\n"; + return; // Skip processing this instruction + } + + // Ensure operands are correctly converted to fixed-point integers + Value * lhs = llvmIrInstruction->getOperand(0); + Value * rhs = llvmIrInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + ConstantFP * lhsConst = dyn_cast(lhs); + ConstantFP * rhsConst = dyn_cast(rhs); + + // Check if either operand is a constant that can be simplified + if (auto rhsConst = dyn_cast(rhs)) + { + if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) + return; + } + if (auto lhsConst = dyn_cast(lhs)) + { + if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) + return; + } + + // If either operand is a float constant, convert it to fixed-point using handleConstant + if (isa(lhs)) + { + llvm::errs() << "LHS is a floating-point constant, handling it with handleConstant\n"; + // handleConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); + lhs = llvmIrInstruction->getOperand(0); + lhsIsFloat = false; // Update float status as it's now a fixed-point + } + + if (isa(rhs)) + { + llvm::errs() << "RHS is a floating-point constant, handling it with handleConstant\n"; + // handleConstant(llvmIrInstruction, quantizedType); + simplifyConstant(llvmIrInstruction, quantizedType); + rhsIsFloat = false; + rhs = llvmIrInstruction->getOperand(1); // Update rhs after conversion + } + + // If either operand is an integer constant, directly use mul + if (isa(lhs) || isa(rhs)) + { + llvm::errs() << "One of the operands is an integer constant, using mul\n"; + Value * newInst = Builder.CreateMul(lhs, rhs); + // Value * newInst = Builder.CreateNSWMul(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + return; + } + + // If either operand is a float, convert both to fixed-point + if (lhsIsFloat) + { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + + if (rhsIsFloat) + { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } + + // If both operands are integers, but neither is a constant, use fixmul + bool lhsIsInteger = lhs->getType()->isIntegerTy(); + bool rhsIsInteger = rhs->getType()->isIntegerTy(); + + // if (lhsIsInteger && rhsIsInteger) + + if (lhsIsInteger && rhsIsInteger) + + + + { + llvm::Value * newInst = performFixedPointMul(Builder, lhs, rhs, FRAC_Q); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + } + + + + llvm::errs() << "Finished handling FMul\n"; +} +void handleFDiv(Instruction *llvmIrInstruction, Type *quantizedType) { + llvm::errs() << "Handling FDiv\n"; + llvm::errs() << "Original Instruction: " << *llvmIrInstruction << "\n"; + IRBuilder<> Builder(llvmIrInstruction); + + if (llvmIrInstruction->getMetadata("quantized")) { + llvm::errs() << "Skipping already quantized instruction.\n"; + return; + } + + Value *lhs = llvmIrInstruction->getOperand(0); + Value *rhs = llvmIrInstruction->getOperand(1); + + llvm::errs() << "LHS: " << *lhs << "\n"; + llvm::errs() << "RHS: " << *rhs << "\n"; + + bool lhsIsFloat = lhs->getType()->isFloatTy() || lhs->getType()->isDoubleTy(); + bool rhsIsFloat = rhs->getType()->isFloatTy() || rhs->getType()->isDoubleTy(); + + if (auto rhsConst = dyn_cast(rhs)) { + if (checkAndSimplifyForConstant(rhsConst, lhs, llvmIrInstruction)) + return; + } + if (auto lhsConst = dyn_cast(lhs)) { + if (checkAndSimplifyForConstant(lhsConst, rhs, llvmIrInstruction)) + return; + } + + // Check if lhs is the constant 1.0 + if (auto lhsConst = dyn_cast(lhs)) { + double val = lhsConst->getValueAPF().convertToDouble(); + if (fabs(val - 1.0) < 1e-6) { + llvm::errs() << "LHS is 1.0, replacing with FRAC_BASE\n"; + lhs = ConstantInt::get(quantizedType, FRAC_BASE, true); + lhsIsFloat = false; + } + } + + if (isa(lhs)) { + llvm::errs() << "LHS is a floating-point constant, handling it with simplifyConstant\n"; + simplifyConstant(llvmIrInstruction, quantizedType); + lhs = llvmIrInstruction->getOperand(0); + lhsIsFloat = false; + } + if (isa(rhs)) { + llvm::errs() << "RHS is a floating-point constant, handling it with simplifyConstant\n"; + simplifyConstant(llvmIrInstruction, quantizedType); + rhs = llvmIrInstruction->getOperand(1); + rhsIsFloat = false; + } + + + + if (lhsIsFloat) { + lhs = Builder.CreateFPToSI(lhs, quantizedType); + llvm::errs() << "Converted LHS to fixed-point: " << *lhs << "\n"; + } + if (rhsIsFloat) { + rhs = Builder.CreateFPToSI(rhs, quantizedType); + llvm::errs() << "Converted RHS to fixed-point: " << *rhs << "\n"; + } + + Value *newInst = Builder.CreateSDiv(lhs, rhs); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->eraseFromParent(); + + llvm::errs() << "Finished handling FDiv\n"; +} + + +void handlePhi(Instruction *inInstruction, Type *quantizedType) { + llvm::errs() << "Handling PHI\n"; + PHINode *phi = dyn_cast(inInstruction); + if (!phi) { + llvm::errs() << "Error: Instruction is not a PHI node.\n"; + return; + } + + // If the PHI node is already of an integer type, skip quantization. + if (phi->getType()->isIntegerTy()) { + llvm::errs() << "PHI node already has an integer type, skipping quantization: " << *phi << "\n"; + return; + } + + // Check if the PHI node is of pointer type. + bool isPtr = phi->getType()->isPointerTy(); + unsigned pointerAddr = 0; + if (isPtr) + pointerAddr = phi->getType()->getPointerAddressSpace(); + + // Determine new PHI node type: if pointer, then quantizedType->getPointerTo(pointerAddr); + // otherwise, simply quantizedType. + Type *newPhiType = isPtr ? quantizedType->getPointerTo(pointerAddr) : quantizedType; + PHINode *newPhi = PHINode::Create(newPhiType, phi->getNumIncomingValues(), + phi->getName() + ".quantized", phi); + + for (unsigned i = 0, e = phi->getNumIncomingValues(); i < e; i++) { + Value *incoming = phi->getIncomingValue(i); + BasicBlock *incomingBB = phi->getIncomingBlock(i); + Value *newVal = nullptr; + + llvm::errs() << "Original PHI incoming value: " << *incoming << "\n"; + + if (!isPtr) { + if (ConstantFP *constFp = dyn_cast(incoming)) { + // Check for infinity. + if (constFp->getValueAPF().isInfinity()) { + int64_t maxVal = 0, minVal = 0; + // Choose maximum/minimum based on BIT_WIDTH. + if (BIT_WIDTH == 16) { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } else if (BIT_WIDTH == 32) { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } else if (BIT_WIDTH == 64) { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } else { + maxVal = (std::numeric_limits::max)(); + minVal = (std::numeric_limits::min)(); + } + + if (!constFp->getValueAPF().isNegative()) { + llvm::errs() << "Detected positive infinity, mapping to maximum integer.\n"; + newVal = llvm::ConstantInt::get(quantizedType, maxVal, true); + } else { + llvm::errs() << "Detected negative infinity, mapping to minimum integer.\n"; + newVal = llvm::ConstantInt::get(quantizedType, minVal, true); + } + } else { + float fpVal = constFp->getValueAPF().convertToFloat(); + int64_t quantizedValue = static_cast(round(fpVal * FRAC_BASE)); + newVal = llvm::ConstantInt::get(quantizedType, quantizedValue, true); + llvm::errs() << "Converted constant: " << *newVal << "\n"; + } + } else if (incoming->getType()->isFloatingPointTy()) { + IRBuilder<> builder(incomingBB->getTerminator()); + newVal = builder.CreateFPToSI(incoming, quantizedType, incoming->getName() + ".to_int"); + llvm::errs() << "Inserted conversion: " << *newVal << "\n"; + } else { + newVal = incoming; + } + } else { + if (incoming->getType() != newPhiType) { + IRBuilder<> builder(incomingBB->getTerminator()); + newVal = builder.CreateBitCast(incoming, newPhiType, incoming->getName() + ".cast"); + llvm::errs() << "BitCast pointer: " << *newVal << "\n"; + } else { + newVal = incoming; + } + } + newPhi->addIncoming(newVal, incomingBB); + } + + phi->replaceAllUsesWith(newPhi); + phi->eraseFromParent(); + llvm::errs() << "Finished handling PHI: " << *newPhi << "\n"; +} + + +void handleFpToSi(Instruction *llvmInst, Type *quantizedType) { + llvm::errs() << "Handling FPToSI\n"; + + // Ensure we are processing an FPToSI instruction. + FPToSIInst *fpToSiInst = dyn_cast(llvmInst); + if (!fpToSiInst) { + llvm::errs() << "Not a FPToSI instruction.\n"; + return; + } + + // Check if the operand to FPToSI is already an integer. + Value *inputVal = fpToSiInst->getOperand(0); + if (inputVal->getType()->isIntegerTy()) { + llvm::errs() << "Input of FPToSI is already integer. Replacing redundant conversion.\n"; + + // Set insertion point immediately after the FPToSI instruction. + IRBuilder<> Builder(fpToSiInst->getNextNode()); + + // Directly apply an arithmetic right shift on the input value. + Value *truncInst = Builder.CreateAShr( + inputVal, + ConstantInt::get(inputVal->getType(), FRAC_Q), + "trunc"); + llvm::errs() << "Created ASHR instruction: " << *truncInst << "\n"; + + // Replace all uses of the FPToSI instruction with the new ASHR result. + fpToSiInst->replaceAllUsesWith(truncInst); + fpToSiInst->eraseFromParent(); + return; + } + + // Otherwise, if the input is not already integer, try to match the standard fptosi/sitofp sequence. + for (User *U : fpToSiInst->users()) { + if (auto *siToFpInst = dyn_cast(U)) { + llvm::errs() << "Detected fptosi/sitofp sequence:\n"; + llvm::errs() << " FPToSI: " << *fpToSiInst << "\n"; + llvm::errs() << " SIToFP: " << *siToFpInst << "\n"; + + // Use the result of FPToSI (assumed to be the fixed-point representation) directly. + IRBuilder<> Builder(siToFpInst->getNextNode()); + Value *truncInst = Builder.CreateAShr( + fpToSiInst, + ConstantInt::get(fpToSiInst->getType(), FRAC_Q), + "trunc"); + llvm::errs() << "Created ASHR instruction: " << *truncInst << "\n"; + + siToFpInst->replaceAllUsesWith(truncInst); + siToFpInst->eraseFromParent(); + if (fpToSiInst->use_empty()) { + fpToSiInst->eraseFromParent(); + } + return; + } + } + + llvm::errs() << "No matching pattern found for FPToSI optimization. Skipping.\n"; +} + + + + + + + +void +setQuantizedType(Value * inValue, Type * quantizedType) +{ + auto valueType = inValue->getType(); + unsigned pointerAddr; + bool isPointer = false; + if (valueType != nullptr) + { + if (valueType->isPointerTy()) + { + llvm::errs() << "Pointer type detected\n"; + isPointer = true; + pointerAddr = valueType->getPointerAddressSpace(); + valueType = valueType->getPointerElementType(); + } + if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) + { + if (isPointer) + { + llvm::errs() << "Original type: " << *valueType << "\n"; + llvm::errs() << "New quantized type: " << *quantizedType << "\n"; + inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); + } + else + { + inValue->mutateType(quantizedType); + } + } + } +} + +void +handleAlloca(AllocaInst * llvmIrAllocaInstruction, Type * quantizedType) +{ + auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); + Type * newType = quantizedType; + + llvm::errs() << "Handling Alloca for variable: " << llvmIrAllocaInstruction->getName() << "\n"; + llvm::errs() << " - Original type: " << *allocaType << "\n"; + + if (allocaType->getTypeID() == Type::ArrayTyID) + { + newType = ArrayType::get(quantizedType, allocaType->getArrayNumElements()); + allocaType = allocaType->getArrayElementType(); + llvm::errs() << " - Detected array type. New quantized array type: " << *newType << "\n"; + } + + if (allocaType->isDoubleTy() || allocaType->isFloatTy()) + { + llvmIrAllocaInstruction->setAllocatedType(newType); + llvm::errs() << " - Updated allocation type to quantized type: " << *newType << "\n"; + } + else + { + llvm::errs() << " - No need to change type for non-floating point allocation.\n"; + } +} + + + +void +handleStore(Instruction * llvmIrInstruction, Type * quantizedType) +{ + if (auto llvmIrStoreInstruction = dyn_cast(llvmIrInstruction)) + { + IRBuilder<> Builder(llvmIrStoreInstruction); + auto valueOperand = llvmIrStoreInstruction->getValueOperand(); + auto pointerOperand = llvmIrStoreInstruction->getPointerOperand(); + auto valueType = llvmIrStoreInstruction->getValueOperand()->getType(); + auto pointerType = pointerOperand->getType()->getPointerElementType(); + + // **Check if pointerOperand comes from GEP float*** + if (auto gepInstruction = dyn_cast(pointerOperand)) + { + if (gepInstruction->getSourceElementType()->isFloatTy()) + { + llvm::errs() << "Skipping store quantization for GEP(float*) case.\n"; + return; + } + } -#include "newton-irPass-LLVMIR-quantization.h" + if (valueType->isFloatTy() || valueType->isDoubleTy()) + { + llvm::errs() << "Original store value type: " << *valueType << "\n"; + llvm::errs() << "New quantized store value type: " << *quantizedType << "\n"; -using namespace llvm; + // Quantize the value operand + auto quantizedValue = Builder.CreateFMul(llvmIrStoreInstruction->getValueOperand(), ConstantFP::get(valueType, FRAC_BASE)); + quantizedValue = Builder.CreateFPToSI(quantizedValue, quantizedType); + llvmIrStoreInstruction->setOperand(0, quantizedValue); + } -#define FRAC_Q 16 -#define FRAC_BASE (1<getType(); - unsigned pointerAddr; - bool isPointer = false; - if (valueType != nullptr) - { - if (valueType->isPointerTy()) { - isPointer = true; - pointerAddr = valueType->getPointerAddressSpace(); - valueType = valueType->getPointerElementType(); - } - if (valueType->isDoubleTy() || valueType->isFloatTy() || valueType->isArrayTy()) - { - if (isPointer) { - inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); - } else { - inValue->mutateType(quantizedType); - } - } - } + // auto pointerType = llvmIrStoreInstruction->getPointerOperand()->getType()->getPointerElementType(); + if (pointerType->isFloatTy() || pointerType->isDoubleTy()) + { + llvm::errs() << "Original store pointer type: " << *pointerType << "\n"; + llvm::errs() << "New quantized store pointer type: " << *quantizedType << "\n"; + + // Set the new quantized type for the pointer operand + llvmIrStoreInstruction->getPointerOperand()->mutateType(quantizedType->getPointerTo()); + } + } } +bool isTargetFunction(Function & func); + +void +bitcastFloatPtrArgs(Function & F, IRBuilder<> & Builder) +{ + // Check if the function is the specific one to be skipped + if (isTargetFunction(F)) + { + llvm::errs() << "Skipping bitcast for function: " << F.getName() << "\n"; + return; // Early exit if it's the function to skip + } -void quantizeConstant(Instruction * inInstruction, Type * quantizedType) { - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { - Value * inValue = inInstruction->getOperand(idx); + SmallVector, 4> argReplacements; - if (!isa(inValue)) { - continue; - } + // Iterate over all function arguments + for (Argument & Arg : F.args()) + { + // Check if the argument is a pointer to float + if (Arg.getType()->isPointerTy() && Arg.getType()->getPointerElementType()->isFloatTy()) + { + // Create a bitcast to i32* at the beginning of the function + llvm::PointerType * i32PtrType = llvm::Type::getInt32PtrTy(F.getContext()); + Builder.SetInsertPoint(&*F.getEntryBlock().getFirstInsertionPt()); + Value * i32Arg = Builder.CreateBitCast(&Arg, i32PtrType, Arg.getName() + ".to_i32_ptr"); - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - constValue *= FRAC_BASE; - newValue = ConstantInt::get(quantizedType, round(constValue), true); - } - else - { - assert(false && "unknown floating type"); - } + // Additional bitcast to i16* if BIT_WIDTH == 16 + Value * newArg = nullptr; + switch (BIT_WIDTH) + { + case 16: + { + llvm::PointerType * i16PtrType = llvm::Type::getInt16PtrTy(F.getContext()); + newArg = Builder.CreateBitCast(i32Arg, i16PtrType, Arg.getName() + ".to_i16_ptr"); + break; + } + case 32: + default: + newArg = i32Arg; // Use i32* as the new argument + break; + } - inInstruction->replaceUsesOfWith(inValue, newValue); - } + // Store the original argument and the final bitcast result + argReplacements.push_back({&Arg, newArg}); + } + } + + llvm::errs() << "Starting use replacement\n"; // Log added + + // Iterate over the function to replace uses of the original arguments + for (auto & replacement : argReplacements) + { + Argument * oldArg = replacement.first; + Value * newArg = replacement.second; + + // Collect all uses of the old argument for replacement + SmallVector usesToReplace; + for (auto & U : oldArg->uses()) + { + User * user = U.getUser(); + + // Skip if the use is a Load instruction + if (isa(user)) + { + llvm::errs() << "Skipping load instruction: " << *user << "\n"; + continue; // Skip Load instructions + } + + // Skip uses where the original float* argument is directly bitcasted + if (auto * bitcastInst = dyn_cast(user)) + { + if (bitcastInst->getSrcTy() == oldArg->getType()) + { + llvm::errs() << "Skipping original bitcast: " << *bitcastInst << "\n"; + continue; + } + } + + // Skip if the user is the newArg itself + if (user == newArg) + { + continue; + } + + usesToReplace.push_back(&U); + } + + // Perform the replacements + for (auto * use : usesToReplace) + { + use->set(newArg); + } + } } -void simplifyConstant(Instruction * inInstruction, Type * quantizedType) { - auto checkDecimal = [](float decimalNum) { - int digits = 0; - /* - * Since the max value of `int16` is 32767, - * we maximum multiply with 1,000 to make sure it won't exceed max_int16 - * */ - while (fabs(round(decimalNum) - decimalNum) > 0.001 && digits < 4) { - decimalNum *= 10; - digits++; - } - return decimalNum; - }; - - auto compensateFP = [inInstruction, quantizedType](float quantizedNum, float decimalNum) { - /* - * 3333.3 / 3.3333 = 1000 - * ===> - * Example 1: - * a * 3.3333 ~= a * (3333 / 1000) ~= (int)a * 3333 / 1000 - * - * Example 2: - * a / 3.3333 ~= a / (3333 / 1000) ~= (int)a * 1000 / 3333 - * - * Example 3: - * 3.3333 / a ~= (3333 / 1000) / a ~= 3333 / (int)a / 1000 - * */ - float compensateNum = quantizedNum / decimalNum; - - Value *constOperand, *nonConstOperand; - unsigned constIdx, nonConstIdx; - if (isa(inInstruction->getOperand(0))) { - constIdx = 0; - nonConstIdx = 1; - constOperand = inInstruction->getOperand(0); - nonConstOperand = inInstruction->getOperand(1); - } else { - constIdx = 1; - nonConstIdx = 0; - constOperand = inInstruction->getOperand(1); - nonConstOperand = inInstruction->getOperand(0); - } +void +handleRsqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixrsqrt) +{ + IRBuilder<> builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); - auto quantizeNumValue = ConstantInt::get(quantizedType, round(quantizedNum), true); - - if (compensateNum == 1) { - inInstruction->setOperand(constIdx, quantizeNumValue); - } else { - auto compensateNumValue = ConstantInt::get(quantizedType, round(compensateNum), true); - - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newFisrtInst = nullptr; - Value * newSecondInst = nullptr; - auto instOpCode = inInstruction->getOpcode(); - if (instOpCode == Instruction::FMul) { - newFisrtInst = Builder.CreateMul(nonConstOperand, quantizeNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(1))) { - newFisrtInst = Builder.CreateMul(nonConstOperand, compensateNumValue); - newSecondInst = Builder.CreateSDiv(newFisrtInst, quantizeNumValue); - } else if (instOpCode == Instruction::FDiv && isa(inInstruction->getOperand(0))) { - newFisrtInst = Builder.CreateSDiv(quantizeNumValue, nonConstOperand); - newSecondInst = Builder.CreateSDiv(newFisrtInst, compensateNumValue); - } - - inInstruction->replaceAllUsesWith(newSecondInst); - inInstruction->removeFromParent(); - } - }; + if (!fixrsqrt) + { + llvm::errs() << "Error: fixrsqrt function is null.\n"; + return; + } - for (size_t idx = 0; idx < inInstruction->getNumOperands(); idx++) { - Value * inValue = inInstruction->getOperand(idx); + CallInst * rsqrtResult = builder.CreateCall(fixrsqrt, {operand}); - if (!isa(inValue)) { - continue; - } + llvmIrCallInstruction->replaceAllUsesWith(rsqrtResult); - ConstantFP * constFp = llvm::dyn_cast(inValue); - Value * newValue = nullptr; - if (inValue->getType()->isFloatTy()) - { - float constValue = constFp->getValueAPF().convertToFloat(); - compensateFP(checkDecimal(constValue), constValue); - } - else if (inValue->getType()->isDoubleTy()) - { - double constValue = constFp->getValueAPF().convertToDouble(); - compensateFP(checkDecimal(constValue), constValue); - } - else - { - assert(false && "unknown floating type"); - } - } + llvmIrCallInstruction->eraseFromParent(); } -llvm::Function * createFixMul(llvm::Function * inFunction, Type * quantizedType, std::vector& functionsToInsert) { - /* - * check if this function is exist - * */ - std::string fixmulFuncName = "fixmul"; - auto irModule = inFunction->getParent(); - for (auto & function : *irModule) { - if (function.getName() == fixmulFuncName) { - return &function; - } - } - llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType, quantizedType}, false); - llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixmulFuncName, irModule); - - llvm::BasicBlock* entryBB = llvm::BasicBlock::Create(inFunction->getContext(), "entry", func); - llvm::IRBuilder<> builder(entryBB); - builder.SetInsertPoint(entryBB); - - /* - * ((int64_t)x*y)>>FRAC_Q - * - * ===========> - * - * define private i32 @mulfix(i32 %0, i32 %1) { - * %3 = sext i32 %0 to i64 - * %4 = sext i32 %1 to i64 - * %5 = mul nsw i64 %3, %4 - * %6 = ashr i64 %5, 8 - * %7 = trunc i64 %6 to i32 - * ret i32 %7 - * } - * */ - Type* higherQuantizedType; - switch (BIT_WIDTH) { - case 8: - higherQuantizedType = Type::getInt16Ty(inFunction->getContext()); - break; - case 16: - higherQuantizedType = Type::getInt32Ty(inFunction->getContext()); - break; - default: - higherQuantizedType = Type::getInt64Ty(inFunction->getContext()); - break; - } +llvm::Value * +performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPointValue, Type * quantizedType, int FRAC_Q, LLVMContext & context) +{ + // Convert the fixed-point value to floating-point for sqrt calculation + llvm::Value * fpValue = builder.CreateSIToFP(fixedPointValue, llvm::Type::getFloatTy(context)); - llvm::Function::arg_iterator arg1 = &*(func->arg_begin()); - llvm::Value* sext1 = builder.CreateSExt(arg1, higherQuantizedType); - llvm::Function::arg_iterator arg2 = &*(++arg1); - llvm::Value* sext2 = builder.CreateSExt(arg2, higherQuantizedType); - llvm::Value* mulInst = builder.CreateMul(sext1, sext2); - llvm::Value* ashrInst = builder.CreateAShr(mulInst, ConstantInt::get(higherQuantizedType, FRAC_Q)); - llvm::Value* truncInst = builder.CreateTrunc(ashrInst, quantizedType); - builder.CreateRet(truncInst); - - functionsToInsert.emplace_back(func); - - return func; -} - -void substituteHardcodeFunc(Instruction * inInstruction, Type * quantizedType, llvm::Function * func) { - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); -// Value * newInst = nullptr; - - llvm::CallInst* callInst = Builder.CreateCall(func, {inInstruction->getOperand(0), inInstruction->getOperand(1)}); -// InlineFunctionInfo inlineFuncInfo; -// llvm::InlineFunction(*callInst, inlineFuncInfo); - - inInstruction->replaceAllUsesWith(callInst); - inInstruction->removeFromParent(); -} - -CmpInst::Predicate quantizePredict(CmpInst::Predicate predict) { - switch (predict) { - case FCmpInst::FCMP_OEQ: - case FCmpInst::FCMP_UEQ: - return ICmpInst::ICMP_EQ; - case FCmpInst::FCMP_OGT: - case FCmpInst::FCMP_UGT: - return ICmpInst::ICMP_SGT; - case FCmpInst::FCMP_OGE: - case FCmpInst::FCMP_UGE: - return ICmpInst::ICMP_SGE; - case FCmpInst::FCMP_OLT: - case FCmpInst::FCMP_ULT: - return ICmpInst::ICMP_SLT; - case FCmpInst::FCMP_OLE: - case FCmpInst::FCMP_ULE: - return ICmpInst::ICMP_SLE; - case FCmpInst::FCMP_ONE: - case FCmpInst::FCMP_UNE: - return ICmpInst::ICMP_NE; - } + // Call sqrt on the floating-point value + llvm::Function * sqrtFunc = llvm::Intrinsic::getDeclaration(irModule, llvm::Intrinsic::sqrt, llvm::Type::getFloatTy(context)); + llvm::Value * sqrtResult = builder.CreateCall(sqrtFunc, {fpValue}); + + // Convert the result back to a fixed-point integer + llvm::Value * res = builder.CreateFPToSI(sqrtResult, quantizedType); + + // Perform a left shift to scale the result (FRAC_Q / 2) + llvm::Value * shlRes = builder.CreateShl(res, FRAC_Q / 2); + + // Initialize the final result as the shifted result + llvm::Value * finalRes = shlRes; + + // Apply compensation if FRAC_Q is odd + if (FRAC_Q % 2 != 0) + { + // Compensation factor for odd FRAC_Q (1.414213562 ≈ sqrt(2)) + llvm::Value * compensationFactor = llvm::ConstantFP::get(llvm::Type::getFloatTy(context), 1.414213562f); + + // Convert the shifted result back to float + llvm::Value * fpShlRes = builder.CreateSIToFP(shlRes, llvm::Type::getFloatTy(context)); + + // Multiply by the compensation factor to adjust for odd FRAC_Q + llvm::Value * compensated = builder.CreateFMul(fpShlRes, compensationFactor); + + // Convert the compensated value back to a fixed-point integer + finalRes = builder.CreateFPToSI(compensated, quantizedType); + } + + return finalRes; } -void quantizeSimpleFPInstruction(Instruction * inInstruction, Type * quantizedType) { - IRBuilder<> Builder(inInstruction); - Instruction * insertPoint = inInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - switch (inInstruction->getOpcode()) - { - case Instruction::FAdd: - { - newInst = Builder.CreateAdd(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FSub: - { - newInst = Builder.CreateSub(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FRem: - { - newInst = Builder.CreateSRem(inInstruction->getOperand(0), inInstruction->getOperand(1)); - break; - } - case Instruction::FCmp: - { - FCmpInst *fcmp_inst = dyn_cast(inInstruction); - newInst = Builder.CreateICmp(quantizePredict(fcmp_inst->getPredicate()), - fcmp_inst->getOperand(0), fcmp_inst->getOperand(1)); - break; - } - /* - * Change fneg(a) to `0-a`. - * */ - case Instruction::FNeg: - { - auto constZero = ConstantInt::get(quantizedType, 0, true); - newInst = Builder.CreateSub(constZero, inInstruction->getOperand(0)); - break; - } - default: - break; - } - inInstruction->replaceAllUsesWith(newInst); - inInstruction->removeFromParent(); -} - -void adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) { - for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { - for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) { - Instruction *llvmIrInstruction = &*itBB++; - switch (llvmIrInstruction->getOpcode()) { - case Instruction::FPToUI: - case Instruction::FPToSI: - case Instruction::SIToFP: - case Instruction::UIToFP: - { - auto sourceOp = llvmIrInstruction->getOperand(0); - if (sourceOp->getType() == llvmIrInstruction->getType()) - { - llvmIrInstruction->replaceAllUsesWith(sourceOp); - llvmIrInstruction->removeFromParent(); - } - } - break; -// case Instruction::ZExt: -// case Instruction::SExt: -// case Instruction::Trunc: - /* - * since the src type changed, adapt the new instruction - * */ - case Instruction::FPExt: - case Instruction::FPTrunc: - { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) { - newInst = Builder.CreateSIToFP( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } else { - newInst = Builder.CreateFPCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - } - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); - break; - } - case Instruction::BitCast: - { - IRBuilder<> Builder(llvmIrInstruction); - Instruction * insertPoint = llvmIrInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = Builder.CreateBitCast( - llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); - llvmIrInstruction->replaceAllUsesWith(newInst); - llvmIrInstruction->removeFromParent(); - break; - } - } - } - } +void +handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) +{ + IRBuilder<> Builder(llvmIrCallInstruction); + auto operand = llvmIrCallInstruction->getOperand(0); + + // Convert the operand to fixed-point format if necessary + if (operand->getType()->isFloatingPointTy()) + { + operand = Builder.CreateFPToSI(operand, quantizedType); + } + + // Create call to the fixed-point sqrt function + //llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); + llvm::Value * sqrtResult = performFixedPointSqrt(Builder, llvmIrCallInstruction->getModule(), operand, quantizedType, FRAC_Q, llvmIrCallInstruction->getContext()); + // No need to apply shl and compensation if it's already done in createFixSqrt + llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); + llvmIrCallInstruction->eraseFromParent(); } void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert) -{ - flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); - - Type* quantizedType; - switch (BIT_WIDTH) { - case 8: - quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); - break; - case 16: - quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); - break; - case 32: - quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); - break; - case 64: - quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); - break; - default: - flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); - return; - } +handleSinCosCall(CallInst * llvmIrCallInstruction, Type * quantizedType, const std::string & mathFunc) +{ + IRBuilder<> builder(llvmIrCallInstruction); - /* - * change the type of this function if it's fp - * */ - if (llvmIrFunction.getReturnType()->isFloatTy() || llvmIrFunction.getReturnType()->isDoubleTy()) { - llvmIrFunction.mutateType(quantizedType); - } + Value * operand = llvmIrCallInstruction->getOperand(0); - /* - * generate hardcode function - fixmul and fixdiv - * */ - llvm::Function * fixmul = createFixMul(&llvmIrFunction, quantizedType, functionsToInsert); - - /* - * quantize the arguments type - * */ - for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) - { - auto paramOp = llvmIrFunction.getArg(idx); - setQuantizedType(paramOp, quantizedType); - } + Value * floatOperand = builder.CreateSIToFP(operand, llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + + // use float version sin/cos + llvm::Function * floatFunc = Intrinsic::getDeclaration( + llvmIrCallInstruction->getModule(), + mathFunc == "sin" ? Intrinsic::sin : Intrinsic::cos, + llvm::Type::getFloatTy(llvmIrCallInstruction->getContext())); + + Value * floatResult = builder.CreateCall(floatFunc, {floatOperand}); + + Value * scaledResult = builder.CreateFMul( + floatResult, + llvm::ConstantFP::get(llvm::Type::getFloatTy(llvmIrCallInstruction->getContext()), FRAC_BASE)); + + Value * quantizedResult = builder.CreateFPToSI(scaledResult, quantizedType); + + llvmIrCallInstruction->replaceAllUsesWith(quantizedResult); + llvmIrCallInstruction->eraseFromParent(); +} + +//void +//handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType, Function * fixsqrt) +//{ +// IRBuilder<> Builder(llvmIrCallInstruction); +// auto operand = llvmIrCallInstruction->getOperand(0); +// +// // Ensure operand is in the correct type +// if (operand->getType()->isFloatingPointTy()) +// { +// operand = Builder.CreateFPToSI(operand, quantizedType); +// } +// +// // Create call to the fixed-point sqrt function +// llvm::Value * sqrtResult = Builder.CreateCall(fixsqrt, {operand}); +// +// // Replace the original instruction with the new fixed-point sqrt result +// llvmIrCallInstruction->replaceAllUsesWith(sqrtResult); +// llvmIrCallInstruction->eraseFromParent(); +//} + + + +void + +// handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) +handleCall(CallInst * llvmIrCallInstruction, Type * quantizedType, std::vector & functionsToInsert, llvm::Function * fixrsqrt) +{ + llvm::errs() << "Handling Call\n"; + Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); + if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) + return; + + std::string funcName = calledFunction->getName().str(); + + if (funcName == "invSqrt") + { + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + llvm::errs() << "Handling invsqrt call\n"; + handleRsqrtCall(llvmIrCallInstruction, quantizedType, fixrsqrt); + + // functionsToErase.erase( + // std::remove(functionsToErase.begin(), functionsToErase.end(), fixrsqrt), + // functionsToErase.end() + // ); + + if (calledFunction->use_empty()) + { + functionsToErase.push_back(calledFunction); + } + } + + if (!calledFunction->getName().startswith("llvm.dbg.value") && + !calledFunction->getName().startswith("llvm.dbg.declare") && + !calledFunction->getName().startswith("llvm.dbg.label")) + { + if (calledFunction->isDeclaration()) + { + // For library functions + IRBuilder<> Builder(llvmIrCallInstruction); + Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + + // Handle specific math functions + if (funcName == "sqrt" || funcName == "sqrtf") + { + // For sqrt + handleSqrtCall(llvmIrCallInstruction, quantizedType); + if (calledFunction->use_empty()) + { + functionsToErase.push_back(calledFunction); + } + } + else if (funcName == "sin" || funcName == "sinf") + { + llvm::errs() << "Handling sin call\n"; + handleSinCosCall(llvmIrCallInstruction, quantizedType, "sin"); + } + else if (funcName == "cos" || funcName == "cosf") + { + llvm::errs() << "Handling cos call\n"; + handleSinCosCall(llvmIrCallInstruction, quantizedType, "cos"); + } + // else + // { + // /* + // * for other lib functions, de-quantize the arguments and quantize the return value + // */ + // // dequantizeArgumentsAndQuantizeReturn(llvmIrCallInstruction, quantizedType); + } + } + else + { + // Quantize the return type if necessary + if (llvmIrCallInstruction->getType()->isPointerTy()) + { + Type * returnElementType = llvmIrCallInstruction->getType()->getPointerElementType(); + if (returnElementType->isFloatTy() || returnElementType->isDoubleTy()) + { + // Keep the pointer type, but track the need for de-quantization + return; + } + } + setQuantizedType(llvmIrCallInstruction, quantizedType); + } +} + +void handleGetElementPtr(GetElementPtrInst *gepInst, Type *quantizedType) { + // 获取原始指令所操作的数组的元素类型 + Type *elementType = gepInst->getSourceElementType(); + + // 仅处理原来操作数为浮点类型的情况(例如 double 或 float) + if (elementType->isFloatingPointTy()) { + IRBuilder<> Builder(gepInst); + + // 获取基指针 + Value *basePtr = gepInst->getPointerOperand(); + + // 收集原 GEP 指令中的所有索引 + SmallVector Indices; + for (Value *Idx : gepInst->indices()) + Indices.push_back(Idx); + + // 直接构造新的 getelementptr 指令, + // 指定元素类型为 quantizedType,从而生成的指针类型为 quantizedType* + // 使用原始指令的名称,不附加后缀 + Value *newGEP = Builder.CreateGEP(quantizedType, basePtr, Indices, gepInst->getName()); + + // 替换所有对原 GEP 指令的使用,并删除原指令 + gepInst->replaceAllUsesWith(newGEP); + gepInst->eraseFromParent(); + + llvm::errs() << "Replaced GEP with quantized version: " << *newGEP << "\n"; + } else { + llvm::errs() << "GEP element type is not floating-point, no quantization applied: " << *gepInst << "\n"; + } +} + + +// Helper function to quantize floating-point parameters +void +quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) +{ + // Skip the function if it is MadgwickAHRSupdateIMU + if (isTargetFunction(func)) + { + llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; + return; + } + + builder.SetInsertPoint(&func.getEntryBlock(), func.getEntryBlock().begin()); + + for (auto & arg : func.args()) + { + if (arg.getType()->isFloatingPointTy()) + { + std::vector usesToReplace; + for (auto & use : arg.uses()) + { + usesToReplace.push_back(&use); + } + Value * processedArg = &arg; + llvm::Instruction * castedFloat = nullptr; + if (arg.getType()->isDoubleTy()) { + castedFloat = cast( + builder.CreateFPTrunc(&arg, llvm::Type::getFloatTy(arg.getContext()), arg.getName() + ".casted_float")); + processedArg = castedFloat; + } + + + + // Create multiplication and rounding instructions + //llvm::Instruction * scaled = cast(builder.CreateFMul(&arg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + llvm::Instruction * scaled = cast( + builder.CreateFMul(processedArg, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat((float)FRAC_BASE)), arg.getName() + ".scaled")); + llvm::Instruction * rounded = cast(builder.CreateFAdd(scaled, llvm::ConstantFP::get(arg.getContext(), llvm::APFloat(0.5f)), arg.getName() + ".rounded")); + // llvm::Instruction * quantized = cast(builder.CreateFPToSI(rounded, llvm::Type::getInt32Ty(arg.getContext()), arg.getName() + ".changed")); + llvm::Instruction * quantized = nullptr; + + switch (BIT_WIDTH) + { + case 16: + { + // Convert floating-point to 16-bit integer + quantized = cast( + builder.CreateFPToSI( + rounded, + llvm::Type::getInt16Ty(arg.getContext()), // Use 16-bit integer type + arg.getName() + ".changed")); + break; + } + default: + { + // Convert floating-point to 32-bit integer + quantized = cast( + builder.CreateFPToSI( + rounded, + llvm::Type::getInt32Ty(arg.getContext()), // Use 32-bit integer type + arg.getName() + ".changed")); + break; + } + } + + // Attach metadata to each instruction + llvm::MDNode * metadataNode = llvm::MDNode::get(arg.getContext(), llvm::MDString::get(arg.getContext(), "quantized")); + scaled->setMetadata("quantized", metadataNode); + rounded->setMetadata("quantized", metadataNode); + quantized->setMetadata("quantized_changed", metadataNode); + + // Replace all occurrences of the original argument with the new quantized value + for (Use * use : usesToReplace) + { + Use & u = *use; + u.set(quantized); + } + // arg.replaceAllUsesWith(quantized); + + llvm::errs() << "Quantizing argument: " << arg.getName() << "\n"; + llvm::errs() << " - Scaled value: " << *scaled << "\n"; + llvm::errs() << " - Rounded value: " << *rounded << "\n"; + llvm::errs() << " - Quantized value: " << *quantized << "\n"; + } + } +} + +bool +shouldSkipFunction(const std::string & functionName) +{ + // List of function names to skip + static const std::unordered_set skipFunctions = { + "llvm.dbg.declare", + "llvm.dbg.value", + "llvm.dbg.label", + "invSqrt", + "fixsqrt", + "fixrsqrt", + "fixmul", + "llvm.sqrt.f64", + "llvm.sqrt.f32", + "sqrt", + "sqrtf"}; + + return skipFunctions.find(functionName) != skipFunctions.end(); +} + +bool +isTargetFunction(Function & func) +{ + return func.getName() == "MadgwickAHRSupdateIMU" || + func.getName() == "MahonyAHRSupdateIMU"; +} + +void +quantizeArguments(llvm::Function & llvmIrFunction, llvm::Type * quantizedType) +{ + if (isTargetFunction(llvmIrFunction)) + { + llvm::errs() << "Quantizing arguments for function: " << llvmIrFunction.getName() << "\n"; + + // Process each argument of the function + for (int idx = 0; idx < llvmIrFunction.arg_size(); idx++) + { + auto * paramOp = llvmIrFunction.getArg(idx); + setQuantizedType(paramOp, quantizedType); // Assuming setQuantizedType is defined elsewhere + } + } +} +void +adaptTypeCast(llvm::Function & llvmIrFunction, Type * quantizedType) +{ for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) { for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) @@ -442,250 +2196,313 @@ irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::ve Instruction * llvmIrInstruction = &*itBB++; switch (llvmIrInstruction->getOpcode()) { - case Instruction::Alloca: - if (auto llvmIrAllocaInstruction = dyn_cast(llvmIrInstruction)) - { - auto allocaType = llvmIrAllocaInstruction->getAllocatedType(); - auto newType = quantizedType; - if (allocaType->getTypeID() == Type::ArrayTyID) { - newType = ArrayType::get(quantizedType, - allocaType->getArrayNumElements()); - allocaType = allocaType->getArrayElementType(); - } - if (allocaType->isDoubleTy() || allocaType->isFloatTy()) { - llvmIrAllocaInstruction->setAllocatedType(newType); - } - setQuantizedType(llvmIrAllocaInstruction, newType); - } - break; - case Instruction::Call: - if (auto llvmIrCallInstruction = dyn_cast(llvmIrInstruction)) - { - Function * calledFunction = llvmIrCallInstruction->getCalledFunction(); - if (calledFunction == nullptr || !calledFunction->hasName() || calledFunction->getName().empty()) - break; - if (!calledFunction->getName().startswith("llvm.dbg.value") && - !calledFunction->getName().startswith("llvm.dbg.declare") && - !calledFunction->getName().startswith("llvm.dbg.label")) - { - if (calledFunction->isDeclaration()) - { - IRBuilder<> Builder(llvmIrCallInstruction); - Instruction * insertPoint = llvmIrCallInstruction->getNextNode(); - Builder.SetInsertPoint(insertPoint); - Value * newInst = nullptr; - if (calledFunction->getName().str() == "sqrt") { - /* - * if the arg's type is int, convert to fp, - * after the call node, convert to int and shl FRAC_Q/2 - * - * int32_t res = (int32_t)sqrt(x)<<(FRAC_Q/2); - * if (FRAC_Q%2) - * return res*1.414213562; - * else - * return res; - * - * %25 = sitofp i32 %0 to double - * %26 = call double @sqrt(double %25) #3 - * %27 = fptosi double %26 to i32 - * %28 = shl i32 %27, 4 - * */ - auto operand = llvmIrCallInstruction->getOperand(0); - if (operand->getType()->isIntegerTy()) { - Value * newOperand = Builder.CreateSIToFP( - operand, llvmIrCallInstruction->getType()); - llvmIrCallInstruction->setOperand(0, newOperand); - } - auto cloneInst = llvmIrCallInstruction->clone(); - Value * fptosiInst = Builder.CreateFPToSI( - cloneInst, quantizedType); - Value * shlInst = Builder.CreateShl(fptosiInst, FRAC_Q/2); - Value * resInst = nullptr; - /* - * if (FRAC_Q%2) then multiply with 1.414213562; - * */ - if (FRAC_Q%2) { - Value * lhsCompensateInst = Builder.CreateSIToFP( - shlInst, llvmIrCallInstruction->getType()); - auto compensateNum = ConstantFP::get(llvmIrCallInstruction->getType(), - 1.414213562); - Value * mulInst = Builder.CreateFMul(lhsCompensateInst, compensateNum); - resInst = Builder.CreateFPToSI(mulInst, quantizedType); - } else { - resInst = shlInst; - } - llvmIrCallInstruction->replaceAllUsesWith(resInst); - ReplaceInstWithInst(llvmIrCallInstruction, cloneInst); - } - else { - /* - * for other lib functions, de-quantize the arguments and quantize the return value - * */ - } - } else { - /* - * for user-defined function, quantize the arguments - * */ - for (size_t idx = 0; idx < llvmIrCallInstruction->getNumOperands() - 1; idx++) - { - setQuantizedType(llvmIrCallInstruction->getOperand(idx), quantizedType); - } - quantizeConstant(llvmIrCallInstruction, quantizedType); - /* - * then quantize the return type - * */ - setQuantizedType(llvmIrCallInstruction, quantizedType); - } - } - } - break; - case Instruction::GetElementPtr: - if (auto gepInst = dyn_cast(llvmIrInstruction)) - { - auto gepType = gepInst->getType(); - auto sourceType = quantizedType; -// bool isPointer = false; -// unsigned pointerAddr = 0; -// if (gepType->isPointerTy()) { -// isPointer = true; -// pointerAddr = gepType->getPointerAddressSpace(); -// valueType = gepType->getPointerElementType(); -// } - if (gepInst->getSourceElementType()->getTypeID() == Type::ArrayTyID) { - sourceType = ArrayType::get(quantizedType, - gepInst->getSourceElementType()->getArrayNumElements()); - } -// if (isPointer) { -// inValue->mutateType(quantizedType->getPointerTo(pointerAddr)); -// } -// if (gepType->isDoubleTy() || gepType->isFloatTy()) { - gepInst->setSourceElementType(sourceType); - gepInst->setResultElementType(quantizedType); -// } - } - case Instruction::Load: - case Instruction::PHI: - { - setQuantizedType(llvmIrInstruction, quantizedType); - } - break; - - case Instruction::Store: - { - /* - * If either of the operands is constant, change it to a int value - * */ - setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); - quantizeConstant(llvmIrInstruction, quantizedType); - } - break; - - /* - * For fmul/fdiv, - * - * if either one of the operands is a constant value, simplify it by multiplying with 10^n, - * then replace the instruction to mul/div; - * - * else substitute this instruction to a pre-implemented function: mulfix/divfix. - * */ - case Instruction::FMul: - case Instruction::FDiv: - { - if (isa(llvmIrInstruction->getOperand(0)) || - isa(llvmIrInstruction->getOperand(1))) { - simplifyConstant(llvmIrInstruction, quantizedType); - } - else { - substituteHardcodeFunc(llvmIrInstruction, quantizedType, fixmul); - } - break; - } - - /* - * If either one of the operands is a constant value, quantize it, - * then replace the instruction to the int version. - * */ - case Instruction::FCmp: - case Instruction::FAdd: - case Instruction::FSub: - case Instruction::FRem: - { - quantizeConstant(llvmIrInstruction, quantizedType); - } - case Instruction::FNeg: - { - quantizeSimpleFPInstruction(llvmIrInstruction, quantizedType); - break; - } - -// case Instruction::Add: -// case Instruction::Sub: -// case Instruction::Mul: -// case Instruction::UDiv: -// case Instruction::SDiv: -// case Instruction::URem: -// case Instruction::SRem: -// -// case Instruction::Shl: -// case Instruction::LShr: -// case Instruction::AShr: -// case Instruction::And: -// case Instruction::Or: -// case Instruction::Xor: -// -// case Instruction::ICmp: - case Instruction::FPToUI: case Instruction::FPToSI: + case Instruction::SIToFP: case Instruction::UIToFP: - case Instruction::ZExt: - case Instruction::SExt: - case Instruction::Trunc: - case Instruction::FPExt: + { + auto sourceOp = llvmIrInstruction->getOperand(0); + if (sourceOp->getType() == llvmIrInstruction->getType()) + { + llvmIrInstruction->replaceAllUsesWith(sourceOp); + llvmIrInstruction->removeFromParent(); + } + } + break; + // case Instruction::ZExt: + // case Instruction::SExt: + // case Instruction::Trunc: + /* + * since the src type changed, adapt the new instruction + * */ + case Instruction::FPExt: case Instruction::FPTrunc: - case Instruction::BitCast: - break; - - case Instruction::Ret: - case Instruction::Switch: - case Instruction::Br: - case Instruction::Select: - case Instruction::IndirectBr: - case Instruction::Invoke: - case Instruction::Resume: - case Instruction::Unreachable: - case Instruction::CleanupRet: - case Instruction::CatchRet: - case Instruction::CatchSwitch: - case Instruction::CallBr: - case Instruction::Fence: - case Instruction::AtomicCmpXchg: - case Instruction::AtomicRMW: - case Instruction::PtrToInt: - case Instruction::IntToPtr: - case Instruction::AddrSpaceCast: - case Instruction::CleanupPad: - case Instruction::CatchPad: - case Instruction::UserOp1: - case Instruction::UserOp2: - case Instruction::VAArg: - case Instruction::ExtractElement: - case Instruction::InsertElement: - case Instruction::ShuffleVector: - case Instruction::ExtractValue: - case Instruction::InsertValue: - case Instruction::LandingPad: - case Instruction::Freeze: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = nullptr; + if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + { + newInst = Builder.CreateSIToFP( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + else + { + newInst = Builder.CreateFPCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + } + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); break; - default: + } + case Instruction::BitCast: + { + IRBuilder<> Builder(llvmIrInstruction); + Instruction * insertPoint = llvmIrInstruction->getNextNode(); + Builder.SetInsertPoint(insertPoint); + Value * newInst = Builder.CreateBitCast( + llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + llvmIrInstruction->replaceAllUsesWith(newInst); + llvmIrInstruction->removeFromParent(); break; + } } } } +} - adaptTypeCast(llvmIrFunction, quantizedType); - return; -} -} +void +irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, + + int maxPrecisionBits) +{ + { + FRAC_Q = maxPrecisionBits; + flexprint(N->Fe, N->Fm, N->Fpinfo, "\tauto quantization.\n"); + llvm::errs() << "Entering irPassLLVMIRAutoQuantization\n"; + + // Usage in the original function + std::string functionName = llvmIrFunction.getName().str(); + if (shouldSkipFunction(functionName)) + { + return; + } + + if (!shouldProcessFunction(llvmIrFunction)) + { + llvm::errs() << "Skipping function: " << llvmIrFunction.getName() << "\n"; + return; + } + + + Type * quantizedType; + switch (BIT_WIDTH) + { + case 8: + quantizedType = Type::getInt8Ty(llvmIrFunction.getContext()); + break; + case 16: + quantizedType = Type::getInt16Ty(llvmIrFunction.getContext()); + break; + case 32: + quantizedType = Type::getInt32Ty(llvmIrFunction.getContext()); + break; + case 64: + quantizedType = Type::getInt64Ty(llvmIrFunction.getContext()); + break; + default: + flexprint(N->Fe, N->Fm, N->Fperr, "\tunknown int type.\n"); + llvm_unreachable("Unknown bit width for quantization"); + return; + } + + // Save the parent module + Module * module = llvmIrFunction.getParent(); + if (!module) + { + llvm::errs() << "Error: Function does not have a parent module.\n"; + return; + } + + // Iterate over the function's arguments to apply quantization + llvm::IRBuilder<> builder(llvmIrFunction.getContext()); + quantizeFunctionArguments(llvmIrFunction, builder); + quantizeArguments(llvmIrFunction, quantizedType); + + +#ifdef IS_POINTER + llvm::errs() << "Performing bitcasting for float pointer arguments.\n"; + bitcastFloatPtrArgs(llvmIrFunction, builder); +#else + llvm::errs() << "Skipping bitcasting for float pointer arguments.\n"; +#endif + + // Update global variables to integer type + updateGlobalVariables(module, quantizedType); + + updateInternalConstants(module, quantizedType); + + /* + * generate hardcode function + * */ + //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); + llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + // functionsToErase.push_back(fixrsqrt); + + for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) + { + for (BasicBlock::iterator itBB = llvmIrBasicBlock.begin(); itBB != llvmIrBasicBlock.end();) + { + Instruction * llvmIrInstruction = &*itBB++; + + + + llvm::errs() << "Processing instruction in main: " << *llvmIrInstruction << "\n"; + switch (llvmIrInstruction->getOpcode()) + { + case Instruction::Alloca: + handleAlloca(cast(llvmIrInstruction), quantizedType); + break; + case Instruction::Call: + // handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); + handleCall(cast(llvmIrInstruction), quantizedType, functionsToInsert, fixrsqrt); + break; + case Instruction::GetElementPtr: + handleGetElementPtr(cast(llvmIrInstruction), quantizedType); + break; + case Instruction::Load: + { + llvm::errs() << "Handling load\n"; + handleLoad(llvmIrInstruction, quantizedType); + break; + } + break; + case Instruction::PHI: + { + // setQuantizedType(llvmIrInstruction, quantizedType); + llvm::errs() << "handle phi " << *llvmIrInstruction << "\n"; + handlePhi(llvmIrInstruction, quantizedType); + } + break; + + case Instruction::Store: + /* + * If either of the operands is constant, change it to a int value + * */ + llvm::errs() << "handle store " << *llvmIrInstruction << "\n"; + // setQuantizedType(llvmIrInstruction->getOperand(0), quantizedType); + handleStore(llvmIrInstruction, quantizedType); + break; + + case Instruction::FMul: + { + handleFMul(llvmIrInstruction, quantizedType); + break; + } + + /* + * If either one of the operands is a constant value, quantize it, + * then replace the instruction to the int version. + * */ + case Instruction::FDiv: + handleFDiv(llvmIrInstruction, quantizedType); + break; + + case Instruction::FCmp: + handleFCmp(llvmIrInstruction, quantizedType); + break; + case Instruction::FAdd: + { + handleFAdd(llvmIrInstruction, quantizedType); + break; + } + case Instruction::FSub: + { + handleFSub(llvmIrInstruction, quantizedType); + break; + } + case Instruction::FRem: + case Instruction::FNeg: + { + handleFNeg(llvmIrInstruction, quantizedType); + break; + } + + case Instruction::FPToUI: + case Instruction::FPToSI: + { + handleFpToSi(llvmIrInstruction, quantizedType); + break; + } + case Instruction::SIToFP: + case Instruction::UIToFP: + + case Instruction::ZExt: + case Instruction::SExt: + case Instruction::Trunc: + case Instruction::FPExt: + case Instruction::FPTrunc: + // { + // IRBuilder<> Builder(llvmIrInstruction); + // Instruction * insertPoint = llvmIrInstruction->getNextNode(); + // Builder.SetInsertPoint(insertPoint); + // Value * newInst = nullptr; + // if (llvmIrInstruction->getOperand(0)->getType()->isIntegerTy()) + // { + // newInst = Builder.CreateSIToFP( + // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // } + // else + // { + // newInst = Builder.CreateFPCast( + // llvmIrInstruction->getOperand(0), llvmIrInstruction->getType()); + // } + // llvmIrInstruction->replaceAllUsesWith(newInst); + // llvmIrInstruction->removeFromParent(); + // break; + // } + + case Instruction::BitCast: + case Instruction::Ret: + // handleReturn(cast(llvmIrInstruction), quantizedType); + // break; + + case Instruction::Switch: + case Instruction::Br: + { + llvm::errs() << "Skipping Br instruction: " << *llvmIrInstruction << "\n"; + break; + } + case Instruction::Select: + { + handleSelect(llvmIrInstruction, quantizedType); + break; + + } + case Instruction::IndirectBr: + case Instruction::Invoke: + case Instruction::Resume: + case Instruction::Unreachable: + case Instruction::CleanupRet: + case Instruction::CatchRet: + case Instruction::CatchSwitch: + case Instruction::CallBr: + case Instruction::Fence: + case Instruction::AtomicCmpXchg: + case Instruction::AtomicRMW: + case Instruction::PtrToInt: + case Instruction::IntToPtr: + case Instruction::AddrSpaceCast: + case Instruction::CleanupPad: + case Instruction::CatchPad: + case Instruction::UserOp1: + case Instruction::UserOp2: + case Instruction::VAArg: + case Instruction::ExtractElement: + case Instruction::InsertElement: + case Instruction::ShuffleVector: + case Instruction::ExtractValue: + case Instruction::InsertValue: + case Instruction::LandingPad: + case Instruction::Freeze: + + + default: + break; + } + } + } + + + + // adaptTypeCast(llvmIrFunction, quantizedType); + + // Process functions that are whitelisted for dequantization + // processWhitelistedFunctions(*module, whitelist); + return; + } + +} \ No newline at end of file diff --git a/src/newton/newton-irPass-LLVMIR-quantization.h b/src/newton/newton-irPass-LLVMIR-quantization.h index 8a18ececa..28adbfada 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.h +++ b/src/newton/newton-irPass-LLVMIR-quantization.h @@ -1,32 +1,32 @@ /* - Authored 2022. Pei Mu. - All rights reserved. - Redistribution and use in source and binary forms, with or without - modification, are permitted provided that the following conditions - are met: - * Redistributions of source code must retain the above - copyright notice, this list of conditions and the following - disclaimer. - * Redistributions in binary form must reproduce the above - copyright notice, this list of conditions and the following - disclaimer in the documentation and/or other materials - provided with the distribution. - * Neither the name of the author nor the names of its - contributors may be used to endorse or promote products - derived from this software without specific prior written - permission. - THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS - "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT - LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS - FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE - COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, - INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, - BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; - LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER - CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT - LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN - ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE - POSSIBILITY OF SUCH DAMAGE. +Authored 2022. Pei Mu. +All rights reserved. +Redistribution and use in source and binary forms, with or without +modification, are permitted provided that the following conditions +are met: +* Redistributions of source code must retain the above + copyright notice, this list of conditions and the following + disclaimer. +* Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following + disclaimer in the documentation and/or other materials + provided with the distribution. +* Neither the name of the author nor the names of its + contributors may be used to endorse or promote products + derived from this software without specific prior written + permission. +THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +"AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS +FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE +COPYRIGHT OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, +INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, +BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; +LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER +CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT +LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN +ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE +POSSIBILITY OF SUCH DAMAGE. */ #include "newton-irPass-LLVMIR-rangeAnalysis.h" @@ -35,10 +35,34 @@ extern "C" { #endif /* __cplusplus */ +extern std::vector functionsToErase; +extern std::vector globalsToErase; +//extern std::vector instructionsToErase; void -irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert); +irPassLLVMIRAutoQuantization(State * N, llvm::Function & llvmIrFunction, std::vector& functionsToInsert,int maxPrecisionBits); +//void +//irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vector &functionsToInsert, +// std::map>> &virtualRegisterVectorRange, +// int maxPrecisionBits); + +//void irPassLLVMIRAutoQuantization(State *N, Function &F, std::vector &functionsToInsert, +// int maxPrecisionBits, std::map>> &virtualRegisterVectorRange, +// int bitWidth, bool enableVectorization, bool enableRangeAnalysis); + + + + + + + + +extern +//void eraseOldFunctions(); + void eraseOldFunctions(llvm::Module &M); +void eraseOldInstructions(); +void eraseOldGlobals(); #ifdef __cplusplus } /* extern "C" */ -#endif /* __cplusplus */ +#endif /* __cplusplus */ \ No newline at end of file diff --git a/src/newton/newton-irPass-sensors.c b/src/newton/newton-irPass-sensors.c index 9f4db81e7..76280e41b 100644 --- a/src/newton/newton-irPass-sensors.c +++ b/src/newton/newton-irPass-sensors.c @@ -280,6 +280,31 @@ irPassSensorsLoadSensor(State * N, IrNode * sensorNode) } } + /* + Modality resolution + * TODO: Currently only handles one resolution setting per sensor modality. + + + */ + tempNode = NULL; + n = 0; + do + { + tempNode = findNthIrNodeOfType(N, sensorNode, kNewtonIrNodeType_PresolutionStatement, n++); + if (tempNode == NULL) + { + error(N, "no resolution statement found for modality"); + break; + } + } while (strcmp(modality->identifier, tempNode->irLeftChild->tokenString) != 0); + + if (tempNode != NULL) + { + modality->resolution = RLL(tempNode)->value; + } + + + /* * Prepare iterator for next `while` iteration */ diff --git a/src/newton/runPass.cpp b/src/newton/runPass.cpp new file mode 100644 index 000000000..07d9462f3 --- /dev/null +++ b/src/newton/runPass.cpp @@ -0,0 +1,45 @@ + +#include "llvm/IR/LLVMContext.h" +#include "llvm/IR/Module.h" +#include "llvm/IRReader/IRReader.h" +#include "llvm/Support/SourceMgr.h" +#include "llvm/Support/raw_ostream.h" +#include "llvm/Transforms/Utils/Cloning.h" +#include "llvm/Transforms/Scalar.h" +#include "llvm/IR/LegacyPassManager.h" + +// 包含你的 RangeAnalysisPass 头文件 +#include "myRangeAnalysis.h" + +using namespace llvm; + +int main(int argc, char **argv) { + if (argc < 2) { + errs() << "Usage: " << argv[0] << " \n"; + return 1; + } + + // 解析输入文件 + LLVMContext Context; + SMDiagnostic Err; + std::unique_ptr M = parseIRFile(argv[1], Err, Context); + + if (!M) { + Err.print(argv[0], errs()); + return 1; + } + + // 创建 PassManager + legacy::PassManager PM; + + // 添加自定义 Pass + PM.add(new RangeAnalysisPass()); + + // 运行 Pass + PM.run(*M); + + // 输出结果 + M->print(outs(), nullptr); + + return 0; +} From bb03344ed167c5f01a902de8f90f728ffb1bef53 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 8 Jun 2025 12:32:09 +0100 Subject: [PATCH 187/213] remove redundant line * quantize. --- ...b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9.txt | 48 +++++++++++++++++++ ...2e6908d8904f59081e7b1adbef130bdbf0c3ac.txt | 48 +++++++++++++++++++ .../newton/llvm-ir/c-files/sensfusion6.c | 1 - 3 files changed, 96 insertions(+), 1 deletion(-) create mode 100644 analysis/statistics/c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9.txt create mode 100644 analysis/statistics/e22e6908d8904f59081e7b1adbef130bdbf0c3ac.txt diff --git a/analysis/statistics/c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9.txt b/analysis/statistics/c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9.txt new file mode 100644 index 000000000..0d07ce9ac --- /dev/null +++ b/analysis/statistics/c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9.txt @@ -0,0 +1,48 @@ + +changeset: 1778:c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9 +char kNewtonVersion[] = "0.3-alpha-1778 (c4b5e5f9dbd8d5fe177ddde16b70d3eb4f69d9f9) (build 06-04-2025-20:59-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/e22e6908d8904f59081e7b1adbef130bdbf0c3ac.txt b/analysis/statistics/e22e6908d8904f59081e7b1adbef130bdbf0c3ac.txt new file mode 100644 index 000000000..9412a5694 --- /dev/null +++ b/analysis/statistics/e22e6908d8904f59081e7b1adbef130bdbf0c3ac.txt @@ -0,0 +1,48 @@ + +changeset: 1779:e22e6908d8904f59081e7b1adbef130bdbf0c3ac +char kNewtonVersion[] = "0.3-alpha-1779 (e22e6908d8904f59081e7b1adbef130bdbf0c3ac) (build 06-04-2025-23:24-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/c-files/sensfusion6.c b/applications/newton/llvm-ir/c-files/sensfusion6.c index 24fd45364..510b6df6d 100644 --- a/applications/newton/llvm-ir/c-files/sensfusion6.c +++ b/applications/newton/llvm-ir/c-files/sensfusion6.c @@ -98,7 +98,6 @@ void sensfusion6UpdateQImpl(float gx, float gy, float gz, float ax, float ay, fl _8qx = 8.0f * qx; _8qy = 8.0f * qy; qwqw = qw * qw; - qwqw = qw * qw; qxqx = qx * qx; qyqy = qy * qy; qzqz = qz * qz; From d7304f6e06b411d069863abba0e19dab013b420a Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Thu, 12 Jun 2025 19:24:12 +0100 Subject: [PATCH 188/213] update * quantize. --- applications/newton/llvm-ir/c-files/e_j0.c | 121 +-------------------- applications/newton/llvm-ir/c-files/e_y0.c | 20 +--- 2 files changed, 11 insertions(+), 130 deletions(-) diff --git a/applications/newton/llvm-ir/c-files/e_j0.c b/applications/newton/llvm-ir/c-files/e_j0.c index df73a3030..75ab7d459 100644 --- a/applications/newton/llvm-ir/c-files/e_j0.c +++ b/applications/newton/llvm-ir/c-files/e_j0.c @@ -6,7 +6,7 @@ * * Developed at SunSoft, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice + * software is freely granted, provided that this notice * is preserved. * ==================================================== */ @@ -30,20 +30,20 @@ * (To avoid cancellation, use * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) * to compute the worse one.) - * + * * 3 Special cases * j0(nan)= nan * j0(0) = 1 * j0(inf) = 0 - * + * * Method -- y0(x): * 1. For x<2. - * Since + * Since * y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...) * therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. * We use the following function to approximate y0, * y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2 - * where + * where * U(z) = u00 + u01*z + ... + u06*z^6 * V(z) = 1 + v01*z + ... + v04*z^4 * with absolute approximation error bounded by 2**-72. @@ -56,13 +56,7 @@ * 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. */ -#define IEEE_IMPLEMENT_SIN_COS - #include "fdlibm.h" -#ifdef IEEE_IMPLEMENT_SIN_COS -#include "s_sin.c" -#include "s_cos.c" -#endif #ifdef __STDC__ static double pzero(double), qzero(double); @@ -76,9 +70,7 @@ static const double static double #endif huge = 1e300, -#ifndef IEEE_IMPLEMENT_SIN_COS one = 1.0, -#endif invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ tpi = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ /* R0/S0 on [0, 2.00] */ @@ -91,9 +83,7 @@ S02 = 1.16926784663337450260e-04, /* 0x3F1EA6D2, 0xDD57DBF4 */ S03 = 5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */ S04 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */ -#ifndef IEEE_IMPLEMENT_SIN_COS static double zero = 0.0; -#endif /* * Definitions generated from Newton @@ -107,9 +97,6 @@ double __ieee754_j0(x) bmx055xAcceleration x; #endif { -#ifdef ASSUME - __builtin_assume(x > -16 && x < 16); -#endif double z, s,c,ss,cc,r,u,v; int hx,ix; @@ -418,100 +405,4 @@ static double qzero(x) r = p[0]+z*(p[1]+z*(p[2]+z*(p[3]+z*(p[4]+z*p[5])))); s = one+z*(q[0]+z*(q[1]+z*(q[2]+z*(q[3]+z*(q[4]+z*q[5]))))); return (-.125 + r/s)/x; -} - -// clang ../c-files/e_j0.c -D DEBUG -D ASSUME -O3 -o e_j0_assume -lm -#ifdef DEBUG - -#include -#include -#include -#include -#include - -#define iteration_num 500000 - -typedef struct timespec timespec; -timespec diff(timespec start, timespec end) -{ - timespec temp; - if ((end.tv_nsec-start.tv_nsec)<0) { - temp.tv_sec = end.tv_sec-start.tv_sec-1; - temp.tv_nsec = 1000000000+end.tv_nsec-start.tv_nsec; - } else { - temp.tv_sec = end.tv_sec-start.tv_sec; - temp.tv_nsec = end.tv_nsec-start.tv_nsec; - } - return temp; -} - -timespec sum(timespec t1, timespec t2) { - timespec temp; - if (t1.tv_nsec + t2.tv_nsec >= 1000000000) { - temp.tv_sec = t1.tv_sec + t2.tv_sec + 1; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec - 1000000000; - } else { - temp.tv_sec = t1.tv_sec + t2.tv_sec; - temp.tv_nsec = t1.tv_nsec + t2.tv_nsec; - } - return temp; -} - -void printTimeSpec(timespec t, const char* prefix) { - printf("%s: %d.%09d\n", prefix, (int)t.tv_sec, (int)t.tv_nsec); -} - -timespec tic( ) -{ - timespec start_time; - clock_gettime(CLOCK_REALTIME, &start_time); - return start_time; -} - -void toc( timespec* start_time, const char* prefix ) -{ - timespec current_time; - clock_gettime(CLOCK_REALTIME, ¤t_time); - printTimeSpec( diff( *start_time, current_time ), prefix ); - *start_time = current_time; -} - -/* - * random floating point, [min, max] - * */ -static bmx055xAcceleration -randomDouble(bmx055xAcceleration min, bmx055xAcceleration max) -{ - bmx055xAcceleration randDbValue = min + 1.0 * rand() / RAND_MAX * (max - min); - return randDbValue; -} - -int main(int argc, char** argv) { - double parameters[2]; - char *pEnd; - if (argc == 3) { - for (size_t idx = 0; idx < argc - 1; idx++) { - parameters[idx] = strtod(argv[idx + 1], &pEnd); - } - } else { - parameters[0] = 3.0; - parameters[1] = 10.0; - } - double result[iteration_num]; - bmx055xAcceleration xOps[iteration_num]; - for (size_t idx = 0; idx < iteration_num; idx++) { - xOps[idx] = randomDouble(parameters[0], parameters[1]); - } - - timespec timer = tic(); - for (size_t idx = 0; idx < iteration_num; idx++) { - result[idx] = __ieee754_j0(xOps[idx]); - } - - toc(&timer, "computation delay"); - - printf("results: %f\t%f\t%f\t%f\t%f\n", result[0], result[1], result[2], result[3], result[4]); - - return 0; -} -#endif +} \ No newline at end of file diff --git a/applications/newton/llvm-ir/c-files/e_y0.c b/applications/newton/llvm-ir/c-files/e_y0.c index 735051697..09f82fed6 100644 --- a/applications/newton/llvm-ir/c-files/e_y0.c +++ b/applications/newton/llvm-ir/c-files/e_y0.c @@ -6,7 +6,7 @@ * * Developed at SunSoft, a Sun Microsystems, Inc. business. * Permission to use, copy, modify, and distribute this - * software is freely granted, provided that this notice + * software is freely granted, provided that this notice * is preserved. * ==================================================== */ @@ -30,20 +30,20 @@ * (To avoid cancellation, use * sin(x) +- cos(x) = -cos(2x)/(sin(x) -+ cos(x)) * to compute the worse one.) - * + * * 3 Special cases * j0(nan)= nan * j0(0) = 1 * j0(inf) = 0 - * + * * Method -- y0(x): * 1. For x<2. - * Since + * Since * y0(x) = 2/pi*(j0(x)*(ln(x/2)+Euler) + x^2/4 - ...) * therefore y0(x)-2/pi*j0(x)*ln(x) is an even function. * We use the following function to approximate y0, * y0(x) = U(z)/V(z) + (2/pi)*(j0(x)*ln(x)), z= x^2 - * where + * where * U(z) = u00 + u01*z + ... + u06*z^6 * V(z) = 1 + v01*z + ... + v04*z^4 * with absolute approximation error bounded by 2**-72. @@ -56,13 +56,7 @@ * 3. Special cases: y0(0)=-inf, y0(x<0)=NaN, y0(inf)=0. */ -#define IEEE_IMPLEMENT_SIN_COS - #include "fdlibm.h" -#ifdef IEEE_IMPLEMENT_SIN_COS -#include "s_sin.c" -#include "s_cos.c" -#endif #ifdef __STDC__ static double pzero(double), qzero(double); @@ -76,9 +70,7 @@ static const double static double #endif huge = 1e300, -#ifndef IEEE_IMPLEMENT_SIN_COS one = 1.0, -#endif invsqrtpi= 5.64189583547756279280e-01, /* 0x3FE20DD7, 0x50429B6D */ tpi = 6.36619772367581382433e-01, /* 0x3FE45F30, 0x6DC9C883 */ /* R0/S0 on [0, 2.00] */ @@ -91,9 +83,7 @@ S02 = 1.16926784663337450260e-04, /* 0x3F1EA6D2, 0xDD57DBF4 */ S03 = 5.13546550207318111446e-07, /* 0x3EA13B54, 0xCE84D5A9 */ S04 = 1.16614003333790000205e-09; /* 0x3E1408BC, 0xF4745D8F */ -#ifndef IEEE_IMPLEMENT_SIN_COS static double zero = 0.0; -#endif #ifdef __STDC__ double __ieee754_j0(double x) From c08963a3535e339784693b9d6137e06bfb9d1994 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Mon, 16 Jun 2025 23:42:09 +0800 Subject: [PATCH 189/213] upload firefly performance * quantize. --- .../e_j0_size_reduction_comparison.png | Bin 0 -> 79247 bytes .../firefly_perf/e_j0_speedup_comparison.png | Bin 0 -> 79169 bytes .../e_y0_size_reduction_comparison.png | Bin 0 -> 74911 bytes .../firefly_perf/e_y0_speedup_comparison.png | Bin 0 -> 76441 bytes .../firefly_perf/perf_quant.log | 103 ++++++++++++++++++ .../firefly_perf/perf_woquant.log | 103 ++++++++++++++++++ 6 files changed, 206 insertions(+) create mode 100644 applications/newton/llvm-ir/performance_test/firefly_perf/e_j0_size_reduction_comparison.png create mode 100644 applications/newton/llvm-ir/performance_test/firefly_perf/e_j0_speedup_comparison.png create mode 100644 applications/newton/llvm-ir/performance_test/firefly_perf/e_y0_size_reduction_comparison.png create mode 100644 applications/newton/llvm-ir/performance_test/firefly_perf/e_y0_speedup_comparison.png create mode 100644 applications/newton/llvm-ir/performance_test/firefly_perf/perf_quant.log create mode 100644 applications/newton/llvm-ir/performance_test/firefly_perf/perf_woquant.log diff --git a/applications/newton/llvm-ir/performance_test/firefly_perf/e_j0_size_reduction_comparison.png b/applications/newton/llvm-ir/performance_test/firefly_perf/e_j0_size_reduction_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..cb83cf0cc071b0e9becdfaacc0c3724869c5e58d GIT binary patch literal 79247 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 literal 0 HcmV?d00001 diff --git a/applications/newton/llvm-ir/performance_test/firefly_perf/e_j0_speedup_comparison.png b/applications/newton/llvm-ir/performance_test/firefly_perf/e_j0_speedup_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..5c450246cbd13e3df5711b2e95d68a1fa9237a7b GIT binary patch literal 79169 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 U7xWy>muCP1Pgg&ebxsLQ04$r4MgRZ+ literal 0 HcmV?d00001 diff --git a/applications/newton/llvm-ir/performance_test/firefly_perf/e_y0_size_reduction_comparison.png b/applications/newton/llvm-ir/performance_test/firefly_perf/e_y0_size_reduction_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..220a2d48f325a587ff03bf16043600fc251d586d GIT binary patch literal 74911 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%0A6x$FSPII=)Nkt8g?CzxDm6m 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 KcW(SYJN_S;LsfwQ literal 0 HcmV?d00001 diff --git a/applications/newton/llvm-ir/performance_test/firefly_perf/e_y0_speedup_comparison.png b/applications/newton/llvm-ir/performance_test/firefly_perf/e_y0_speedup_comparison.png new file mode 100644 index 0000000000000000000000000000000000000000..e556145a814e5c49ea19c99bee8cb67ca0967c66 GIT binary patch literal 76441 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«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%0AShJ!s5e>P`ye?7zq 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 W|I6I=rw1zdV=;2P`AM@G8~+dU8A2@p literal 0 HcmV?d00001 diff --git a/applications/newton/llvm-ir/performance_test/firefly_perf/perf_quant.log b/applications/newton/llvm-ir/performance_test/firefly_perf/perf_quant.log new file mode 100644 index 000000000..2133a5cbd --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/firefly_perf/perf_quant.log @@ -0,0 +1,103 @@ +test case param precision_bits instruction count time consumption ir lines library size compile time +perf_j0 -2.000000 2.000000 12 -1 0.00419785 672 12806 8.47906 +perf_j0_opt -2.000000 2.000000 12 -1 0.00414156 553 9086 8.87451 +speed up after optimization -2.000000 2.000000 0% 1% 21% 40% -4% +perf_j0 -4.000000 4.000000 12 -1 0.0182643 672 12806 8.60355 +perf_j0_opt -4.000000 4.000000 12 -1 0.0162081 553 9070 8.77285 +speed up after optimization -4.000000 4.000000 0% 13% 21% 41% -2% +perf_j0 -8.000000 8.000000 12 -1 0.0256526 672 12806 8.6312 +perf_j0_opt -8.000000 8.000000 12 -1 0.0223749 553 9062 9.12953 +speed up after optimization -8.000000 8.000000 0% 15% 21% 41% -5% +perf_j0 -16.000000 16.000000 12 -1 0.0292059 672 12806 8.71164 +perf_j0_opt -16.000000 16.000000 12 -1 0.025191 553 9054 8.77293 +speed up after optimization -16.000000 16.000000 0% 16% 21% 41% -1% +perf_j0 -125.000000 125.000000 16 -1 0.0320668 672 12806 8.52784 +perf_j0_opt -125.000000 125.000000 16 -1 0.02817 553 9070 9.18924 +speed up after optimization -125.000000 125.000000 0% 14% 21% 41% -7% +perf_j0 -40.000000 110.000000 10 -1 0.0317711 672 12806 8.5338 +perf_j0_opt -40.000000 110.000000 10 -1 0.0317366 551 8990 11.0385 +speed up after optimization -40.000000 110.000000 0% 0% 21% 42% -23% +perf_j0 -55.000000 150.000000 11 -1 0.0320452 672 12806 8.68425 +perf_j0_opt -55.000000 150.000000 11 -1 0.0275624 553 9014 9.18352 +speed up after optimization -55.000000 150.000000 0% 16% 21% 42% -5% +perf_j0 0.000000 100.000000 10 -1 0.0330468 672 12806 8.47503 +perf_j0_opt 0.000000 100.000000 10 -1 0.0272468 517 8694 8.85619 +speed up after optimization 0.000000 100.000000 0% 21% 29% 47% -4% +perf_j0 0.000000 70.000000 10 -1 0.0317489 672 12806 8.50697 +perf_j0_opt 0.000000 70.000000 10 -1 0.0270736 517 8694 8.86639 +speed up after optimization 0.000000 70.000000 0% 17% 29% 47% -4% +perf_j0 260.000000 1260.000000 14 -1 0.0324252 672 12806 8.51517 +perf_j0_opt 260.000000 1260.000000 14 -1 0.0275411 397 7414 9.35105 +speed up after optimization 260.000000 1260.000000 0% 18% 69% 72% -9% +perf_j0 10.000000 45.000000 12 -1 0.0324191 672 12806 8.48592 +perf_j0_opt 10.000000 45.000000 12 -1 0.0276295 397 7430 8.8447 +speed up after optimization 10.000000 45.000000 0% 17% 69% 72% -4% +perf_j0 -55.000000 125.000000 11 -1 0.031883 672 12806 8.67599 +perf_j0_opt -55.000000 125.000000 11 -1 0.0275522 553 9014 9.54866 +speed up after optimization -55.000000 125.000000 0% 16% 21% 42% -9% +perf_j0 20.000000 80.000000 8 -1 0.0324229 672 12806 8.56161 +perf_j0_opt 20.000000 80.000000 8 -1 0.027502 395 7366 9.1907 +speed up after optimization 20.000000 80.000000 0% 18% 70% 73% -7% +perf_j0 0.000000 50.000000 8 -1 0.0314762 672 12806 8.53386 +perf_j0_opt 0.000000 50.000000 8 -1 0.0267758 515 8678 9.13387 +speed up after optimization 0.000000 50.000000 0% 18% 30% 47% -7% +perf_j0 -0.200000 2.000000 14 -1 0.00418735 672 12806 8.51811 +perf_j0_opt -0.200000 2.000000 14 -1 0.00417219 553 9118 9.32047 +speed up after optimization -0.200000 2.000000 0% 0% 21% 40% -9% +perf_j0 30.000000 130.000000 8 -1 0.0324139 672 12806 8.51825 +perf_j0_opt 30.000000 130.000000 8 -1 0.0273428 393 7342 8.94168 +speed up after optimization 30.000000 130.000000 0% 19% 70% 74% -5% +perf_j0 1.000000 200.000000 10 -1 0.0323296 672 12806 8.51264 +perf_j0_opt 1.000000 200.000000 10 -1 0.0273609 458 8286 9.26989 +speed up after optimization 1.000000 200.000000 0% 18% 46% 54% -8% +perf_y0 -2.000000 2.000000 12 -1 0.0064308 938 16974 8.66754 +perf_y0_opt -2.000000 2.000000 12 -1 0.00641738 809 13166 9.19182 +speed up after optimization -2.000000 2.000000 0% 0% 15% 28% -6% +perf_y0 -4.000000 4.000000 12 -1 0.0117311 938 16974 8.93471 +perf_y0_opt -4.000000 4.000000 12 -1 0.0110676 809 13142 9.85309 +speed up after optimization -4.000000 4.000000 0% 6% 15% 29% -9% +perf_y0 -8.000000 8.000000 12 -1 0.0149058 938 16974 8.81807 +perf_y0_opt -8.000000 8.000000 12 -1 0.0130485 809 13134 9.12111 +speed up after optimization -8.000000 8.000000 0% 14% 15% 29% -3% +perf_y0 -16.000000 16.000000 12 -1 0.0156341 938 16974 8.99968 +perf_y0_opt -16.000000 16.000000 12 -1 0.0140399 809 13118 9.26084 +speed up after optimization -16.000000 16.000000 0% 11% 15% 29% -3% +perf_y0 -125.000000 125.000000 16 -1 0.0165835 938 16974 8.58749 +perf_y0_opt -125.000000 125.000000 16 -1 0.0151456 809 13142 9.16466 +speed up after optimization -125.000000 125.000000 0% 9% 15% 29% -6% +perf_y0 -40.000000 110.000000 10 -1 0.0233336 938 16974 8.60218 +perf_y0_opt -40.000000 110.000000 10 -1 0.0208857 807 13070 9.63137 +speed up after optimization -40.000000 110.000000 0% 12% 16% 29% -11% +perf_y0 -55.000000 150.000000 11 -1 0.0233398 938 16974 8.62408 +perf_y0_opt -55.000000 150.000000 11 -1 0.0211171 809 13078 9.74726 +speed up after optimization -55.000000 150.000000 0% 11% 15% 29% -12% +perf_y0 0.000000 100.000000 10 -1 0.0310157 938 16974 8.62416 +perf_y0_opt 0.000000 100.000000 10 -1 0.0274478 776 12774 8.86065 +speed up after optimization 0.000000 100.000000 0% 13% 20% 32% -3% +perf_y0 0.000000 70.000000 10 -1 0.030885 938 16974 8.53802 +perf_y0_opt 0.000000 70.000000 10 -1 0.0274011 776 12774 8.97721 +speed up after optimization 0.000000 70.000000 0% 13% 20% 32% -5% +perf_y0 260.000000 1260.000000 14 -1 0.0312785 938 16974 8.71655 +perf_y0_opt 260.000000 1260.000000 14 -1 0.0274198 676 11318 9.64757 +speed up after optimization 260.000000 1260.000000 0% 14% 38% 49% -10% +perf_y0 10.000000 45.000000 12 -1 0.0312994 938 16974 8.56439 +perf_y0_opt 10.000000 45.000000 12 -1 0.0275052 676 11334 9.71338 +speed up after optimization 10.000000 45.000000 0% 14% 38% 49% -12% +perf_y0 -55.000000 125.000000 11 -1 0.0221734 938 16974 8.6586 +perf_y0_opt -55.000000 125.000000 11 -1 0.0198868 809 13078 9.64884 +speed up after optimization -55.000000 125.000000 0% 11% 15% 29% -10% +perf_y0 20.000000 80.000000 8 -1 0.0313044 938 16974 8.56893 +perf_y0_opt 20.000000 80.000000 8 -1 0.0274119 674 11270 9.54451 +speed up after optimization 20.000000 80.000000 0% 14% 39% 50% -10% +perf_y0 0.000000 50.000000 8 -1 0.0307357 938 16974 8.69925 +perf_y0_opt 0.000000 50.000000 8 -1 0.0270917 774 12750 9.67551 +speed up after optimization 0.000000 50.000000 0% 13% 21% 33% -10% +perf_y0 -0.200000 2.000000 14 -1 0.0101515 938 16974 8.83691 +perf_y0_opt -0.200000 2.000000 14 -1 0.010111 823 13342 9.90323 +speed up after optimization -0.200000 2.000000 0% 0% 13% 27% -11% +perf_y0 30.000000 130.000000 8 -1 0.0312822 938 16974 8.66924 +perf_y0_opt 30.000000 130.000000 8 -1 0.0272322 672 11254 9.36284 +speed up after optimization 30.000000 130.000000 0% 15% 39% 50% -7% +perf_y0 1.000000 200.000000 10 -1 0.0312822 938 16974 8.62417 +perf_y0_opt 1.000000 200.000000 10 -1 0.0274822 741 12558 8.92709 +speed up after optimization 1.000000 200.000000 0% 14% 26% 35% -3% diff --git a/applications/newton/llvm-ir/performance_test/firefly_perf/perf_woquant.log b/applications/newton/llvm-ir/performance_test/firefly_perf/perf_woquant.log new file mode 100644 index 000000000..29927500e --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/firefly_perf/perf_woquant.log @@ -0,0 +1,103 @@ +test case param instruction count time consumption ir lines library size compile time +perf_j0 -2.000000 2.000000 -1 0.0041891 672 12806 8.64551 +perf_j0_opt -2.000000 2.000000 -1 0.00415031 666 12710 9.01051 +speed up after optimization -2.000000 2.000000 0% 1% 0% 0% -4% +perf_j0 -4.000000 4.000000 -1 0.0182625 672 12806 8.6457 +perf_j0_opt -4.000000 4.000000 -1 0.0182068 666 12710 8.87372 +speed up after optimization -4.000000 4.000000 0% 0% 0% 0% -3% +perf_j0 -8.000000 8.000000 -1 0.0256698 672 12806 8.77255 +perf_j0_opt -8.000000 8.000000 -1 0.025617 666 12710 8.77032 +speed up after optimization -8.000000 8.000000 0% 0% 0% 0% 0% +perf_j0 -16.000000 16.000000 -1 0.0292126 672 12806 8.46476 +perf_j0_opt -16.000000 16.000000 -1 0.0291192 666 12710 8.7701 +speed up after optimization -16.000000 16.000000 0% 0% 0% 0% -3% +perf_j0 -125.000000 125.000000 -1 0.0321245 672 12806 8.62104 +perf_j0_opt -125.000000 125.000000 -1 0.0317704 666 12710 8.79059 +speed up after optimization -125.000000 125.000000 0% 1% 0% 0% -2% +perf_j0 -40.000000 110.000000 -1 0.0317742 672 12806 8.54451 +perf_j0_opt -40.000000 110.000000 -1 0.0315552 666 12710 9.03265 +speed up after optimization -40.000000 110.000000 0% 1% 0% 0% -5% +perf_j0 -55.000000 150.000000 -1 0.0319148 672 12806 8.65462 +perf_j0_opt -55.000000 150.000000 -1 0.0316328 666 12710 8.7713 +speed up after optimization -55.000000 150.000000 0% 1% 0% 0% -1% +perf_j0 0.000000 100.000000 -1 0.0319399 672 12806 8.65606 +perf_j0_opt 0.000000 100.000000 -1 0.0314426 630 12390 8.85576 +speed up after optimization 0.000000 100.000000 0% 2% 6% 3% -2% +perf_j0 0.000000 70.000000 -1 0.0317582 672 12806 8.5159 +perf_j0_opt 0.000000 70.000000 -1 0.0313274 630 12390 8.86189 +speed up after optimization 0.000000 70.000000 0% 1% 6% 3% -4% +perf_j0 260.000000 1260.000000 -1 0.0324552 672 12806 8.8024 +perf_j0_opt 260.000000 1260.000000 -1 0.0307152 510 11086 8.74592 +speed up after optimization 260.000000 1260.000000 0% 6% 31% 15% 1% +perf_j0 10.000000 45.000000 -1 0.0324576 672 12806 8.52251 +perf_j0_opt 10.000000 45.000000 -1 0.0311419 510 11086 8.71426 +speed up after optimization 10.000000 45.000000 0% 4% 31% 15% -2% +perf_j0 -55.000000 125.000000 -1 0.0319125 672 12806 8.46449 +perf_j0_opt -55.000000 125.000000 -1 0.0315628 666 12710 8.7548 +speed up after optimization -55.000000 125.000000 0% 1% 0% 0% -3% +perf_j0 20.000000 80.000000 -1 0.0324182 672 12806 8.5206 +perf_j0_opt 20.000000 80.000000 -1 0.0321373 510 11086 8.77055 +speed up after optimization 20.000000 80.000000 0% 1% 31% 15% -3% +perf_j0 0.000000 50.000000 -1 0.0314788 672 12806 8.57177 +perf_j0_opt 0.000000 50.000000 -1 0.0309062 630 12390 8.8181 +speed up after optimization 0.000000 50.000000 0% 2% 6% 3% -3% +perf_j0 -0.200000 2.000000 -1 0.0041856 672 12806 8.46162 +perf_j0_opt -0.200000 2.000000 -1 0.00416198 666 12710 8.85254 +speed up after optimization -0.200000 2.000000 0% 1% 0% 0% -4% +perf_j0 30.000000 130.000000 -1 0.0326378 672 12806 8.4693 +perf_j0_opt 30.000000 130.000000 -1 0.0307111 510 11086 8.75236 +speed up after optimization 30.000000 130.000000 0% 6% 31% 15% -3% +perf_j0 1.000000 200.000000 -1 0.0323319 672 12806 8.70759 +perf_j0_opt 1.000000 200.000000 -1 0.031696 573 12014 8.74695 +speed up after optimization 1.000000 200.000000 0% 2% 17% 6% 0% +perf_y0 -2.000000 2.000000 -1 0.00642437 938 16974 8.57486 +perf_y0_opt -2.000000 2.000000 -1 0.00636692 922 16782 8.90978 +speed up after optimization -2.000000 2.000000 0% 1% 1% 1% -4% +perf_y0 -4.000000 4.000000 -1 0.0117262 938 16974 8.64497 +perf_y0_opt -4.000000 4.000000 -1 0.0116535 922 16782 8.93764 +speed up after optimization -4.000000 4.000000 0% 1% 1% 1% -3% +perf_y0 -8.000000 8.000000 -1 0.0144246 938 16974 8.54897 +perf_y0_opt -8.000000 8.000000 -1 0.0143566 922 16782 9.20771 +speed up after optimization -8.000000 8.000000 0% 0% 1% 1% -7% +perf_y0 -16.000000 16.000000 -1 0.015684 938 16974 8.56913 +perf_y0_opt -16.000000 16.000000 -1 0.0155574 922 16782 8.94524 +speed up after optimization -16.000000 16.000000 0% 1% 1% 1% -4% +perf_y0 -125.000000 125.000000 -1 0.0171761 938 16974 8.55576 +perf_y0_opt -125.000000 125.000000 -1 0.0168725 922 16782 8.95235 +speed up after optimization -125.000000 125.000000 0% 2% 1% 1% -4% +perf_y0 -40.000000 110.000000 -1 0.0233237 938 16974 8.67136 +perf_y0_opt -40.000000 110.000000 -1 0.02359 922 16782 8.91701 +speed up after optimization -40.000000 110.000000 0% 0% 1% 1% -4% +perf_y0 -55.000000 150.000000 -1 0.0233467 938 16974 8.81824 +perf_y0_opt -55.000000 150.000000 -1 0.023303 922 16782 10.975 +speed up after optimization -55.000000 150.000000 0% 0% 1% 1% -20% +perf_y0 0.000000 100.000000 -1 0.0310112 938 16974 8.63068 +perf_y0_opt 0.000000 100.000000 -1 0.0306758 889 16478 9.03746 +speed up after optimization 0.000000 100.000000 0% 1% 5% 3% -5% +perf_y0 0.000000 70.000000 -1 0.0308814 938 16974 10.6839 +perf_y0_opt 0.000000 70.000000 -1 0.0305064 889 16478 8.92305 +speed up after optimization 0.000000 70.000000 0% 1% 5% 3% 20% +perf_y0 260.000000 1260.000000 -1 0.031307 938 16974 8.59541 +perf_y0_opt 260.000000 1260.000000 -1 0.0306499 789 15006 8.83276 +speed up after optimization 260.000000 1260.000000 0% 2% 18% 13% -3% +perf_y0 10.000000 45.000000 -1 0.0315187 938 16974 8.58749 +perf_y0_opt 10.000000 45.000000 -1 0.030616 789 15006 8.85693 +speed up after optimization 10.000000 45.000000 0% 3% 18% 13% -3% +perf_y0 -55.000000 125.000000 -1 0.022237 938 16974 8.55153 +perf_y0_opt -55.000000 125.000000 -1 0.0221264 922 16782 8.97193 +speed up after optimization -55.000000 125.000000 0% 0% 1% 1% -5% +perf_y0 20.000000 80.000000 -1 0.0313478 938 16974 8.56306 +perf_y0_opt 20.000000 80.000000 -1 0.0306105 789 15006 8.82615 +speed up after optimization 20.000000 80.000000 0% 2% 18% 13% -3% +perf_y0 0.000000 50.000000 -1 0.0307467 938 16974 8.54775 +perf_y0_opt 0.000000 50.000000 -1 0.030323 889 16478 8.88378 +speed up after optimization 0.000000 50.000000 0% 1% 5% 3% -4% +perf_y0 -0.200000 2.000000 -1 0.0101133 938 16974 8.67696 +perf_y0_opt -0.200000 2.000000 -1 0.0101148 936 16950 8.94634 +speed up after optimization -0.200000 2.000000 0% 0% 0% 0% -4% +perf_y0 30.000000 130.000000 -1 0.0313155 938 16974 8.65747 +perf_y0_opt 30.000000 130.000000 -1 0.0305937 789 15006 8.90506 +speed up after optimization 30.000000 130.000000 0% 2% 18% 13% -3% +perf_y0 1.000000 200.000000 -1 0.0312584 938 16974 8.55173 +perf_y0_opt 1.000000 200.000000 -1 0.0307293 856 16270 8.88375 +speed up after optimization 1.000000 200.000000 0% 2% 9% 4% -4% From a40d9506a94b2e0385e9b12a449a87fec0a737ef Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sat, 21 Jun 2025 20:34:11 +0100 Subject: [PATCH 190/213] update new bar plot * quantize. --- ...4975268fa3dac9e85718f0d4660c8d615e3e5c.txt | 48 ++++ ...03344ed167c5f01a902de8f90f728ffb1bef53.txt | 48 ++++ .../llvm-ir/performance_test/new_bar_plot.py | 220 ++++++++++++++++++ .../llvm-ir/performance_test/perf_quant.log | 103 ++++++++ .../llvm-ir/performance_test/perf_woquant.log | 103 ++++++++ 5 files changed, 522 insertions(+) create mode 100644 analysis/statistics/9c4975268fa3dac9e85718f0d4660c8d615e3e5c.txt create mode 100644 analysis/statistics/bb03344ed167c5f01a902de8f90f728ffb1bef53.txt create mode 100644 applications/newton/llvm-ir/performance_test/new_bar_plot.py create mode 100644 applications/newton/llvm-ir/performance_test/perf_quant.log create mode 100644 applications/newton/llvm-ir/performance_test/perf_woquant.log diff --git a/analysis/statistics/9c4975268fa3dac9e85718f0d4660c8d615e3e5c.txt b/analysis/statistics/9c4975268fa3dac9e85718f0d4660c8d615e3e5c.txt new file mode 100644 index 000000000..a96a25f73 --- /dev/null +++ b/analysis/statistics/9c4975268fa3dac9e85718f0d4660c8d615e3e5c.txt @@ -0,0 +1,48 @@ + +changeset: 1780:9c4975268fa3dac9e85718f0d4660c8d615e3e5c +char kNewtonVersion[] = "0.3-alpha-1780 (9c4975268fa3dac9e85718f0d4660c8d615e3e5c) (build 06-08-2025-12:32-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/analysis/statistics/bb03344ed167c5f01a902de8f90f728ffb1bef53.txt b/analysis/statistics/bb03344ed167c5f01a902de8f90f728ffb1bef53.txt new file mode 100644 index 000000000..99d1ed820 --- /dev/null +++ b/analysis/statistics/bb03344ed167c5f01a902de8f90f728ffb1bef53.txt @@ -0,0 +1,48 @@ + +changeset: 1781:bb03344ed167c5f01a902de8f90f728ffb1bef53 +char kNewtonVersion[] = "0.3-alpha-1781 (bb03344ed167c5f01a902de8f90f728ffb1bef53) (build 06-12-2025-19:24-xyf@aurum-Linux-6.6.87.1-microsoft-standard-WSL2-x86_64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/new_bar_plot.py b/applications/newton/llvm-ir/performance_test/new_bar_plot.py new file mode 100644 index 000000000..5d91eb521 --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/new_bar_plot.py @@ -0,0 +1,220 @@ +import matplotlib.pyplot as plt +import numpy as np +import pandas as pd +from matplotlib.ticker import MultipleLocator + +# The data processing functions remain the same as the last version. +# They are included here so the script is complete. + +def build_precision_map_from_quant_file(file_content): + precision_map = {} + current_benchmark_key = None + lines = file_content.strip().split('\n') + if lines and "test case\tparam\tprecision_bits" in lines[0]: + lines = lines[1:] + + precision_bits_col_index = 2 + min_parts_for_precision_line = precision_bits_col_index + 1 + + for line in lines: + parts = line.split('\t') + if not parts: continue + test_case = parts[0].strip() + if test_case.startswith("perf_j0"): current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): current_benchmark_key = "y0" + else: continue + + if len(parts) > min_parts_for_precision_line: + params_str = parts[1].strip() + if len(params_str.split()) >= 2: + try: + precision_val = parts[precision_bits_col_index].strip() + if precision_val: + int(precision_val) + map_key = (current_benchmark_key, params_str) + if map_key not in precision_map: + precision_map[map_key] = precision_val + except (IndexError, ValueError): + pass + if not precision_map: + print("Warning: Precision map is empty. Check 'perf_quant.log' format.") + return precision_map + +def parse_summary_perf_data(file_content, quantization_type): + data = [] + current_benchmark_key = None + lines = file_content.strip().split('\n') + if lines and "test case\tparam" in lines[0]: + lines = lines[1:] + + for line in lines: + parts = line.split('\t') + if not parts: continue + test_case = parts[0].strip() + if test_case.startswith("perf_j0"): current_benchmark_key = "j0" + elif test_case.startswith("perf_y0"): current_benchmark_key = "y0" + elif test_case == "speed up after optimization": + if current_benchmark_key is None: continue + if len(parts) < 7: continue + try: + params_str = parts[1].strip() + param_values = params_str.split() + if len(param_values) < 2: continue + formatted_params = f"[{float(param_values[0]):.1f}, {float(param_values[1]):.1f}]" + time_speedup_pct = float(parts[3].replace('%', '')) + library_size_reduction_pct = float(parts[5].replace('%', '')) + speedup_factor = 1 + (time_speedup_pct / 100.0) + data.append({ + "benchmark": current_benchmark_key, "params_raw": params_str, + "params_formatted": formatted_params, "quant_type": quantization_type, + "speedup_factor": speedup_factor, "library_size_reduction_pct": library_size_reduction_pct, + "precision_bits": None + }) + except (IndexError, ValueError): + continue + return data + +def process_performance_data(wo_quant_path, w_quant_path): + quant_precision_map = {} + try: + with open(w_quant_path, 'r', encoding='utf-8') as f: + content = f.read() + quant_precision_map = build_precision_map_from_quant_file(content) + except FileNotFoundError: + print(f"CRITICAL: '{w_quant_path}' not found.") + return pd.DataFrame() + + all_data_collected = [] + try: + with open(wo_quant_path, 'r', encoding='utf-8') as f: + content = f.read() + all_data_collected.extend(parse_summary_perf_data(content, "w/o Auto Quantization")) + except FileNotFoundError: print(f"Warning: '{wo_quant_path}' not found.") + + try: + with open(w_quant_path, 'r', encoding='utf-8') as f: + content = f.read() + all_data_collected.extend(parse_summary_perf_data(content, "with Auto Quantization")) + except FileNotFoundError: print(f"Warning: '{w_quant_path}' not found for summary parsing.") + + if not all_data_collected: return pd.DataFrame() + + for item in all_data_collected: + item['precision_bits'] = quant_precision_map.get((item['benchmark'], item['params_raw'])) + df = pd.DataFrame(all_data_collected) + + if df.empty: return df + + df['range_over_precision_bits'] = df.apply( + lambda row: f"{row['params_formatted']}/{row['precision_bits']}" if pd.notna(row['precision_bits']) else row['params_formatted'], + axis=1 + ) + + pivot_df = df.pivot_table( + index=['benchmark', 'params_raw', 'range_over_precision_bits'], + columns='quant_type', + values=['speedup_factor', 'library_size_reduction_pct'] + ) + + pivot_df.columns = [f'{val}_{quant}' for val, quant in pivot_df.columns] + pivot_df.reset_index(inplace=True) + + pivot_df['relative_speedup'] = pivot_df.get('speedup_factor_with Auto Quantization', 1) / pivot_df.get('speedup_factor_w/o Auto Quantization', 1) + pivot_df['size_reduction_improvement'] = pivot_df.get('library_size_reduction_pct_with Auto Quantization', 0) - pivot_df.get('library_size_reduction_pct_w/o Auto Quantization', 0) + + return pivot_df + +# --- UPDATED Plotting function for single bars and refined Y-axis --- +def create_single_bar_plot(df_benchmark_data, benchmark_id, metric_column, y_axis_label, plot_title_suffix, output_filename): + if df_benchmark_data.empty: + print(f"No data available for {benchmark_id}, skipping '{plot_title_suffix}' plot.") + return + + df_sorted = df_benchmark_data.copy() + df_sorted['sort_key_params_numeric'] = df_sorted['params_raw'].apply(lambda x: tuple(map(float, x.split()))) + df_sorted = df_sorted.sort_values(by='sort_key_params_numeric').reset_index(drop=True) + + num_param_groups = len(df_sorted.index) + x_positions = np.arange(num_param_groups) + fig_width = max(12, num_param_groups * 0.8) + fig, ax = plt.subplots(figsize=(fig_width, 7)) + + bar_data = pd.to_numeric(df_sorted[metric_column], errors='coerce').fillna(0) + ax.bar(x_positions, bar_data, width=0.6, color='#6A0DAD', label=plot_title_suffix) + + ax.set_xlabel("Range/PrecisionBits") + ax.set_ylabel(y_axis_label) + ax.set_title(f"{benchmark_id.upper()} - {plot_title_suffix}") + ax.set_xticks(x_positions) + ax.set_xticklabels(df_sorted['range_over_precision_bits'], rotation=45, ha="right") + + # === Y-axis refinement logic starts here === + + all_bar_values = bar_data.dropna().tolist() + if not all_bar_values: + min_data_val, max_data_val = (0.9, 1.1) + else: + min_data_val = min(all_bar_values) if all_bar_values else 0 + max_data_val = max(all_bar_values) if all_bar_values else 1 + + # Check if this is a speedup plot to apply special formatting + is_speedup_plot = "Speedup" in y_axis_label + + if is_speedup_plot: + # Refined y-axis limits for speedup + ax.axhline(1.0, color='grey', linestyle='-.', linewidth=1.0) + y_bottom = min(0.95, min_data_val) - 0.02 # Start slightly below the lowest value or 0.95 + y_top = max_data_val + 0.05 + ax.set_ylim(bottom=y_bottom, top=y_top) + + # Set major ticks every 0.05 + ax.yaxis.set_major_locator(MultipleLocator(0.05)) + # Set minor ticks every 0.025 + ax.yaxis.set_minor_locator(MultipleLocator(0.025)) + + # Enable grid for both major and minor ticks, with different styles + ax.grid(which='major', linestyle='--', alpha=0.7, axis='y') + ax.grid(which='minor', linestyle=':', alpha=0.5, axis='y') + else: + # Default behavior for other plots (e.g., size reduction) + ax.axhline(0, color='grey', linestyle='-.', linewidth=1.0) + ax.grid(True, linestyle='--', alpha=0.7, axis='y') # Just the major grid + + # === Y-axis refinement logic ends here === + + plt.tight_layout() + plt.savefig(output_filename) + print(f"Plot saved: {output_filename}") + plt.close(fig) + +# --- Main script execution --- +file_path_wo_quant = "perf_woquant.log" +file_path_w_quant = "perf_quant.log" + +# Process data and calculate relative improvements +df_processed = process_performance_data(file_path_wo_quant, file_path_w_quant) + +if not df_processed.empty: + df_j0_final = df_processed[df_processed["benchmark"] == "j0"] + create_single_bar_plot(df_j0_final, "e_j0", "relative_speedup", + "Relative Speedup (Quant / w/o Quant)", + "Quantization Speedup Improvement", + "e_j0_relative_speedup.png") # New filename + create_single_bar_plot(df_j0_final, "e_j0", "size_reduction_improvement", + "Additional Size Reduction (%)", + "Quantization Size Reduction Improvement", + "e_j0_relative_size_reduction.png") # New filename + + df_y0_final = df_processed[df_processed["benchmark"] == "y0"] + create_single_bar_plot(df_y0_final, "e_y0", "relative_speedup", + "Relative Speedup (Quant / w/o Quant)", + "Quantization Speedup Improvement", + "e_y0_relative_speedup.png") # New filename + create_single_bar_plot(df_y0_final, "e_y0", "size_reduction_improvement", + "Additional Size Reduction (%)", + "Quantization Size Reduction Improvement", + "e_y0_relative_size_reduction.png") # New filename + + print("\n--- Plot generation complete ---") +else: + print("\n--- No data processed, plot generation skipped ---") \ No newline at end of file diff --git a/applications/newton/llvm-ir/performance_test/perf_quant.log b/applications/newton/llvm-ir/performance_test/perf_quant.log new file mode 100644 index 000000000..2133a5cbd --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/perf_quant.log @@ -0,0 +1,103 @@ +test case param precision_bits instruction count time consumption ir lines library size compile time +perf_j0 -2.000000 2.000000 12 -1 0.00419785 672 12806 8.47906 +perf_j0_opt -2.000000 2.000000 12 -1 0.00414156 553 9086 8.87451 +speed up after optimization -2.000000 2.000000 0% 1% 21% 40% -4% +perf_j0 -4.000000 4.000000 12 -1 0.0182643 672 12806 8.60355 +perf_j0_opt -4.000000 4.000000 12 -1 0.0162081 553 9070 8.77285 +speed up after optimization -4.000000 4.000000 0% 13% 21% 41% -2% +perf_j0 -8.000000 8.000000 12 -1 0.0256526 672 12806 8.6312 +perf_j0_opt -8.000000 8.000000 12 -1 0.0223749 553 9062 9.12953 +speed up after optimization -8.000000 8.000000 0% 15% 21% 41% -5% +perf_j0 -16.000000 16.000000 12 -1 0.0292059 672 12806 8.71164 +perf_j0_opt -16.000000 16.000000 12 -1 0.025191 553 9054 8.77293 +speed up after optimization -16.000000 16.000000 0% 16% 21% 41% -1% +perf_j0 -125.000000 125.000000 16 -1 0.0320668 672 12806 8.52784 +perf_j0_opt -125.000000 125.000000 16 -1 0.02817 553 9070 9.18924 +speed up after optimization -125.000000 125.000000 0% 14% 21% 41% -7% +perf_j0 -40.000000 110.000000 10 -1 0.0317711 672 12806 8.5338 +perf_j0_opt -40.000000 110.000000 10 -1 0.0317366 551 8990 11.0385 +speed up after optimization -40.000000 110.000000 0% 0% 21% 42% -23% +perf_j0 -55.000000 150.000000 11 -1 0.0320452 672 12806 8.68425 +perf_j0_opt -55.000000 150.000000 11 -1 0.0275624 553 9014 9.18352 +speed up after optimization -55.000000 150.000000 0% 16% 21% 42% -5% +perf_j0 0.000000 100.000000 10 -1 0.0330468 672 12806 8.47503 +perf_j0_opt 0.000000 100.000000 10 -1 0.0272468 517 8694 8.85619 +speed up after optimization 0.000000 100.000000 0% 21% 29% 47% -4% +perf_j0 0.000000 70.000000 10 -1 0.0317489 672 12806 8.50697 +perf_j0_opt 0.000000 70.000000 10 -1 0.0270736 517 8694 8.86639 +speed up after optimization 0.000000 70.000000 0% 17% 29% 47% -4% +perf_j0 260.000000 1260.000000 14 -1 0.0324252 672 12806 8.51517 +perf_j0_opt 260.000000 1260.000000 14 -1 0.0275411 397 7414 9.35105 +speed up after optimization 260.000000 1260.000000 0% 18% 69% 72% -9% +perf_j0 10.000000 45.000000 12 -1 0.0324191 672 12806 8.48592 +perf_j0_opt 10.000000 45.000000 12 -1 0.0276295 397 7430 8.8447 +speed up after optimization 10.000000 45.000000 0% 17% 69% 72% -4% +perf_j0 -55.000000 125.000000 11 -1 0.031883 672 12806 8.67599 +perf_j0_opt -55.000000 125.000000 11 -1 0.0275522 553 9014 9.54866 +speed up after optimization -55.000000 125.000000 0% 16% 21% 42% -9% +perf_j0 20.000000 80.000000 8 -1 0.0324229 672 12806 8.56161 +perf_j0_opt 20.000000 80.000000 8 -1 0.027502 395 7366 9.1907 +speed up after optimization 20.000000 80.000000 0% 18% 70% 73% -7% +perf_j0 0.000000 50.000000 8 -1 0.0314762 672 12806 8.53386 +perf_j0_opt 0.000000 50.000000 8 -1 0.0267758 515 8678 9.13387 +speed up after optimization 0.000000 50.000000 0% 18% 30% 47% -7% +perf_j0 -0.200000 2.000000 14 -1 0.00418735 672 12806 8.51811 +perf_j0_opt -0.200000 2.000000 14 -1 0.00417219 553 9118 9.32047 +speed up after optimization -0.200000 2.000000 0% 0% 21% 40% -9% +perf_j0 30.000000 130.000000 8 -1 0.0324139 672 12806 8.51825 +perf_j0_opt 30.000000 130.000000 8 -1 0.0273428 393 7342 8.94168 +speed up after optimization 30.000000 130.000000 0% 19% 70% 74% -5% +perf_j0 1.000000 200.000000 10 -1 0.0323296 672 12806 8.51264 +perf_j0_opt 1.000000 200.000000 10 -1 0.0273609 458 8286 9.26989 +speed up after optimization 1.000000 200.000000 0% 18% 46% 54% -8% +perf_y0 -2.000000 2.000000 12 -1 0.0064308 938 16974 8.66754 +perf_y0_opt -2.000000 2.000000 12 -1 0.00641738 809 13166 9.19182 +speed up after optimization -2.000000 2.000000 0% 0% 15% 28% -6% +perf_y0 -4.000000 4.000000 12 -1 0.0117311 938 16974 8.93471 +perf_y0_opt -4.000000 4.000000 12 -1 0.0110676 809 13142 9.85309 +speed up after optimization -4.000000 4.000000 0% 6% 15% 29% -9% +perf_y0 -8.000000 8.000000 12 -1 0.0149058 938 16974 8.81807 +perf_y0_opt -8.000000 8.000000 12 -1 0.0130485 809 13134 9.12111 +speed up after optimization -8.000000 8.000000 0% 14% 15% 29% -3% +perf_y0 -16.000000 16.000000 12 -1 0.0156341 938 16974 8.99968 +perf_y0_opt -16.000000 16.000000 12 -1 0.0140399 809 13118 9.26084 +speed up after optimization -16.000000 16.000000 0% 11% 15% 29% -3% +perf_y0 -125.000000 125.000000 16 -1 0.0165835 938 16974 8.58749 +perf_y0_opt -125.000000 125.000000 16 -1 0.0151456 809 13142 9.16466 +speed up after optimization -125.000000 125.000000 0% 9% 15% 29% -6% +perf_y0 -40.000000 110.000000 10 -1 0.0233336 938 16974 8.60218 +perf_y0_opt -40.000000 110.000000 10 -1 0.0208857 807 13070 9.63137 +speed up after optimization -40.000000 110.000000 0% 12% 16% 29% -11% +perf_y0 -55.000000 150.000000 11 -1 0.0233398 938 16974 8.62408 +perf_y0_opt -55.000000 150.000000 11 -1 0.0211171 809 13078 9.74726 +speed up after optimization -55.000000 150.000000 0% 11% 15% 29% -12% +perf_y0 0.000000 100.000000 10 -1 0.0310157 938 16974 8.62416 +perf_y0_opt 0.000000 100.000000 10 -1 0.0274478 776 12774 8.86065 +speed up after optimization 0.000000 100.000000 0% 13% 20% 32% -3% +perf_y0 0.000000 70.000000 10 -1 0.030885 938 16974 8.53802 +perf_y0_opt 0.000000 70.000000 10 -1 0.0274011 776 12774 8.97721 +speed up after optimization 0.000000 70.000000 0% 13% 20% 32% -5% +perf_y0 260.000000 1260.000000 14 -1 0.0312785 938 16974 8.71655 +perf_y0_opt 260.000000 1260.000000 14 -1 0.0274198 676 11318 9.64757 +speed up after optimization 260.000000 1260.000000 0% 14% 38% 49% -10% +perf_y0 10.000000 45.000000 12 -1 0.0312994 938 16974 8.56439 +perf_y0_opt 10.000000 45.000000 12 -1 0.0275052 676 11334 9.71338 +speed up after optimization 10.000000 45.000000 0% 14% 38% 49% -12% +perf_y0 -55.000000 125.000000 11 -1 0.0221734 938 16974 8.6586 +perf_y0_opt -55.000000 125.000000 11 -1 0.0198868 809 13078 9.64884 +speed up after optimization -55.000000 125.000000 0% 11% 15% 29% -10% +perf_y0 20.000000 80.000000 8 -1 0.0313044 938 16974 8.56893 +perf_y0_opt 20.000000 80.000000 8 -1 0.0274119 674 11270 9.54451 +speed up after optimization 20.000000 80.000000 0% 14% 39% 50% -10% +perf_y0 0.000000 50.000000 8 -1 0.0307357 938 16974 8.69925 +perf_y0_opt 0.000000 50.000000 8 -1 0.0270917 774 12750 9.67551 +speed up after optimization 0.000000 50.000000 0% 13% 21% 33% -10% +perf_y0 -0.200000 2.000000 14 -1 0.0101515 938 16974 8.83691 +perf_y0_opt -0.200000 2.000000 14 -1 0.010111 823 13342 9.90323 +speed up after optimization -0.200000 2.000000 0% 0% 13% 27% -11% +perf_y0 30.000000 130.000000 8 -1 0.0312822 938 16974 8.66924 +perf_y0_opt 30.000000 130.000000 8 -1 0.0272322 672 11254 9.36284 +speed up after optimization 30.000000 130.000000 0% 15% 39% 50% -7% +perf_y0 1.000000 200.000000 10 -1 0.0312822 938 16974 8.62417 +perf_y0_opt 1.000000 200.000000 10 -1 0.0274822 741 12558 8.92709 +speed up after optimization 1.000000 200.000000 0% 14% 26% 35% -3% diff --git a/applications/newton/llvm-ir/performance_test/perf_woquant.log b/applications/newton/llvm-ir/performance_test/perf_woquant.log new file mode 100644 index 000000000..29927500e --- /dev/null +++ b/applications/newton/llvm-ir/performance_test/perf_woquant.log @@ -0,0 +1,103 @@ +test case param instruction count time consumption ir lines library size compile time +perf_j0 -2.000000 2.000000 -1 0.0041891 672 12806 8.64551 +perf_j0_opt -2.000000 2.000000 -1 0.00415031 666 12710 9.01051 +speed up after optimization -2.000000 2.000000 0% 1% 0% 0% -4% +perf_j0 -4.000000 4.000000 -1 0.0182625 672 12806 8.6457 +perf_j0_opt -4.000000 4.000000 -1 0.0182068 666 12710 8.87372 +speed up after optimization -4.000000 4.000000 0% 0% 0% 0% -3% +perf_j0 -8.000000 8.000000 -1 0.0256698 672 12806 8.77255 +perf_j0_opt -8.000000 8.000000 -1 0.025617 666 12710 8.77032 +speed up after optimization -8.000000 8.000000 0% 0% 0% 0% 0% +perf_j0 -16.000000 16.000000 -1 0.0292126 672 12806 8.46476 +perf_j0_opt -16.000000 16.000000 -1 0.0291192 666 12710 8.7701 +speed up after optimization -16.000000 16.000000 0% 0% 0% 0% -3% +perf_j0 -125.000000 125.000000 -1 0.0321245 672 12806 8.62104 +perf_j0_opt -125.000000 125.000000 -1 0.0317704 666 12710 8.79059 +speed up after optimization -125.000000 125.000000 0% 1% 0% 0% -2% +perf_j0 -40.000000 110.000000 -1 0.0317742 672 12806 8.54451 +perf_j0_opt -40.000000 110.000000 -1 0.0315552 666 12710 9.03265 +speed up after optimization -40.000000 110.000000 0% 1% 0% 0% -5% +perf_j0 -55.000000 150.000000 -1 0.0319148 672 12806 8.65462 +perf_j0_opt -55.000000 150.000000 -1 0.0316328 666 12710 8.7713 +speed up after optimization -55.000000 150.000000 0% 1% 0% 0% -1% +perf_j0 0.000000 100.000000 -1 0.0319399 672 12806 8.65606 +perf_j0_opt 0.000000 100.000000 -1 0.0314426 630 12390 8.85576 +speed up after optimization 0.000000 100.000000 0% 2% 6% 3% -2% +perf_j0 0.000000 70.000000 -1 0.0317582 672 12806 8.5159 +perf_j0_opt 0.000000 70.000000 -1 0.0313274 630 12390 8.86189 +speed up after optimization 0.000000 70.000000 0% 1% 6% 3% -4% +perf_j0 260.000000 1260.000000 -1 0.0324552 672 12806 8.8024 +perf_j0_opt 260.000000 1260.000000 -1 0.0307152 510 11086 8.74592 +speed up after optimization 260.000000 1260.000000 0% 6% 31% 15% 1% +perf_j0 10.000000 45.000000 -1 0.0324576 672 12806 8.52251 +perf_j0_opt 10.000000 45.000000 -1 0.0311419 510 11086 8.71426 +speed up after optimization 10.000000 45.000000 0% 4% 31% 15% -2% +perf_j0 -55.000000 125.000000 -1 0.0319125 672 12806 8.46449 +perf_j0_opt -55.000000 125.000000 -1 0.0315628 666 12710 8.7548 +speed up after optimization -55.000000 125.000000 0% 1% 0% 0% -3% +perf_j0 20.000000 80.000000 -1 0.0324182 672 12806 8.5206 +perf_j0_opt 20.000000 80.000000 -1 0.0321373 510 11086 8.77055 +speed up after optimization 20.000000 80.000000 0% 1% 31% 15% -3% +perf_j0 0.000000 50.000000 -1 0.0314788 672 12806 8.57177 +perf_j0_opt 0.000000 50.000000 -1 0.0309062 630 12390 8.8181 +speed up after optimization 0.000000 50.000000 0% 2% 6% 3% -3% +perf_j0 -0.200000 2.000000 -1 0.0041856 672 12806 8.46162 +perf_j0_opt -0.200000 2.000000 -1 0.00416198 666 12710 8.85254 +speed up after optimization -0.200000 2.000000 0% 1% 0% 0% -4% +perf_j0 30.000000 130.000000 -1 0.0326378 672 12806 8.4693 +perf_j0_opt 30.000000 130.000000 -1 0.0307111 510 11086 8.75236 +speed up after optimization 30.000000 130.000000 0% 6% 31% 15% -3% +perf_j0 1.000000 200.000000 -1 0.0323319 672 12806 8.70759 +perf_j0_opt 1.000000 200.000000 -1 0.031696 573 12014 8.74695 +speed up after optimization 1.000000 200.000000 0% 2% 17% 6% 0% +perf_y0 -2.000000 2.000000 -1 0.00642437 938 16974 8.57486 +perf_y0_opt -2.000000 2.000000 -1 0.00636692 922 16782 8.90978 +speed up after optimization -2.000000 2.000000 0% 1% 1% 1% -4% +perf_y0 -4.000000 4.000000 -1 0.0117262 938 16974 8.64497 +perf_y0_opt -4.000000 4.000000 -1 0.0116535 922 16782 8.93764 +speed up after optimization -4.000000 4.000000 0% 1% 1% 1% -3% +perf_y0 -8.000000 8.000000 -1 0.0144246 938 16974 8.54897 +perf_y0_opt -8.000000 8.000000 -1 0.0143566 922 16782 9.20771 +speed up after optimization -8.000000 8.000000 0% 0% 1% 1% -7% +perf_y0 -16.000000 16.000000 -1 0.015684 938 16974 8.56913 +perf_y0_opt -16.000000 16.000000 -1 0.0155574 922 16782 8.94524 +speed up after optimization -16.000000 16.000000 0% 1% 1% 1% -4% +perf_y0 -125.000000 125.000000 -1 0.0171761 938 16974 8.55576 +perf_y0_opt -125.000000 125.000000 -1 0.0168725 922 16782 8.95235 +speed up after optimization -125.000000 125.000000 0% 2% 1% 1% -4% +perf_y0 -40.000000 110.000000 -1 0.0233237 938 16974 8.67136 +perf_y0_opt -40.000000 110.000000 -1 0.02359 922 16782 8.91701 +speed up after optimization -40.000000 110.000000 0% 0% 1% 1% -4% +perf_y0 -55.000000 150.000000 -1 0.0233467 938 16974 8.81824 +perf_y0_opt -55.000000 150.000000 -1 0.023303 922 16782 10.975 +speed up after optimization -55.000000 150.000000 0% 0% 1% 1% -20% +perf_y0 0.000000 100.000000 -1 0.0310112 938 16974 8.63068 +perf_y0_opt 0.000000 100.000000 -1 0.0306758 889 16478 9.03746 +speed up after optimization 0.000000 100.000000 0% 1% 5% 3% -5% +perf_y0 0.000000 70.000000 -1 0.0308814 938 16974 10.6839 +perf_y0_opt 0.000000 70.000000 -1 0.0305064 889 16478 8.92305 +speed up after optimization 0.000000 70.000000 0% 1% 5% 3% 20% +perf_y0 260.000000 1260.000000 -1 0.031307 938 16974 8.59541 +perf_y0_opt 260.000000 1260.000000 -1 0.0306499 789 15006 8.83276 +speed up after optimization 260.000000 1260.000000 0% 2% 18% 13% -3% +perf_y0 10.000000 45.000000 -1 0.0315187 938 16974 8.58749 +perf_y0_opt 10.000000 45.000000 -1 0.030616 789 15006 8.85693 +speed up after optimization 10.000000 45.000000 0% 3% 18% 13% -3% +perf_y0 -55.000000 125.000000 -1 0.022237 938 16974 8.55153 +perf_y0_opt -55.000000 125.000000 -1 0.0221264 922 16782 8.97193 +speed up after optimization -55.000000 125.000000 0% 0% 1% 1% -5% +perf_y0 20.000000 80.000000 -1 0.0313478 938 16974 8.56306 +perf_y0_opt 20.000000 80.000000 -1 0.0306105 789 15006 8.82615 +speed up after optimization 20.000000 80.000000 0% 2% 18% 13% -3% +perf_y0 0.000000 50.000000 -1 0.0307467 938 16974 8.54775 +perf_y0_opt 0.000000 50.000000 -1 0.030323 889 16478 8.88378 +speed up after optimization 0.000000 50.000000 0% 1% 5% 3% -4% +perf_y0 -0.200000 2.000000 -1 0.0101133 938 16974 8.67696 +perf_y0_opt -0.200000 2.000000 -1 0.0101148 936 16950 8.94634 +speed up after optimization -0.200000 2.000000 0% 0% 0% 0% -4% +perf_y0 30.000000 130.000000 -1 0.0313155 938 16974 8.65747 +perf_y0_opt 30.000000 130.000000 -1 0.0305937 789 15006 8.90506 +speed up after optimization 30.000000 130.000000 0% 2% 18% 13% -3% +perf_y0 1.000000 200.000000 -1 0.0312584 938 16974 8.55173 +perf_y0_opt 1.000000 200.000000 -1 0.0307293 856 16270 8.88375 +speed up after optimization 1.000000 200.000000 0% 2% 9% 4% -4% From 8bdfeae48f820ddec6f6bb760152c92bae36a496 Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sat, 5 Jul 2025 23:32:17 +0100 Subject: [PATCH 191/213] save before change * quantize. --- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 138 +++++++++++------- 1 file changed, 85 insertions(+), 53 deletions(-) diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index cb8edc9a4..6348dc3ea 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -1107,7 +1107,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } } - if (enableQuantization) + if (enableQuantization) { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); llvm::errs() << "Auto quantization enabled\n"; @@ -1125,6 +1125,10 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } + + + + /** * Check for potential overflows */ @@ -1147,27 +1151,27 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } } - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = true; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } + /* + * remove the functions that are optimized by passes. + * */ + if (useOverLoad) + cleanFunctionMap(Mod, callerMap); + + if (useOverLoad) + overloadFunc(Mod, callerMap); + + callerMap.clear(); + funcBoundInfo.clear(); + useOverLoad = true; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } /* * simplify the condition of each branch @@ -1195,28 +1199,28 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - // - // flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - // callerMap.clear(); - // funcBoundInfo.clear(); - // useOverLoad = false; - // for (auto & mi : *Mod) - // { - // auto boundInfo = new BoundInfo(); - // mergeBoundInfo(boundInfo, globalBoundInfo); - // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - // funcBoundInfo.emplace(mi.getName().str(), boundInfo); - // std::vector calleeNames; - // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - // } + /* + * remove the functions that are optimized by passes. + * */ + if (useOverLoad) + cleanFunctionMap(Mod, callerMap); + + if (useOverLoad) + overloadFunc(Mod, callerMap); + + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + callerMap.clear(); + funcBoundInfo.clear(); + useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } // flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); for (auto & mi : *Mod) @@ -1231,15 +1235,15 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // assert(false); // } } - // - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - // - // if (useOverLoad) - // overloadFunc(Mod, callerMap); + + /* + * remove the functions that are optimized by passes. + * */ + if (useOverLoad) + cleanFunctionMap(Mod, callerMap); + + if (useOverLoad) + overloadFunc(Mod, callerMap); @@ -1260,6 +1264,34 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl eraseOldFunctions(*Mod); + // if (enableQuantization) + // { + // flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); + // llvm::errs() << "Auto quantization enabled\n"; + // std::vector functionsToInsert; + // for (auto & mi : *Mod) + // { + // llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; + + // irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); + // } + // for (auto mi : functionsToInsert) + // { + // Mod->getFunctionList().remove(mi); + // Mod->getFunctionList().push_front(mi); + // } + // } + + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + + const char * homeDir = getenv("HOME"); From c26040fc4e60a4c617898bfd212412ee3798b34d Mon Sep 17 00:00:00 2001 From: xyf2002 <2316639449@qq.com> Date: Sun, 6 Jul 2025 21:09:18 +0100 Subject: [PATCH 192/213] update warm up and trimmedMean * quantize. --- .../llvm-ir/performance_test/auto_test.cpp | 252 +++++++++++------- .../performance_test/auto_test_woquant.cpp | 117 +++++--- .../performance_test/plot_sensor_ranges.py | 6 +- .../newton-irPass-LLVMIR-optimizeByRange.cpp | 138 ++++------ .../newton-irPass-LLVMIR-quantization.cpp | 26 +- .../newton-irPass-LLVMIR-rangeAnalysis.cpp | 2 +- 6 files changed, 319 insertions(+), 222 deletions(-) diff --git a/applications/newton/llvm-ir/performance_test/auto_test.cpp b/applications/newton/llvm-ir/performance_test/auto_test.cpp index 96202c8d3..a0fd10450 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test.cpp @@ -19,6 +19,7 @@ #include const size_t iteration_num = 1; +constexpr int WARMUP_ITERATIONS = 3; struct perfData { int64_t inst_count_avg; @@ -69,6 +70,20 @@ timespec toc(timespec* start_time, const char* prefix, bool print) { return diff_time; } +/* ---------- trimmed mean helper ---------- */ +template +double trimmedMean(std::vector v, double trim_ratio = 0.10) { + if (v.empty()) return 0.0; + std::sort(v.begin(), v.end()); + + size_t n = v.size(); + size_t cut = std::max(1, static_cast(n * trim_ratio)); + if (cut * 2 >= n) cut = n / 2; + + double sum = std::accumulate(v.begin() + cut, v.end() - cut, 0.0); + return sum / static_cast(n - 2 * cut); +} + double compileTargetCode(const std::string& test_case) { timespec compile_timer = tic(); @@ -138,7 +153,7 @@ std::pair processDataPerf(const std::string test_case, const s // perf command //todo Quantization prefix std::string quant_prefix = (test_case.find("_opt") != std::string::npos) ? "AUTO_QUANT=1 " : ""; - std::string cmd = "bash -c '" + quant_prefix + "ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + std::string cmd = "bash -c '" + quant_prefix + "make " + test_case + " >& compile.log'"; std::cout << "[DEBUG] Running command: " << cmd << std::endl; // std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; int command_return = system(cmd.c_str()); @@ -187,6 +202,19 @@ std::pair> processDataTimer(const std::string test_c double time_consumption; std::vector function_results; + + /* ---------- Warm-up phase ---------- */ + for (int i = 0; i < WARMUP_ITERATIONS; ++i) { + std::string warm_cmd = "bash -c './main_out " + params + " > /dev/null 2>&1'"; + int warm_ret = system(warm_cmd.c_str()); + if (warm_ret != 0) { + std::cerr << "[Warm-up] iteration " << i + << " failed for " << test_case + << " with params: " << params << std::endl; + } + } + /* ---------- End Warm-up ---------- */ + // perf command //TODO Addd Quantization prefix // std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; @@ -196,7 +224,7 @@ std::pair> processDataTimer(const std::string test_c // std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; // std::cout << "[DEBUG] Running command: " << cmd << std::endl; std::string quant_prefix = (test_case.find("_opt") != std::string::npos) ? "AUTO_QUANT=1 " : ""; - std::string cmd = "bash -c '" + quant_prefix + "make " + test_case + " >& compile.log'"; + std::string cmd = "bash -c '" + quant_prefix + " make " + test_case + " >& compile.log'"; std::cout << "[DEBUG] Running command: " << cmd << std::endl; @@ -346,12 +374,25 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin timer_data.ir_lines = getIrLines(); timer_data.library_size = getLibSize(); -ofs << test_cases << "\t" << param_str << "\t" << precision_bits << "\t" - << timer_data.inst_count_avg << "\t" - << std::accumulate(timer_data.ms_time_consumption.begin(), timer_data.ms_time_consumption.end(), 0.0) / timer_data.ms_time_consumption.size() << "\t" - << timer_data.ir_lines << "\t" << timer_data.library_size << "\t" - << std::accumulate(timer_data.compile_time.begin(), timer_data.compile_time.end(), 0.0) / timer_data.compile_time.size() - << std::endl; + + /* ---------- runtime trimmed-mean ---------- */ + timer_data.time_consumption_avg = trimmedMean(timer_data.ms_time_consumption); + /* ------------------------------------------ */ + + ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg + << "\t" << timer_data.time_consumption_avg + << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + << "\t" << std::accumulate(timer_data.compile_time.begin(), + timer_data.compile_time.end(), + 0.0) / timer_data.compile_time.size() + << std::endl; + +// ofs << test_cases << "\t" << param_str << "\t" << precision_bits << "\t" +// << timer_data.inst_count_avg << "\t" +// << std::accumulate(timer_data.ms_time_consumption.begin(), timer_data.ms_time_consumption.end(), 0.0) / timer_data.ms_time_consumption.size() << "\t" +// << timer_data.ir_lines << "\t" << timer_data.library_size << "\t" +// << std::accumulate(timer_data.compile_time.begin(), timer_data.compile_time.end(), 0.0) / timer_data.compile_time.size() +// << std::endl; return timer_data; } @@ -359,12 +400,13 @@ ofs << test_cases << "\t" << param_str << "\t" << precision_bits << "\t" int main(int argc, char** argv) { std::vector test_cases{ - // "perf_exp", "perf_log", - // "perf_acosh", "perf_j0", - // "perf_y0", "perf_rem_pio2", "perf_sincosf", - // "perf_float64_add", "perf_float64_div", - // "perf_float64_mul"}; - "perf_j0","perf_y0"}; + // "perf_exp", "perf_log", + // "perf_acosh", "perf_j0", + // "perf_y0", "perf_rem_pio2", "perf_sincosf", + // "perf_float64_add", "perf_float64_div", + // "perf_float64_mul"}; + "perf_j0","perf_y0"}; + //"perf_y0"}; if (argc >= 2) @@ -418,52 +460,52 @@ int main(int argc, char** argv) // param_range, precision_bits, frac_q std::vector, int, int>> normalParameters = { - // BMX055 acceleration - {{-2, 2}, 12, 9}, - {{-4, 4}, 12, 8}, - {{-8, 8}, 12, 7}, - {{-16, 16}, 12, 6}, + // BMX055 acceleration + // {{-2, 2}, 12, 9}, + {{-4, 4}, 12, 8}, + {{-8, 8}, 12, 7}, + {{-16, 16}, 12, 6}, - // BMX055 gyroscope - {{-125, 125}, 16, 8}, + // BMX055 gyroscope + {{-125, 125}, 16, 8}, - // LM35 Centigrade Temperature Sensor - {{-40, 110}, 10, 2}, - {{-55, 150}, 11, 3}, - {{0, 100}, 10, 3}, - {{0, 70}, 10, 3}, + // LM35 Centigrade Temperature Sensor + {{-40, 110}, 10, 2}, + {{-55, 150}, 11, 3}, + {{0, 100}, 10, 3}, + {{0, 70}, 10, 3}, - // LPS25H Pressure Sensor - {{260, 1260}, 14, 4}, + // LPS25H Pressure Sensor + {{260, 1260}, 14, 4}, - // MAX31820 1-Wire Ambient Temperature Sensor - {{10, 45}, 12, 6}, - {{-55, 125}, 11, 3}, + // MAX31820 1-Wire Ambient Temperature Sensor + {{10, 45}, 12, 6}, + {{-55, 125}, 11, 3}, - // DHT11 Humidity Sensor - {{20, 80}, 8, 2}, - {{0, 50}, 8, 2}, + // DHT11 Humidity Sensor + {{20, 80}, 8, 2}, + {{0, 50}, 8, 2}, - // LMP82064 Current Sensor and Voltage Monitor with SPI - {{-0.2, 2}, 14, 12}, + // LMP82064 Current Sensor and Voltage Monitor with SPI + {{-0.2, 2}, 14, 12}, - // PCE-353 LEQ Sound Level Meter - {{30, 130}, 8, 1}, + // PCE-353 LEQ Sound Level Meter + {{30, 130}, 8, 1}, - // LLS05-A Linear Light Sensor - {{1, 200}, 10, 2}}; + // LLS05-A Linear Light Sensor + {{1, 200}, 10, 2}}; std::vector> trigonometricParams{ - {0, 0.17453292519943295}, // (0, pi/18) - {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) - {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) - {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) - {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) - {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) - {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) - {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) - {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) - {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) + {0, 0.17453292519943295}, // (0, pi/18) + {0.6981317007977318, 0.8726646259971648}, // (2pi/9, 5pi/18) + {1.3962634015954636, 1.5707963267948966}, // (4pi/9, pi/2) + {2.0943951023931953, 2.2689280275926285}, // (2pi/3, 13pi/18) + {2.792526803190927, 2.9670597283903604}, // (8pi/9, 17pi/18) + {3.490658503988659, 3.665191429188092}, // (10pi/9, 7pi/6) + {4.1887902047863905, 4.363323129985824}, // (8pi/6, 25pi/18) + {4.886921905584122, 5.061454830783556}, // (14pi/9, 29pi/18) + {5.585053606381854, 5.759586531581288}, // (16pi/9, 33pi/18) + {5.934119456780721, 6.1086523819801535} // (17pi/9, 35pi/18) }; if (argc == 4) @@ -512,8 +554,8 @@ int main(int argc, char** argv) "echo \"[DEBUG] Running make with MAX_PRECISION_BITS=" + frac_str + "\" && " "make -C ../../../../src/newton MAX_PRECISION_BITS=" + frac_str + " BIT_WIDTH=32 > build_" + frac_str + ".log 2>&1'"; -// "2>&1 | tee build_" + frac_str + ".log'"; -//\\ std::string rebuild_cmd = "make -C ../../../src/newton MAX_PRECISION_BITS=" + std::to_string(frac_q) + " VERBOSE=1 rebuild-quant-opt"; + // "2>&1 | tee build_" + frac_str + ".log'"; + //\\ std::string rebuild_cmd = "make -C ../../../src/newton MAX_PRECISION_BITS=" + std::to_string(frac_q) + " VERBOSE=1 rebuild-quant-opt"; std::cout << "[INFO] Rebuilding Newton with MAX_PRECISION_BITS = " << frac_q << std::endl; int ret = system(rebuild_cmd.c_str()); if (ret != 0) @@ -565,14 +607,20 @@ int main(int argc, char** argv) } else { - ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), - ori_perf_data.ms_time_consumption.end(), - 0.0) / - ori_perf_data.ms_time_consumption.size(); - opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), - opt_perf_data.ms_time_consumption.end(), - 0.0) / - opt_perf_data.ms_time_consumption.size(); + // ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), + // ori_perf_data.ms_time_consumption.end(), + // 0.0) / + // ori_perf_data.ms_time_consumption.size(); + // opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), + // opt_perf_data.ms_time_consumption.end(), + // 0.0) / + // opt_perf_data.ms_time_consumption.size(); + + ori_perf_data.time_consumption_avg =trimmedMean(ori_perf_data.ms_time_consumption); + opt_perf_data.time_consumption_avg = + trimmedMean(opt_perf_data.ms_time_consumption); + + ori_perf_data.compile_time_avg = std::accumulate(ori_perf_data.compile_time.begin(), ori_perf_data.compile_time.end(), 0.0) / ori_perf_data.compile_time.size(); @@ -585,18 +633,27 @@ int main(int argc, char** argv) compile_time_speedup = round((ori_perf_data.compile_time_avg - opt_perf_data.compile_time_avg) * 100 / opt_perf_data.compile_time_avg); } - - if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) - { - ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); - lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); - } - else - { - // assert(false && "Need to check why this case increase size!!!!!!"); - ir_reduce = 0; - lib_size_reduce = 0; - } + auto pct_change = [](double ori, double opt) -> int { + if (ori == 0) return 0; + return static_cast(round((ori - opt) * 100.0 / ori)); + }; + + ir_reduce = pct_change(ori_perf_data.ir_lines, + opt_perf_data.ir_lines); + lib_size_reduce = pct_change(ori_perf_data.library_size, + opt_perf_data.library_size); + + // if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) + // { + // ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); + // lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); + // } + // else + // { + // // assert(false && "Need to check why this case increase size!!!!!!"); + // ir_reduce = 0; + // lib_size_reduce = 0; + // } ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup @@ -614,34 +671,35 @@ int main(int argc, char** argv) // {p.front(), p.back()}); {range.front(), range.back()}); change_nt_range("sed -i 's/", "/15 mjf, 36 mjf/g' ../../sensors/test.nt", {p1, p2}); + // } + size_t count = is_trig ? trigonometricParams.size() : normalParameters.size(); + avg_inst_speedup = round(avg_inst_speedup / count); + avg_time_speedup = round(avg_time_speedup / count); + avg_ir_reduce = round(avg_ir_reduce / count); + avg_lib_size_reduce = round(avg_lib_size_reduce / count); + avg_compile_time_speedup = round(avg_compile_time_speedup / count); + // avg_inst_speedup = round(avg_inst_speedup / parameters.size()); + // avg_time_speedup = round(avg_time_speedup / parameters.size()); + // avg_ir_reduce = round(avg_ir_reduce / parameters.size()); + // avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); + avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" + << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%\t" + << avg_compile_time_speedup << "%" << std::endl; + + if (test_cases[case_id] == "perf_float64_sin") + { + // trigonometricParams cannot have extention + break; + } } - size_t count = is_trig ? trigonometricParams.size() : normalParameters.size(); - avg_inst_speedup = round(avg_inst_speedup / count); - avg_time_speedup = round(avg_time_speedup / count); - avg_ir_reduce = round(avg_ir_reduce / count); - avg_lib_size_reduce = round(avg_lib_size_reduce / count); - avg_compile_time_speedup = round(avg_compile_time_speedup / count); - // avg_inst_speedup = round(avg_inst_speedup / parameters.size()); - // avg_time_speedup = round(avg_time_speedup / parameters.size()); - // avg_ir_reduce = round(avg_ir_reduce / parameters.size()); - // avg_lib_size_reduce = round(avg_lib_size_reduce / parameters.size()); - avg_speedup << test_cases[case_id] << "\t" << avg_inst_speedup << "%\t" - << avg_time_speedup << "%\t" << avg_ir_reduce << "%\t" << avg_lib_size_reduce << "%\t" - << avg_compile_time_speedup << "%" << std::endl; - - if (test_cases[case_id] == "perf_float64_sin") - { - // trigonometricParams cannot have extention - break; - } + + // ofs.close(); + // + // return 0; } + ofs.close(); -// ofs.close(); -// -// return 0; + return 0; } - ofs.close(); - - return 0; } diff --git a/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp index 481031320..da765c5f8 100644 --- a/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp +++ b/applications/newton/llvm-ir/performance_test/auto_test_woquant.cpp @@ -17,7 +17,8 @@ #include #include -const size_t iteration_num = 1; +const size_t iteration_num = 5; +constexpr int WARMUP_ITERATIONS = 3; struct perfData { int64_t inst_count_avg; @@ -92,6 +93,20 @@ timespec toc( timespec* start_time, const char* prefix, bool print ) return time_diff; } +/* ---------- trimmed mean helper ---------- */ +template +double trimmedMean(std::vector v, double trim_ratio = 0.10) { + if (v.empty()) return 0.0; + std::sort(v.begin(), v.end()); + + size_t n = v.size(); + size_t cut = std::max(1, static_cast(n * trim_ratio)); + if (cut * 2 >= n) cut = n / 2; + + double sum = std::accumulate(v.begin() + cut, v.end() - cut, 0.0); + return sum / static_cast(n - 2 * cut); +} + /* * Get number from: * 36,200,478 instructions @@ -113,6 +128,7 @@ int64_t getPerfCount(const std::string& string, size_t position) { return std::stoi(substring); } + /* * Get number from: * computation delay: 0.001342399 @@ -147,7 +163,7 @@ std::pair processDataPerf(const std::string test_case, const s int64_t inst_count, time_consumption; // perf command - std::string cmd = "bash -c 'ENABLE_OVERLOAD=true make " + test_case + " >& compile.log'"; + std::string cmd = "bash -c 'make " + test_case + " >& compile.log'"; int command_return = system(cmd.c_str()); if (command_return != 0) { return std::make_pair(0, 0); @@ -207,6 +223,18 @@ std::pair> processDataTimer(const std::string test_c double time_consumption; std::vector function_results; + /* ---------- Warm-up phase ---------- */ + for (int i = 0; i < WARMUP_ITERATIONS; ++i) { + std::string warm_cmd = "bash -c './main_out " + params + " > /dev/null 2>&1'"; + int warm_ret = system(warm_cmd.c_str()); + if (warm_ret != 0) { + std::cerr << "[Warm-up] iteration " << i + << " failed for " << test_case + << " with params: " << params << std::endl; + } + } + /* ---------- End Warm-up ---------- */ + // perf command std::string cmd = "bash -c './main_out " + params; cmd += " 2>&1 | tee tmp.log'"; @@ -347,15 +375,27 @@ struct timerData recordTimerData(const std::string& test_cases, const std::strin timer_data.ir_lines = getIrLines(); timer_data.library_size = getLibSize(); - ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg - << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), - timer_data.ms_time_consumption.end(), - 0.0) / timer_data.ms_time_consumption.size() - << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size - << "\t" << std::accumulate(timer_data.compile_time.begin(), - timer_data.compile_time.end(), - 0.0) / timer_data.compile_time.size() - << std::endl; + /* ---------- runtime trimmed-mean ---------- */ + timer_data.time_consumption_avg = trimmedMean(timer_data.ms_time_consumption); + /* ------------------------------------------ */ + + ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg + << "\t" << timer_data.time_consumption_avg + << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + << "\t" << std::accumulate(timer_data.compile_time.begin(), + timer_data.compile_time.end(), + 0.0) / timer_data.compile_time.size() + << std::endl; + + // ofs << test_cases << "\t" << param_str << "\t" << timer_data.inst_count_avg + // << "\t" << std::accumulate(timer_data.ms_time_consumption.begin(), + // timer_data.ms_time_consumption.end(), + // 0.0) / timer_data.ms_time_consumption.size() + // << "\t" << timer_data.ir_lines << "\t" << timer_data.library_size + // << "\t" << std::accumulate(timer_data.compile_time.begin(), + // timer_data.compile_time.end(), + // 0.0) / timer_data.compile_time.size() + // << std::endl; return timer_data; } @@ -368,12 +408,12 @@ int main(int argc, char** argv) { // "perf_float64_add", "perf_float64_div", // "perf_float64_mul"}; std::vector test_cases{ - // "perf_exp", "perf_log", - // "perf_acosh", "perf_j0", - // "perf_y0", "perf_rem_pio2", "perf_sincosf", - // "perf_float64_add", "perf_float64_div", - // "perf_float64_mul"}; - "perf_j0","perf_y0"}; + "perf_exp", "perf_log", + "perf_acosh", "perf_j0", + "perf_y0", "perf_rem_pio2", "perf_sincosf", + "perf_float64_add", "perf_float64_div", + "perf_float64_mul"}; + //"perf_y0"}; if (argc >= 2) { test_cases.clear(); @@ -496,12 +536,17 @@ int main(int argc, char** argv) { inst_speedup = 0; time_speedup = 0; } else { - ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), - ori_perf_data.ms_time_consumption.end(), - 0.0) / ori_perf_data.ms_time_consumption.size(); - opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), - opt_perf_data.ms_time_consumption.end(), - 0.0) / opt_perf_data.ms_time_consumption.size(); + // ori_perf_data.time_consumption_avg = std::accumulate(ori_perf_data.ms_time_consumption.begin(), + // ori_perf_data.ms_time_consumption.end(), + // 0.0) / ori_perf_data.ms_time_consumption.size(); + // opt_perf_data.time_consumption_avg = std::accumulate(opt_perf_data.ms_time_consumption.begin(), + // opt_perf_data.ms_time_consumption.end(), + + // 0.0) / opt_perf_data.ms_time_consumption.size(); + + ori_perf_data.time_consumption_avg =trimmedMean(ori_perf_data.ms_time_consumption); + opt_perf_data.time_consumption_avg = + trimmedMean(opt_perf_data.ms_time_consumption); ori_perf_data.compile_time_avg = std::accumulate(ori_perf_data.compile_time.begin(), ori_perf_data.compile_time.end(), @@ -518,14 +563,24 @@ int main(int argc, char** argv) { * 100 / opt_perf_data.compile_time_avg); } - if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) { - ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); - lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); - } else { - // assert(false && "Need to check why this case increase size!!!!!!"); - ir_reduce = 0; - lib_size_reduce = 0; - } + auto pct_change = [](double ori, double opt) -> int { + if (ori == 0) return 0; + return static_cast(round((ori - opt) * 100.0 / ori)); + }; + + ir_reduce = pct_change(ori_perf_data.ir_lines, + opt_perf_data.ir_lines); + lib_size_reduce = pct_change(ori_perf_data.library_size, + opt_perf_data.library_size); + + // if (ori_perf_data.ir_lines > opt_perf_data.ir_lines) { + // ir_reduce = round((ori_perf_data.ir_lines - opt_perf_data.ir_lines) * 100 / opt_perf_data.ir_lines); + // lib_size_reduce = round((ori_perf_data.library_size - opt_perf_data.library_size) * 100 / opt_perf_data.library_size); + // } else { + // // assert(false && "Need to check why this case increase size!!!!!!"); + // ir_reduce = 0; + // lib_size_reduce = 0; + // } ofs << "speed up after optimization\t" << param_str << "\t" << inst_speedup << "%\t" << time_speedup << "%\t" << ir_reduce << "%\t" << lib_size_reduce << "%\t" << compile_time_speedup << "%" << std::endl; std::cout << test_cases[case_id] << ": speed up after optimization\t" << param_str << "\t" << inst_speedup diff --git a/applications/newton/llvm-ir/performance_test/plot_sensor_ranges.py b/applications/newton/llvm-ir/performance_test/plot_sensor_ranges.py index dfcbfb418..4a949cec1 100644 --- a/applications/newton/llvm-ir/performance_test/plot_sensor_ranges.py +++ b/applications/newton/llvm-ir/performance_test/plot_sensor_ranges.py @@ -78,7 +78,7 @@ "times, lib size reduce: ", format(average_libsize_reduce[i], '.2f'), "%") performance_data = [] -with open('perf.log', 'r') as f: +with open('perf_woquant.log', 'r') as f: for line in f.readlines(): line_list = line.strip('\n').split('\t') performance_data.append(line_list) @@ -153,7 +153,7 @@ perf_data_speedup = [inst_speedup, time_speedup, ir_reduction, lib_size_reduction] -y_labels = ["instruction counts (million)", "time consumption speedup", "IR lines", "library size reduction ratio"] +y_labels = ["instruction counts (million)", "time consumption speedup", "IR lines", "library size reduction ratio","compile time"] machine = platform.machine() # machine = "aarch64" @@ -193,5 +193,5 @@ plt.close() -os.system('cp perf.log ' + machine + "_perf.log") +os.system('cp perf_woquant.log ' + machine + "_perf_woquant.log") os.system('cp average_speedup.log ' + machine + "_average_speedup.log") diff --git a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp index 6348dc3ea..2faef266e 100644 --- a/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp +++ b/src/newton/newton-irPass-LLVMIR-optimizeByRange.cpp @@ -1107,7 +1107,7 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } } - if (enableQuantization) + if (enableQuantization) { flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); llvm::errs() << "Auto quantization enabled\n"; @@ -1125,10 +1125,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl } } - - - - /** * Check for potential overflows */ @@ -1151,27 +1147,27 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // } } - /* - * remove the functions that are optimized by passes. - * */ - if (useOverLoad) - cleanFunctionMap(Mod, callerMap); - - if (useOverLoad) - overloadFunc(Mod, callerMap); - - callerMap.clear(); - funcBoundInfo.clear(); - useOverLoad = true; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - } + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + // callerMap.clear(); + // funcBoundInfo.clear(); + // useOverLoad = true; + // for (auto & mi : *Mod) + // { + // auto boundInfo = new BoundInfo(); + // mergeBoundInfo(boundInfo, globalBoundInfo); + // rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + // funcBoundInfo.emplace(mi.getName().str(), boundInfo); + // std::vector calleeNames; + // collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + // } /* * simplify the condition of each branch @@ -1199,28 +1195,28 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl - /* - * remove the functions that are optimized by passes. - * */ - if (useOverLoad) - cleanFunctionMap(Mod, callerMap); - - if (useOverLoad) - overloadFunc(Mod, callerMap); - - flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); - callerMap.clear(); - funcBoundInfo.clear(); - useOverLoad = false; - for (auto & mi : *Mod) - { - auto boundInfo = new BoundInfo(); - mergeBoundInfo(boundInfo, globalBoundInfo); - rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); - funcBoundInfo.emplace(mi.getName().str(), boundInfo); - std::vector calleeNames; - collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); - } + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); + // + flexprint(N->Fe, N->Fm, N->Fpinfo, "infer bound\n"); + callerMap.clear(); + funcBoundInfo.clear(); + useOverLoad = false; + for (auto & mi : *Mod) + { + auto boundInfo = new BoundInfo(); + mergeBoundInfo(boundInfo, globalBoundInfo); + rangeAnalysis(N, mi, boundInfo, callerMap, typeRange, virtualRegisterVectorRange, useOverLoad); + funcBoundInfo.emplace(mi.getName().str(), boundInfo); + std::vector calleeNames; + collectCalleeInfo(calleeNames, funcBoundInfo, boundInfo); + } // flexprint(N->Fe, N->Fm, N->Fpinfo, "constant substitution\n"); for (auto & mi : *Mod) @@ -1235,15 +1231,15 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl // assert(false); // } } - - /* - * remove the functions that are optimized by passes. - * */ - if (useOverLoad) - cleanFunctionMap(Mod, callerMap); - - if (useOverLoad) - overloadFunc(Mod, callerMap); + // + // /* + // * remove the functions that are optimized by passes. + // * */ + // if (useOverLoad) + // cleanFunctionMap(Mod, callerMap); + // + // if (useOverLoad) + // overloadFunc(Mod, callerMap); @@ -1264,34 +1260,6 @@ irPassLLVMIROptimizeByRange(State * N, bool enableQuantization, bool enableOverl eraseOldFunctions(*Mod); - // if (enableQuantization) - // { - // flexprint(N->Fe, N->Fm, N->Fpinfo, "auto quantization\n"); - // llvm::errs() << "Auto quantization enabled\n"; - // std::vector functionsToInsert; - // for (auto & mi : *Mod) - // { - // llvm::errs() << "Quantizing function: " << mi.getName() << "\n"; - - // irPassLLVMIRAutoQuantization(N, mi, functionsToInsert, maxPrecisionBits); - // } - // for (auto mi : functionsToInsert) - // { - // Mod->getFunctionList().remove(mi); - // Mod->getFunctionList().push_front(mi); - // } - // } - - // /* - // * remove the functions that are optimized by passes. - // * */ - // if (useOverLoad) - // cleanFunctionMap(Mod, callerMap); - - // if (useOverLoad) - // overloadFunc(Mod, callerMap); - - const char * homeDir = getenv("HOME"); diff --git a/src/newton/newton-irPass-LLVMIR-quantization.cpp b/src/newton/newton-irPass-LLVMIR-quantization.cpp index bad060b5f..3e1cb8fd1 100644 --- a/src/newton/newton-irPass-LLVMIR-quantization.cpp +++ b/src/newton/newton-irPass-LLVMIR-quantization.cpp @@ -3,6 +3,7 @@ #include "llvm/IR/Constant.h" #include "llvm/IR/Constants.h" #include "llvm/IR/GlobalVariable.h" +#include #include #include #include "newton-irPass-LLVMIR-quantization.h" @@ -18,6 +19,9 @@ unsigned int FRAC_Q; #define FRAC_BASE (1 << FRAC_Q) +inline bool isNewtonInternal(const llvm::Function &F) { + return F.hasFnAttribute("newton.internal"); +} llvm::Value * performFixedPointMul(llvm::IRBuilder<> & Builder, llvm::Value * lhs, llvm::Value * rhs, unsigned int FRAC_Q) @@ -155,10 +159,14 @@ createFixRsqrt(llvm::Module * irModule, llvm::Type * quantizedType, std::vector< } } + + // Define the function type: int16_t/int32_t fixrsqrt(int16_t/int32_t x) llvm::FunctionType * funcType = llvm::FunctionType::get(quantizedType, {quantizedType}, false); llvm::Function * func = llvm::Function::Create(funcType, llvm::Function::PrivateLinkage, fixrsqrtFuncName, irModule); + func->addFnAttr("newton.internal"); + llvm::BasicBlock * entryBB = llvm::BasicBlock::Create(irModule->getContext(), "", func); llvm::IRBuilder<> builder(entryBB); @@ -1871,6 +1879,7 @@ performFixedPointSqrt(IRBuilder<> & builder, Module * irModule, Value * fixedPoi void handleSqrtCall(CallInst * llvmIrCallInstruction, Type * quantizedType) { + IRBuilder<> Builder(llvmIrCallInstruction); auto operand = llvmIrCallInstruction->getOperand(0); @@ -2061,7 +2070,7 @@ void handleGetElementPtr(GetElementPtrInst *gepInst, Type *quantizedType) { void quantizeFunctionArguments(llvm::Function & func, llvm::IRBuilder<> & builder) { - // Skip the function if it is MadgwickAHRSupdateIMU + if (isTargetFunction(func)) { llvm::errs() << "Skipping quantization for: " << func.getName() << "\n"; @@ -2155,8 +2164,8 @@ shouldSkipFunction(const std::string & functionName) "fixsqrt", "fixrsqrt", "fixmul", - "llvm.sqrt.f64", - "llvm.sqrt.f32", + //"llvm.sqrt.f64", + //"llvm.sqrt.f32", "sqrt", "sqrtf"}; @@ -2266,7 +2275,7 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect // Usage in the original function std::string functionName = llvmIrFunction.getName().str(); - if (shouldSkipFunction(functionName)) + if (shouldSkipFunction(functionName) || isNewtonInternal(llvmIrFunction)) { return; } @@ -2329,7 +2338,14 @@ irPassLLVMIRAutoQuantization(State *N, llvm::Function &llvmIrFunction, std::vect * generate hardcode function * */ //llvm::Function * fixsqrt = createFixSqrt(module, quantizedType, functionsToInsert); - llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + //llvm::Function * fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + llvm::errs() << "Start Creating fixsqrt function\n"; + static llvm::Function * fixrsqrt = nullptr; + if (!fixrsqrt) { + fixrsqrt = createFixRsqrt(module, quantizedType, functionsToInsert); + fixrsqrt->addFnAttr("newton.internal"); + } + llvm::errs() << "finish Creating fixsqrt function\n"; // functionsToErase.push_back(fixrsqrt); for (BasicBlock & llvmIrBasicBlock : llvmIrFunction) diff --git a/src/newton/newton-irPass-LLVMIR-rangeAnalysis.cpp b/src/newton/newton-irPass-LLVMIR-rangeAnalysis.cpp index b2fb8c7f7..c6b53a04d 100644 --- a/src/newton/newton-irPass-LLVMIR-rangeAnalysis.cpp +++ b/src/newton/newton-irPass-LLVMIR-rangeAnalysis.cpp @@ -2949,7 +2949,7 @@ rangeAnalysis(State * N, llvm::Function & llvmIrFunction, BoundInfo * boundInfo, * and reinterpret it if necessary * */ unionAddress.emplace(llvmIrBitCastInstruction, llvmIrBitCastInstruction->getOperand(0)); - assert(llvmIrBitCastInstruction->getDestTy()->getTypeID() == Type::PointerTyID); + //assert(llvmIrBitCastInstruction->getDestTy()->getTypeID() == Type::PointerTyID); auto vrRangeIt = boundInfo->virtualRegisterRange.find(llvmIrBitCastInstruction->getOperand(0)); if (vrRangeIt != boundInfo->virtualRegisterRange.end()) { From 42d656f59c7faaa64a1da1234f44a20bd5813d43 Mon Sep 17 00:00:00 2001 From: xyf <2316639449@qq.com> Date: Tue, 8 Jul 2025 20:04:53 +0800 Subject: [PATCH 193/213] update fig * quantize. --- ...7abd6a8529a6c5b16e8c2cf5ba64c657b1fad0.txt | 48 ++ .../aarch64-library_size_reduction_ratio.png | Bin 0 -> 583933 bytes .../fig/aarch64-time_consumption_speedup.png | Bin 0 -> 439280 bytes .../performance_test/fig/perf_woquant.log | 511 ++++++++++++++++++ 4 files changed, 559 insertions(+) create mode 100644 analysis/statistics/f57abd6a8529a6c5b16e8c2cf5ba64c657b1fad0.txt create mode 100644 applications/newton/llvm-ir/performance_test/fig/aarch64-library_size_reduction_ratio.png create mode 100644 applications/newton/llvm-ir/performance_test/fig/aarch64-time_consumption_speedup.png create mode 100644 applications/newton/llvm-ir/performance_test/fig/perf_woquant.log diff --git a/analysis/statistics/f57abd6a8529a6c5b16e8c2cf5ba64c657b1fad0.txt b/analysis/statistics/f57abd6a8529a6c5b16e8c2cf5ba64c657b1fad0.txt new file mode 100644 index 000000000..8ad0872cc --- /dev/null +++ b/analysis/statistics/f57abd6a8529a6c5b16e8c2cf5ba64c657b1fad0.txt @@ -0,0 +1,48 @@ + +changeset: 1785:f57abd6a8529a6c5b16e8c2cf5ba64c657b1fad0 +char kNewtonVersion[] = "0.3-alpha-1785 (f57abd6a8529a6c5b16e8c2cf5ba64c657b1fad0) (build 06-16-2025-23:42-yufeng@firefly-Linux-4.4.178+-aarch64)"; + +./src/noisy/noisy-linux-EN -O0 applications/noisy/helloWorld.n -s + +./src/newton/newton-linux-EN -v 0 -eP applications/newton/invariants/ViolinWithTemperatureDependence-pigroups.nt + +Informational Report: +--------------------- +Invariant "ViolinWithTemperatureDependenceForPiGroups" has 2 unique kernels, each with 2 column(s)... + + Kernel 0 is a valid kernel: + + 1 1 + -0.5 -0 + 1 0 + 0.5 0 + 0 -1 + -0 -1 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 0, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^( 0) P5^(-0) + + Pi group 0, Pi 1 is: P0^(-0) P1^( 1) P2^( 0) P3^( 0) P4^(-1) P5^(-1) + + + Kernel 1 is a valid kernel: + + 1 0 + -0.5 1 + 1 -2 + 0.5 -1 + -0 -2 + 0 -2 + + + The ordering of parameters is: P1 P0 P3 P2 P4 P5 + + Pi group 1, Pi 0 is: P0^(-0.5) P1^( 1) P2^(0.5) P3^( 1) P4^(-0) P5^( 0) + + Pi group 1, Pi 1 is: P0^( 1) P1^( 0) P2^(-1) P3^(-2) P4^(-2) P5^(-2) + + + + diff --git a/applications/newton/llvm-ir/performance_test/fig/aarch64-library_size_reduction_ratio.png b/applications/newton/llvm-ir/performance_test/fig/aarch64-library_size_reduction_ratio.png new file mode 100644 index 0000000000000000000000000000000000000000..8f2766d6880ce644afaea6e0e880b87a8aee746b GIT binary patch literal 583933 zcmd43c{r7A+djNBDUl{+s8o_#l3B=5X%JB@mZ^jc84{61hDxY3vxp2C7M3wnW{M;d zB}0bHL&!X3?Ax#I`+c|P_x^tWeB1YZ+xGo&KlgKMwOrSEp2uc1Quypb1O6p@?U>Fr4I8s-jz)H-BsHUJwwG< z4Z7lW5)+AZ^5|jB8^OPRIJj)+vy|e{*Pcx=wf&2anX24~AI}+dXzPeSiAs z(W8_{OiEq@w)O2vo|M}ZVafg>i=pWgZfZBPcd1ot&J-9V7qo>o%TMoe%)V*Ydg2Ca zvB>tjYmcnl@t^-g=^eGoA^qo1{`o+S$C9`I+fON{&yZgH&u=)=zlw#l;eUMX5oP(q zOospShbtKZdq}7L*9X97CYLS0wd#L)D*P}i^c6|ufBx`p&4!Jn-T(8so;u(8f9YYL z>P5YJMS8?@V8xTtrB6ydQc^^pKYt!){f9m$v~VuNlA19%rq>oI z-mswk>lrb-Plr~>)}KFr-ZDpda$9Z8N$d8Gjw0F30;faIe>=AG?pn&De;145Ge0|! zypo-Lv#_v!&c?dBI!T?KN0hCNjg38y`E0r$P+VL*+GJB$Yk2>^*Ov0~pRHn)aC+U2 zj~9&L$oBGD(b3US^M<#~|J~SPdV0E{kr9)&w)VBbMvn2G&&4hIH=l)uUg)z^9{5b< zkk4JSMS8oVqGas`aWS#=h4W)RHySD{A9Hl=xcijivsu_oa*b~8bs_vMZ4Z7t?mwHY zZ2ix^M{UZz+UZqe782?-b+G zojZ3%luzZ~oW7FLm1b4HKcg%1%BKL~6NeAqzTs5%oc~LGee%2Y>yB6o^6~NQJD+tX zFE}8;n?q2m=I=W{&AIPY#-FiVjs4Kd9Y;g{S+|;tV@qhcj>9X8MQU}pXINIRzS{M1 zUHavQ^#`y2ihb@HEa$XdS67!-&L=0g#c{ZWs&T(YVEcB`iN{;k37^x^X&bLn{O6bE zHl$rDe*gYxp36wv;XJ4D=oP9*|7)e6Hs1Ps_>_fcc)4#er$4#K@7#IBT4|^1w>YXG z!+qPfZLc#knV*#U=ICF!vR~oPynV)Gosh7wF#Cxo+Y&Xm-JJTxpxz#*w7O1jjXyu$ zTjhQG_F*>vduu68Ts%CTKEefSg#-Nj47O6FRob;B&km#{!`iU=a(HDH*HF- zz9F|wP&o_hH}R_>O=W7hw^v|#vZCTtbDq-%9;wSu8zZmxR0RI4rmqf?u%IyPUIYfF z^lZld95AbR5l*>qBz*1M)Sxjv@a0QBA)!QyX+lDMvO(wk%y@^XnwlD|m2XE^v6y|& zsf2Tx>Ml0YPGh@@ii^L-o{i+*A82Q3T)gE-9EB>?!FRW2Urx-k#v_f_C^#7kTWXdP zd4oaUCAUe~{p1O^ z4-dJPvazv^j&+tkp>FG#=&y~x_vjIa@XNqJqqn!1X#0iN0H5}1!Ed9mIO3DN(% zZrWKnIT5GB6go`9&)j3GtJeY0C8!Y}-i7mfWT|MMXzw5%lpsc8FbYCZ+6ajn0_JuN3NrHTADb? zusQ2$Lc6@`&DEKfwQ;gOWnW(%UV6jXdFP%z_h^f=lUA%-Wz#86>|A2Y{QdovPo88V z?b^M&qnfVDDQ0r7$O*5z_Smsw9o_yy=OV0|-q6SavcFmj!!qPdKCU&luwV%N85yz7 zbDm_mdiCnZ^71=Kq}-C0ch_u{eOpv?#Cqk`cwTN)Y~Zp{A{J))%a?AbFWS6A2XO-;q$zHJB%4W*`5<>krAIgKqF9UWCx zSI2@dB_t#mT3fRrsHA0O<6OgJWMo#ZS#w-jc_l?d<5@@u+t#gHaoFEAHgdPLw7ky9 zpw79`_mas)2)EIx!E;ejQLkRVUW+XL^Lq-P?6sd~+Nb#W`H4r^$;Y>jmzSON`t|Eu zF);#p`T5}t#$uS^iWMt_m&$Bq3yb?VfYv`eQw3rc5ZW>&6Rb=cNcghHV_ zrO}on;D|@Au3n#bF4N1$N0nCKO~FHrjE@%&)F(fC@#5Z{J4-k@IbWH6^8bmg&F~A) z>F(|2+p}k-O-tTkXJ^^ew6yT>aANt0Z)#}Zdc-Y>hf`A9oS}zgyJm55kyt=GJ243f z2}28uRX1#XVxxRx)clW1Z)1Yzdzz?=Lb%y%P)#%hyzjL zvzhq&6w3O$M|Kc%k(N!$H%kva)v`%kkaGI-$AD1+q-uagTW9H z5xJF-AsHMT{H4J4X2zmFKdSLbRsxM!(v~u>5h3Zgqp)l$Tpa6GtsFq{jIU7$2?>#L zio5vn>5gERO+tE6BU71Ieso;^)A>YE4A5)!rhSHV_N}s-jMR+F4bL+cyx4d+y^LPq zQLj#Mad9y;TjEfnI)BZ5Pcf!dYVX-7cF=Liq%qy1xU6vEw33oXUwtyK{q&H2-J9nF ziRZH^bg{1PZqdR&iwD0kGz5+-DJhxBkVFt56~cw{MO|H8d;F%6He{UgTxKYAJO2ld z9-01jQ4p*;8+n{cKJ7I1tATGi@8--HzqnnO^6_)$&h_-xrCVt9WVX5)W0`6w(MWQl zNHeC_u3aO`T@chxGQl=cTePoU6}6x2rBaF!+@h`Sf8>3G zgA=M11ZOLjE?pXUe}jM+VudoTXi>c@dqzM&KpYEZjMHLLz>IA$YcKLVMWH+|>Za5T z{m9jI8o432d-v|+eJ*6xTC02HrjxmsnsXdf=bdldkQO&B-+0`>z@SIe;>#j&D@NLjP2FNl@=t`H`Pf|O9;TafZSOa1;&Ba)w4!0}$Vk34!*@$| z%gXBYO}`W2aQFZE^=n12j6ElwgfTb#HIlhlgq1)bl~$PP*~HJU6*%l1ISNxhzbf@#iv2m;|2zI?&hEWZQvNgj z@yRcu)n1l=)>c*Z>8nfRu?G-2zGovj<4O60hY$U3u8EF{5@~fAXEV0463qBjSy{<3 zgaZ6XH^Y*nv%8yrda#;oa$wPm(;q4VSeE*ZOBkw{4PVW@B>QkTEKZETG? z>FekAq(A3?=)>85e`J&j{Mr{-)lZ4MOc6H?Ysi4_Ps!eiwa-JJ7b~95yD5Gq&U?pUAlD1>|3;&WqbfRj$Ak{Dk@rG z;J#48$|ZKRXCZk$te`+a-0&@_2lZJcpV=YUC$%8#dEq!Me<!xgGYMN5_jMXgYBr63`QBU57}=R#aXAx(#lGF@7}$v#Wj~nzmp3WJAUQ57%FqDTet31 zeUd)S*-*r$Meg|awIZyMq0Cyqeulq({ql3OH8KWXLA5NJM}CxP+OPBaP@kRuG*F-Y zSl+ch3VqD)M2knQq*dLXjU#&(tXlH2%h~4$o}SC?XU4i&KG9h% z*bimev?g6spwas!kJ1LamJ0!i3LQ}1Ia804+%r2lppvhx%hVPTvD2(FScU^2ZfcuN zYr$t}85zd>!k3yFlY+USgE&7&8Phw5(-816&D&+qY|5+n^vSs5^Jm>;ho4^#Gg6gh zU4GkUgf`*4pPC-}K~-2mdV#t@%?iueNUFqVHwj;;K!PXdKS5C!V`5@r89cGIYq>{d zm|~TTBL5%!U(~BK;Ij_^JbhK~YHMlPu5)ixh8a;&cV~KD+W5usq?>=evx=Sl5@)pA zvRlZ1ag-nH*Dm)s9W4E>JoydtEqiSh`wa>z^rl8eZqaD8@|j~tkG{G&J(T7Cr-0Ig zqOoej?o*^<5%Mq(3FlkXb@p1t3(=(rD;1kC)aBC(Gs_ege@8Y?B7b{eJyWXzeeQO2 zT%=FyTA~sqm?CSqOnf~-?v07VHniss7id^M9(eoq5TN^psxU=uXLs$S^M|p9BflEN z>6vOLPrlo1;BtHU>P?4`Ir4r00+bYI$L~ZRVc%O>lAxQWSs9rpW8c#@H(aRgd|{OG zXMUXh!y_I7pL%AVR8uQyd*^Yry=XaYYVRW-pVgN*4a=-5ye|&aCBBe#AdiiYbF}mT zMsP~oZr^_)Plw@3bR9LZXMf`hsX5UAM>~<1yBAh3TY}%VLdkm#btZWKFv>;oMBle( z0Rb^zW&uNqFB2Rr^A)%;Ge$2r{hs3>Xw~rjJ7<{dRABi`*#-^{&m{fa4rC?jb9eoT z#f6&)f5U~@$vu1aoN>PC?(TkVsF^EZzkb}E>b*lhzeaM(UfWY$U9H)uaPS}xaQ?dk ztuA~S1?Qf9{HQ!J*c6~4ka!PmCXye~Jk@Ry zwL(m{j5tj$U5d{(p$hQxFVAOi2GU81E zx>#(cQR}Y_w`>Kpq&2#mQl}2O|5;p6S&(x6J+cF+aJ0WxkTxD`;pF7Rso)~*Ha~j8 zbAYK^Kj+%25Lt&ig@x`Mevj84xwo_b&--OS63Sas(e75VtCblFlU$}6F6p3D4V3=I z+K8YVBMV2$Rl7AyTGl9A&zugC&o*{mh4(;h&tASyH9-|Lv*1eED)lHn|)tA&nIlxKLMHTXKtO>6gTF z2fjBn><|!OZf|dwcAb@IDRBMNC?zhw0Z_v;Ia!!n>T>Lt#VbE@JW*0rU9)h4tXQ|n zaqi#0|I4>;N=J??8EVeVfm#t@G9SqkOO=a&cKN0OLOsctwq0YEN&{P?km znAn}r&~0{hc54CQ6%`fF=R0!%FeVJXv2OALN;3oqA`fmRUH|p{w$QmZ_X5yte$BKo zxYk#_TuMsHkaGn}U6#Xuf6aJ%SDEkc4j*CZdTV@$h*IKlJv^2o0j%4w!DIF2{cV8u zq@%ZUKxk;)VAD9Bdwo!ceU6`JqwC6Tif#{w3+B}G5*Up87xJCM(Vp7c+8#18+7S^M zd3C5+M*U1`Z0t(D6OZ46HTs%je1bMrc(H^f_@JYD-sLM-9y2Xn@e0k+>o;$XySXV` zG&a6EGiK`9v+Yer#xbCRuL-*1D6wx5XtT4kA3uLS22{Re$r9V{a*kd6{3&m8ZQGX= zEKHrNlRT#TXJMMxZKiwkXzJs~Yn-Qkt>We7{odGkL_=dkN=gdR!VDb#gANwe%l$RC z*X18M97rZls!dn2uyiyF3Y>aD0<`03Ia?B~9zvERwPJ@5#Bb5+L`O$|sjfb9{kl~3 zw{PpVY+*$_%egO}XKcTpigTDiX-m<>4))41l+pXiUjKFP=TGxWE9@TF5c#&gzEx-e zNQdz7+vJ_^ss`*&S#kxZiX zwzya++_hG!q@?6$eR5dM3N9A~UNptW5K2T}t)rv!3aldfij^x@zV#J2J%R>1&z;(@9xb*^#L!FK=&V5JF3UfjPOj4sTz(9l`kS-8+;!g#kzM_8hOr+>$Iv zKq5f1f$lT*qWkupnxA7Ov9hv$$-3If@+mekQNLZbd@YM7k*t#p3M-vHfBd)-33Zc! zdunRxT5j$K;Kum&?R%JU1y7#iIJ^chK0{$MDCvk-+ygiN(!!f=A9{);{+0{Zg$O@ zH3?Pe(O$RKu(Edjazs@j_X@~d;~{-`|Nd@6hE+g?HTZ@=MCgP10IBnF5lVH5Iw^P3 z644ph|N1UEI#OM|ar0(z0w$uN)cuPt@#c1)_RFw0pn!6a11m1 zj=0MLgP~^^Z`Pnw+$}Aw(=&zo`g~U29R2T+Z)9bjB{>tp)nC3C7W`SfnQgyXHB^I@ zPxdr8!;dI{UQTHCJWUNb&n~oM34&+XoaDBRPxNVcjy;{y_5b7caEr`yr|F@PqTz0| zyJp>{hK8@I^V+L>T$1D3?+*XSEp5;S6bqc29u`b@1@mE#&58z~s;D<`*KeOKU&Ex& zg{P(T3bdx0`czn}sHnU^mc06=4}jLwA$elrn%UQgW5Mkj`uh4Z--2Rarneh@3j3s> zpkSB$wYngnx?cxGYlZut1%c_%mjMB#3HrJI8U~``;u_}W-$6F3Y`t?GM4y=VZw^3B zq<>B@$$`^>IbzEu01Bh(4jg~TxnsKV0$D=BZ>HUw7d!BhrFW-xy&QwC=~{*D@^fBv zn;XoeRH=Eb`>~X$X$}Q5XgUSc4a)4#qknc8LL)CqWXg(J7i9Ld%MD_TZmsmkT3po8 zkV~9iU+e2H?N$qVe?HsJv+UC+jo5v+Z{I%ETHq?v9VmY3anbOJAJZ$SRf8E;^(qUC zpI?Z2lzsg8H2Vjz5`FQw1@>A4f&pV1Aor-#KT3$@eH0#Bw>?7oIhsAQ$f4Z{g_gBs z+LV3+$@c2il$z{5IXOAM@)*#M#Ucr}_(?tJZ~|V&bQOQHDO~WMbXHq_3&8hN!!S)y zTeEziGUQ-z9*%w>zH4v#0=QrL;#=*B+qFnTTnb@TjY%3Y>UO+qlyM3>mraAi-0t`i zY4CMjJ(`i0Ligkm1+L>0wpwZm>{R8Ouk~gRTxpi}>=|~Mo!C3TWYgEWxG*Q! zd>#C5@ZfoUeGcdCLK|5E0n_Z?WTvK4eli7$^?F8WCttYwxE^pX^2?hsg>i5T`hBUV z0>wOiPXicloMpwYs{&tCw(m(?{VQa1yD_vmuT`8FDm1HmaRhbXw>b0r_eYo5a~D7# z#Ef!~dc1kvVr|b^Fc}`I***ZW9bb8z4Y>GxRor{KXGa-k}Z_{Yb` z1Ml8lmOW78MH*R{o2Iv%-pHsIefTpos>^A`E%84o64l%?BtvwAT);q{Q^JW?_Rbtk zBFD}hG*GFXwLE!pl{oO6mJIUw#?XLlsb#ki9Q06CtGa}<=3#)7oy~=}%O$TvdW(QK zN|4$a??JW5C_YCeHcbi(3#+owANchvq1vOfeA%tEw6ws}iVK?kPlj+<_EOgzTk;mY zxN-g!)O_)_^;}$KeKI>{L^7k7ylr)x6S`Cg!t(51;j%+}NZIo4{Hr{VM5C#pJPF^) zp0Mj?WK&asQ%QawFAe_1J~Wh+*Q0WMHJMfFRfGC%PRh-356q?bz`tsOL3E+n*XU)NNR z)-K+2=bBh}xj(Ux+$U?bS%%O3*p=*qzsDl~-hWc}jZN!?p*?F-S9%^%=kxCQ{8Ers zs`zKVc<*Vdl=;)V06b1+`BaHW?FJIaAG5|c)}Lm!ls@F*%Pe^D^5rM4cEc~P4wGqz z?flq~XXr!r<euyR|Q!J-Y>yLFT;{fIdszQ?h2&I-J+srduDqpLpt-A zO?eJoZY|L7zjSuydgcq;@b*5tyt#Q+S=}l8J?|Mg&ab+Ijmo~jDTdI zrC-~1t7PToc7A-u7d%PmWwajSL}j8e`Sokxh02PG_h>HZNx7G*w$(KUqhr-JUqkOd z;}%*;LHSqd^?myEZII3SwIK&JqEDTbX5aS2TkWOP-h7)w5~coKd4*W`v>OVCOds+b zSc9-lNJnn}c)k$r-0QS7WBTBaTy2KyP(zw`#$`jpGm-Wde-`J4RX(>%UhOzq+5DBz zjTYy#E3B^%HQxsVuhm!)DE<`)*mF9b!Pq+)%y^vL^5F?@RnS4ny?gJAd#6Q!cCAY` zNS?S3PO-hhGY>DQqpQ0&*gGaBrr9Zp+WzzB$CJL-6B)x=iF(TkmZ*Mubo3n-K-Xfp zwXhU4IjtoVLHw-ZV`x_vNyS0C*N5T6@k{+gN%n3;36Fu(f}}@;{3;Flh7SVG&iyVaRY=6 z{Id(XX4nt)8H<1uscVjKD8YtG_ud0hsg-;|Ux`1?8In-2Y%=(Dx8Lo|^fBFZ^K~)% z7k+-_V~lHSN1^Kc)tClezI+HhVDdeV#FS`*TNBCp4~GA;DXo~~s} zq0#B_n^KNlzP!&$*T5ib((ue#uxlgg`+d5s@=jM$+=o3xVtQp-EZR#B$%%>1u0x>tw674ksAz zWn0}XuUT+?kXt}a>`df6^hdVb3_>44p!enZ8C|@%gdnOL39Ymo?ePWF|Fg~q%c`_b zoH)@xnY9z=T+^Ay2^_EV?fWlYaApiezWIhAJimG4C{$GY>MqwOCiq3C9_cHQ&b~^5 zP>>)&E#yUz{A|h3EjJAmGi5=I(6Ok#qgO5GI_tOLHQ1gw3Y&=^Ki7yW`o`WxJ#7(I z4~9le2b2k8I`l=BnA2fNBldoInbZBXtD%{aX(eT4tI(Vn(#vu)wU>jMP!~DA?s=HU z%a=UP6F*L3dhIBSi*>tb+YBz&eyZ!5rtURl~kSM93AkxNm5G1G+mclV|*vj8OBcF^cOt(g&=uvZ$_i9A90c@N7a&Pl={7LS(WB%~fvdTz;`E49^hkS~1p|QowSQB(#Zu&Q z+7$b(@mn)!@a*ctun7R4r7BHnm!2GX_X{*ciu1jI9&L>k?Aurs3}&VR`HnwqTejX* z-h{k&Z~MfG%Ru?fUsrT-$vaELIM$$s`pGhLdEExr;^OMMZtdDz-@ogC6yp;V^!X+d ztqFi`l|ApM&-I+WlLI{1C>Tg}g}4dl>4}NExw*OJ@-OIsvM0>UqHpM?Y8$)uc6Kh^ zuwer|bKqMH`@=_%bnDHi+v?)%)Tz5fMAia7coY^Yg6DWfqj~=Nby;0_f?{N3lwgXp z^<3W3&-CKOI|M_Aed!{oZm8A|XIT|U6v$Z!w+cb7IBtSq?k^^vB>ChokOP-Sl z8W&<=4;%;>zqzEXjyj;?A?Fb41Mbul)JuFpM0vLIf264I0NEf-DpUqq3k!>s?yCn& zuS1p1zmW}f+I!8`B6R;XdX*1gaJUHUOx-rWoCL-TzKGQT*re?&+i{@1B_)DQlG{X$ zKh(rAXMvBzLi19T*9`t_Ya{XS@X*FAo8BZ+20-uHF4+YQScQ#$-^nqflMqosW|^+Z z{0`tL1g(tRkSgWeWyA~R(o#Kykur}5#2Ze*l-Z2`!CSO~ z;!2aQopV@mYjR>jhsm(4Q@@qrybr|_xTb_V;x|a5kLa5ETf)m__|?#0LF>IsP@cBx}-ZUxfT zC=*%N`qsmZ^$_rdVA#l#H4JrhzHO}5HbzFbp?cazt3CZtUmvi+r8)m*X7gSy^3H~p z;qxQawTEf;G71Wz;&&I(JdvrTPJb5Vt#s>pw-0Vh>d*KL!Tb94>y#6}7>^($%G@4B z+}6j*x=^2P89jt`>u%45Y)q)uU0o)DGulUC$LddC&+)8|6z>uel@mK~ft7@feG9SJ z7Cm?%+Xjvuuu=K?YD7lf-E*WdJEGraxwW-5TuskwZN;GcdwP1>xyK-Jo#p)ps7sz| zeERtT>!0(MMR!-}q+DDc-#rS(jJ%cPKlSDCo{c*N1y_Ug3pOf;bxq{r`;`@8ioAIJ zGtMh7nyQ7!Qn!#+9L<*jQL#!P>gQTqE zYG%Q61$(~-T30@OT05JyI*Lv&W3xPsri}wzewx5CqX?pp%{}O9@7=qHY8fi0+vAjL z-6ZAdysVcMtxT{v` ze0m)FW`6JIT6en~*Tl9A>mS?Y{n78h-N9`8-q#>d>0{GVQy-AGspNAGvA*z|QEVucTI*T71v&1zzY1u6hNYV}_ zWjo#v@RjBG!URG;UNbr}VyN#(v%VJIuS}`tzO1#|JdW(pTnL^`#P|d2mV?(%vGK1j ztEfnzPP1CDbV3@3{OHr0e!j-&rl+>W0Cg?fu7F0?tO*Yw1J^NffSCea2v(`F(fGNpj`=RO= zqQ)cL0S2kgR8U3QS4cW`ZsrPM!kO(QfO&+jQo=ItUOu&+(wicwLTfB3k zdhOqDYG(G~r0=g1p+5AZF97L!nQV}VZBrt`&(NRMy-bxYbmVw8U+{525(d5K2`Bx) zak^~moQ{c!$sP`pR(CKN?fkjlzdN7EL>|>V?%<{v1ukZd;M#RuT=xl)0eu0bUbxkm zlf*4yehVBegT9YrX6%zNB5~yNfmUT}IRN0>s6*{ANRf`R?I&1mBFTI^t>{Uon`ZcF z1fkKq2pE{K;x=%CwDZIsd?wabq4ugx^3Q^#D{;Y}YDZ;0#?94E&7Gn?*YbXU1*Liiv^x(}Uwvbw@?c>pt(jXqwEM>L&Qt@)>tx za9i#@!fn_4c$<7VmkJ?Ge!+H=c`a@WTFCp<9@Ey|3@BWRlm*>qMdbjR2Of1qy6K#- zkVltX8M+jvw_6vpclSEbj@eZjPi|tD0@2@O;6JI&5%T29hP>@$Rf$#-> zf9f3{{d$7eXs|IO%dVT{&Ye3?pFh8YDgqd2TfiGv(X>a+v_;hk1=SlOji8#{<5miA zw4Bdtmy{xx$bgkY?TDC`2#2M-#?B*NdI^ zF$8$6SrorfLeZybTW4p+`;vR@)ZEr&EGTHu%BzYm#J!n*p~>t}*K2B>{A(d$fBdfn zD|8SPk3s-woh;iOuts?S4DVD>*ai`5514~yA655&I>~E5oXQJF{yL_kR-9a2eWmFQ zo7}e3j6!s}_G2@EJJca++(U@JuY0!dpDfvIpy@Qcs>~<^GSZ`SnO9PJ)_kj}d5=CL zK7C8v(=9>!1O-)x&LpOS&rk6e9B@LCudf{S-JFS`C@ifmZr|8vhD7mex`<7 zg&lri^Xyx!tj%}58Zs7ey7Eya{_%qUEPThh`QuDqgL~l{Ae=7+^JA(LLqB+ihK5AM z#XZmq*BD)l&z;tBsvYHui%a)D?bOe?B4s}V8}`F(apqE8@S|Ji9|zmo3<>QGrV&Z( zJ2N{wJ7kf!kh?7n({~s0R-0Q|UW7F@Asx0L;83Z5TPGCMIoA3aS3?PCyPviPpmc zjMDF++~K%ug)J>D=J1jH?Uu3Pml6rrO!jyw5a+gsN06~*8g_Z#({M;SL48A$sxduQ z{6~ue;vDc^YBf%ufP;Qnd)ViS3K7tS*)z#{87h6=InU=9_hg@h;Hhi81*^S`e5Gol zBBHDXty;2x#ex;xLuqx3{w?}W}Lb#?VDw*}ee=4Oz{(MiQ)E{ik03CvxfCVwN0bp{Ao`BG)D z5gk6HgyY`#B}7HvK@@szT)KwPs(=TwAU$H?ciUg6e)@DRl&O2)crwX^=hC3iEqJyV zb(}6$sd3agC$l~(Xnu?3xa)D<@-;W#y`og=090iMbL=4%N(_xIdfC-4gX zWD^XXIsf_z6zSvl@7+72 z{Va~wm93+x3H)SB8~2slJmaE3OmswXpZz;7!eGLC5Wrth?c6z@M7@l-ny{`X3?yRh zrYEPN=n`9)AaVQ9Eg1SW``k*O@Cu9HGO@N!oM?8erNbT*EJ58ilwsZEpRr~0W^cGj zY2{r51MxMpIXOAzwQ(GRLc4Yy?OxEXg9k*qI9z3_2tBrN4AtG5bCK)J;3bWBq_8Ib_(LW>s_}@pf(w zRD$!L?1$UiuIF_!OZ8A>+=V7WTB%+sUhQ?!UrN#D7|^<(o&gpV$``ivnPL5LzvemFGnC9g z=rg$gWy#*ojQ7eYR^WH9QB*8cSxAKbM-u=Dmw6ah9Y>9qPMmtetN0j_Hn}N=M&khK zux~O0hA7)|jdM3{-n4f%U$Jc2o{-a}kGINLpxkLOPJt_998d1L|8BB`~)Rl+|-oYSiYwUxPALZ7FvsXW3SL)9_eWQB)_*6`erH3h_qcEjbLrSw+aM3WB;NAWu8rE?1uEHE@Nc^ z!7Z;*Xf*c5Mm<2DFd~4a1V$eK@U;5eOxecIpYwKKadeyo{jwizH&@=-qlD(j&VpQ6 z*4M4K4iqa*&k^oqKi@ZQ3(l1yp|!_SA&ruhO z@|YyGCL1WKL#$}>YZ(U5yB3ZG6|06+dWB3T8!M|cXu=C)cfGwk5eO>Q8XUV6TfvuC z#;$n)+08rW!LnO7*+wTOx+b)ehFMrxY%yI!&-A^%4>V+r+SXQB79Lt!PtYIWM5OxL zzz>A#Q7NfxV8H9UTP?0Kf*RB%pC8wwzkuQ@UE1*O9TU3LXR=kf}uKU$&%elPHI_()FqP#j(!8J~E8_F4Z(EayFdvH3T52y_I z!|#^R9UL4?ZVHgV%k@k&E84c*s+JF1zT|ezT*C=oS53y4t1?2Xyv2v94MP}boc>6< z?uXVB`i;~D;WXI7e`AX2r%meIAwF=B=l?T#qM4SJB@Hk{qrvd%2^~~h)&`V6>lW#! zv`w&WH5gE5i5EKj6Eq@YW3y%rAk8!I(X* zBBhsbKuWWlz(^#RsXY#xfQ-Bk_BC45M)LNd?UTXJ9Jj%RbG6#9?<7BraSr`8mcy%d ze8vRVD>Q&A=klF1DE3deot+)zZeM zYL$z??%fwB`)gIY(}-@#PYkkl`@@1mCcb&Rz%TlY4>hpiX!kq5*i~jPDketh^U9~8 zE%ty-B&`~}&nS9ZGOY?mcw5laT0xk=>2(yu7+u`Iu^a@+DPVlW1dW;{Pn?e4A=TQt zKXJAW4kbX6x)!X(nte(<+nz8(`l}1e$Z((yp4Q*xF`1p6P3hH4yqC^=2V*SS(#bIP zb>cj!3}@B^Nm@x4>)JGDtpf_xoVk6>&~Q7r@mS5Z%p{QOBG-TEXf-0T*CAxN$WBlf zsbjYa#4sE}m{~m)G6~m|^EW0A#qDp>)4L{WHm_U;4_4HX@nvGH#GBc3J78Sr6I-n6 zOC{^iL6R(=_wK>LKo!&zXQ#t;1a6>IMHQ}7Oowd@a)%VQ>aV`~&RdRf34*;7R2$6}Go9Q*0#QiA*a|f`@iIU$JJ($5jeQnmlmn zKRPmZW8&wtY9^kO&m*@3;H@F(GV>;ke-hL%dH5y-wBU5GpKsUU74;RWQXLn+hw z1Zj@aYnH467N{#~0hNM<-Q5)yy?q7+!M+zX9*GT*%VsT)9=%({*NyIU386Y@zSSB# zUjf+@P=Q)`nghyscg^j`D8kUr)xrl23S7!o?Ogiy&hph{$rJJzQ{fOH<}n#kN5Y?4 ze1E-T>8;7>-=oGEgORUZ-2wzP>Q1K9OW=C@)cm2LAy6Z_{NCfoW{}WioZuZS|1ktY zB-k1b)>0K3eJ6bXoxd;xnyNCb8%KPBq@?NKO#}qFTM{E3m2)Ea!PL+XLHn2Rg7%=7 z-jJLe6H{7d4BG_DpnGi`HQ1U-1Y}YLVw0nskDuSOA;p*(elT9I(oRuQS7>G`#<}il zJU06rbhR2(UCgzX{#3}j>Q2`X1b=wNOjd5-179P(i%QGDRy$l-Y6w(h z56ooR5i_+m_8wN{wMKeuq*b=yw`!-EkybQ7H1Pqz~V|rInXgh(wI5rdAMh zshXjX(4RRcJ%%PVG@X_Q(a;Ry%qd6_oTP`CQ}t}ZY^lb1Uhl&*H2Mk ztgJxx;w1bORksN$)ts#ntabkUL(GjSTkBrnAV@@HE+cGl z!UP(gwj$5fwG^+MR}(t4u=oLFe9BFE&8{lNKkorpb?lqXOiT>Ny2^g{#e@;`JTV^k z<;&Xp_wOSKt=oS->ur#BKYY6Hq0LjBg$1<|4&j8y1QZcA;{2PAa8eYPmNH|1MGhSB zhUg?FCiXNm^f4BtxW0ZfVZ8Y?Qh*P`j359S?Y#wlWRwV^LnCqgCb{FUg6S{gIJ5=P zl;VRU;OA0$YS2LvaPun| zi2rhN*VY~*T-+`BJ_r=$!FJh8m0LW$KIy|};5-0?E}MV+5XBrOF`fSA%^S=H z96Epg?<0VQPbtp=v;V6xV>M_gj-g~S=D3XzAe<05<6}y}*L+DeRV7q0*V&2Np#Lzr z0*kyiFnkA?j*dwxnsR_z#gmh>=)gju^w0 z2exe4f|ltnrq{uzYyOL7yMp=EgKmd zy931oiamxFA3T4)9%@QE%68WE!JwLft7up;E5MmSD{E~HvmdO5!ycjY8s<;h_*NuM z!ePNkwy?D90N{ogAwD3F>J6-82&{vS^Em#6VM-Q^<-nN)Mtr-B3>O5!k;%zpnwoy) z-SI+*S+CcxMbw3dfkxW_42galB0Uo_A}G9DRlV>vECtE4l9lx?Am|Y#CE5_jcZl~D zAqO|Vl(N1^A8trv7Elj<`*epA*gIyx20jdO?AWn`AQUk_UNZ3J$`5Y5E&0(}V&D?P z$9}Z~xlvKeV0c|lge@zt!WMu#uQzX`;NEQ?81Mnxj_&qk-XfGS&7rS<;Hv9D8HZJW zli&hkh?pzDPzKSHfy+g&wgp@WCPh@DL|vd^I%-yE^toP8rS}sy^P3Xga+!yzP^ND zM^Wc4J(A^HyHW%PdmdgHVj={+<13&RD18LwjFu=t#?Jck<-g#(W_1A`2l|uYBjt6N zMTPhE@afa+V7qYhLGNWpnAG8m7R*=F#hBJZ1YkSTUBU#Op$$t4!^}!Fzs%5fO-xO9 z?AynRhWm)Q`5pmbsPbE6uPIyeg%XnBkzqo?n0zAnQLy zFcO2IG}+@2+ce@%6d~UxjujBjDI7+cvv_PSvn>Ngdd9U%D@#i;wEY^Y*%)c?%c;}i z|HsrEAM+K=0admT;3W_sSaAMA?T@7|jE?sD#q76C0_2f)3bJzAq`EW+|FJ_DvtSE4#Qc`+78;E($+4rAcAnBDVP%QIF) z!emn8X4;)-iH9(H0VcNx=IW%HY;$xazh`Fl=ADK;rT^GJ^Zm8E5QZR?x}rY46xX2Q z1fIY<=n(x3<~i;+PV@V~;!f24liGAgE7!UVEA7f=*SN~g@3Ck3&`U7`y_~fquDa-i z0LU`Ah2~w=YYb!$d7MJ;RUl|xjCsk}UnE=3{~onF7l=GYt&N~E90ux)i2msFlkUF0 z`=CD~prWe`+rYh*g{BW-NNcGpDp~@aq6A&RyI#S?v<^_mA29G2Q!^8d5e_(!H-W*3 zpKyx10$a^{pnStCSCU4g0QRJBPVEE9l;AJ6MZx7o#xEpSoY{E(;T&`LY6&KRl7Ay4 zIM{ZyomzKzO3X@ZE&fnNB|TX3qaQveJU^_`Z-k?h8&{y zU_j(3z>pv<0A@C`s?fwQIhg#wYZRxNe)8*=?`?0tjnBL-DLGa`d4Tzv4itspnS0$| z>*_uX7yfySg`--w-uh{;=<-_x+{`lAw`gOS>AdUddCYS_El6S&Oh9*`E~?C(-3)RW z-mulg5J0v3ZaDCK0RW>|W#^w%^+tk? zBp?=%%;`%WbM)cng!%b-f=hcy*qz?$U?CxyvIa|ET|$geGTWA?rKG$AEkQ4+L(!11 zsyjt@i7&`Ch zalB`Su@5vgEGMt4cO~!TjX)z@+}g^EY0#&0U#j71Bizgv3f&$7X=@s?%Op=ltn4OQuW zeC4GuS1*ueC;=YQhaimX1U80gW%m$Z09AW$AL8f&}|}Fte3d zR9umu-0u3Z6A%GoT=VneRRDgz*`cVza36WV?-{qCAmKQ4{^-%8hCgx~Fec^dImv|Q zfdQfoD}59wqKGXCQ|MkGy-@R*u~fGL0yd*#p|ZG9z0iN5_&-2>g+QE9w1Nyb9p}O8 z``}BviWw1HTS3)0IUo$Z5JO~JfYUTQ!`$Hnc@48C)DBz;q9HWLD|bT#_Bvb4@0@xm z#YSM+#6%$&72`5Qqj2pJF^hTE%j+V%{2WqbQ3;9nD4K+2@O*(wAootpVzPCLK78)` zlrSpaU7JBBlkXthLAVR6A9P>L({qkMoAIuyiXGJL$I41CIB_8v-6O8LsIE?}o)NtB z&qWouNC-$sSxwFJ%pQd8x>IUuxEzNMhnE#|akZ4toLz9+pyMEJCV^c8cz_vr12EIh z`Ek4|5H&F;2roY-_QEj@2r&lpeqLBF@Q^h*g_v5#r3SnxDN4BALqby0$im`2NKC@E zHCh9O4no;%XCXhN2YR4heDfw|9c$T_qjBVBEdO`8Z_8>djo z(lYM%tTnI~EK$hJkw{;XVqw<_OE_kT`3d7-P|yQRZ;#BlK+t|yQNapDotRzHD~u;@ zih+Lz4AC<`zh(d2x#H71muLwbOWf~6+@=x`zziTHVNtz|sN`>~C|Et9KrbjsU_tnB z?+<_o(H`aI9t2F=c;)@s&2X^-zY|~zlmM3N7CLlXTG57Bu(27|Yf?^9z<4MDmdD4H zR8&~7l16w1xbYwx%zEUq=ef>pLiHssNU^je<1`wA?p?vg{R(}g%9D#YYEk|F-lrC| z={Eo@;emM&0A|G#Ow!*wiyV#spkEzsksloc%n^kQ!M$(Y4i1R6g7oiPE3z>NeqgMTQO#6cVB#Vz-3C-`kUvg|Ler7+FlIhOplow3l^9l)4VC z0JFk)SnbAn{)`q-oI39eNH5SbFL(d8`eJ$33&v;7{^E@nD9)Y>w-7C_WZkluW^bB4d3?9 z^>i7_!m3K~(YL(;)UW>gbScDLWHnl74>M8MS%xnleY8e4lW-Zup?3y< z-wlvhyr1ds=pp6Pucl0Pl-iDNK%nBr#vI}nC59UAe|rrIe>4@Bs3FociSHMJfdlRQ zG7`Kd+js9?0jv-_i3V})#*NE1Z1C;hzmvEs2G3e!^^d6b_p$FaB5UK;1nQ71m?y$? z2tUa=HTu7Ir#)q7W@A$O_XY1hwKO&{xeGQ!VP9;>-+De zARhbwtE&Z%q(!gBrE+(bF?|*;slSZknVv3bnX@wLLCMkRx@+r{Ik*p2#z*@cTFJOe zZ9SjZxAQ4k!d7!UX#Wz%#Yg&|t?*Hn5JeuHZ^yP%WA!fu{QZMOy~f&W>sFF@TOI`M zKSIm-Jbwm%?m(LSe|o!N(BF&J{&UZ3R?BTm#`7sZiu^Zb{&ScA?_KHmPpk1CfB)(1 z?Pxr*^Z(%jNZ0@K1(4<|&HuME8FB_=96*u;?1I4yW;HMLD!7H~7P@;a@-Vzt1o8!A zfVpJ%sGZDZrQmKJ%-L^Y7btS+nxnpIYOig&VdSoZ~r>ZZ#YkU9H8dq-6afsIU0#q1AegL2Lb2 zp4I%nSUT@`uKVx*ze`0VWM+?yi;QfUA&L;mo*{ehy?01Lb~f2$?;SF;$;{q+^E>a) z_xIn`RdMU>^?E+fd7Q`NeiVK3^KWNl{1a7a$+7p0k9Sj^9m`;qLTsN_-LCWx>5EGhu7yN~eZz~+zr}4dcl4pr z(YXwa`~mGs{DsFeg)g=^gi-gsMc^q`{8pGuM_jAkGW>?(K`SMVAjLe@f6+A(j+^VZ z-BFkkB1Ts1AJRC671h$7dVeQsw$z$;|L_#XU^*rr!s%qxn}-yRTw+pYX~bXp-$t03 zCk>S7$=Tzd_!2yR46Xu%zJ69~QxkR=aZRf8LCoC2dy-+cXKR8iS^^k|xr4J)yWe*p z1@xorS9wfo+N4h#Wm|Hhv=@=MyfBk+cB4zVXYFHzpPzpZ6asF>jzf@3(iQWD! z<53|$l%s75SW7=S7?*$xN{kJ5^Rk?@bF+cIos#h@!h8i zA%XfeoRqIc$`+gsPyi1()6&!H!5ujcrWE+#GYkJfW9A0KM+nR344qkm*$)Z3gxJMO zIg;fEEws{e?8MKXz50bN9zcPE5Pz1vE&@liNhDG}g2)e4n((GT0Rjy&XpG`uw$QaG zba^S61QPBX2xL%1!tDgKz1tucN`hYhJy+F{g$W3ckh}m80aB9u{}m{xf5-|Irfm@E z`my#w$=EatOI|_(JuO`f8z0|ibrl=J^J(8HwJ&R`tGSelW(q2<0AjQ-TEO9Sbs+J`4w#I;Pp+?|c|ETY`}?JQMO>CM57Q<472qEg7Z-4KF%~Ku zadzOTWO6Y{DJXbXUfvQ1|4hm4lWON!3$gG`om^&J<2SzV3I^&2f3*B57-#BjaSZ#n z#VlA(PAiNC$y36U3hXF(IXEzD9gA>1$uS78eheRTy;N66k;VKyH)nKPou6iPy}N-W z_A43=VL~v^i{Mvp{&_fbvvCefFtMUmRmDRn^ujArm*`1Df52=H?-SZ(hG?D$R8C9d zz-8vm@0pp+E;0=I*f`1PvxYlwRaDXx8$Nz?S)CricN4;*r4>!&$ITf&wto+a*4wvH zu(9VNz^`$q{^5r!w4argoj~u&{ptvk_2}gx&Gz5MPUl`=HexKZHm%C1FeL576sQ+l z-e@62pps@YJSL6qKB4-)s9w9aZrqYy%{(2imKvFG_rU{u{X!;eG@O09`&io&)QpK2 zY%dAMB$mGkWk)>^!9a|kozRGPPc+6<+&h10<`b@&$T#NcEfUmul5TaiuZ~B^MEKic zK{u?KM&K60x%#D(9jnw@>DbT1Lp*WuJ2cb_%+xeKIQaM>K0X3_8w3R0r{-7Qj8mRw zZ=NU>CHsDPobJaPnDA3=3?nHQ$det7W!bL)&{yX6UzsB*M(60vP z1Bxo!6FS6q&&G%i`(0)D3HB8(TiLMtC;8q7Ysgr--i!g|fbgvy+)~Av>;O87n3#LT z72JN&VE`P&)YWgh{=}M$Au3vDaxu5(rKI)HXlaSEoScjx{u4b`y1Yt`h8~$MhP&O) zgo;Llkn?7=)KcDSMZ3=9dQRZ~Cz;KGr!GlMZBCMJ^0`lTEx=fHr5j@Oy__{CC{XA9|;zMk-w3#C)9_UK8VWU1o!WO{Hkh$qmpzgt+v*&>99S zoJqX&hxS|>Y)rt4^gn9M+lj-SQKfwA9T*Iifa4&pNE`S%0Ay=|H+eAQHLib8JxFrayN=)&7#W=}-WE4O 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

v75)-yn8@2aM2>{oiGq15a931uTH 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 z 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

Xpkvqg&2aIN(-~GeKctVFRO# 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

m)w8#6arJbQ76$#U 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

pyyP-!!K_efeyj)B@?5@WG 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

Q*7f4FUe*b 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 z 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%0D)@8-U2s+F3E63= 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
Ip)%pG@2g#*D_4g>!z&1kTW;Rs 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

WV_hTv!#^z4KuGZC$+9tr?{q_nc^=XsB(I5k4 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 n`2|f(47_14+l?X@YCd`n7m+vZI5T}S^5Z~eyG literal 0 HcmV?d00001 diff --git a/applications/newton/llvm-ir/performance_test/fig/aarch64-time_consumption_speedup.png b/applications/newton/llvm-ir/performance_test/fig/aarch64-time_consumption_speedup.png new file mode 100644 index 0000000000000000000000000000000000000000..58ad0cc063559a7f04b79dd147dfed7175cac14f GIT binary patch literal 439280 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[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