They are called "atomic builtins" for a reason.
9D02C498 278280E0 ADDIU V0, GP, -32544 // $V0 = &lock
9D02C49C C0430000 LL V1, 0(V0) // $V1 = linked load from *($V0)
9D02C4A0 24010001 ADDIU AT, ZERO, 1 // $1 = 1
9D02C4A4 E0410000 SC AT, 0(V0) // Try storing $1 to *($V0)
9D02C4A8 1020FFFC BEQ AT, ZERO, 0x9D02C49C // If $1 == 0, the sequence was interrupted and the store aborted, try again
9D02C4AC 00000000 NOP
9D02C4B0 0000000F SYNC // Sync is only really required for SMP
9D02C4B4 24020001 ADDIU V0, ZERO, 1 // $V0 = 1
9D02C4B8 10620008 BEQ V1, V0, 0x9D02C4DC // If $V1 (ie. lock) == 1, branch ahead (to function exit?)
9D02C4BC 2402FFFF ADDIU V0, ZERO, -1 // $V0 = -1
Edit: On single-processor machines, "sequence was interrupted" in practice means the exception return instruction ERET was executed, but on shared-memory multicore machines this also includes writes detected from other CPUs. This detection is not perfect, which is why the documentation for LL/SC includes caveats about distance between the load and stores, not executing other memory access instructions and so on.
post edited by andersm - 2019/04/10 14:57:57