semaphore implementation with test and set
The purpose of the spinlock is to prevent other cores from entering the critical section. The purpose of disabling interrupts is to prevent this core from re-entering the critical section.

The only way this core could try to re-acquire the semaphore is if an interrupt occurs and the interrupt handler needs the semaphore for some reason. Disabling interrupts prevents this from occurring.

As for the reason why sleep() is called after releasing the spinlock but before interrupts are enabled... well, that depends too much on the specifics of the operating system design to give a general answer.

The whole of the "waiting on the semaphore" operation should be atomic. That includes waiting on its queue and transitioning the thread from its "running" state to its "waiting" state. Presumably, that's what the call to sleep() is supposed to do.

You wonder if there's a possible race condition where this CPU releases the spinlock on the semaphore, then some other CPU signals the semaphore, then this cpu calls sleep(). That isn't necessarily a problem. If sleep() is written such that if it finds that the current thread is runnable it handles that case correctly, it won't cause a problem.

So why are interrupts disabled when you call sleep()? Some kernels are just designed that way, in that interrupts need to be disabled when you call into the scheduler. The call to sleep() may not actually "sleep"; it might just be whatever the scheduler has to do to make a thread sleep.

Alternatively, if there is a thread context switch hidden deep inside that call to sleep(), that may be where interrupts are re-enabled.

Ultimately, this code is designed to get the general idea across. How you would implement it in a real kernel would be like this, but this isn't necessarily drop-in code.

(Consider that load-linked/store-conditional instructions are spelled out in the example but there's no memory barrier operation on the spinlock release. That's a very specific ISA!)