llvmbot wrote:
<!--LLVM PR SUMMARY COMMENT-->
@llvm/pr-subscribers-tablegen
Author: Alexander Richardson (arichardson)
<details>
<summary>Changes</summary>
This does not yet handle all cases but at least for the simple
cases such as:
```
def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
(PTR_MOV_SMALL PtrRC:$dst, PtrRC:$src)>;
```
tablegen generates sensible code instead of emitting confusing errors.
---
Full diff: https://github.com/llvm/llvm-project/pull/171061.diff
2 Files Affected:
- (added) llvm/test/TableGen/RegClassByHwModeCompressPat.td (+315)
- (modified) llvm/utils/TableGen/CompressInstEmitter.cpp (+10-9)
``````````diff
diff --git a/llvm/test/TableGen/RegClassByHwModeCompressPat.td
b/llvm/test/TableGen/RegClassByHwModeCompressPat.td
new file mode 100644
index 0000000000000..263b53f6fbba5
--- /dev/null
+++ b/llvm/test/TableGen/RegClassByHwModeCompressPat.td
@@ -0,0 +1,315 @@
+// RUN: llvm-tblgen --gen-compress-inst-emitter -I %p/../../include -I %S %s
-o - | FileCheck %s
+
+include "Common/RegClassByHwModeCommon.td"
+
+def IsPtr64 : Predicate<"Subtarget->isPtr64()">;
+defvar Ptr32 = DefaultMode;
+def Ptr64 : HwMode<[IsPtr64]>;
+def PtrRC : RegClassByHwMode<[Ptr32, Ptr64], [XRegs, YRegs]>;
+
+
+def X_MOV : TestInstruction {
+ let OutOperandList = (outs XRegs:$dst);
+ let InOperandList = (ins XRegs:$src);
+ let AsmString = "x_mov $dst, $src";
+ let opcode = 0;
+}
+
+def X_MOV_SMALL : TestInstruction {
+ let OutOperandList = (outs XRegs:$dst);
+ let InOperandList = (ins XRegs:$src);
+ let AsmString = "x_mov.small $dst, $src";
+ let opcode = 1;
+ let Size = 1;
+}
+def X_MOV_ZERO : TestInstruction {
+ let OutOperandList = (outs XRegs:$dst);
+ let InOperandList = (ins);
+ let AsmString = "x_mov.zero $dst";
+ let opcode = 2;
+ let Size = 1;
+}
+def X_MOV_TIED : TestInstruction {
+ let OutOperandList = (outs XRegs:$dst);
+ let InOperandList = (ins XRegs:$src);
+ let Constraints = "$src = $dst";
+ let AsmString = "x_mov.tied $dst, $src";
+ let opcode = 3;
+ let Size = 1;
+}
+
+def PTR_MOV : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins PtrRC:$src);
+ let AsmString = "ptr_mov $dst, $src";
+ let opcode = 3;
+}
+
+def PTR_MOV_SMALL : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins PtrRC:$src);
+ let AsmString = "ptr_mov.small $dst, $src";
+ let opcode = 4;
+ let Size = 1;
+}
+def PTR_MOV_ZERO : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins);
+ let AsmString = "ptr_mov.zero $dst";
+ let opcode = 3;
+ let Size = 1;
+}
+def PTR_MOV_TIED : TestInstruction {
+ let OutOperandList = (outs PtrRC:$dst);
+ let InOperandList = (ins PtrRC:$src);
+ let Constraints = "$src = $dst";
+ let AsmString = "ptr_mov.tied $dst, $src";
+ let opcode = 3;
+ let Size = 1;
+}
+
+def : CompressPat<(X_MOV XRegs:$dst, X0),
+ (X_MOV_ZERO XRegs:$dst)>;
+def : CompressPat<(X_MOV XRegs:$dst, XRegs:$dst),
+ (X_MOV_TIED XRegs:$dst)>;
+def : CompressPat<(X_MOV XRegs:$dst, XRegs:$src),
+ (X_MOV_SMALL XRegs:$dst, XRegs:$src)>;
+// TODO: Should also be able to use a fixed register with RegClassByHwMode
+// def : CompressPat<(PTR_MOV PtrRC:$dst, X0),
+// (PTR_MOV_ZERO PtrRC:$dst)>;
+def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$dst),
+ (PTR_MOV_TIED PtrRC:$dst)>;
+def : CompressPat<(PTR_MOV PtrRC:$dst, PtrRC:$src),
+ (PTR_MOV_SMALL PtrRC:$dst, PtrRC:$src)>;
+
+// CHECK: static bool compressInst(MCInst &OutInst,
+// CHECK-NEXT: const MCInst &MI,
+// CHECK-NEXT: const MCSubtargetInfo &STI) {
+// CHECK-NEXT: switch (MI.getOpcode()) {
+// CHECK-NEXT: default: return false;
+// CHECK-NEXT: case MyTarget::PTR_MOV: {
+// CHECK-NEXT: if (MI.getOperand(1).isReg() && MI.getOperand(0).isReg() &&
+// CHECK-NEXT: (MI.getOperand(1).getReg() ==
MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[1])].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // ptr_mov.tied $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_TIED);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[0])].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[1])].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case PTR_MOV
+// CHECK-NEXT: case MyTarget::X_MOV: {
+// CHECK-NEXT: if (MI.getOperand(1).isReg() &&
+// CHECK-NEXT: (MI.getOperand(1).getReg() == MyTarget::X0) &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg()))
{
+// CHECK-NEXT: // x_mov.zero $dst
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV_ZERO);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(1).isReg() && MI.getOperand(0).isReg() &&
+// CHECK-NEXT: (MI.getOperand(1).getReg() ==
MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // x_mov.tied $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV_TIED);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // x_mov.small $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV_SMALL);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case X_MOV
+// CHECK-NEXT: } // switch
+// CHECK-NEXT: return false;
+// CHECK-NEXT: }
+
+// CHECK: static bool uncompressInst(MCInst &OutInst,
+// CHECK-NEXT: const MCInst &MI,
+// CHECK-NEXT: const MCSubtargetInfo &STI) {
+// CHECK-NEXT: switch (MI.getOpcode()) {
+// CHECK-NEXT: default: return false;
+// CHECK-NEXT: case MyTarget::PTR_MOV_SMALL: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[0])].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[1])].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // ptr_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case PTR_MOV_SMALL
+// CHECK-NEXT: case MyTarget::PTR_MOV_TIED: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[0])].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[0])].contains(MI.getOperand(0).getReg()))
{
+// CHECK-NEXT: // ptr_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::PTR_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case PTR_MOV_TIED
+// CHECK-NEXT: case MyTarget::X_MOV_SMALL: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // x_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(1));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case X_MOV_SMALL
+// CHECK-NEXT: case MyTarget::X_MOV_TIED: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg()))
{
+// CHECK-NEXT: // x_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case X_MOV_TIED
+// CHECK-NEXT: case MyTarget::X_MOV_ZERO: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg()))
{
+// CHECK-NEXT: // x_mov $dst, $src
+// CHECK-NEXT: OutInst.setOpcode(MyTarget::X_MOV);
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: OutInst.addOperand(MI.getOperand(0));
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: OutInst.addOperand(MCOperand::createReg(MyTarget::X0));
+// CHECK-NEXT: OutInst.setLoc(MI.getLoc());
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case X_MOV_ZERO
+// CHECK-NEXT: } // switch
+// CHECK-NEXT: return false;
+// CHECK-NEXT: }
+
+// CHECK: static bool isCompressibleInst(const MachineInstr &MI,
+// CHECK-NEXT: const MyTargetSubtarget &STI) {
+// CHECK-NEXT: switch (MI.getOpcode()) {
+// CHECK-NEXT: default: return false;
+// CHECK-NEXT: case MyTarget::PTR_MOV: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[0])].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[1])].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // ptr_mov.small $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(1).isReg() && MI.getOperand(0).isReg() &&
+// CHECK-NEXT: (MI.getOperand(1).getReg() ==
MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[STI.getInstrInfo().getOpRegClassID(MI.getDesc().operands()[1])].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // ptr_mov.tied $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case PTR_MOV
+// CHECK-NEXT: case MyTarget::X_MOV: {
+// CHECK-NEXT: if (MI.getOperand(0).isReg() &&
MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg())
&&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // x_mov.small $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(1).isReg() && MI.getOperand(0).isReg() &&
+// CHECK-NEXT: (MI.getOperand(1).getReg() ==
MI.getOperand(0).getReg()) &&
+// CHECK-NEXT: MI.getOperand(1).isReg() &&
MI.getOperand(1).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(1).getReg()))
{
+// CHECK-NEXT: // x_mov.tied $dst, $src
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: // Operand: src
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: if (MI.getOperand(1).isReg() &&
+// CHECK-NEXT: (MI.getOperand(1).getReg() == MyTarget::X0) &&
+// CHECK-NEXT: MI.getOperand(0).isReg() &&
MI.getOperand(0).getReg().isPhysical() &&
+// CHECK-NEXT:
MyTargetMCRegisterClasses[MyTarget::XRegsRegClassID].contains(MI.getOperand(0).getReg()))
{
+// CHECK-NEXT: // x_mov.zero $dst
+// CHECK-NEXT: // Operand: dst
+// CHECK-NEXT: return true;
+// CHECK-NEXT: } // if
+// CHECK-NEXT: break;
+// CHECK-NEXT: } // case X_MOV
+// CHECK-NEXT: } // switch
+// CHECK-NEXT: return false;
+// CHECK-NEXT: }
+
+def MyTargetISA : InstrInfo;
+def MyTargetAsmWriter : AsmWriter {
+ int PassSubtarget = 1;
+}
+def MyTarget : Target {
+ let InstructionSet = MyTargetISA;
+ let AssemblyWriters = [MyTargetAsmWriter];
+}
diff --git a/llvm/utils/TableGen/CompressInstEmitter.cpp
b/llvm/utils/TableGen/CompressInstEmitter.cpp
index 94fe3823fae22..58993ce5b5985 100644
--- a/llvm/utils/TableGen/CompressInstEmitter.cpp
+++ b/llvm/utils/TableGen/CompressInstEmitter.cpp
@@ -735,7 +735,7 @@ void
CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
switch (SourceOperandMap[OpNo].Kind) {
case OpData::Operand:
if (SourceOperandMap[OpNo].OpInfo.TiedOpIdx != -1) {
- if (Source.Operands[OpNo].Rec->isSubClassOf("RegisterClass"))
+ if (Source.Operands[OpNo].Rec->isSubClassOf("RegisterClassLike"))
CondStream << CondSep << "MI.getOperand(" << OpNo
<< ").isReg() && MI.getOperand("
<< SourceOperandMap[OpNo].OpInfo.TiedOpIdx
@@ -788,11 +788,7 @@ void
CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
const Record *DagRec = DestOperandMap[OpNo].OpInfo.DagRec;
// Check that the operand in the Source instruction fits
// the type for the Dest instruction.
- if (DagRec->isSubClassOf("RegisterClass") ||
- DagRec->isSubClassOf("RegisterOperand")) {
- auto *ClassRec = DagRec->isSubClassOf("RegisterClass")
- ? DagRec
- : DagRec->getValueAsDef("RegClass");
+ if (auto *ClassRec = Target.getAsRegClassLike(DagRec)) {
// This is a register operand. Check the register class.
// Don't check register class if this is a tied operand, it was
done
// for the operand it's tied to.
@@ -801,9 +797,14 @@ void
CompressInstEmitter::emitCompressInstEmitter(raw_ostream &OS,
if (EType == EmitterType::CheckCompress)
CondStream << " && MI.getOperand(" << OpIdx
<< ").getReg().isPhysical()";
- CondStream << CondSep << TargetName << "MCRegisterClasses["
- << TargetName << "::" << ClassRec->getName()
- << "RegClassID].contains(MI.getOperand(" << OpIdx
+ CondStream << CondSep << TargetName << "MCRegisterClasses[";
+ if (ClassRec->isSubClassOf("RegClassByHwMode"))
+ CondStream << "STI.getInstrInfo().getOpRegClassID("
+ << "MI.getDesc().operands()[" << OpIdx << "])";
+ else
+ CondStream << TargetName << "::" << ClassRec->getName()
+ << "RegClassID";
+ CondStream << "].contains(MI.getOperand(" << OpIdx
<< ").getReg())";
}
``````````
</details>
https://github.com/llvm/llvm-project/pull/171061
_______________________________________________
llvm-branch-commits mailing list
[email protected]
https://lists.llvm.org/cgi-bin/mailman/listinfo/llvm-branch-commits