xref: /DragonOS/kernel/src/mm/allocator/buddy.rs (revision f79998f626801329580c782fd05e36cb2027f474)
1 use log::{debug, warn};
2 
3 /// @Author: longjin@dragonos.org
4 /// @Author: kongweichao@dragonos.org
5 /// @Date: 2023-03-28 16:03:47
6 /// @FilePath: /DragonOS/kernel/src/mm/allocator/buddy.rs
7 /// @Description: 伙伴分配器
8 use crate::arch::MMArch;
9 use crate::mm::allocator::bump::BumpAllocator;
10 use crate::mm::allocator::page_frame::{FrameAllocator, PageFrameCount, PageFrameUsage};
11 use crate::mm::{MemoryManagementArch, PhysAddr, PhysMemoryArea, VirtAddr};
12 
13 use core::cmp::min;
14 use core::fmt::Debug;
15 use core::intrinsics::{likely, unlikely};
16 
17 use core::{marker::PhantomData, mem};
18 
19 // 一个全局变量MAX_ORDER,用来表示buddy算法的最大阶数 [MIN_ORDER, MAX_ORDER)左闭右开区间
20 const MAX_ORDER: usize = 31;
21 // 4KB
22 const MIN_ORDER: usize = 12;
23 
24 /// 保存buddy算法中每一页存放的BuddyEntry的信息,占据每个页的起始位置
25 #[derive(Debug)]
26 pub struct PageList<A> {
27     // 页存放entry的数量
28     entry_num: usize,
29     // 下一个页面的地址
30     next_page: PhysAddr,
31     phantom: PhantomData<A>,
32 }
33 
34 impl<A> Clone for PageList<A> {
35     fn clone(&self) -> Self {
36         Self {
37             entry_num: self.entry_num,
38             next_page: self.next_page,
39             phantom: PhantomData,
40         }
41     }
42 }
43 
44 impl<A> PageList<A> {
45     #[allow(dead_code)]
46     fn empty() -> Self {
47         Self {
48             entry_num: 0,
49             next_page: PhysAddr::new(0),
50             phantom: PhantomData,
51         }
52     }
53     fn new(entry_num: usize, next_page: PhysAddr) -> Self {
54         Self {
55             entry_num,
56             next_page,
57             phantom: PhantomData,
58         }
59     }
60 }
61 
62 /// @brief: 用来表示 buddy 算法中的一个 buddy 块,整体存放在area的头部
63 // 这种方式会出现对齐问题
64 // #[repr(packed)]
65 #[repr(C)]
66 #[derive(Debug)]
67 pub struct BuddyAllocator<A> {
68     // 存放每个阶的空闲“链表”的头部地址
69     free_area: [PhysAddr; MAX_ORDER - MIN_ORDER],
70     /// 总页数
71     total: PageFrameCount,
72     phantom: PhantomData<A>,
73 }
74 
75 impl<A: MemoryManagementArch> BuddyAllocator<A> {
76     const BUDDY_ENTRIES: usize =
77         // 定义一个变量记录buddy表的大小
78         (A::PAGE_SIZE - mem::size_of::<PageList<A>>()) / mem::size_of::<PhysAddr>();
79 
80     pub unsafe fn new(mut bump_allocator: BumpAllocator<A>) -> Option<Self> {
81         let initial_free_pages = bump_allocator.usage().free();
82         let total_memory = bump_allocator.usage().total();
83         debug!("Free pages before init buddy: {:?}", initial_free_pages);
84         debug!("Buddy entries: {}", Self::BUDDY_ENTRIES);
85 
86         let mut free_area: [PhysAddr; MAX_ORDER - MIN_ORDER] =
87             [PhysAddr::new(0); MAX_ORDER - MIN_ORDER];
88 
89         // Buddy初始占用的空间从bump分配
90         for f in free_area.iter_mut() {
91             let curr_page = bump_allocator.allocate_one();
92             // 保存每个阶的空闲链表的头部地址
93             *f = curr_page.unwrap();
94             // 清空当前页
95             core::ptr::write_bytes(MMArch::phys_2_virt(*f)?.data() as *mut u8, 0, A::PAGE_SIZE);
96 
97             let page_list: PageList<A> = PageList::new(0, PhysAddr::new(0));
98             Self::write_page(*f, page_list);
99         }
100 
101         let mut allocator = Self {
102             free_area,
103             total: PageFrameCount::new(0),
104             phantom: PhantomData,
105         };
106 
107         let mut total_pages_to_buddy = PageFrameCount::new(0);
108         let mut res_areas = [PhysMemoryArea::default(); 128];
109         let mut offset_in_remain_area = bump_allocator
110             .remain_areas(&mut res_areas)
111             .expect("BuddyAllocator: failed to get remain areas from bump allocator");
112 
113         let remain_areas = &res_areas[0..];
114 
115         for area in remain_areas {
116             let mut paddr = (area.area_base_aligned() + offset_in_remain_area).data();
117             let mut remain_pages =
118                 PageFrameCount::from_bytes(area.area_end_aligned().data() - paddr).unwrap();
119 
120             if remain_pages.data() == 0 {
121                 continue;
122             }
123             debug!("area: {area:?}, paddr: {paddr:#x}, remain_pages: {remain_pages:?}");
124 
125             total_pages_to_buddy += remain_pages;
126 
127             if offset_in_remain_area != 0 {
128                 offset_in_remain_area = 0;
129             }
130 
131             // 先从低阶开始,尽可能地填满空闲链表
132             for i in MIN_ORDER..MAX_ORDER {
133                 // debug!("i {i}, remain pages={}", remain_pages.data());
134                 if remain_pages.data() < (1 << (i - MIN_ORDER)) {
135                     break;
136                 }
137 
138                 assert!(paddr & ((1 << i) - 1) == 0);
139 
140                 if likely(i != MAX_ORDER - 1) {
141                     // 要填写entry
142                     if paddr & (1 << i) != 0 {
143                         allocator.buddy_free(PhysAddr::new(paddr), i as u8);
144 
145                         paddr += 1 << i;
146                         remain_pages -= 1 << (i - MIN_ORDER);
147                     };
148                 } else {
149                     // 往最大的阶数的链表中添加entry(注意要考虑到最大阶数的链表可能有多页)
150                     // 断言剩余页面数量是MAX_ORDER-1阶的整数倍
151 
152                     let mut entries = (remain_pages.data() * A::PAGE_SIZE) >> i;
153                     while entries > 0 {
154                         allocator.buddy_free(PhysAddr::new(paddr), i as u8);
155                         paddr += 1 << i;
156                         remain_pages -= 1 << (i - MIN_ORDER);
157 
158                         entries -= 1;
159                     }
160                 }
161             }
162             // 然后从高往低,把剩余的页面加入链表
163             let mut remain_bytes = remain_pages.data() * A::PAGE_SIZE;
164 
165             assert!(remain_bytes < (1 << MAX_ORDER) - 1);
166 
167             for i in (MIN_ORDER..MAX_ORDER).rev() {
168                 if remain_bytes >= (1 << i) {
169                     assert!(paddr & ((1 << i) - 1) == 0);
170                     allocator.buddy_free(PhysAddr::new(paddr), i as u8);
171 
172                     paddr += 1 << i;
173                     remain_bytes -= 1 << i;
174                 }
175             }
176 
177             assert!(remain_bytes == 0);
178         }
179 
180         debug!("Total pages to buddy: {:?}", total_pages_to_buddy);
181         allocator.total = total_memory;
182 
183         Some(allocator)
184     }
185     /// 获取第j个entry的虚拟地址,
186     /// j从0开始计数
187     pub fn entry_virt_addr(base_addr: PhysAddr, j: usize) -> VirtAddr {
188         let entry_virt_addr = unsafe { A::phys_2_virt(Self::entry_addr(base_addr, j)) };
189         return entry_virt_addr.unwrap();
190     }
191     pub fn entry_addr(base_addr: PhysAddr, j: usize) -> PhysAddr {
192         let entry_addr = base_addr + mem::size_of::<PageList<A>>() + j * mem::size_of::<PhysAddr>();
193         return entry_addr;
194     }
195     pub fn read_page<T>(addr: PhysAddr) -> T {
196         let page_list = unsafe { A::read(A::phys_2_virt(addr).unwrap()) };
197         return page_list;
198     }
199 
200     pub fn write_page(curr_page: PhysAddr, page_list: PageList<A>) {
201         // 把物理地址转换为虚拟地址
202         let virt_addr = unsafe { A::phys_2_virt(curr_page) };
203         let virt_addr = virt_addr.unwrap();
204         unsafe { A::write(virt_addr, page_list) };
205     }
206 
207     /// 从order转换为free_area的下标
208     ///
209     /// # 参数
210     ///
211     /// - `order` - order
212     ///
213     /// # 返回值
214     ///
215     /// free_area的下标
216     #[inline]
217     fn order2index(order: u8) -> usize {
218         order as usize - MIN_ORDER
219     }
220 
221     /// 从空闲链表的开头,取出1个指定阶数的伙伴块,如果没有,则返回None
222     ///
223     /// ## 参数
224     ///
225     /// - `order` - 伙伴块的阶数
226     fn pop_front(&mut self, order: u8) -> Option<PhysAddr> {
227         let mut alloc_in_specific_order = |spec_order: u8| {
228             // 先尝试在order阶的“空闲链表”的开头位置分配一个伙伴块
229             let mut page_list_addr = self.free_area[Self::order2index(spec_order)];
230             let mut page_list: PageList<A> = Self::read_page(page_list_addr);
231 
232             // 循环删除头部的空闲链表页
233             while page_list.entry_num == 0 {
234                 let next_page_list_addr = page_list.next_page;
235                 // 找完了,都是空的
236                 if next_page_list_addr.is_null() {
237                     return None;
238                 }
239 
240                 if !next_page_list_addr.is_null() {
241                     // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list
242                     self.free_area[Self::order2index(spec_order)] = next_page_list_addr;
243                     // debug!("FREE: page_list_addr={:b}", page_list_addr.data());
244                     unsafe {
245                         self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8);
246                     }
247                 }
248                 // 由于buddy_free可能导致首部的链表页发生变化,因此需要重新读取
249                 let next_page_list_addr = self.free_area[Self::order2index(spec_order)];
250                 assert!(!next_page_list_addr.is_null());
251                 page_list = Self::read_page(next_page_list_addr);
252                 page_list_addr = next_page_list_addr;
253             }
254 
255             // 有空闲页面,直接分配
256             if page_list.entry_num > 0 {
257                 let entry: PhysAddr = unsafe {
258                     A::read(Self::entry_virt_addr(
259                         page_list_addr,
260                         page_list.entry_num - 1,
261                     ))
262                 };
263                 // 清除该entry
264                 unsafe {
265                     A::write(
266                         Self::entry_virt_addr(page_list_addr, page_list.entry_num - 1),
267                         PhysAddr::new(0),
268                     )
269                 };
270                 if entry.is_null() {
271                     panic!(
272                         "entry is null, entry={:?}, order={}, entry_num = {}",
273                         entry,
274                         spec_order,
275                         page_list.entry_num - 1
276                     );
277                 }
278                 // debug!("entry={entry:?}");
279 
280                 // 更新page_list的entry_num
281                 page_list.entry_num -= 1;
282                 let tmp_current_entry_num = page_list.entry_num;
283                 if page_list.entry_num == 0 {
284                     if !page_list.next_page.is_null() {
285                         // 此时page_list已经没有空闲伙伴块了,又因为非唯一页,需要删除该page_list
286                         self.free_area[Self::order2index(spec_order)] = page_list.next_page;
287                         let _ = page_list;
288                         unsafe { self.buddy_free(page_list_addr, MMArch::PAGE_SHIFT as u8) };
289                     } else {
290                         Self::write_page(page_list_addr, page_list);
291                     }
292                 } else {
293                     // 若entry_num不为0,说明该page_list还有空闲伙伴块,需要更新该page_list
294                     // 把更新后的page_list写回
295                     Self::write_page(page_list_addr, page_list.clone());
296                 }
297 
298                 // 检测entry 是否对齐
299                 if !entry.check_aligned(1 << spec_order) {
300                     panic!("entry={:?} is not aligned, spec_order={spec_order}, page_list.entry_num={}", entry, tmp_current_entry_num);
301                 }
302                 return Some(entry);
303             }
304             return None;
305         };
306         let result: Option<PhysAddr> = alloc_in_specific_order(order);
307         // debug!("result={:?}", result);
308         if result.is_some() {
309             return result;
310         }
311         // 尝试从更大的链表中分裂
312 
313         let mut current_order = (order + 1) as usize;
314         let mut x: Option<PhysAddr> = None;
315         while current_order < MAX_ORDER {
316             x = alloc_in_specific_order(current_order as u8);
317             // debug!("current_order={:?}", current_order);
318             if x.is_some() {
319                 break;
320             }
321             current_order += 1;
322         }
323 
324         // debug!("x={:?}", x);
325         // 如果找到一个大的块,就进行分裂
326         if x.is_some() {
327             // 分裂到order阶
328             while current_order > order as usize {
329                 current_order -= 1;
330                 // 把后面那半块放回空闲链表
331 
332                 let buddy = *x.as_ref().unwrap() + (1 << current_order);
333                 // debug!("x={:?}, buddy={:?}", x, buddy);
334                 // debug!("current_order={:?}, buddy={:?}", current_order, buddy);
335                 unsafe { self.buddy_free(buddy, current_order as u8) };
336             }
337             return x;
338         }
339 
340         return None;
341     }
342 
343     /// 从伙伴系统中分配count个页面
344     ///
345     /// ## 参数
346     ///
347     /// - `count`:需要分配的页面数
348     ///
349     /// ## 返回值
350     ///
351     /// 返回分配的页面的物理地址和页面数
352     fn buddy_alloc(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> {
353         assert!(count.data().is_power_of_two());
354         // 计算需要分配的阶数
355         let mut order = log2(count.data());
356         if count.data() & ((1 << order) - 1) != 0 {
357             order += 1;
358         }
359         let order = (order + MIN_ORDER) as u8;
360         if order as usize >= MAX_ORDER {
361             return None;
362         }
363 
364         // debug!("buddy_alloc: order = {}", order);
365         // 获取该阶数的一个空闲页面
366         let free_addr = self.pop_front(order);
367         // debug!(
368         //     "buddy_alloc: order = {}, free_addr = {:?}",
369         //     order,
370         //     free_addr
371         // );
372         return free_addr
373             .map(|addr| (addr, PageFrameCount::new(1 << (order as usize - MIN_ORDER))));
374     }
375 
376     /// 释放一个块
377     ///
378     /// ## 参数
379     ///
380     /// - `base` - 块的起始地址
381     /// - `order` - 块的阶数
382     unsafe fn buddy_free(&mut self, mut base: PhysAddr, order: u8) {
383         // debug!("buddy_free: base = {:?}, order = {}", base, order);
384         let mut order = order as usize;
385 
386         while order < MAX_ORDER {
387             // 检测地址是否合法
388             if base.data() & ((1 << (order)) - 1) != 0 {
389                 panic!(
390                     "buddy_free: base is not aligned, base = {:#x}, order = {}",
391                     base.data(),
392                     order
393                 );
394             }
395 
396             // 在链表中寻找伙伴块
397             // 伙伴块的地址是base ^ (1 << order)
398             let buddy_addr = PhysAddr::new(base.data() ^ (1 << order));
399 
400             let first_page_list_paddr = self.free_area[Self::order2index(order as u8)];
401             let mut page_list_paddr = first_page_list_paddr;
402             let mut page_list: PageList<A> = Self::read_page(page_list_paddr);
403             let first_page_list = page_list.clone();
404 
405             let mut buddy_entry_virt_vaddr = None;
406             let mut buddy_entry_page_list_paddr = None;
407             // 除非order是最大的,否则尝试查找伙伴块
408             if likely(order != MAX_ORDER - 1) {
409                 'outer: loop {
410                     for i in 0..page_list.entry_num {
411                         let entry_virt_addr = Self::entry_virt_addr(page_list_paddr, i);
412                         let entry: PhysAddr = unsafe { A::read(entry_virt_addr) };
413                         if entry == buddy_addr {
414                             // 找到了伙伴块,记录该entry相关信息,然后退出查找
415                             buddy_entry_virt_vaddr = Some(entry_virt_addr);
416                             buddy_entry_page_list_paddr = Some(page_list_paddr);
417                             break 'outer;
418                         }
419                     }
420                     if page_list.next_page.is_null() {
421                         break;
422                     }
423                     page_list_paddr = page_list.next_page;
424                     page_list = Self::read_page(page_list_paddr);
425                 }
426             }
427 
428             // 如果没有找到伙伴块
429             if let Some(buddy_entry_virt_addr) = buddy_entry_virt_vaddr {
430                 // 如果找到了伙伴块,合并,向上递归
431 
432                 // 伙伴块所在的page_list的物理地址
433                 let buddy_entry_page_list_paddr = buddy_entry_page_list_paddr.unwrap();
434 
435                 let mut page_list_paddr = self.free_area[Self::order2index(order as u8)];
436                 let mut page_list = Self::read_page::<PageList<A>>(page_list_paddr);
437                 // 找第一个有空闲块的链表页。跳过空闲链表页。不进行回收的原因是担心出现死循环
438                 while page_list.entry_num == 0 {
439                     if page_list.next_page.is_null() {
440                         panic!(
441                             "buddy_free: page_list.entry_num == 0 && page_list.next_page.is_null()"
442                         );
443                     }
444                     page_list_paddr = page_list.next_page;
445                     page_list = Self::read_page(page_list_paddr);
446                 }
447 
448                 // 如果伙伴块不在第一个链表页,则把第一个链表中的某个空闲块替换到伙伴块的位置
449                 if page_list_paddr != buddy_entry_page_list_paddr {
450                     let entry: PhysAddr = unsafe {
451                         A::read(Self::entry_virt_addr(
452                             page_list_paddr,
453                             page_list.entry_num - 1,
454                         ))
455                     };
456                     // 把这个空闲块写入到伙伴块的位置
457                     unsafe {
458                         A::write(buddy_entry_virt_addr, entry);
459                     }
460                     // 设置刚才那个entry为空
461                     unsafe {
462                         A::write(
463                             Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1),
464                             PhysAddr::new(0),
465                         );
466                     }
467                     // 更新当前链表页的统计数据
468                     page_list.entry_num -= 1;
469                     Self::write_page(page_list_paddr, page_list);
470                 } else {
471                     // 伙伴块所在的链表页就是第一个链表页
472                     let last_entry: PhysAddr = unsafe {
473                         A::read(Self::entry_virt_addr(
474                             page_list_paddr,
475                             page_list.entry_num - 1,
476                         ))
477                     };
478 
479                     // 如果最后一个空闲块不是伙伴块,则把最后一个空闲块移动到伙伴块的位置
480                     // 否则后面的操作也将删除这个伙伴块
481                     if last_entry != buddy_addr {
482                         unsafe {
483                             A::write(buddy_entry_virt_addr, last_entry);
484                             A::write(
485                                 Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1),
486                                 PhysAddr::new(0),
487                             );
488                         }
489                     } else {
490                         unsafe {
491                             A::write(
492                                 Self::entry_virt_addr(page_list_paddr, page_list.entry_num - 1),
493                                 PhysAddr::new(0),
494                             );
495                         }
496                     }
497                     // 更新当前链表页的统计数据
498                     page_list.entry_num -= 1;
499                     Self::write_page(page_list_paddr, page_list);
500                 }
501             } else {
502                 assert!(
503                     page_list.entry_num <= Self::BUDDY_ENTRIES,
504                     "buddy_free: page_list.entry_num > Self::BUDDY_ENTRIES"
505                 );
506 
507                 // 当前第一个page_list没有空间了
508                 if first_page_list.entry_num == Self::BUDDY_ENTRIES {
509                     // 如果当前order是最小的,那么就把这个块当作新的page_list使用
510                     let new_page_list_addr = if order == MIN_ORDER {
511                         base
512                     } else {
513                         // 否则分配新的page_list
514                         // 请注意,分配之后,有可能当前的entry_num会减1(伙伴块分裂),造成出现整个链表为null的entry数量为Self::BUDDY_ENTRIES+1的情况
515                         // 但是不影响,我们在后面插入链表项的时候,会处理这种情况,检查链表中的第2个页是否有空位
516                         self.buddy_alloc(PageFrameCount::new(1))
517                             .expect("buddy_alloc failed: no enough memory")
518                             .0
519                     };
520 
521                     // 清空这个页面
522                     core::ptr::write_bytes(
523                         A::phys_2_virt(new_page_list_addr)
524                             .expect(
525                                 "Buddy free: failed to get virt address of [new_page_list_addr]",
526                             )
527                             .as_ptr::<u8>(),
528                         0,
529                         1 << order,
530                     );
531                     assert!(
532                         first_page_list_paddr == self.free_area[Self::order2index(order as u8)]
533                     );
534                     // 初始化新的page_list
535                     let new_page_list = PageList::new(0, first_page_list_paddr);
536                     Self::write_page(new_page_list_addr, new_page_list);
537                     self.free_area[Self::order2index(order as u8)] = new_page_list_addr;
538                 }
539 
540                 // 由于上面可能更新了第一个链表页,因此需要重新获取这个值
541                 let first_page_list_paddr = self.free_area[Self::order2index(order as u8)];
542                 let first_page_list: PageList<A> = Self::read_page(first_page_list_paddr);
543 
544                 // 检查第二个page_list是否有空位
545                 let second_page_list = if first_page_list.next_page.is_null() {
546                     None
547                 } else {
548                     Some(Self::read_page::<PageList<A>>(first_page_list.next_page))
549                 };
550 
551                 let (paddr, mut page_list) = if let Some(second) = second_page_list {
552                     // 第二个page_list有空位
553                     // 应当符合之前的假设:还有1个空位
554                     assert!(second.entry_num == Self::BUDDY_ENTRIES - 1);
555 
556                     (first_page_list.next_page, second)
557                 } else {
558                     // 在第一个page list中分配
559                     (first_page_list_paddr, first_page_list)
560                 };
561 
562                 // debug!("to write entry, page_list_base={paddr:?}, page_list.entry_num={}, value={base:?}", page_list.entry_num);
563                 assert!(page_list.entry_num < Self::BUDDY_ENTRIES);
564                 // 把要归还的块,写入到链表项中
565                 unsafe { A::write(Self::entry_virt_addr(paddr, page_list.entry_num), base) }
566                 page_list.entry_num += 1;
567                 Self::write_page(paddr, page_list);
568                 return;
569             }
570             base = min(base, buddy_addr);
571             order += 1;
572         }
573         // 走到这一步,order应该为MAX_ORDER-1
574         assert!(order == MAX_ORDER - 1);
575     }
576 }
577 
578 impl<A: MemoryManagementArch> FrameAllocator for BuddyAllocator<A> {
579     unsafe fn allocate(&mut self, count: PageFrameCount) -> Option<(PhysAddr, PageFrameCount)> {
580         return self.buddy_alloc(count);
581     }
582 
583     /// 释放一个块
584     ///
585     /// ## 参数
586     ///
587     /// - `base` - 块的起始地址
588     /// - `count` - 块的页数(必须是2的幂)
589     ///
590     /// ## Panic
591     ///
592     /// 如果count不是2的幂,会panic
593     unsafe fn free(&mut self, base: PhysAddr, count: PageFrameCount) {
594         // 要求count是2的幂
595         if unlikely(!count.data().is_power_of_two()) {
596             warn!("buddy free: count is not power of two");
597         }
598         let mut order = log2(count.data());
599         if count.data() & ((1 << order) - 1) != 0 {
600             order += 1;
601         }
602         let order = (order + MIN_ORDER) as u8;
603         // debug!("free: base={:?}, count={:?}", base, count);
604         self.buddy_free(base, order);
605     }
606 
607     unsafe fn usage(&self) -> PageFrameUsage {
608         let mut free_page_num: usize = 0;
609         for index in 0..(MAX_ORDER - MIN_ORDER) {
610             let mut pagelist: PageList<A> = Self::read_page(self.free_area[index]);
611             loop {
612                 free_page_num += pagelist.entry_num << index;
613                 if pagelist.next_page.is_null() {
614                     break;
615                 }
616                 pagelist = Self::read_page(pagelist.next_page);
617             }
618         }
619         let free = PageFrameCount::new(free_page_num);
620         PageFrameUsage::new(self.total - free, self.total)
621     }
622 }
623 
624 /// 一个用于计算整数的对数的函数,会向下取整。(由于内核不能进行浮点运算,因此需要这个函数)
625 fn log2(x: usize) -> usize {
626     let leading_zeros = x.leading_zeros() as usize;
627     let log2x = 63 - leading_zeros;
628     return log2x;
629 }
630