// File: /workspace/github_c_makefile_star2_O0/dpw[P]skinny-mutex/skinny_mutex.c
// Function: fat_mutex_peg
static int fat_mutex_peg(skinny_mutex_t *skinny, struct common *p,
			 struct fat_mutex **fatp)
{
	int res;
	unsigned int peg_refcount_decr;
	struct fat_mutex *fat;
	struct peg *peg = malloc(sizeof *peg);
	if (!peg)
		return ENOMEM;

	/* Install our peg.  The initial ref count is two: One for the
	 * reference from this thread, and one that will be from the
	 * skinny_mutex. */
	peg->common.peg = 1;
	peg->refcount = 2;
	peg->next = p;

	while (!cas(&skinny->val, p, peg)) {
		/* The value in the skinny_mutex has changed from what
		   we saw earlier. */

		p = skinny->val;
		if ((uintptr_t)p <= 1) {
			/* There is no longer a fat_mutex to peg, so
			   backtrack. */
			free(peg);
			return -1;
		}

		/* There is a new fat_mutex, so try again to install
		   our peg. */
		peg->next = p;
	}

	/* Our peg is now installed.  Now we know the rest of the
	 * chain won't disappear under us, so we can walk it to find
	 * the fat_mutex and lock it. */
	while (p->peg)
		p = ((struct peg *)p)->next;

	*fatp = fat = (struct fat_mutex *)p;
	res = pthread_mutex_lock(&fat->mutex);

	/* The fat_mutex is locked, and we know it won't go away while
	 * we hold its lock.  So we can release our peg.
	 *
	 * To do this, we set the skinny_mutex to point to the
	 * fat_mutex, turning the primary chain into a secondary
	 * chain.  Note that we don't know whether this thread's peg
	 * is still on the primary chain when we do this.  Handling
	 * the various cases correctly hinges on the refcounts.  By
	 * the end of this function, the fat_mutex refcount can be
	 * incremented, decremented, or returned to its original
	 * value. */
	p = atomic_xchg(&skinny->val, fat);

	/* By setting the skinny_mutex to point to the fat_mutex, we
	 * have theoretically created a new reference to it.  This
	 * might be a real reference (e.g. from a new secondary chain)
	 * or not.  If not, we'll decrement the fat_mutex refcount
	 * below. */
	fat->refcount++;

	/* In this loop, we walk the peg chain starting with the old
	 * value of skinny_mutex. */
	for (;;) {
		struct peg *chain_peg;

		peg_refcount_decr = 2;
		if (p == &peg->common)
			/* We have reached our peg, so fall through to
			 * the loop below. */
			break;

		peg_refcount_decr = 1;
		if (p == &fat->common) {
			/* We have reached the fat_mutex at the end of
			   the chain, eliminating a reference to it. */
			fat->refcount--;
			break;
		}

		/* Decrement the ref count of the peg, and see whether
		 * we can free it yet. */
		chain_peg = (struct peg *)p;
		if (atomic_sub_and_test(&chain_peg->refcount, 1))
			/* We can't free this peg yet, so leave a
			 * secondary chain in place. */
			break;

		/* Free the peg, and proceed to the next peg in the
		 * chain. */
		p = chain_peg->next;
		free(chain_peg);
	}

	for (;;) {
		if (atomic_sub_and_test(&peg->refcount, peg_refcount_decr))
			/* We can't free this peg yet, so leave a
			 * secondary chain in place. */
			break;

		/* No references to the peg remain, so free it. */
		p = peg->next;
		free(peg);

		if (p == &fat->common) {
			/* We have reached the fat_mutex at the end of
			   the chain, eliminating a reference to it. */
			fat->refcount--;
			break;
		}

		/* Proceed to the next peg in the chain. */
		peg = (struct peg *)p;
		peg_refcount_decr =  1;
	}

	return res;
}

// Assembly:
pushq %rbp
movq %rsp, %rbp
subq $0x40, %rsp
movq %rdi, -0x10(%rbp)
movq %rsi, -0x18(%rbp)
movq %rdx, -0x20(%rbp)
movl $0x10, %edi
callq 0x1110
movq %rax, -0x38(%rbp)
cmpq $0x0, -0x38(%rbp)
jne 0x1625
movl $0xc, -0x4(%rbp)
jmp 0x17cb
movq -0x38(%rbp), %rax
movb $0x1, (%rax)
movq -0x38(%rbp), %rax
movb $0x2, 0x1(%rax)
movq -0x18(%rbp), %rcx
movq -0x38(%rbp), %rax
movq %rcx, 0x8(%rax)
movq -0x10(%rbp), %rcx
movq -0x18(%rbp), %rax
movq -0x38(%rbp), %rdx
lock
cmpxchgq %rdx, (%rcx)
movq %rax, %rcx
sete %al
xorb $-0x1, %al
testb $0x1, %al
jne 0x165f
jmp 0x1697
movq -0x10(%rbp), %rax
movq (%rax), %rax
movq %rax, -0x18(%rbp)
movq -0x18(%rbp), %rax
cmpq $0x1, %rax
ja 0x1689
movq -0x38(%rbp), %rdi
callq 0x1030
movl $0xffffffff, -0x4(%rbp) # imm = 0xFFFFFFFF
jmp 0x17cb
movq -0x18(%rbp), %rcx
movq -0x38(%rbp), %rax
movq %rcx, 0x8(%rax)
jmp 0x1640
jmp 0x1699
movq -0x18(%rbp), %rax
cmpb $0x0, (%rax)
je 0x16b0
movq -0x18(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x18(%rbp)
jmp 0x1699
movq -0x18(%rbp), %rcx
movq %rcx, -0x30(%rbp)
movq -0x20(%rbp), %rax
movq %rcx, (%rax)
movq -0x30(%rbp), %rdi
addq $0x18, %rdi
callq 0x11b0
movl %eax, -0x24(%rbp)
movq -0x10(%rbp), %rdi
movq -0x30(%rbp), %rsi
callq 0x1d80
movq %rax, -0x18(%rbp)
movq -0x30(%rbp), %rax
movq 0x10(%rax), %rcx
addq $0x1, %rcx
movq %rcx, 0x10(%rax)
movl $0x2, -0x28(%rbp)
movq -0x18(%rbp), %rax
movq -0x38(%rbp), %rcx
cmpq %rcx, %rax
jne 0x1706
jmp 0x1764
movl $0x1, -0x28(%rbp)
movq -0x18(%rbp), %rax
movq -0x30(%rbp), %rcx
cmpq %rcx, %rax
jne 0x172c
movq -0x30(%rbp), %rax
movq 0x10(%rax), %rcx
addq $-0x1, %rcx
movq %rcx, 0x10(%rax)
jmp 0x1764
movq -0x18(%rbp), %rax
movq %rax, -0x40(%rbp)
movq -0x40(%rbp), %rdi
addq $0x1, %rdi
movl $0x1, %esi
callq 0x1db0
cmpl $0x0, %eax
je 0x174d
jmp 0x1764
movq -0x40(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x18(%rbp)
movq -0x40(%rbp), %rdi
callq 0x1030
jmp 0x16f0
jmp 0x1766
movq -0x38(%rbp), %rdi
addq $0x1, %rdi
movl -0x28(%rbp), %eax
movzbl %al, %esi
callq 0x1db0
cmpl $0x0, %eax
je 0x1780
jmp 0x17c5
movq -0x38(%rbp), %rax
movq 0x8(%rax), %rax
movq %rax, -0x18(%rbp)
movq -0x38(%rbp), %rdi
callq 0x1030
movq -0x18(%rbp), %rax
movq -0x30(%rbp), %rcx
cmpq %rcx, %rax
jne 0x17b4
movq -0x30(%rbp), %rax
movq 0x10(%rax), %rcx
addq $-0x1, %rcx
movq %rcx, 0x10(%rax)
jmp 0x17c5
movq -0x18(%rbp), %rax
movq %rax, -0x38(%rbp)
movl $0x1, -0x28(%rbp)
jmp 0x1766
movl -0x24(%rbp), %eax
movl %eax, -0x4(%rbp)
movl -0x4(%rbp), %eax
addq $0x40, %rsp
popq %rbp
retq
nopw %cs:(%rax,%rax)